Files
PuzGame/src/game/Level.cpp
2025-03-29 22:27:34 +01:00

428 lines
16 KiB
C++

#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 <SDL3/SDL.h>
#include <bx/math.h>
#include <cstdint>
#include <tracy/Tracy.hpp>
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