#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 "bx/constants.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; texInfo[0] = textures[TextureHandle.TextureIdx].Info.width; texInfo[1] = textures[TextureHandle.TextureIdx].Info.height; } 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); 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 (!IsValid(PlayerOutsideViewCube)) { PlayerOutsideViewCube = Cubes.New(); Cubes.Get(PlayerOutsideViewCube).Setup(); } 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; float moveSpeed = player.MovementSpeed; float rotSpeed = player.MouseSensitivity; 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::mtxRotateXYZ(player.FreeflyCamTransform.Rotation.M, player.FreeflyXRot, player.FreeflyYRot, 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::mtxRotateXYZ(player.PlayerCamTransform.Rotation.M, player.WalkXRot, player.WalkYRot, 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(); auto& player = GetInstance().Player; bx::mtxProj(&player.Projection.M[0], 75.0f, float(shared.Window.WindowWidth) / float(shared.Window.WindowHeight), 0.1f, 1000.0f, bgfx::getCaps()->homogeneousDepth); bx::mtxInverse(&player.ProjectionInverse.M[0], &player.Projection.M[0]); bool isFreefly = player.CameraM == CameraMode::Freefly; Cubes.Get(PlayerOutsideViewCube).EData.Visible = isFreefly; if (isFreefly) { player.FreeflyCamTransform.UpdateMatrix(); bgfx::setViewTransform(viewId, player.FreeflyCamTransform.MI.M, &player.Projection.M[0]); } else { player.PlayerCamTransform.UpdateMatrix(); bgfx::setViewTransform(viewId, player.PlayerCamTransform.MI.M, &player.Projection.M[0]); } 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::UI; EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf"); } void Cube::Update() { EData.Transform.Position = GetInstance().Player.PlayerCamTransform.Position; EData.Transform.Rotation = GetInstance().Player.PlayerCamTransform.Rotation; EData.Transform.Scale = {0.2f, 0.2f, 0.2f}; } void TestEntity::Setup() { EData.MaterialHandle = EMaterial::Default; EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf"); } 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() { Level& level = GetInstance().GameLevel; auto& window = GetShared().Window; auto& staticCards = Puzzle::GetStaticPuzzleData().Cards; Transform& camTransform = GetInstance().Player.PlayerCamTransform; camTransform.UpdateMatrix(); Vec3 cameraPos = camTransform.GetPosition(); Transform boardTransform; boardTransform.Rotation = camTransform.Rotation; Vec3 fw = {camTransform.M.M[8], camTransform.M.M[9], camTransform.M.M[10]}; Vec3 pos = cameraPos; pos += fw * 10.0f; boardTransform.SetPosition(pos); bool clicked = GetMouseButtonPressedNow(MouseButton::Left); Vec2 mousePos = GetMousePos(); mousePos.x = mousePos.x / window.WindowWidth; mousePos.y = mousePos.y / window.WindowHeight; mousePos *= 2.0f; mousePos -= 1.0f; Vec4 mousePosView = {mousePos.x, -mousePos.y, 0.0f, 1.0f}; Vec4 mousePosCam = GetInstance().Player.ProjectionInverse.Mul(mousePosView); mousePosCam.x /= mousePosCam.w; mousePosCam.y /= mousePosCam.w; mousePosCam.z /= mousePosCam.w; mousePosCam.w = 1.0f; Vec4 mousePosWorld = camTransform.M.Mul(mousePosCam); auto& t = level.Tests.Get({0}); t.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf"); t.EData.Transform.Position = {mousePosWorld.x, mousePosWorld.y, mousePosWorld.z}; t.EData.Transform.Scale = {0.01f, 0.01f, 0.01f}; for (int8_t y = 0; y < Data.HeightTiles / Puzzle::Config::CardSize; ++y) { for (int8_t x = 0; x < Data.WidthTiles / Puzzle::Config::CardSize; ++x) { int32_t cardIdx = y * Puzzle::Config::MaxPuzzleSizeCards + x; Generated::PlacedPuzzleCard& card = Data.PlacedCards[cardIdx]; auto& tile = level.PuzzleTiles.Get(TileHandles[cardIdx]); auto& quad = level.UIQuads.Get(UIPlacedCards[cardIdx]); bool IsValid = Puzzle::IsValid(card.RefCard); tile.EData.Visible = true; tile.EData.ModelH = IsValid ? staticCards[card.RefCard.Idx].ModelHandle : staticCards[0].ModelHandle; 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.Visible = IsValid; quad.EData.TextureHandle = IsValid ? staticCards[card.RefCard.Idx].BoardTextureHandle : Generated::TextureHandle{}; quad.EData.Transform = boardTransform; quad.EData.Transform.TranslateLocal(Vec3{(float)card.Position.X, (float)card.Position.Y, 0.0f} * 3.0f); quad.EData.Transform.Scale = {1.0f, 1.0f, 1.0f}; // quad.EData.Transform.Scale = {0.1f, 0.1f, 0.1f}; quad.EData.Transform.Rotate(Vec3{bx::kPi * 0.5f, 0.0f, bx::kPi}); if (clicked && IsValid && x == 1 && y == 1) { quad.EData.Transform.UpdateMatrix(); // boardTransform.UpdateMatrix(); Vec4 posInQuad = quad.EData.Transform.MI.Mul(mousePosWorld); // Vec4 posInQuad = boardTransform.MI.Mul(mousePosWorld); // if (posInQuad.x >= -1.0f && posInQuad.x <= 1.0f && posInQuad.z >= -1.0f && posInQuad.z <= 1.0f) { LOG("---"); LOG("%.03f %.03f: Click", mousePos.x, mousePos.y); LOG("%.03f %.03f %.03f %.03f: Cam", mousePosCam.x, mousePosCam.y, mousePosCam.z, mousePosCam.w); LOG("%.03f %.03f %.03f %.03f: World", mousePosWorld.x, mousePosWorld.y, mousePosWorld.z, mousePosWorld.w); LOG("%.03f %.03f %.03f %.03f: Card (%u %u)", posInQuad.x, posInQuad.y, posInQuad.z, posInQuad.w, x, y); LOG("%.03f %.03f %.03f: Player", camTransform.Position.x, camTransform.Position.y, camTransform.Position.z); } } } } } } // namespace Game