diff --git a/Game/Entity/Entity.h b/Game/Entity/Entity.h index 6723252..62ede4f 100644 --- a/Game/Entity/Entity.h +++ b/Game/Entity/Entity.h @@ -23,6 +23,7 @@ struct Entity final const Juliet::Class* Kind; DerivedType Derived; float X, Y; + index_t MeshInstance = indexMax; }; template diff --git a/Game/game.cpp b/Game/game.cpp index a10456e..24abd02 100644 --- a/Game/game.cpp +++ b/Game/game.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include GameState* gGameState = nullptr; @@ -92,6 +93,10 @@ extern "C" JULIET_API void __cdecl GameUpdate(Juliet::GameData* params, [[maybe_ rock->Health = 100; Assert(door->Base != rock->Base); + MeshAssetID cubeAsset = GetCubePrimitiveMeshAssetID(); + MeshInstanceID meshInst = CreateMeshInstance(cubeAsset, 0, MatrixIdentity()); + rock->Base->MeshInstance = meshInst; + printf("Door is %s\n", door->IsOpened ? "Opened" : "Closed"); printf("Rock has %d health points\n", rock->Health); } @@ -106,6 +111,16 @@ extern "C" JULIET_API void __cdecl GameUpdate(Juliet::GameData* params, [[maybe_ { // update game } + + // Sync entity transforms to meshes + auto& manager = GetEntityManager(); + for (Entity& ent : manager.Entities) + { + if (ent.MeshInstance != static_cast(-1)) + { + SetMeshInstanceTransform(ent.MeshInstance, MatrixTranslation(ent.X, ent.Y, 0.0f)); + } + } if (gGameState->Mode == GameMode::Debug) { diff --git a/Juliet/include/Core/Common/CoreTypes.h b/Juliet/include/Core/Common/CoreTypes.h index 72e3738..c1c4e95 100644 --- a/Juliet/include/Core/Common/CoreTypes.h +++ b/Juliet/include/Core/Common/CoreTypes.h @@ -41,5 +41,7 @@ constexpr int16 int16Max = MaxValueOf(); constexpr int32 int32Max = MaxValueOf(); constexpr int64 int64Max = MaxValueOf(); +constexpr index_t indexMax = MaxValueOf(); + #define Kilobytes(value) value * 1024 #define Megabytes(value) Kilobytes(value) * 1024 diff --git a/Juliet/include/Engine/Asset.h b/Juliet/include/Engine/Asset.h index ea8e633..09950e6 100644 --- a/Juliet/include/Engine/Asset.h +++ b/Juliet/include/Engine/Asset.h @@ -5,5 +5,5 @@ namespace Juliet { - JULIET_API extern MeshID LoadMesh(String filename); + JULIET_API extern MeshAssetID LoadMesh(String filename); } diff --git a/Juliet/include/Engine/Engine.h b/Juliet/include/Engine/Engine.h index 8e3f337..473c58c 100644 --- a/Juliet/include/Engine/Engine.h +++ b/Juliet/include/Engine/Engine.h @@ -10,6 +10,7 @@ namespace Juliet { IApplication* Application = nullptr; Arena* PlatformArena = nullptr; + Arena* AssetArena = nullptr; }; void InitializeEngine(JulietInit_Flags flags); diff --git a/Juliet/include/Graphics/Mesh.h b/Juliet/include/Graphics/Mesh.h index 6528817..0f35410 100644 --- a/Juliet/include/Graphics/Mesh.h +++ b/Juliet/include/Graphics/Mesh.h @@ -2,7 +2,9 @@ #include #include +#include #include +#include #include namespace Juliet @@ -10,14 +12,29 @@ namespace Juliet struct Arena; struct Vertex; - struct Mesh + using MeshAssetID = index_t; + using MaterialAssetID = index_t; + using MeshInstanceID = index_t; + + struct MeshAsset { + String Name; size_t VertexCount; size_t IndexCount; index_t VertexOffset; index_t IndexOffset; + }; - Matrix Transform = MatrixIdentity(); + struct MaterialAsset + { + Vector4 AlbedoColor = {1.0f, 1.0f, 1.0f, 1.0f}; + }; + + struct MeshInstance + { + MeshAssetID MeshAsset; + MaterialAssetID MaterialAsset; + Matrix Transform = MatrixIdentity(); }; } // namespace Juliet diff --git a/Juliet/include/Graphics/MeshRenderer.h b/Juliet/include/Graphics/MeshRenderer.h index 9dfc1b0..fb845c6 100644 --- a/Juliet/include/Graphics/MeshRenderer.h +++ b/Juliet/include/Graphics/MeshRenderer.h @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace Juliet @@ -17,9 +18,6 @@ namespace Juliet struct Window; struct GraphicsPipeline; struct GraphicsDevice; - struct Mesh; - - using MeshID = index_t; using LightID = index_t; constexpr size_t kGeometryPage = Megabytes(64); @@ -29,26 +27,7 @@ namespace Juliet constexpr size_t kDefaultIndexCount = 16'000'000; // Fit less than one index page constexpr size_t kDefaultLightCount = 1024; - struct MeshRenderer - { - VectorArena Meshes; - VectorArena Vertices; - VectorArena Indices; - VectorArena PointLights; - - GraphicsBuffer* VertexBuffer; - GraphicsBuffer* IndexBuffer; - GraphicsTransferBuffer* StreamCopyBuffer; - GraphicsTransferBuffer* LoadCopyBuffer; - - GraphicsBuffer* LightsBuffer; - PointLight* MappedLights; - - GraphicsDevice* Device; - GraphicsPipeline* Pipeline; - }; - - JULIET_API void InitializeMeshRenderer(NonNullPtr arena); + JULIET_API void InitializeMeshRenderer(NonNullPtr assetArena, NonNullPtr instanceArena); [[nodiscard]] JULIET_API bool InitializeMeshRendererGraphics(NonNullPtr device, NonNullPtr window); JULIET_API void ShutdownMeshRendererGraphics(); JULIET_API void ShutdownMeshRenderer(); @@ -63,13 +42,15 @@ namespace Juliet JULIET_API void SetPointLightIntensity(LightID id, float intensity); JULIET_API void ClearPointLights(); - // Primitives - JULIET_API MeshID GetCubePrimitiveMeshID(); + // Assets & Instances + [[nodiscard]] JULIET_API MeshAssetID GetOrCreateMeshAsset(String name); + [[nodiscard]] JULIET_API MeshInstanceID CreateMeshInstance(MeshAssetID meshAsset, MaterialAssetID materialAsset, const Matrix& transform); + JULIET_API void SetMeshInstanceTransform(MeshInstanceID id, const Matrix& transform); - // Utils - [[nodiscard]] JULIET_API MeshID CreateCubePrimitive(); - [[nodiscard]] JULIET_API MeshID CreateQuadPrimitive(); - JULIET_API void SetMeshTransform(MeshID id, const Matrix& transform); + // Primitives + JULIET_API MeshAssetID GetCubePrimitiveMeshAssetID(); + JULIET_API MeshAssetID GetQuadPrimitiveMeshAssetID(); + JULIET_API MeshAssetID GetSpherePrimitiveMeshAssetID(); #if ALLOW_SHADER_HOT_RELOAD JULIET_API void ReloadMeshRendererShaders(); diff --git a/Juliet/src/Engine/Asset.cpp b/Juliet/src/Engine/Asset.cpp index 86de235..f3867a3 100644 --- a/Juliet/src/Engine/Asset.cpp +++ b/Juliet/src/Engine/Asset.cpp @@ -2,5 +2,5 @@ namespace Juliet { - MeshID LoadMesh(String filename) {} + MeshAssetID LoadMesh([[maybe_unused]] String filename) { return 0; } } // namespace Juliet diff --git a/Juliet/src/Engine/Engine.cpp b/Juliet/src/Engine/Engine.cpp index b5e11b8..5c82091 100644 --- a/Juliet/src/Engine/Engine.cpp +++ b/Juliet/src/Engine/Engine.cpp @@ -153,6 +153,8 @@ namespace Juliet { EngineInstance.PlatformArena = ArenaAllocate({ .ReserveSize = Megabytes(128) } JULIET_DEBUG_PARAM("Platform Arena")); + EngineInstance.AssetArena = + ArenaAllocate({ .ReserveSize = Megabytes(256) } JULIET_DEBUG_PARAM("Asset Arena")); InitializeLogManager(); @@ -172,6 +174,7 @@ namespace Juliet ShutdownFilesystem(); ShutdownLogManager(); + ArenaRelease(EngineInstance.AssetArena); ArenaRelease(EngineInstance.PlatformArena); } @@ -179,7 +182,7 @@ namespace Juliet { EngineInstance.Application = &app; - InitializeMeshRenderer(EngineInstance.PlatformArena); + InitializeMeshRenderer(EngineInstance.AssetArena, EngineInstance.PlatformArena); EngineInstance.Application->Init(EngineInstance.PlatformArena); diff --git a/Juliet/src/Graphics/MeshRenderer.cpp b/Juliet/src/Graphics/MeshRenderer.cpp index 9aac5e6..8863ec7 100644 --- a/Juliet/src/Graphics/MeshRenderer.cpp +++ b/Juliet/src/Graphics/MeshRenderer.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,10 @@ namespace Juliet GraphicsBuffer* TransformsBuffer = nullptr; GraphicsTransferBuffer* LoadCopyBuffer = nullptr; - VectorArena Meshes; + VectorArena MeshAssets; + VectorArena MaterialAssets; + VectorArena MeshInstances; + VectorArena Vertices; VectorArena Indices; VectorArena PointLights; @@ -33,18 +37,28 @@ namespace Juliet MeshRendererState g_MeshRenderer; - MeshID CubePrimitiveMeshID = 0; + MeshAssetID CubePrimitiveMeshAssetID = static_cast(-1); + MeshAssetID QuadPrimitiveMeshAssetID = static_cast(-1); + MeshAssetID SpherePrimitiveMeshAssetID = static_cast(-1); } // namespace - void InitializeMeshRenderer(NonNullPtr arena) + MeshAssetID CreateCubePrimitive(); + MeshAssetID CreateQuadPrimitive(); + MeshAssetID CreateSpherePrimitive(); + + void InitializeMeshRenderer(NonNullPtr assetArena, NonNullPtr instanceArena) { - g_MeshRenderer.Meshes.Create(arena JULIET_DEBUG_PARAM("Meshes")); - g_MeshRenderer.Vertices.Create(arena JULIET_DEBUG_PARAM("Vertices")); - g_MeshRenderer.Indices.Create(arena JULIET_DEBUG_PARAM("Indices")); - g_MeshRenderer.PointLights.Create(arena JULIET_DEBUG_PARAM("PointLights")); + g_MeshRenderer.MeshAssets.Create(assetArena JULIET_DEBUG_PARAM("MeshAssets")); + g_MeshRenderer.MaterialAssets.Create(assetArena JULIET_DEBUG_PARAM("MaterialAssets")); + g_MeshRenderer.MeshInstances.Create(instanceArena JULIET_DEBUG_PARAM("MeshInstances")); + g_MeshRenderer.Vertices.Create(assetArena JULIET_DEBUG_PARAM("Vertices")); + g_MeshRenderer.Indices.Create(assetArena JULIET_DEBUG_PARAM("Indices")); + g_MeshRenderer.PointLights.Create(instanceArena JULIET_DEBUG_PARAM("PointLights")); // Create primitives - CubePrimitiveMeshID = AddCube(); + CubePrimitiveMeshAssetID = CreateCubePrimitive(); + QuadPrimitiveMeshAssetID = CreateQuadPrimitive(); + // SpherePrimitiveMeshAssetID = CreateSpherePrimitive(); // Will implement later } bool InitializeMeshRendererGraphics(NonNullPtr device, NonNullPtr window) @@ -181,13 +195,15 @@ namespace Juliet { g_MeshRenderer.Indices.Destroy(); g_MeshRenderer.Vertices.Destroy(); - g_MeshRenderer.Meshes.Destroy(); + g_MeshRenderer.MeshInstances.Destroy(); + g_MeshRenderer.MaterialAssets.Destroy(); + g_MeshRenderer.MeshAssets.Destroy(); g_MeshRenderer.PointLights.Destroy(); } void LoadMeshesOnGPU(NonNullPtr cmdList) { - if (g_MeshRenderer.Meshes.IsEmpty()) + if (g_MeshRenderer.MeshAssets.IsEmpty()) { return; } @@ -273,24 +289,29 @@ namespace Juliet SetIndexBuffer(cmdList, g_MeshRenderer.IndexBuffer, IndexFormat::UInt16, g_MeshRenderer.Indices.Count, 0); uint32 meshIndex = 0; - for (Mesh& mesh : g_MeshRenderer.Meshes) + for (MeshInstance& instance : g_MeshRenderer.MeshInstances) { if (g_MeshRenderer.MappedTransforms) { - g_MeshRenderer.MappedTransforms[meshIndex] = mesh.Transform; + g_MeshRenderer.MappedTransforms[meshIndex] = instance.Transform; } + MeshAsset& meshAsset = g_MeshRenderer.MeshAssets.Data[instance.MeshAsset]; + // If we used MaterialAsset, we would pass properties here. + // For now, the shader doesn't read material color, or maybe we can pass it if PushData supports it. + // pushData has GlobalLightColor, but no individual mesh color yet. We can just keep it simple. + pushData.MeshIndex = meshIndex; - pushData.TextureIndex = 0; - pushData.VertexOffset = static_cast(mesh.VertexOffset); + pushData.TextureIndex = 0; // Would be instance.MaterialAsset.BaseColorTexture later + pushData.VertexOffset = static_cast(meshAsset.VertexOffset); pushData.Padding = 0; pushData.Scale[0] = 1.0f; pushData.Scale[1] = 1.0f; pushData.Translate[0] = 0.0f; pushData.Translate[1] = 0.0f; SetPushConstants(cmdList, ShaderStage::Vertex, 0, sizeof(pushData) / 4, &pushData); - DrawIndexedPrimitives(pass, static_cast(mesh.IndexCount), 1, static_cast(mesh.IndexOffset), - static_cast(mesh.VertexOffset), 0); + DrawIndexedPrimitives(pass, static_cast(meshAsset.IndexCount), 1, + static_cast(meshAsset.IndexOffset), static_cast(meshAsset.VertexOffset), 0); meshIndex++; } } @@ -352,14 +373,58 @@ namespace Juliet g_MeshRenderer.PointLights.Clear(); } - MeshID GetCubePrimitiveMeshID() + MeshAssetID GetCubePrimitiveMeshAssetID() { - return CubePrimitiveMeshID; + Assert(CubePrimitiveMeshAssetID != static_cast(-1)); + return CubePrimitiveMeshAssetID; } - MeshID CreateCubePrimitive() + MeshAssetID GetQuadPrimitiveMeshAssetID() { - Mesh result = {}; + Assert(QuadPrimitiveMeshAssetID != static_cast(-1)); + return QuadPrimitiveMeshAssetID; + } + + MeshAssetID GetSpherePrimitiveMeshAssetID() + { + Assert(SpherePrimitiveMeshAssetID != static_cast(-1)); + return SpherePrimitiveMeshAssetID; + } + + MeshAssetID GetOrCreateMeshAsset(String name) + { + for (index_t i = 0; i < g_MeshRenderer.MeshAssets.Count; ++i) + { + if (StringCompare(g_MeshRenderer.MeshAssets.Data[i].Name, name) == 0) + { + return i; + } + } + + return LoadMesh(name); + } + + MeshInstanceID CreateMeshInstance(MeshAssetID meshAsset, MaterialAssetID materialAsset, const Matrix& transform) + { + MeshInstance instance = {}; + instance.MeshAsset = meshAsset; + instance.MaterialAsset = materialAsset; + instance.Transform = transform; + MeshInstanceID id = g_MeshRenderer.MeshInstances.Count; + g_MeshRenderer.MeshInstances.PushBack(instance); + return id; + } + + void SetMeshInstanceTransform(MeshInstanceID id, const Matrix& transform) + { + Assert(id < static_cast(g_MeshRenderer.MeshInstances.Count)); + g_MeshRenderer.MeshInstances.Data[id].Transform = transform; + } + + MeshAssetID CreateCubePrimitive() + { + MeshAsset result = {}; + result.Name = WrapString("Cube"); constexpr Vertex vertexData[] = { // Front Face (Z = -0.5f) — Red { { -0.5f, 0.5f, -0.5f }, { 0.0f, 0.0f, -1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, @@ -413,47 +478,50 @@ namespace Juliet result.IndexOffset = g_MeshRenderer.Indices.Count; g_MeshRenderer.Indices.PushBack(indices, indexCubeCount); - MeshID id = g_MeshRenderer.Meshes.Count; - g_MeshRenderer.Meshes.PushBack(result); + MeshAssetID id = g_MeshRenderer.MeshAssets.Count; + g_MeshRenderer.MeshAssets.PushBack(result); return id; } - MeshID CreateQuadPrimitive() + MeshAssetID CreateQuadPrimitive() { - // Mesh result = {}; + MeshAsset result = {}; + result.Name = WrapString("Quad"); - // // Using the exact 6 vertices from the working triangles! - // constexpr Vertex vertexData[] = { - // // Triangle 1 (Clockwise) - // { { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, // 0: Red - // { { 0.0f, 0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, // 1: Green - // { { 0.5f, -0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }, // 2: Blue - // - // // Triangle 2 (Clockwise) - // { { -0.5f, 0.5f, 0.0f }, { 1.0f, 1.0f, 0.0f, 1.0f } }, // 3: Yellow - // { { 0.0f, 0.8f, 0.0f }, { 0.0f, 1.0f, 1.0f, 1.0f } }, // 4: Cyan - // { { 0.5f, 0.5f, 0.0f }, { 1.0f, 0.0f, 1.0f, 1.0f } } // 5: Magenta - // }; - // constexpr size_t triVertexCount = ArraySize(vertexData); - // result.VertexCount = triVertexCount; - // result.Vertices = ArenaPushArray(arena.Get(), triVertexCount); - // MemCopy(result.Vertices, vertexData, sizeof(Vertex) * triVertexCount); - // - // // Just the 6 indices for the two triangles - // constexpr uint16 indices[] = { 0, 1, 2, 3, 4, 5 }; - // constexpr size_t triIndexCount = ArraySize(indices); - // result.IndexCount = triIndexCount; - // result.Indices = ArenaPushArray(arena.Get(), triIndexCount JULIET_DEBUG_PARAM("Indices")); - // MemCopy(result.Indices, indices, sizeof(uint16) * triIndexCount); - // - // g_MeshRenderer.Meshes.PushBack(std::move(result)); - return g_MeshRenderer.Meshes.Count - 1; + constexpr Vertex vertexData[] = { + // Triangle 1 (Clockwise) + { { -0.5f, -0.5f, 0.0f }, { 0.0f, 0.0f, -1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, // 0 + { { 0.0f, 0.5f, 0.0f }, { 0.0f, 0.0f, -1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, // 1 + { { 0.5f, -0.5f, 0.0f }, { 0.0f, 0.0f, -1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, // 2 + + // Triangle 2 (Clockwise) + { { -0.5f, 0.5f, 0.0f }, { 0.0f, 0.0f, -1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, // 3 + { { 0.0f, 0.8f, 0.0f }, { 0.0f, 0.0f, -1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, // 4 + { { 0.5f, 0.5f, 0.0f }, { 0.0f, 0.0f, -1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } } // 5 + }; + constexpr size_t triVertexCount = ArraySize(vertexData); + result.VertexCount = triVertexCount; + result.VertexOffset = g_MeshRenderer.Vertices.Count; + g_MeshRenderer.Vertices.PushBack(vertexData, triVertexCount); + + constexpr uint16 indices[] = { 0, 1, 2, 3, 4, 5 }; + constexpr size_t triIndexCount = ArraySize(indices); + result.IndexCount = triIndexCount; + result.IndexOffset = g_MeshRenderer.Indices.Count; + g_MeshRenderer.Indices.PushBack(indices, triIndexCount); + + MeshAssetID id = g_MeshRenderer.MeshAssets.Count; + g_MeshRenderer.MeshAssets.PushBack(result); + return id; } - void SetMeshTransform(MeshID id, const Matrix& transform) + // Sphere primitive implementation omitted for brevity, returning Cube for now if called. + MeshAssetID CreateSpherePrimitive() { - Assert(id < static_cast(g_MeshRenderer.Meshes.Count)); - g_MeshRenderer.Meshes.Data[id].Transform = transform; + // Just returning cube for now until proper sphere generation is written + MeshAssetID id = CreateCubePrimitive(); + g_MeshRenderer.MeshAssets.Data[id].Name = WrapString("Sphere"); // Rename just to pass cache + return id; } #if ALLOW_SHADER_HOT_RELOAD diff --git a/JulietApp/main.cpp b/JulietApp/main.cpp index 8df3728..4caa122 100644 --- a/JulietApp/main.cpp +++ b/JulietApp/main.cpp @@ -152,13 +152,13 @@ void JulietApplication::Init(NonNullPtr) { for (int col = 0; col < kGridSize; ++col) { - MeshID cube = AddCube(); + MeshAssetID cubeAsset = GetCubePrimitiveMeshAssetID(); float x = static_cast(col) * kSpacing - kOffset; float y = static_cast(row) * kSpacing - kOffset; float seed = static_cast(row * kGridSize + col); Matrix rotation = MatrixRotation(seed * 0.73f, seed * 1.17f, seed * 0.53f); - SetMeshTransform(cube, MatrixTranslation(x, y, 0.0f) * rotation); + CreateMeshInstance(cubeAsset, 0, MatrixTranslation(x, y, 0.0f) * rotation); } }