#include "../gen/Def.h" #include "Gen.h" #include "Global.h" #include "Input.h" #include "Instance.h" #include "Level.h" #include "Log.h" #include "Puzzle.h" #include "rendering/Rendering.h" #include "SDL3/SDL_mouse.h" #include "bgfx/bgfx.h" #include "bx/error.h" #include "bx/file.h" #include "bx/filepath.h" #include "bx/string.h" #include "imgui.h" #include #include #include #include namespace Game { void EntityRenderData::Render(const Model* models, const Material* materials, const Texture* textures) { if (models == nullptr || materials == nullptr || textures == nullptr) return; if (!Generated::IsValid(ModelH) || MaterialHandle == EMaterial::UNDEFINED) return; if (!Visible) return; auto& rendering = GameRendering::Get(); Transform.UpdateMatrix(); bgfx::setTransform(Transform.M.M); const Model& currentModel = models[ModelH.ModelIdx]; const Material& currentMaterial = materials[(uint16_t)MaterialHandle]; if (!isValid(currentModel.IndexBuffer) || !isValid(currentModel.VertexBuffer)) return; if (!isValid(currentMaterial.Shader)) return; bgfx::setVertexBuffer(0, currentModel.VertexBuffer); bgfx::setIndexBuffer(currentModel.IndexBuffer); bgfx::setState(currentMaterial.State); float timeValues[4]{0.0f}; timeValues[0] = GetInstance().Time.Now; float texInfo[4]{0.0f}; texInfo[0] = textures[0].Info.width; texInfo[1] = textures[0].Info.height; texInfo[2] = rendering.DitherTextures.DitherTexWH; texInfo[3] = rendering.DitherTextures.DitherTexDepth; bgfx::UniformHandle sampler = rendering.DefaultSampler; bgfx::TextureHandle tex = rendering.Textures[0].RenderHandle; if (IsValid(TextureHandle)) { sampler = textures[TextureHandle.TextureIdx].SamplerHandle; tex = textures[TextureHandle.TextureIdx].RenderHandle; } bgfx::setTexture(0, sampler, tex); bgfx::setTexture(1, rendering.DitherTextures.DitherSampler, rendering.DitherTextures.FinalTex); bgfx::setTexture(2, rendering.DitherTextures.RampSampler, rendering.DitherTextures.RampTex); bgfx::setUniform(currentMaterial.Uniforms[Material::UTime], timeValues); bgfx::setUniform(currentMaterial.Uniforms[Material::UDotColor], &TestColor.x); bgfx::setUniform(currentMaterial.Uniforms[Material::UTexInfo], texInfo); bgfx::setUniform(currentMaterial.Uniforms[Material::UBaseColor], &BaseColor.x); bgfx::submit(currentMaterial.ViewID, currentMaterial.Shader); } namespace { void UpdatePlayerInputMode() { bool IsGaming = GetInstance().Player.InputM == InputMode::Game; SDL_SetWindowRelativeMouseMode(GetShared().Window.SDLWindow, IsGaming); auto& IO = ImGui::GetIO(); IO.ConfigFlags = FlagBool(IO.ConfigFlags, ImGuiConfigFlags_NoMouse | ImGuiConfigFlags_NoKeyboard, IsGaming); GameRendering::Get().UIVisible = IsGaming ? UIVisibilityState::Game : UIVisibilityState::Debug; } } // namespace void Level::Setup(GameData& data) { LOG("Level setup"); void* storagePtr = data.EntityStorage; bool needReset = false; needReset |= Cubes.Setup(storagePtr, needReset); needReset |= Tests.Setup(storagePtr, needReset); needReset |= PuzzleTiles.Setup(storagePtr, needReset); needReset |= UIQuads.Setup(storagePtr, needReset); Tests.IsEnabled = false; Cubes.IsEnabled = false; Puzzle::Setup(); bx::Error err; bx::DirectoryReader dirIter; bx::FileInfo info; bx::FilePath puzzleDirPath{Puzzle::PuzzleFileDir}; if (dirIter.open(puzzleDirPath, &err)) { while (true) { int32_t readCount = dirIter.read(&info, sizeof(info), &err); if (readCount == 0) break; if (err.isOk()) { if (info.type != bx::FileType::File) continue; bx::StringView pathEnd = info.filePath.getExt(); if (bx::strCmpI(pathEnd, ".pzl") != 0) continue; bx::FilePath fullPath = puzzleDirPath; fullPath.join(info.filePath); LOG("Loading %s", fullPath.getCPtr()); Generated::Deserializer ser; Generated::PuzzleData dataBuf; if (ser.Init(fullPath, "PZZL") && ser.ReadT(dataBuf)) { if (dataBuf.ID >= BX_COUNTOF(Puzzles)) { LOG_ERROR("Puzzle ID out of bounds: %u", dataBuf.ID); ser.Finish(); continue; } Puzzles[dataBuf.ID].Data = dataBuf; Puzzles[dataBuf.ID].Setup(); } else { LOG_WARN("Failed to load puzzle!"); } ser.Finish(); } else { LOG_ERROR("Failed parsing file name at %s:\n%s", puzzleDirPath.getCPtr(), err.getMessage()); break; } } } else { LOG_ERROR("Failed to open puzzle dir at %s:\n%s", puzzleDirPath.getCPtr(), err.getMessage().getCPtr()); } if (Cubes.Count == 0) { for (uint32_t yy = 0; yy < 11; ++yy) { for (uint32_t xx = 0; xx < 11; ++xx) { Cube& c = Cubes.Get(Cubes.New()); c.TestX = xx; c.TestY = yy; c.Setup(); } } Cubes.New(); // Floor } if (Tests.Count == 0) { Tests.Get(Tests.New()).Setup(); } UIQuads.Count = 0; PuzzleTiles.Count = 0; for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i) { if (Puzzles[i].Data.ID != UINT16_MAX) { Puzzles[i].Setup(); } } LOG("Tiles: %u", PuzzleTiles.Count); UpdatePlayerInputMode(); } void Level::Update() { ZoneScopedN("Level update"); START_PERF(); PlayerData& player = GetInstance().Player; // Input float delta = GetInstance().Time.Delta; constexpr float moveSpeed = 10.0f; constexpr float rotSpeed = 1.6f; float forwardInput = (GetKey(ScanCode::W) ? 1.0f : 0.0f) + (GetKey(ScanCode::S) ? -1.0f : 0.0f); float rightInput = (GetKey(ScanCode::D) ? 1.0f : 0.0f) + (GetKey(ScanCode::A) ? -1.0f : 0.0f); bx::Vec3 moveInput = bx::Vec3{rightInput, forwardInput, 0.0f}; moveInput = bx::normalize(moveInput); bx::Vec3 inputVec = {moveInput.x * delta * moveSpeed, 0.0f, moveInput.y * delta * moveSpeed}; Vec2 mouseMovement = GetMouseMovement(); bx::Vec3 rotInput = {mouseMovement.y * delta * rotSpeed, mouseMovement.x * delta * rotSpeed, 0.0f}; if (GetKeyPressedNow(ScanCode::F1)) { player.CameraM = player.CameraM == CameraMode::Walk ? CameraMode::Freefly : CameraMode::Walk; } if (GetKeyPressedNow(ScanCode::F2)) { if (player.InputM == InputMode::Game) { player.InputM = InputMode::UI; } else { player.InputM = InputMode::Game; } UpdatePlayerInputMode(); } if (player.CameraM == CameraMode::Freefly) { if (GetMouseButton(MouseButton::Left)) { player.FreeflyXRot += rotInput.x; player.FreeflyYRot += rotInput.y; bx::mtxRotateY(player.FreeflyCamTransform.Rotation.M, player.FreeflyYRot); player.FreeflyCamTransform.RotateLocal({player.FreeflyXRot, 0.0f, 0.0f}); } player.FreeflyCamTransform.TranslateLocal({0.0f, 0.0f, -inputVec.z}); player.FreeflyCamTransform.TranslateLocal({-inputVec.x, 0.0f, 0.0f}); } else if (player.CameraM == CameraMode::Walk) { player.PlayerCamTransform.TranslateLocal({0.0f, 0.0f, -inputVec.z}); player.PlayerCamTransform.TranslateLocal({-inputVec.x, 0.0f, 0.0f}); player.PlayerCamTransform.Position.y = -3.0f; player.WalkXRot += rotInput.x; player.WalkYRot += rotInput.y; bx::mtxRotateY(player.PlayerCamTransform.Rotation.M, player.WalkYRot); player.PlayerCamTransform.RotateLocal({player.WalkXRot, 0.0f, 0.0f}); } // Cubes for (uint16_t i = 0; i < Cubes.Count; ++i) { Cubes.Get({i}).Update(); } // Puzzle tiles for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i) { Puzzles[i].Update(); } END_PERF(GetShared().Window.PerfCounters, PerfCounterType::GameLevelUpdate, GetShared().Window.FrameCounter); } void Level::Render(uint16_t viewId, const Model* models, const Material* materials, const Texture* textures) { ZoneScopedN("Level Render"); auto& shared = GetShared(); float proj[16]; bx::mtxProj(proj, 75.0f, float(shared.Window.WindowWidth) / float(shared.Window.WindowHeight), 0.1f, 1000.0f, bgfx::getCaps()->homogeneousDepth); auto& player = GetInstance().Player; if (player.CameraM == CameraMode::Freefly) { player.FreeflyCamTransform.UpdateMatrixForCam(); bgfx::setViewTransform(viewId, player.FreeflyCamTransform.M.M, proj); } else { player.PlayerCamTransform.UpdateMatrixForCam(); bgfx::setViewTransform(viewId, player.PlayerCamTransform.M.M, proj); } bgfx::touch(viewId); Cubes.Render(models, materials, textures); Tests.Render(models, materials, textures); PuzzleTiles.Render(models, materials, textures); UIQuads.Render(models, materials, textures); } void Cube::Setup() { EData.MaterialHandle = EMaterial::Default; EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf"); } void Cube::Update() { if (TestX >= 0 && TestY >= 0) { double globalTime = GetInstance().Time.Now; double time = TestY <= 5 ? globalTime * 1.0f : 0.0f; float scale = 1.0f + TestX * 0.4f; EData.Transform.Position = bx::Vec3{TestX * 2.0f, TestY * 2.0f, 0.0f}; EData.Transform.Scale = {scale, scale, scale}; } else { EData.Transform.Position = {0.0f, -1.0f, 0.0f}; EData.Transform.Scale = {100.0f, 1.0f, 100.0f}; } } void TestEntity::Setup() { EData.MaterialHandle = EMaterial::Default; EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/zurg.gltf"); EData.Transform.Position = {0.0f, 0.0f, 0.0f}; } void WorldPuzzle::Setup() { auto& level = GetInstance().GameLevel; for (int32_t i = 0; i < Puzzle::Config::MaxCardsInPuzzle; ++i) { TileHandles[i] = level.PuzzleTiles.New(); auto& tile = level.PuzzleTiles.Get(TileHandles[i]); tile.EData.MaterialHandle = EMaterial::Default; tile.EData.Visible = false; UIPlacedCards[i] = level.UIQuads.New(); auto& quad = level.UIQuads.Get(UIPlacedCards[i]); quad.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/plane.glb"); quad.EData.MaterialHandle = EMaterial::UI; quad.EData.Visible = false; } IsSetup = true; LOG("finished setup!"); } void WorldPuzzle::Update() { Transform& camTransform = GetInstance().Player.PlayerCamTransform; camTransform.UpdateMatrixForCam(); Vec3 cameraPos = camTransform.GetPosition() * -1; Level& level = GetInstance().GameLevel; auto& staticCards = Puzzle::GetStaticPuzzleData().Cards; Transform boardTransform; boardTransform.Rotation = camTransform.Rotation.Inverse(); Vec3 fw = {camTransform.M.M[2], camTransform.M.M[6], camTransform.M.M[10]}; Vec3 pos = camTransform.GetPosition() * -1; pos += fw; boardTransform.SetPosition(pos); for (int8_t y = 0; y < Data.HeightTiles / Puzzle::Config::CardSize; ++y) { for (int8_t x = 0; x < Data.WidthTiles / Puzzle::Config::CardSize; ++x) { Generated::PlacedPuzzleCard& card = Data.PlacedCards[y * Puzzle::Config::MaxPuzzleSizeCards + x]; auto& tile = level.PuzzleTiles.Get(TileHandles[y * Puzzle::Config::MaxPuzzleSizeCards + x]); auto& quad = level.UIQuads.Get(UIPlacedCards[y * Puzzle::Config::MaxPuzzleSizeCards + x]); bool IsValid = Puzzle::IsValid(card.RefCard); tile.EData.Visible = true; quad.EData.Visible = IsValid; tile.EData.ModelH = IsValid ? staticCards[card.RefCard.Idx].ModelHandle : staticCards[0].ModelHandle; quad.EData.TextureHandle = IsValid ? staticCards[card.RefCard.Idx].BoardTextureHandle : Generated::TextureHandle{}; Vec3 cardPos = { (float)card.Position.X * Puzzle::Config::CardScaleWorld, -5.0f, (float)card.Position.Y * Puzzle::Config::CardScaleWorld, }; if (!IsValid) { cardPos = {x * Puzzle::Config::CardScaleWorld, -5.0f, y * Puzzle::Config::CardScaleWorld}; } tile.EData.Transform.SetPosition(cardPos); bx::mtxRotateY(tile.EData.Transform.Rotation.M, card.Rotation * bx::kPi * 0.5f); quad.EData.Transform = boardTransform; quad.EData.Transform.TranslateLocal(Vec3{(float)card.Position.X, (float)card.Position.Y, 0.0f} * 0.21f); quad.EData.Transform.Scale = {0.1f, 0.1f, 0.1f}; quad.EData.Transform.Rotate(Vec3{bx::kPi * 0.5f, 0.0f, 0.0f}); } } } } // namespace Game