From 50ed8b4028410087ce15c56995d8fe56263046d4 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sat, 27 Aug 2022 03:07:48 +0200 Subject: [PATCH] Add initial support for skeletal entities / components --- examples/DeferredShading/main.cpp | 10 +- examples/GraphicsTest/main.cpp | 4 +- examples/PhysicallyBasedRendering/main.cpp | 2 +- examples/Showcase/main.cpp | 126 +++++++++++------- include/Nazara/Graphics.hpp | 1 + .../Graphics/Components/GraphicsComponent.hpp | 2 +- .../Graphics/Components/GraphicsComponent.inl | 4 +- .../Nazara/Graphics/ForwardFramePipeline.hpp | 10 +- include/Nazara/Graphics/FramePipeline.hpp | 10 +- include/Nazara/Graphics/FramePipelinePass.hpp | 2 + .../Nazara/Graphics/InstancedRenderable.hpp | 3 +- .../Nazara/Graphics/LinearSlicedSprite.hpp | 2 +- include/Nazara/Graphics/Model.hpp | 2 +- .../Nazara/Graphics/RenderQueueRegistry.hpp | 4 + .../Nazara/Graphics/RenderQueueRegistry.inl | 13 ++ include/Nazara/Graphics/RenderSubmesh.hpp | 5 +- include/Nazara/Graphics/RenderSubmesh.inl | 19 ++- include/Nazara/Graphics/SkeletonInstance.hpp | 54 ++++++++ include/Nazara/Graphics/SkeletonInstance.inl | 26 ++++ include/Nazara/Graphics/SlicedSprite.hpp | 2 +- include/Nazara/Graphics/Sprite.hpp | 2 +- .../Nazara/Graphics/Systems/RenderSystem.hpp | 30 ++++- include/Nazara/Graphics/TextSprite.hpp | 2 +- include/Nazara/Graphics/WorldInstance.hpp | 1 - .../Physics2D/Systems/Physics2DSystem.hpp | 2 +- .../Physics3D/Systems/Physics3DSystem.hpp | 2 +- include/Nazara/Utility/Components.hpp | 3 + .../Components/SharedSkeletonComponent.hpp | 47 +++++++ .../Components/SharedSkeletonComponent.inl | 16 +++ .../Utility/Components/SkeletonComponent.hpp | 36 +++++ .../Utility/Components/SkeletonComponent.inl | 12 ++ .../Components/SkeletonComponentBase.hpp | 38 ++++++ .../Components/SkeletonComponentBase.inl | 21 +++ include/Nazara/Utility/Systems.hpp | 1 + .../Nazara/Utility/Systems/SkeletonSystem.hpp | 41 ++++++ .../Nazara/Utility/Systems/SkeletonSystem.inl | 12 ++ src/Nazara/Graphics/DepthPipelinePass.cpp | 2 +- src/Nazara/Graphics/ForwardFramePipeline.cpp | 60 ++++++++- src/Nazara/Graphics/ForwardPipelinePass.cpp | 2 +- src/Nazara/Graphics/LinearSlicedSprite.cpp | 2 +- src/Nazara/Graphics/Model.cpp | 4 +- .../Graphics/PredefinedShaderStructs.cpp | 2 +- src/Nazara/Graphics/SkeletonInstance.cpp | 72 ++++++++++ src/Nazara/Graphics/SlicedSprite.cpp | 2 +- src/Nazara/Graphics/Sprite.cpp | 2 +- src/Nazara/Graphics/SubmeshRenderer.cpp | 23 ++-- src/Nazara/Graphics/Systems/RenderSystem.cpp | 114 +++++++++++++--- src/Nazara/Graphics/TextSprite.cpp | 2 +- .../Physics2D/Systems/Physics2DSystem.cpp | 2 - .../Physics3D/Systems/Physics3DSystem.cpp | 2 - .../Components/SharedSkeletonComponent.cpp | 96 +++++++++++++ .../Utility/Components/SkeletonComponent.cpp | 24 ++++ .../Components/SkeletonComponentBase.cpp | 10 ++ src/Nazara/Utility/Skeleton.cpp | 6 +- src/Nazara/Utility/Systems/SkeletonSystem.cpp | 56 ++++++++ 55 files changed, 919 insertions(+), 131 deletions(-) create mode 100644 include/Nazara/Graphics/SkeletonInstance.hpp create mode 100644 include/Nazara/Graphics/SkeletonInstance.inl create mode 100644 include/Nazara/Utility/Components/SharedSkeletonComponent.hpp create mode 100644 include/Nazara/Utility/Components/SharedSkeletonComponent.inl create mode 100644 include/Nazara/Utility/Components/SkeletonComponent.hpp create mode 100644 include/Nazara/Utility/Components/SkeletonComponent.inl create mode 100644 include/Nazara/Utility/Components/SkeletonComponentBase.hpp create mode 100644 include/Nazara/Utility/Components/SkeletonComponentBase.inl create mode 100644 include/Nazara/Utility/Systems/SkeletonSystem.hpp create mode 100644 include/Nazara/Utility/Systems/SkeletonSystem.inl create mode 100644 src/Nazara/Graphics/SkeletonInstance.cpp create mode 100644 src/Nazara/Utility/Components/SharedSkeletonComponent.cpp create mode 100644 src/Nazara/Utility/Components/SkeletonComponent.cpp create mode 100644 src/Nazara/Utility/Components/SkeletonComponentBase.cpp create mode 100644 src/Nazara/Utility/Systems/SkeletonSystem.cpp diff --git a/examples/DeferredShading/main.cpp b/examples/DeferredShading/main.cpp index 1bda90d67..71ce63ce0 100644 --- a/examples/DeferredShading/main.cpp +++ b/examples/DeferredShading/main.cpp @@ -794,9 +794,9 @@ int main() builder.SetViewport(env.renderRect); std::vector> elements; - spaceshipModel.BuildElement(forwardPassIndex, modelInstance1, elements, env.renderRect); - spaceshipModel.BuildElement(forwardPassIndex, modelInstance2, elements, env.renderRect); - planeModel.BuildElement(forwardPassIndex, planeInstance, elements, env.renderRect); + spaceshipModel.BuildElement(forwardPassIndex, modelInstance1, nullptr, elements, env.renderRect); + spaceshipModel.BuildElement(forwardPassIndex, modelInstance2, nullptr, elements, env.renderRect); + planeModel.BuildElement(forwardPassIndex, planeInstance, nullptr, elements, env.renderRect); std::vector> elementPointers; std::vector renderStates(elements.size()); @@ -860,7 +860,7 @@ int main() builder.DrawIndexed(Nz::SafeCast(cubeMeshGfx->GetIndexCount(0))); std::vector> elements; - flareSprite.BuildElement(forwardPassIndex, flareInstance, elements, env.renderRect); + flareSprite.BuildElement(forwardPassIndex, flareInstance, nullptr, elements, env.renderRect); std::vector> elementPointers; std::vector renderStates(elements.size()); @@ -889,7 +889,7 @@ int main() builder.SetViewport(env.renderRect); std::vector> elements; - flareSprite.BuildElement(forwardPassIndex, flareInstance, elements, env.renderRect); + flareSprite.BuildElement(forwardPassIndex, flareInstance, nullptr, elements, env.renderRect); std::vector> elementPointers; std::vector renderStates(elements.size()); diff --git a/examples/GraphicsTest/main.cpp b/examples/GraphicsTest/main.cpp index de0349787..f1577641c 100644 --- a/examples/GraphicsTest/main.cpp +++ b/examples/GraphicsTest/main.cpp @@ -102,8 +102,8 @@ int main() std::size_t cameraIndex = framePipeline.RegisterViewer(&camera, 0); std::size_t worldInstanceIndex1 = framePipeline.RegisterWorldInstance(modelInstance); std::size_t worldInstanceIndex2 = framePipeline.RegisterWorldInstance(modelInstance2); - framePipeline.RegisterRenderable(worldInstanceIndex1, &model, 0xFFFFFFFF, scissorBox); - framePipeline.RegisterRenderable(worldInstanceIndex2, &model, 0xFFFFFFFF, scissorBox); + framePipeline.RegisterRenderable(worldInstanceIndex1, Nz::FramePipeline::NoSkeletonInstance, &model, 0xFFFFFFFF, scissorBox); + framePipeline.RegisterRenderable(worldInstanceIndex2, Nz::FramePipeline::NoSkeletonInstance, &model, 0xFFFFFFFF, scissorBox); std::shared_ptr light = std::make_shared(); light->UpdateInnerAngle(Nz::DegreeAnglef(15.f)); diff --git a/examples/PhysicallyBasedRendering/main.cpp b/examples/PhysicallyBasedRendering/main.cpp index 0bce32591..b34f14d38 100644 --- a/examples/PhysicallyBasedRendering/main.cpp +++ b/examples/PhysicallyBasedRendering/main.cpp @@ -94,7 +94,7 @@ int main() Nz::ForwardFramePipeline framePipeline; std::size_t cameraIndex = framePipeline.RegisterViewer(&camera, 0); std::size_t worldInstanceIndex1 = framePipeline.RegisterWorldInstance(modelInstance); - framePipeline.RegisterRenderable(worldInstanceIndex1, &model, 0xFFFFFFFF, scissorBox); + framePipeline.RegisterRenderable(worldInstanceIndex1, Nz::FramePipeline::NoSkeletonInstance, &model, 0xFFFFFFFF, scissorBox); std::shared_ptr light = std::make_shared(); light->UpdateRotation(Nz::EulerAnglesf(-45.f, 0.f, 0.f)); diff --git a/examples/Showcase/main.cpp b/examples/Showcase/main.cpp index f029663b8..74231e310 100644 --- a/examples/Showcase/main.cpp +++ b/examples/Showcase/main.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ int main() entt::registry registry; Nz::SystemGraph systemGraph(registry); + systemGraph.AddSystem(); Nz::Physics3DSystem& physSytem = systemGraph.AddSystem(); Nz::RenderSystem& renderSystem = systemGraph.AddSystem(); @@ -76,12 +78,6 @@ int main() cameraComponent.UpdateClearColor(Nz::Color(0.5f, 0.5f, 0.5f)); } - nzsl::FieldOffsets skeletalOffsets(nzsl::StructLayout::Std140); - std::size_t arrayOffset = skeletalOffsets.AddMatrixArray(nzsl::StructFieldType::Float1, 4, 4, true, 200); - - std::vector skeletalBufferMem(skeletalOffsets.GetAlignedSize()); - Nz::Matrix4f* matrices = Nz::AccessByOffset(skeletalBufferMem.data(), arrayOffset); - Nz::MeshParams meshParams; meshParams.animated = true; meshParams.center = true; @@ -108,31 +104,9 @@ int main() return __LINE__; } - Nz::Skeleton skeleton = *bobMesh->GetSkeleton(); + std::shared_ptr skeleton = std::make_shared(*bobMesh->GetSkeleton()); - std::cout << "joint count: " << skeleton.GetJointCount() << std::endl; - //std::cout << "anim joint count: " << bobAnim->GetJointCount() << std::endl; - - //bobAnim->AnimateSkeleton(&skeleton, 0, 1, 0.5f); - - /*for (std::size_t i = 0; i < bobMesh->GetSubMeshCount(); ++i) - { - Nz::VertexMapper mapper(*bobMesh->GetSubMesh(i)); - - Nz::SkinningData skinningData; - skinningData.joints = skeleton.GetJoints(); - skinningData.inputJointIndices = mapper.GetComponentPtr(Nz::VertexComponent::JointIndices); - skinningData.inputJointWeights = mapper.GetComponentPtr(Nz::VertexComponent::JointWeights); - skinningData.outputPositions = mapper.GetComponentPtr(Nz::VertexComponent::Position); - skinningData.inputPositions = skinningData.outputPositions; - - Nz::SkinLinearBlend(skinningData, 0, mapper.GetVertexCount()); - }*/ - - /*for (std::size_t i = 0; i < skeleton.GetJointCount(); ++i) - matrices[i] = skeleton.GetJoint(i)->GetSkinningMatrix();*/ - - std::shared_ptr renderBuffer = device->InstantiateBuffer(Nz::BufferType::Uniform, skeletalBufferMem.size(), Nz::BufferUsage::Write, skeletalBufferMem.data()); + std::cout << "joint count: " << skeleton->GetJointCount() << std::endl; const Nz::Boxf& bobAABB = bobMesh->GetAABB(); std::shared_ptr bobGfxMesh = Nz::GraphicalMesh::BuildFromMesh(*bobMesh); @@ -147,7 +121,6 @@ int main() std::shared_ptr bobMat = std::make_shared(); std::shared_ptr bobMatPass = std::make_shared(Nz::BasicMaterial::GetSettings()); - bobMatPass->SetSharedUniformBuffer(1, renderBuffer); bobMatPass->EnableDepthBuffer(true); { @@ -178,16 +151,16 @@ int main() for (std::size_t i = 0; i < bobMesh->GetSubMeshCount(); ++i) bobModel->SetMaterial(i, materials[bobMesh->GetSubMesh(i)->GetMaterialIndex()]); - /*for (std::size_t y = 0; y < 50; ++y) + /*for (std::size_t y = 0; y < 10; ++y) { - for (std::size_t x = 0; x < 50; ++x) + for (std::size_t x = 0; x < 10; ++x) { entt::entity bobEntity = registry.create(); auto& bobNode = registry.emplace(bobEntity); - bobNode.SetPosition(Nz::Vector3f(x - 25.f, bobAABB.height / 2.f, -float(y))); - bobNode.SetRotation(Nz::EulerAnglesf(-90.f, -90.f, 0.f)); - bobNode.SetScale(1.f / 40.f * 0.5f); + bobNode.SetPosition(Nz::Vector3f(x - 5.f, 0.f, -float(y))); + //bobNode.SetRotation(Nz::EulerAnglesf(-90.f, -90.f, 0.f)); + //bobNode.SetScale(1.f / 40.f * 0.5f); auto& bobGfx = registry.emplace(bobEntity); bobGfx.AttachRenderable(bobModel, 0xFFFFFFFF); @@ -203,8 +176,69 @@ int main() auto& bobGfx = registry.emplace(bobEntity); bobGfx.AttachRenderable(bobModel, 0xFFFFFFFF); + + auto& sharedSkeleton = registry.emplace(bobEntity, skeleton); + + + entt::entity sphereEntity = registry.create(); + { + std::shared_ptr sphereMesh = std::make_shared(); + sphereMesh->CreateStatic(); + sphereMesh->BuildSubMesh(Nz::Primitive::UVSphere(1.f, 50, 50)); + sphereMesh->SetMaterialCount(1); + sphereMesh->GenerateNormalsAndTangents(); + + std::shared_ptr gfxMesh = Nz::GraphicalMesh::BuildFromMesh(*sphereMesh); + + // Textures + Nz::TextureParams texParams; + texParams.renderDevice = device; + + Nz::TextureParams srgbTexParams = texParams; + srgbTexParams.loadFormat = Nz::PixelFormat::RGBA8_SRGB; + + std::shared_ptr material = std::make_shared(); + + std::shared_ptr forwardPass = std::make_shared(Nz::BasicMaterial::GetSettings()); + forwardPass->EnableDepthBuffer(true); + forwardPass->EnableFaceCulling(true); + + material->AddPass("ForwardPass", forwardPass); + + std::shared_ptr normalMap = Nz::Texture::LoadFromFile(resourceDir / "Rusty/rustediron2_normal.png", texParams); + + Nz::BasicMaterial pbrMat(*forwardPass); + pbrMat.EnableAlphaTest(false); + pbrMat.SetAlphaMap(Nz::Texture::LoadFromFile(resourceDir / "alphatile.png", texParams)); + pbrMat.SetBaseColorMap(Nz::Texture::LoadFromFile(resourceDir / "Rusty/rustediron2_basecolor.png", srgbTexParams)); + + std::shared_ptr sphereModel = std::make_shared(std::move(gfxMesh), sphereMesh->GetAABB()); + for (std::size_t i = 0; i < sphereModel->GetSubMeshCount(); ++i) + sphereModel->SetMaterial(i, material); + + auto& sphereNode = registry.emplace(sphereEntity); + sphereNode.SetScale(0.1f); + sphereNode.SetInheritScale(false); + sphereNode.SetParent(sharedSkeleton.GetAttachedJoint(83)); + + auto& sphereBody = registry.emplace(sphereEntity, &physSytem.GetPhysWorld()); + sphereBody.SetGeom(std::make_shared(0.1f)); + + auto& sphereGfx = registry.emplace(sphereEntity); + sphereGfx.AttachRenderable(sphereModel, 0xFFFFFFFF); + } + + entt::entity smallBobEntity = registry.create(); + auto& smallBobNode = registry.emplace(smallBobEntity); + smallBobNode.SetParent(sharedSkeleton.GetAttachedJoint(59)); + + auto& smallBobGfx = registry.emplace(smallBobEntity); + smallBobGfx.AttachRenderable(bobModel, 0xFFFFFFFF); + + registry.emplace(smallBobEntity, skeleton); } + entt::entity planeEntity = registry.create(); { Nz::MeshParams meshPrimitiveParams; @@ -249,14 +283,6 @@ int main() window.EnableEventPolling(true); - renderSystem.GetFramePipeline().OnTransfer.Connect([&](Nz::FramePipeline* pipeline, Nz::RenderFrame& renderFrame, Nz::CommandBufferBuilder& builder) - { - auto& skeletalAllocation = renderFrame.GetUploadPool().Allocate(skeletalBufferMem.size()); - std::memcpy(skeletalAllocation.mappedPtr, skeletalBufferMem.data(), skeletalBufferMem.size()); - - builder.CopyBuffer(skeletalAllocation, Nz::RenderBufferView(renderBuffer.get())); - }); - Nz::Clock fpsClock, updateClock; float incr = 0.f; unsigned int currentFrame = 0; @@ -327,8 +353,8 @@ int main() if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Right) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::D)) playerBody.AddForce(Nz::Vector3f::Right() * 25.f * mass, Nz::CoordSys::Local); - incr += 1.f / 60.f * 30.f; - if (incr >= 1.f) + incr += updateTime * bobAnim->GetSequence(0)->frameRate * 1.5f; + while (incr >= 1.f) { incr -= 1.f; @@ -340,9 +366,9 @@ int main() std::cout << currentFrame << std::endl; - bobAnim->AnimateSkeleton(&skeleton, currentFrame, nextFrame, incr); - for (std::size_t i = 0; i < skeleton.GetJointCount(); ++i) - matrices[i] = skeleton.GetJoint(i)->GetSkinningMatrix(); + bobAnim->AnimateSkeleton(skeleton.get(), currentFrame, nextFrame, incr); + //for (std::size_t i = 0; i < skeleton.GetJointCount(); ++i) + // matrices[i] = skeleton.GetJoint(i)->GetSkinningMatrix(); //renderBuffer->Fill(skeletalBufferMem.data(), 0, skeletalBufferMem.size()); @@ -393,7 +419,7 @@ int main() } Nz::DebugDrawer& debugDrawer = renderSystem.GetFramePipeline().GetDebugDrawer(); - debugDrawer.DrawSkeleton(skeleton, Nz::Color::Red); + //debugDrawer.DrawSkeleton(*skeleton, Nz::Color::Red); systemGraph.Update(); diff --git a/include/Nazara/Graphics.hpp b/include/Nazara/Graphics.hpp index f79410bc8..1e9b6d3ba 100644 --- a/include/Nazara/Graphics.hpp +++ b/include/Nazara/Graphics.hpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Graphics/Components/GraphicsComponent.hpp b/include/Nazara/Graphics/Components/GraphicsComponent.hpp index 9e6a59b69..85eaff328 100644 --- a/include/Nazara/Graphics/Components/GraphicsComponent.hpp +++ b/include/Nazara/Graphics/Components/GraphicsComponent.hpp @@ -23,7 +23,7 @@ namespace Nz struct Renderable; static constexpr std::size_t MaxRenderableCount = 8; - inline GraphicsComponent(bool initialyVisible = true); + inline GraphicsComponent(bool initiallyVisible = true); GraphicsComponent(const GraphicsComponent&) = default; GraphicsComponent(GraphicsComponent&&) = default; ~GraphicsComponent() = default; diff --git a/include/Nazara/Graphics/Components/GraphicsComponent.inl b/include/Nazara/Graphics/Components/GraphicsComponent.inl index e76d6f90f..2ac4baa81 100644 --- a/include/Nazara/Graphics/Components/GraphicsComponent.inl +++ b/include/Nazara/Graphics/Components/GraphicsComponent.inl @@ -7,9 +7,9 @@ namespace Nz { - inline GraphicsComponent::GraphicsComponent(bool initialyVisible) : + inline GraphicsComponent::GraphicsComponent(bool initiallyVisible) : m_scissorBox(-1, -1, -1, -1), - m_isVisible(initialyVisible) + m_isVisible(initiallyVisible) { m_worldInstance = std::make_shared(); //< FIXME: Use pools } diff --git a/include/Nazara/Graphics/ForwardFramePipeline.hpp b/include/Nazara/Graphics/ForwardFramePipeline.hpp index eea8b656e..e0e9b7d76 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.hpp +++ b/include/Nazara/Graphics/ForwardFramePipeline.hpp @@ -42,12 +42,14 @@ namespace Nz ForwardFramePipeline(ForwardFramePipeline&&) = delete; ~ForwardFramePipeline(); + void InvalidateSkeletalInstance(std::size_t skeletalInstanceIndex) override; void InvalidateViewer(std::size_t viewerIndex) override; void InvalidateWorldInstance(std::size_t renderableIndex) override; std::size_t RegisterLight(std::shared_ptr light, UInt32 renderMask) override; void RegisterMaterialPass(MaterialPass* materialPass) override; - std::size_t RegisterRenderable(std::size_t worldInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) override; + std::size_t RegisterRenderable(std::size_t worldInstanceIndex, std::size_t skeletonInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) override; + std::size_t RegisterSkeleton(SkeletonInstancePtr skeletonInstance) override; std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) override; std::size_t RegisterWorldInstance(WorldInstancePtr worldInstance) override; @@ -56,6 +58,7 @@ namespace Nz void UnregisterLight(std::size_t lightIndex) override; void UnregisterMaterialPass(MaterialPass* material) override; void UnregisterRenderable(std::size_t renderableIndex) override; + void UnregisterSkeleton(std::size_t skeletonIndex) override; void UnregisterViewer(std::size_t viewerIndex) override; void UnregisterWorldInstance(std::size_t worldInstance) override; @@ -89,6 +92,7 @@ namespace Nz struct RenderableData { + std::size_t skeletonInstanceIndex; std::size_t worldInstanceIndex; const InstancedRenderable* renderable; Recti scissorBox; @@ -127,11 +131,15 @@ namespace Nz std::vector m_visibleRenderables; std::vector m_visibleLights; BakedFrameGraph m_bakedFrameGraph; + Bitset m_invalidatedSkeletonInstances; Bitset m_invalidatedViewerInstances; Bitset m_invalidatedWorldInstances; + Bitset m_removedSkeletonInstances; + Bitset m_removedViewerInstances; Bitset m_removedWorldInstances; MemoryPool m_renderablePool; MemoryPool m_lightPool; + MemoryPool m_skeletonInstances; MemoryPool m_viewerPool; MemoryPool m_worldInstances; RenderFrame* m_currentRenderFrame; diff --git a/include/Nazara/Graphics/FramePipeline.hpp b/include/Nazara/Graphics/FramePipeline.hpp index 09317ddab..6d5ade71b 100644 --- a/include/Nazara/Graphics/FramePipeline.hpp +++ b/include/Nazara/Graphics/FramePipeline.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -39,14 +40,16 @@ namespace Nz inline ElementRenderer& GetElementRenderer(std::size_t elementIndex); inline std::size_t GetElementRendererCount() const; + virtual void InvalidateSkeletalInstance(std::size_t skeletalInstanceIndex) = 0; virtual void InvalidateViewer(std::size_t viewerIndex) = 0; - virtual void InvalidateWorldInstance(std::size_t) = 0; + virtual void InvalidateWorldInstance(std::size_t worldInstance) = 0; template void ProcessRenderQueue(const RenderQueue& renderQueue, F&& callback); virtual std::size_t RegisterLight(std::shared_ptr light, UInt32 renderMask) = 0; virtual void RegisterMaterialPass(MaterialPass* materialPass) = 0; - virtual std::size_t RegisterRenderable(std::size_t worldInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) = 0; + virtual std::size_t RegisterRenderable(std::size_t worldInstanceIndex, std::size_t skeletonInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) = 0; + virtual std::size_t RegisterSkeleton(SkeletonInstancePtr skeletonInstance) = 0; virtual std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) = 0; virtual std::size_t RegisterWorldInstance(WorldInstancePtr worldInstance) = 0; @@ -55,6 +58,7 @@ namespace Nz virtual void UnregisterLight(std::size_t lightIndex) = 0; virtual void UnregisterMaterialPass(MaterialPass* materialPass) = 0; virtual void UnregisterRenderable(std::size_t renderableIndex) = 0; + virtual void UnregisterSkeleton(std::size_t skeletonIndex) = 0; virtual void UnregisterViewer(std::size_t viewerIndex) = 0; virtual void UnregisterWorldInstance(std::size_t worldInstance) = 0; @@ -68,6 +72,8 @@ namespace Nz NazaraSignal(OnTransfer, FramePipeline* /*pipeline*/, RenderFrame& /*renderFrame*/, CommandBufferBuilder& /*builder*/); + static constexpr std::size_t NoSkeletonInstance = std::numeric_limits::max(); + private: std::vector> m_elementRenderers; DebugDrawer m_debugDrawer; diff --git a/include/Nazara/Graphics/FramePipelinePass.hpp b/include/Nazara/Graphics/FramePipelinePass.hpp index 7d68e14f6..264977b76 100644 --- a/include/Nazara/Graphics/FramePipelinePass.hpp +++ b/include/Nazara/Graphics/FramePipelinePass.hpp @@ -14,6 +14,7 @@ namespace Nz { class InstancedRenderable; + class SkeletonInstance; class WorldInstance; class NAZARA_GRAPHICS_API FramePipelinePass @@ -30,6 +31,7 @@ namespace Nz struct VisibleRenderable { const InstancedRenderable* instancedRenderable; + const SkeletonInstance* skeletonInstance; const WorldInstance* worldInstance; Recti scissorBox; }; diff --git a/include/Nazara/Graphics/InstancedRenderable.hpp b/include/Nazara/Graphics/InstancedRenderable.hpp index 230e33944..4bd01cf26 100644 --- a/include/Nazara/Graphics/InstancedRenderable.hpp +++ b/include/Nazara/Graphics/InstancedRenderable.hpp @@ -18,6 +18,7 @@ namespace Nz class CommandBufferBuilder; class Material; class RenderElement; + class SkeletonInstance; class WorldInstance; class NAZARA_GRAPHICS_API InstancedRenderable @@ -28,7 +29,7 @@ namespace Nz InstancedRenderable(InstancedRenderable&&) noexcept = default; ~InstancedRenderable(); - virtual void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const = 0; + virtual void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const = 0; inline const Boxf& GetAABB() const; virtual const std::shared_ptr& GetMaterial(std::size_t i) const = 0; diff --git a/include/Nazara/Graphics/LinearSlicedSprite.hpp b/include/Nazara/Graphics/LinearSlicedSprite.hpp index 14e522ce1..af502174b 100644 --- a/include/Nazara/Graphics/LinearSlicedSprite.hpp +++ b/include/Nazara/Graphics/LinearSlicedSprite.hpp @@ -30,7 +30,7 @@ namespace Nz inline void AddSection(float size, float textureCoord); - void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const override; + void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const override; inline void Clear(); diff --git a/include/Nazara/Graphics/Model.hpp b/include/Nazara/Graphics/Model.hpp index c10884b17..37221f4b5 100644 --- a/include/Nazara/Graphics/Model.hpp +++ b/include/Nazara/Graphics/Model.hpp @@ -28,7 +28,7 @@ namespace Nz Model(Model&&) noexcept = default; ~Model() = default; - void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const override; + void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const override; const std::shared_ptr& GetIndexBuffer(std::size_t subMeshIndex) const; std::size_t GetIndexCount(std::size_t subMeshIndex) const; diff --git a/include/Nazara/Graphics/RenderQueueRegistry.hpp b/include/Nazara/Graphics/RenderQueueRegistry.hpp index 2e97f97c1..3f7a4e5d5 100644 --- a/include/Nazara/Graphics/RenderQueueRegistry.hpp +++ b/include/Nazara/Graphics/RenderQueueRegistry.hpp @@ -15,6 +15,7 @@ namespace Nz { class MaterialPass; class RenderPipeline; + class Skeleton; class VertexDeclaration; class RenderQueueRegistry @@ -28,6 +29,7 @@ namespace Nz inline std::size_t FetchLayerIndex(int renderLayer) const; inline std::size_t FetchMaterialPassIndex(const MaterialPass* materialPass) const; inline std::size_t FetchPipelineIndex(const RenderPipeline* pipeline) const; + inline std::size_t FetchSkeletonIndex(const Skeleton* skeleton) const; inline std::size_t FetchVertexBuffer(const RenderBuffer* vertexBuffer) const; inline std::size_t FetchVertexDeclaration(const VertexDeclaration* vertexDeclaration) const; @@ -36,6 +38,7 @@ namespace Nz inline void RegisterLayer(int renderLayer); inline void RegisterMaterialPass(const MaterialPass* materialPass); inline void RegisterPipeline(const RenderPipeline* pipeline); + inline void RegisterSkeleton(const Skeleton* skeleton); inline void RegisterVertexBuffer(const RenderBuffer* vertexBuffer); inline void RegisterVertexDeclaration(const VertexDeclaration* vertexDeclaration); @@ -45,6 +48,7 @@ namespace Nz robin_hood::unordered_map m_materialPassRegistry; robin_hood::unordered_map m_pipelineRegistry; robin_hood::unordered_map m_vertexBufferRegistry; + robin_hood::unordered_map m_skeletonRegistry; robin_hood::unordered_map m_vertexDeclarationRegistry; }; } diff --git a/include/Nazara/Graphics/RenderQueueRegistry.inl b/include/Nazara/Graphics/RenderQueueRegistry.inl index 14dfafa8f..687281882 100644 --- a/include/Nazara/Graphics/RenderQueueRegistry.inl +++ b/include/Nazara/Graphics/RenderQueueRegistry.inl @@ -42,6 +42,14 @@ namespace Nz return it->second; } + inline std::size_t RenderQueueRegistry::FetchSkeletonIndex(const Skeleton* skeleton) const + { + auto it = m_skeletonRegistry.find(skeleton); + assert(it != m_skeletonRegistry.end()); + + return it->second; + } + inline std::size_t RenderQueueRegistry::FetchVertexBuffer(const RenderBuffer* vertexBuffer) const { auto it = m_vertexBufferRegistry.find(vertexBuffer); @@ -80,6 +88,11 @@ namespace Nz m_pipelineRegistry.try_emplace(pipeline, m_pipelineRegistry.size()); } + inline void RenderQueueRegistry::RegisterSkeleton(const Skeleton* skeleton) + { + m_skeletonRegistry.try_emplace(skeleton, m_skeletonRegistry.size()); + } + inline void RenderQueueRegistry::RegisterVertexBuffer(const RenderBuffer* vertexBuffer) { m_vertexBufferRegistry.try_emplace(vertexBuffer, m_vertexBufferRegistry.size()); diff --git a/include/Nazara/Graphics/RenderSubmesh.hpp b/include/Nazara/Graphics/RenderSubmesh.hpp index 677793e27..a04a947fa 100644 --- a/include/Nazara/Graphics/RenderSubmesh.hpp +++ b/include/Nazara/Graphics/RenderSubmesh.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,7 @@ namespace Nz class RenderSubmesh : public RenderElement { public: - inline RenderSubmesh(int renderLayer, std::shared_ptr materialPass, std::shared_ptr renderPipeline, const WorldInstance& worldInstance, std::size_t indexCount, IndexType indexType, std::shared_ptr indexBuffer, std::shared_ptr vertexBuffer, const Recti& scissorBox); + inline RenderSubmesh(int renderLayer, std::shared_ptr materialPass, std::shared_ptr renderPipeline, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::size_t indexCount, IndexType indexType, std::shared_ptr indexBuffer, std::shared_ptr vertexBuffer, const Recti& scissorBox); ~RenderSubmesh() = default; inline UInt64 ComputeSortingScore(const Frustumf& frustum, const RenderQueueRegistry& registry) const override; @@ -34,6 +35,7 @@ namespace Nz inline const MaterialPass& GetMaterialPass() const; inline const RenderPipeline* GetRenderPipeline() const; inline const Recti& GetScissorBox() const; + inline const SkeletonInstance* GetSkeletonInstance() const; inline const RenderBuffer* GetVertexBuffer() const; inline const WorldInstance& GetWorldInstance() const; @@ -45,6 +47,7 @@ namespace Nz std::shared_ptr m_materialPass; std::shared_ptr m_renderPipeline; std::size_t m_indexCount; + const SkeletonInstance* m_skeletonInstance; const WorldInstance& m_worldInstance; IndexType m_indexType; Recti m_scissorBox; diff --git a/include/Nazara/Graphics/RenderSubmesh.inl b/include/Nazara/Graphics/RenderSubmesh.inl index b2afc5117..627b4c5bd 100644 --- a/include/Nazara/Graphics/RenderSubmesh.inl +++ b/include/Nazara/Graphics/RenderSubmesh.inl @@ -9,13 +9,14 @@ namespace Nz { - inline RenderSubmesh::RenderSubmesh(int renderLayer, std::shared_ptr materialPass, std::shared_ptr renderPipeline, const WorldInstance& worldInstance, std::size_t indexCount, IndexType indexType, std::shared_ptr indexBuffer, std::shared_ptr vertexBuffer, const Recti& scissorBox) : + inline RenderSubmesh::RenderSubmesh(int renderLayer, std::shared_ptr materialPass, std::shared_ptr renderPipeline, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::size_t indexCount, IndexType indexType, std::shared_ptr indexBuffer, std::shared_ptr vertexBuffer, const Recti& scissorBox) : RenderElement(BasicRenderElement::Submesh), m_indexBuffer(std::move(indexBuffer)), m_vertexBuffer(std::move(vertexBuffer)), m_materialPass(std::move(materialPass)), m_renderPipeline(std::move(renderPipeline)), m_indexCount(indexCount), + m_skeletonInstance(skeletonInstance), m_worldInstance(worldInstance), m_indexType(indexType), m_scissorBox(scissorBox), @@ -51,6 +52,10 @@ namespace Nz UInt64 pipelineIndex = registry.FetchPipelineIndex(m_renderPipeline.get()); UInt64 vertexBufferIndex = registry.FetchVertexBuffer(m_vertexBuffer.get()); + UInt64 skeletonIndex = 0; + if (m_skeletonInstance) + skeletonIndex = registry.FetchSkeletonIndex(m_skeletonInstance->GetSkeleton().get()); + UInt64 matFlags = 0; // Opaque RQ index: @@ -60,14 +65,15 @@ namespace Nz // - Pipeline (16bits) // - MaterialPass (16bits) // - VertexBuffer (8bits) - // - ?? (8bits) - Depth? + // - Skeleton (8bits) return (layerIndex & 0xFF) << 60 | (matFlags) << 52 | (elementType & 0xF) << 51 | (pipelineIndex & 0xFFFF) << 35 | (materialPassIndex & 0xFFFF) << 23 | - (vertexBufferIndex & 0xFF) << 7; + (vertexBufferIndex & 0xFF) << 7 | + (skeletonIndex & 0xFF); } } @@ -101,6 +107,11 @@ namespace Nz return m_scissorBox; } + inline const SkeletonInstance* RenderSubmesh::GetSkeletonInstance() const + { + return m_skeletonInstance; + } + inline const RenderBuffer* RenderSubmesh::GetVertexBuffer() const { return m_vertexBuffer.get(); @@ -117,6 +128,8 @@ namespace Nz registry.RegisterMaterialPass(m_materialPass.get()); registry.RegisterPipeline(m_renderPipeline.get()); registry.RegisterVertexBuffer(m_vertexBuffer.get()); + if (m_skeletonInstance) + registry.RegisterSkeleton(m_skeletonInstance->GetSkeleton().get()); } } diff --git a/include/Nazara/Graphics/SkeletonInstance.hpp b/include/Nazara/Graphics/SkeletonInstance.hpp new file mode 100644 index 000000000..8d7881a87 --- /dev/null +++ b/include/Nazara/Graphics/SkeletonInstance.hpp @@ -0,0 +1,54 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_GRAPHICS_SKELETONINSTANCE_HPP +#define NAZARA_GRAPHICS_SKELETONINSTANCE_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class CommandBufferBuilder; + class RenderBuffer; + class SkeletonInstance; + class UploadPool; + + using SkeletonInstancePtr = std::shared_ptr; + + class NAZARA_GRAPHICS_API SkeletonInstance + { + public: + SkeletonInstance(std::shared_ptr skeleton); + SkeletonInstance(const SkeletonInstance&) = delete; + SkeletonInstance(SkeletonInstance&& skeletonInstance) noexcept; + ~SkeletonInstance() = default; + + inline std::shared_ptr& GetSkeletalBuffer(); + inline const std::shared_ptr& GetSkeletalBuffer() const; + inline const std::shared_ptr& GetSkeleton() const; + + void UpdateBuffers(UploadPool& uploadPool, CommandBufferBuilder& builder); + + SkeletonInstance& operator=(const SkeletonInstance&) = delete; + SkeletonInstance& operator=(SkeletonInstance&& skeletonInstance) noexcept; + + private: + NazaraSlot(Skeleton, OnSkeletonJointsInvalidated, m_onSkeletonJointsInvalidated); + + std::shared_ptr m_skeletalDataBuffer; + std::shared_ptr m_skeleton; + bool m_dataInvalided; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_SKELETONINSTANCE_HPP diff --git a/include/Nazara/Graphics/SkeletonInstance.inl b/include/Nazara/Graphics/SkeletonInstance.inl new file mode 100644 index 000000000..e5aeb3250 --- /dev/null +++ b/include/Nazara/Graphics/SkeletonInstance.inl @@ -0,0 +1,26 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline std::shared_ptr& SkeletonInstance::GetSkeletalBuffer() + { + return m_skeletalDataBuffer; + } + + inline const std::shared_ptr& SkeletonInstance::GetSkeletalBuffer() const + { + return m_skeletalDataBuffer; + } + + inline const std::shared_ptr& SkeletonInstance::GetSkeleton() const + { + return m_skeleton; + } +} + +#include diff --git a/include/Nazara/Graphics/SlicedSprite.hpp b/include/Nazara/Graphics/SlicedSprite.hpp index 1abd67c79..1da860243 100644 --- a/include/Nazara/Graphics/SlicedSprite.hpp +++ b/include/Nazara/Graphics/SlicedSprite.hpp @@ -27,7 +27,7 @@ namespace Nz SlicedSprite(SlicedSprite&&) noexcept = default; ~SlicedSprite() = default; - void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const override; + void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const override; inline const Color& GetColor() const; inline const Corner& GetBottomRightCorner() const; diff --git a/include/Nazara/Graphics/Sprite.hpp b/include/Nazara/Graphics/Sprite.hpp index f29661d92..485bce66f 100644 --- a/include/Nazara/Graphics/Sprite.hpp +++ b/include/Nazara/Graphics/Sprite.hpp @@ -25,7 +25,7 @@ namespace Nz Sprite(Sprite&&) noexcept = default; ~Sprite() = default; - void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const override; + void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const override; inline const Color& GetColor() const; inline const Color& GetCornerColor(RectCorner corner) const; diff --git a/include/Nazara/Graphics/Systems/RenderSystem.hpp b/include/Nazara/Graphics/Systems/RenderSystem.hpp index 39c0bd7ba..d7685ba8a 100644 --- a/include/Nazara/Graphics/Systems/RenderSystem.hpp +++ b/include/Nazara/Graphics/Systems/RenderSystem.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -53,10 +54,14 @@ namespace Nz void OnGraphicsDestroy(entt::registry& registry, entt::entity entity); void OnLightDestroy(entt::registry& registry, entt::entity entity); void OnNodeDestroy(entt::registry& registry, entt::entity entity); + void OnSharedSkeletonDestroy(entt::registry& registry, entt::entity entity); + void OnSkeletonDestroy(entt::registry& registry, entt::entity entity); void UpdateInstances(); void UpdateObservers(); void UpdateVisibility(); + static constexpr std::size_t NoInstance = std::numeric_limits::max(); + struct CameraEntity { entt::entity entity; @@ -71,6 +76,7 @@ namespace Nz entt::entity entity; std::array renderableIndices; std::size_t poolIndex; + std::size_t skeletonInstanceIndex; std::size_t worldInstanceIndex; NazaraSlot(GraphicsComponent, OnRenderableAttached, onRenderableAttached); @@ -78,6 +84,7 @@ namespace Nz NazaraSlot(GraphicsComponent, OnScissorBoxUpdate, onScissorBoxUpdate); NazaraSlot(GraphicsComponent, OnVisibilityUpdate, onVisibilityUpdate); NazaraSlot(Node, OnNodeInvalidation, onNodeInvalidation); + NazaraSlot(Skeleton, OnSkeletonJointsInvalidated, onSkeletonJointsInvalidated); //< only connected for owned skeleton }; struct LightEntity @@ -92,14 +99,26 @@ namespace Nz NazaraSlot(Node, OnNodeInvalidation, onNodeInvalidation); }; - entt::connection m_cameraDestroyConnection; - entt::connection m_graphicsDestroyConnection; - entt::connection m_lightDestroyConnection; - entt::connection m_nodeDestroyConnection; + struct SharedSkeleton + { + std::size_t skeletonInstanceIndex; + std::size_t useCount; + + NazaraSlot(Skeleton, OnSkeletonJointsInvalidated, onJointsInvalidated); + }; + + entt::registry& m_registry; entt::observer m_cameraConstructObserver; entt::observer m_graphicsConstructObserver; entt::observer m_lightConstructObserver; - entt::registry& m_registry; + entt::observer m_sharedSkeletonConstructObserver; + entt::observer m_skeletonConstructObserver; + entt::scoped_connection m_cameraDestroyConnection; + entt::scoped_connection m_graphicsDestroyConnection; + entt::scoped_connection m_lightDestroyConnection; + entt::scoped_connection m_nodeDestroyConnection; + entt::scoped_connection m_sharedSkeletonDestroyConnection; + entt::scoped_connection m_skeletonDestroyConnection; std::set m_invalidatedCameraNode; std::set m_invalidatedGfxWorldNode; std::set m_invalidatedLightWorldNode; @@ -107,6 +126,7 @@ namespace Nz std::unordered_map m_cameraEntities; std::unordered_map m_graphicsEntities; std::unordered_map m_lightEntities; + std::unordered_map m_sharedSkeletonInstances; std::unordered_set m_newlyHiddenGfxEntities; std::unordered_set m_newlyVisibleGfxEntities; std::unordered_set m_newlyHiddenLightEntities; diff --git a/include/Nazara/Graphics/TextSprite.hpp b/include/Nazara/Graphics/TextSprite.hpp index 0781498dc..6e5e381f7 100644 --- a/include/Nazara/Graphics/TextSprite.hpp +++ b/include/Nazara/Graphics/TextSprite.hpp @@ -28,7 +28,7 @@ namespace Nz TextSprite(TextSprite&&) noexcept = default; ~TextSprite() = default; - void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const override; + void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const override; inline void Clear(); diff --git a/include/Nazara/Graphics/WorldInstance.hpp b/include/Nazara/Graphics/WorldInstance.hpp index d3356985e..d16776d00 100644 --- a/include/Nazara/Graphics/WorldInstance.hpp +++ b/include/Nazara/Graphics/WorldInstance.hpp @@ -16,7 +16,6 @@ namespace Nz { class CommandBufferBuilder; - class MaterialSettings; class RenderBuffer; class UploadPool; class WorldInstance; diff --git a/include/Nazara/Physics2D/Systems/Physics2DSystem.hpp b/include/Nazara/Physics2D/Systems/Physics2DSystem.hpp index 71136ea3a..df197f576 100644 --- a/include/Nazara/Physics2D/Systems/Physics2DSystem.hpp +++ b/include/Nazara/Physics2D/Systems/Physics2DSystem.hpp @@ -40,7 +40,7 @@ namespace Nz static void OnConstruct(entt::registry& registry, entt::entity entity); entt::registry& m_registry; - entt::connection m_constructConnection; + entt::scoped_connection m_constructConnection; PhysWorld2D m_physWorld; }; } diff --git a/include/Nazara/Physics3D/Systems/Physics3DSystem.hpp b/include/Nazara/Physics3D/Systems/Physics3DSystem.hpp index 4a73eac5b..1ac772ac9 100644 --- a/include/Nazara/Physics3D/Systems/Physics3DSystem.hpp +++ b/include/Nazara/Physics3D/Systems/Physics3DSystem.hpp @@ -40,7 +40,7 @@ namespace Nz static void OnConstruct(entt::registry& registry, entt::entity entity); entt::registry& m_registry; - entt::connection m_constructConnection; + entt::scoped_connection m_constructConnection; PhysWorld3D m_physWorld; }; } diff --git a/include/Nazara/Utility/Components.hpp b/include/Nazara/Utility/Components.hpp index a08f6ad6c..497dd73fd 100644 --- a/include/Nazara/Utility/Components.hpp +++ b/include/Nazara/Utility/Components.hpp @@ -30,6 +30,9 @@ #define NAZARA_UTILITY_COMPONENTS_HPP #include +#include +#include +#include #include #endif // NAZARA_UTILITY_COMPONENTS_HPP diff --git a/include/Nazara/Utility/Components/SharedSkeletonComponent.hpp b/include/Nazara/Utility/Components/SharedSkeletonComponent.hpp new file mode 100644 index 000000000..5a117b2ca --- /dev/null +++ b/include/Nazara/Utility/Components/SharedSkeletonComponent.hpp @@ -0,0 +1,47 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_UTILITY_COMPONENTS_SHAREDSKELETONCOMPONENT_HPP +#define NAZARA_UTILITY_COMPONENTS_SHAREDSKELETONCOMPONENT_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_UTILITY_API SharedSkeletonComponent final : public SkeletonComponentBase + { + friend class SkeletonSystem; + + public: + SharedSkeletonComponent(std::shared_ptr skeleton); + SharedSkeletonComponent(const SharedSkeletonComponent& sharedSkeletalComponent); + SharedSkeletonComponent(SharedSkeletonComponent&& sharedSkeletalComponent) noexcept; + ~SharedSkeletonComponent() = default; + + const Joint& GetAttachedJoint(std::size_t jointIndex) const override; + + SharedSkeletonComponent& operator=(const SharedSkeletonComponent& sharedSkeletalComponent); + SharedSkeletonComponent& operator=(SharedSkeletonComponent&& sharedSkeletalComponent) noexcept; + + private: + inline bool IsAttachedSkeletonOutdated() const; + void OnReferenceJointsInvalidated(const Skeleton* skeleton); + void SetSkeletonParent(Node* parent); + void SetupSkeleton(); + void UpdateAttachedSkeletonJoints(); + + NazaraSlot(Skeleton, OnSkeletonJointsInvalidated, m_onSkeletonJointsInvalidated); + + Skeleton m_attachedSkeleton; + bool m_skeletonJointInvalidated; + }; +} + +#include + +#endif // NAZARA_UTILITY_COMPONENTS_SHAREDSKELETONCOMPONENT_HPP diff --git a/include/Nazara/Utility/Components/SharedSkeletonComponent.inl b/include/Nazara/Utility/Components/SharedSkeletonComponent.inl new file mode 100644 index 000000000..97b175bd9 --- /dev/null +++ b/include/Nazara/Utility/Components/SharedSkeletonComponent.inl @@ -0,0 +1,16 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline bool SharedSkeletonComponent::IsAttachedSkeletonOutdated() const + { + return m_skeletonJointInvalidated; + } +} + +#include diff --git a/include/Nazara/Utility/Components/SkeletonComponent.hpp b/include/Nazara/Utility/Components/SkeletonComponent.hpp new file mode 100644 index 000000000..b31a5bd67 --- /dev/null +++ b/include/Nazara/Utility/Components/SkeletonComponent.hpp @@ -0,0 +1,36 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_UTILITY_COMPONENTS_SKELETONCOMPONENT_HPP +#define NAZARA_UTILITY_COMPONENTS_SKELETONCOMPONENT_HPP + +#include +#include +#include + +namespace Nz +{ + class Node; + + class NAZARA_UTILITY_API SkeletonComponent final : public SkeletonComponentBase + { + public: + SkeletonComponent(std::shared_ptr skeleton); + SkeletonComponent(const SkeletonComponent&) = delete; + SkeletonComponent(SkeletonComponent&& skeletalComponent) noexcept = default; + ~SkeletonComponent() = default; + + const Joint& GetAttachedJoint(std::size_t jointIndex) const override; + Node* GetRootNode(); + + SkeletonComponent& operator=(const SkeletonComponent&) = delete; + SkeletonComponent& operator=(SkeletonComponent&& skeletalComponent) noexcept = default; + }; +} + +#include + +#endif // NAZARA_UTILITY_COMPONENTS_SKELETONCOMPONENT_HPP diff --git a/include/Nazara/Utility/Components/SkeletonComponent.inl b/include/Nazara/Utility/Components/SkeletonComponent.inl new file mode 100644 index 000000000..ef9494b07 --- /dev/null +++ b/include/Nazara/Utility/Components/SkeletonComponent.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/include/Nazara/Utility/Components/SkeletonComponentBase.hpp b/include/Nazara/Utility/Components/SkeletonComponentBase.hpp new file mode 100644 index 000000000..016e1b5d4 --- /dev/null +++ b/include/Nazara/Utility/Components/SkeletonComponentBase.hpp @@ -0,0 +1,38 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_UTILITY_COMPONENTS_SKELETONCOMPONENTBASE_HPP +#define NAZARA_UTILITY_COMPONENTS_SKELETONCOMPONENTBASE_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_UTILITY_API SkeletonComponentBase + { + public: + SkeletonComponentBase(const SkeletonComponentBase&) = default; + SkeletonComponentBase(SkeletonComponentBase&&) noexcept = default; + ~SkeletonComponentBase() = default; + + virtual const Joint& GetAttachedJoint(std::size_t jointIndex) const = 0; + inline const std::shared_ptr& GetSkeleton() const; + + SkeletonComponentBase& operator=(const SkeletonComponentBase&) = default; + SkeletonComponentBase& operator=(SkeletonComponentBase&&) noexcept = default; + + protected: + SkeletonComponentBase(std::shared_ptr skeleton); + + std::shared_ptr m_referenceSkeleton; + }; +} + +#include + +#endif // NAZARA_UTILITY_COMPONENTS_SKELETONCOMPONENTBASE_HPP diff --git a/include/Nazara/Utility/Components/SkeletonComponentBase.inl b/include/Nazara/Utility/Components/SkeletonComponentBase.inl new file mode 100644 index 000000000..48d93e30a --- /dev/null +++ b/include/Nazara/Utility/Components/SkeletonComponentBase.inl @@ -0,0 +1,21 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline SkeletonComponentBase::SkeletonComponentBase(std::shared_ptr skeleton) : + m_referenceSkeleton(std::move(skeleton)) + { + } + + inline const std::shared_ptr& SkeletonComponentBase::GetSkeleton() const + { + return m_referenceSkeleton; + } +} + +#include diff --git a/include/Nazara/Utility/Systems.hpp b/include/Nazara/Utility/Systems.hpp index 89e275012..291bff42c 100644 --- a/include/Nazara/Utility/Systems.hpp +++ b/include/Nazara/Utility/Systems.hpp @@ -29,6 +29,7 @@ #ifndef NAZARA_UTILITY_SYSTEMS_HPP #define NAZARA_UTILITY_SYSTEMS_HPP +#include #include #endif // NAZARA_UTILITY_SYSTEMS_HPP diff --git a/include/Nazara/Utility/Systems/SkeletonSystem.hpp b/include/Nazara/Utility/Systems/SkeletonSystem.hpp new file mode 100644 index 000000000..46e7f0b76 --- /dev/null +++ b/include/Nazara/Utility/Systems/SkeletonSystem.hpp @@ -0,0 +1,41 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_UTILITY_SYSTEMS_SKELETONSYSTEM_HPP +#define NAZARA_UTILITY_SYSTEMS_SKELETONSYSTEM_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_UTILITY_API SkeletonSystem + { + public: + static constexpr bool AllowConcurrent = false; + static constexpr Int64 ExecutionOrder = -1'000; + + SkeletonSystem(entt::registry& registry); + SkeletonSystem(const SkeletonSystem&) = delete; + SkeletonSystem(SkeletonSystem&&) = delete; + ~SkeletonSystem(); + + void Update(float elapsedTime); + + SkeletonSystem& operator=(const SkeletonSystem&) = delete; + SkeletonSystem& operator=(SkeletonSystem&&) = delete; + + private: + entt::registry& m_registry; + entt::observer m_sharedSkeletonConstructObserver; + entt::observer m_skeletonConstructObserver; + }; +} + +#include + +#endif // NAZARA_UTILITY_SYSTEMS_SKELETONSYSTEM_HPP diff --git a/include/Nazara/Utility/Systems/SkeletonSystem.inl b/include/Nazara/Utility/Systems/SkeletonSystem.inl new file mode 100644 index 000000000..5c22d33d0 --- /dev/null +++ b/include/Nazara/Utility/Systems/SkeletonSystem.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/src/Nazara/Graphics/DepthPipelinePass.cpp b/src/Nazara/Graphics/DepthPipelinePass.cpp index 3826b8279..0280cdd0f 100644 --- a/src/Nazara/Graphics/DepthPipelinePass.cpp +++ b/src/Nazara/Graphics/DepthPipelinePass.cpp @@ -39,7 +39,7 @@ namespace Nz m_renderElements.clear(); for (const auto& renderableData : visibleRenderables) - renderableData.instancedRenderable->BuildElement(m_depthPassIndex, *renderableData.worldInstance, m_renderElements, renderableData.scissorBox); + renderableData.instancedRenderable->BuildElement(m_depthPassIndex, *renderableData.worldInstance, renderableData.skeletonInstance, m_renderElements, renderableData.scissorBox); m_renderQueueRegistry.Clear(); m_renderQueue.Clear(); diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index 9755ea3ce..22838127d 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -29,6 +29,7 @@ namespace Nz ForwardFramePipeline::ForwardFramePipeline() : m_renderablePool(4096), m_lightPool(64), + m_skeletonInstances(1024), m_viewerPool(8), m_worldInstances(2048), m_rebuildFrameGraph(true) @@ -41,6 +42,11 @@ namespace Nz m_viewerPool.Clear(); } + void ForwardFramePipeline::InvalidateSkeletalInstance(std::size_t skeletalInstanceIndex) + { + m_invalidatedSkeletonInstances.Set(skeletalInstanceIndex); + } + void ForwardFramePipeline::InvalidateViewer(std::size_t viewerIndex) { m_invalidatedViewerInstances.Set(viewerIndex); @@ -89,13 +95,14 @@ namespace Nz it->second.usedCount++; } - std::size_t ForwardFramePipeline::RegisterRenderable(std::size_t worldInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) + std::size_t ForwardFramePipeline::RegisterRenderable(std::size_t worldInstanceIndex, std::size_t skeletonInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) { std::size_t renderableIndex; RenderableData* renderableData = m_renderablePool.Allocate(renderableIndex); renderableData->renderable = instancedRenderable; renderableData->renderMask = renderMask; renderableData->scissorBox = scissorBox; + renderableData->skeletonInstanceIndex = skeletonInstanceIndex; renderableData->worldInstanceIndex = worldInstanceIndex; renderableData->onElementInvalidated.Connect(instancedRenderable->OnElementInvalidated, [=](InstancedRenderable* /*instancedRenderable*/) @@ -159,6 +166,16 @@ namespace Nz return renderableIndex; } + std::size_t ForwardFramePipeline::RegisterSkeleton(SkeletonInstancePtr skeletonInstance) + { + std::size_t skeletonInstanceIndex; + m_skeletonInstances.Allocate(skeletonInstanceIndex, std::move(skeletonInstance)); + + m_invalidatedSkeletonInstances.UnboundedSet(skeletonInstanceIndex); + + return skeletonInstanceIndex; + } + std::size_t ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) { std::size_t viewerIndex; @@ -191,10 +208,24 @@ namespace Nz Graphics* graphics = Graphics::Instance(); - // Destroy world instances at the end of the frame + // Destroy instances at the end of the frame + for (std::size_t skeletonInstanceIndex = m_removedSkeletonInstances.FindFirst(); skeletonInstanceIndex != m_removedSkeletonInstances.npos; skeletonInstanceIndex = m_removedSkeletonInstances.FindNext(skeletonInstanceIndex)) + { + renderFrame.PushForRelease(std::move(*m_skeletonInstances.RetrieveFromIndex(skeletonInstanceIndex))); + m_skeletonInstances.Free(skeletonInstanceIndex); + } + m_removedSkeletonInstances.Clear(); + + for (std::size_t viewerIndex = m_removedViewerInstances.FindFirst(); viewerIndex != m_removedViewerInstances.npos; viewerIndex = m_removedViewerInstances.FindNext(viewerIndex)) + { + renderFrame.PushForRelease(std::move(*m_viewerPool.RetrieveFromIndex(viewerIndex))); + m_viewerPool.Free(viewerIndex); + } + m_removedSkeletonInstances.Clear(); + for (std::size_t worldInstanceIndex = m_removedWorldInstances.FindFirst(); worldInstanceIndex != m_removedWorldInstances.npos; worldInstanceIndex = m_removedWorldInstances.FindNext(worldInstanceIndex)) { - renderFrame.PushForRelease(*m_worldInstances.RetrieveFromIndex(worldInstanceIndex)); + renderFrame.PushForRelease(std::move(*m_worldInstances.RetrieveFromIndex(worldInstanceIndex))); m_worldInstances.Free(worldInstanceIndex); } m_removedWorldInstances.Clear(); @@ -216,6 +247,13 @@ namespace Nz OnTransfer(this, renderFrame, builder); + for (std::size_t skeletonIndex = m_invalidatedSkeletonInstances.FindFirst(); skeletonIndex != m_invalidatedSkeletonInstances.npos; skeletonIndex = m_invalidatedSkeletonInstances.FindNext(skeletonIndex)) + { + SkeletonInstancePtr& skeletonInstance = *m_skeletonInstances.RetrieveFromIndex(skeletonIndex); + skeletonInstance->UpdateBuffers(uploadPool, builder); + } + m_invalidatedSkeletonInstances.Reset(); + for (std::size_t viewerIndex = m_invalidatedViewerInstances.FindFirst(); viewerIndex != m_invalidatedViewerInstances.npos; viewerIndex = m_invalidatedViewerInstances.FindNext(viewerIndex)) { ViewerData* viewerData = m_viewerPool.RetrieveFromIndex(viewerIndex); @@ -277,6 +315,11 @@ namespace Nz visibleRenderable.scissorBox = renderableData.scissorBox; visibleRenderable.worldInstance = worldInstance.get(); + if (renderableData.skeletonInstanceIndex != NoSkeletonInstance) + visibleRenderable.skeletonInstance = m_skeletonInstances.RetrieveFromIndex(renderableData.skeletonInstanceIndex)->get(); + else + visibleRenderable.skeletonInstance = nullptr; + visibilityHash = CombineHash(visibilityHash, std::hash()(&renderableData)); } @@ -423,11 +466,16 @@ namespace Nz m_renderablePool.Free(renderableIndex); } + void ForwardFramePipeline::UnregisterSkeleton(std::size_t skeletonIndex) + { + // Defer world instance release + m_removedSkeletonInstances.UnboundedSet(skeletonIndex); + } + void ForwardFramePipeline::UnregisterViewer(std::size_t viewerIndex) { - m_viewerPool.Free(viewerIndex); - m_invalidatedViewerInstances.Reset(viewerIndex); - m_rebuildFrameGraph = true; + // Defer world instance release + m_removedViewerInstances.UnboundedSet(viewerIndex); } void ForwardFramePipeline::UnregisterWorldInstance(std::size_t worldInstance) diff --git a/src/Nazara/Graphics/ForwardPipelinePass.cpp b/src/Nazara/Graphics/ForwardPipelinePass.cpp index 4e8b7c77e..0ce09502d 100644 --- a/src/Nazara/Graphics/ForwardPipelinePass.cpp +++ b/src/Nazara/Graphics/ForwardPipelinePass.cpp @@ -148,7 +148,7 @@ namespace Nz lightUboView = it->second; std::size_t previousCount = m_renderElements.size(); - renderableData.instancedRenderable->BuildElement(m_forwardPassIndex, *renderableData.worldInstance, m_renderElements, renderableData.scissorBox); + renderableData.instancedRenderable->BuildElement(m_forwardPassIndex, *renderableData.worldInstance, renderableData.skeletonInstance, m_renderElements, renderableData.scissorBox); for (std::size_t i = previousCount; i < m_renderElements.size(); ++i) { const RenderElement* element = m_renderElements[i].get(); diff --git a/src/Nazara/Graphics/LinearSlicedSprite.cpp b/src/Nazara/Graphics/LinearSlicedSprite.cpp index a7c75e3cb..4e76bb9da 100644 --- a/src/Nazara/Graphics/LinearSlicedSprite.cpp +++ b/src/Nazara/Graphics/LinearSlicedSprite.cpp @@ -22,7 +22,7 @@ namespace Nz UpdateVertices(); } - void LinearSlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const + void LinearSlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const { const auto& materialPass = m_material->GetPass(passIndex); if (!materialPass) diff --git a/src/Nazara/Graphics/Model.cpp b/src/Nazara/Graphics/Model.cpp index 081b63f24..e67ff50d6 100644 --- a/src/Nazara/Graphics/Model.cpp +++ b/src/Nazara/Graphics/Model.cpp @@ -37,7 +37,7 @@ namespace Nz UpdateAABB(aabb); } - void Model::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const + void Model::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const { for (std::size_t i = 0; i < m_submeshes.size(); ++i) { @@ -54,7 +54,7 @@ namespace Nz std::size_t indexCount = m_graphicalMesh->GetIndexCount(i); IndexType indexType = m_graphicalMesh->GetIndexType(i); - elements.emplace_back(std::make_unique(GetRenderLayer(), materialPass, renderPipeline, worldInstance, indexCount, indexType, indexBuffer, vertexBuffer, scissorBox)); + elements.emplace_back(std::make_unique(GetRenderLayer(), materialPass, renderPipeline, worldInstance, skeletonInstance, indexCount, indexType, indexBuffer, vertexBuffer, scissorBox)); } } diff --git a/src/Nazara/Graphics/PredefinedShaderStructs.cpp b/src/Nazara/Graphics/PredefinedShaderStructs.cpp index a513e39bb..988b9d228 100644 --- a/src/Nazara/Graphics/PredefinedShaderStructs.cpp +++ b/src/Nazara/Graphics/PredefinedShaderStructs.cpp @@ -93,7 +93,7 @@ namespace Nz nzsl::FieldOffsets skeletalStruct(nzsl::StructLayout::Std140); PredefinedSkeletalData skeletalData; - skeletalData.jointMatricesOffset = skeletalStruct.AddMatrixArray(nzsl::StructFieldType::Float1, 4, 4, true, 100); + skeletalData.jointMatricesOffset = skeletalStruct.AddMatrixArray(nzsl::StructFieldType::Float1, 4, 4, true, MaxMatricesCount); skeletalData.totalSize = skeletalStruct.GetAlignedSize(); diff --git a/src/Nazara/Graphics/SkeletonInstance.cpp b/src/Nazara/Graphics/SkeletonInstance.cpp new file mode 100644 index 000000000..94b4d3f68 --- /dev/null +++ b/src/Nazara/Graphics/SkeletonInstance.cpp @@ -0,0 +1,72 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + SkeletonInstance::SkeletonInstance(std::shared_ptr skeleton) : + m_skeleton(std::move(skeleton)), + m_dataInvalided(true) + { + NazaraAssert(m_skeleton, "invalid skeleton"); + + PredefinedSkeletalData skeletalUboOffsets = PredefinedSkeletalData::GetOffsets(); + + m_skeletalDataBuffer = Graphics::Instance()->GetRenderDevice()->InstantiateBuffer(BufferType::Uniform, skeletalUboOffsets.totalSize, BufferUsage::DeviceLocal | BufferUsage::Dynamic | BufferUsage::Write); + m_onSkeletonJointsInvalidated.Connect(m_skeleton->OnSkeletonJointsInvalidated, [this](const Skeleton*) + { + m_dataInvalided = true; + }); + } + + SkeletonInstance::SkeletonInstance(SkeletonInstance&& skeletonInstance) noexcept : + m_skeletalDataBuffer(std::move(skeletonInstance.m_skeletalDataBuffer)), + m_skeleton(std::move(skeletonInstance.m_skeleton)), + m_dataInvalided(skeletonInstance.m_dataInvalided) + { + m_onSkeletonJointsInvalidated.Connect(m_skeleton->OnSkeletonJointsInvalidated, [this](const Skeleton*) + { + m_dataInvalided = true; + }); + } + + void SkeletonInstance::UpdateBuffers(UploadPool& uploadPool, CommandBufferBuilder& builder) + { + if (m_dataInvalided) + { + PredefinedSkeletalData skeletalUboOffsets = PredefinedSkeletalData::GetOffsets(); + + auto& allocation = uploadPool.Allocate(m_skeletalDataBuffer->GetSize()); + Matrix4f* matrices = AccessByOffset(allocation.mappedPtr, skeletalUboOffsets.jointMatricesOffset); + + for (std::size_t i = 0; i < m_skeleton->GetJointCount(); ++i) + matrices[i] = m_skeleton->GetJoint(i)->GetSkinningMatrix(); + + builder.CopyBuffer(allocation, m_skeletalDataBuffer.get()); + + m_dataInvalided = false; + } + } + + SkeletonInstance& SkeletonInstance::operator=(SkeletonInstance&& skeletonInstance) noexcept + { + m_skeletalDataBuffer = std::move(skeletonInstance.m_skeletalDataBuffer); + m_skeleton = std::move(skeletonInstance.m_skeleton); + m_dataInvalided = skeletonInstance.m_dataInvalided; + + m_onSkeletonJointsInvalidated.Connect(m_skeleton->OnSkeletonJointsInvalidated, [this](const Skeleton*) + { + m_dataInvalided = true; + }); + + return *this; + } +} diff --git a/src/Nazara/Graphics/SlicedSprite.cpp b/src/Nazara/Graphics/SlicedSprite.cpp index 4cf485293..73dbe9c37 100644 --- a/src/Nazara/Graphics/SlicedSprite.cpp +++ b/src/Nazara/Graphics/SlicedSprite.cpp @@ -19,7 +19,7 @@ namespace Nz UpdateVertices(); } - void SlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const + void SlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const { const auto& materialPass = m_material->GetPass(passIndex); if (!materialPass) diff --git a/src/Nazara/Graphics/Sprite.cpp b/src/Nazara/Graphics/Sprite.cpp index 325732314..117786a9a 100644 --- a/src/Nazara/Graphics/Sprite.cpp +++ b/src/Nazara/Graphics/Sprite.cpp @@ -23,7 +23,7 @@ namespace Nz UpdateVertices(); } - void Sprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const + void Sprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const { const auto& materialPass = m_material->GetPass(passIndex); if (!materialPass) diff --git a/src/Nazara/Graphics/SubmeshRenderer.cpp b/src/Nazara/Graphics/SubmeshRenderer.cpp index 8dc547561..26f7ee94e 100644 --- a/src/Nazara/Graphics/SubmeshRenderer.cpp +++ b/src/Nazara/Graphics/SubmeshRenderer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -30,10 +31,10 @@ namespace Nz const MaterialPass* currentMaterialPass = nullptr; const RenderPipeline* currentPipeline = nullptr; const ShaderBinding* currentShaderBinding = nullptr; + const SkeletonInstance* currentSkeletonInstance = nullptr; const WorldInstance* currentWorldInstance = nullptr; Recti currentScissorBox = invalidScissorBox; RenderBufferView currentLightData; - RenderBufferView currentSkeletalData; auto FlushDrawCall = [&]() { @@ -82,6 +83,12 @@ namespace Nz currentVertexBuffer = vertexBuffer; } + if (const SkeletonInstance* skeletonInstance = submesh.GetSkeletonInstance(); currentSkeletonInstance != skeletonInstance) + { + FlushDrawData(); + currentSkeletonInstance = skeletonInstance; + } + if (const WorldInstance* worldInstance = &submesh.GetWorldInstance(); currentWorldInstance != worldInstance) { // TODO: Flushing draw calls on instance binding means we can have e.g. 1000 sprites rendered using a draw call for each one @@ -96,12 +103,6 @@ namespace Nz currentLightData = renderState.lightData; } - if (currentSkeletalData != renderState.skeletalData) - { - FlushDrawData(); - currentSkeletalData = renderState.skeletalData; - } - const Recti& scissorBox = submesh.GetScissorBox(); const Recti& targetScissorBox = (scissorBox.width >= 0) ? scissorBox : invalidScissorBox; if (currentScissorBox != targetScissorBox) @@ -142,13 +143,15 @@ namespace Nz }; } - if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::SkeletalDataUbo); bindingIndex != MaterialSettings::InvalidIndex && currentSkeletalData) + if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::SkeletalDataUbo); bindingIndex != MaterialSettings::InvalidIndex && currentSkeletonInstance) { + const auto& skeletalBuffer = currentSkeletonInstance->GetSkeletalBuffer(); + auto& bindingEntry = m_bindingCache.emplace_back(); bindingEntry.bindingIndex = bindingIndex; bindingEntry.content = ShaderBinding::UniformBufferBinding{ - currentSkeletalData.GetBuffer(), - currentSkeletalData.GetOffset(), currentSkeletalData.GetSize() + skeletalBuffer.get(), + 0, skeletalBuffer->GetSize() }; } diff --git a/src/Nazara/Graphics/Systems/RenderSystem.cpp b/src/Nazara/Graphics/Systems/RenderSystem.cpp index a82a13f74..9e831b310 100644 --- a/src/Nazara/Graphics/Systems/RenderSystem.cpp +++ b/src/Nazara/Graphics/Systems/RenderSystem.cpp @@ -13,15 +13,19 @@ #include #include #include +#include +#include #include namespace Nz { RenderSystem::RenderSystem(entt::registry& registry) : + m_registry(registry), m_cameraConstructObserver(registry, entt::collector.group()), m_graphicsConstructObserver(registry, entt::collector.group()), m_lightConstructObserver(registry, entt::collector.group()), - m_registry(registry), + m_sharedSkeletonConstructObserver(registry, entt::collector.group(entt::exclude)), + m_skeletonConstructObserver(registry, entt::collector.group(entt::exclude)), m_cameraEntityPool(8), m_graphicsEntityPool(1024), m_lightEntityPool(32) @@ -30,6 +34,8 @@ namespace Nz m_graphicsDestroyConnection = registry.on_destroy().connect<&RenderSystem::OnGraphicsDestroy>(this); m_lightDestroyConnection = registry.on_destroy().connect<&RenderSystem::OnLightDestroy>(this); m_nodeDestroyConnection = registry.on_destroy().connect<&RenderSystem::OnNodeDestroy>(this); + m_sharedSkeletonDestroyConnection = registry.on_destroy().connect<&RenderSystem::OnSharedSkeletonDestroy>(this); + m_skeletonDestroyConnection = registry.on_destroy().connect<&RenderSystem::OnSkeletonDestroy>(this); m_pipeline = std::make_unique(); } @@ -39,10 +45,6 @@ namespace Nz m_cameraConstructObserver.disconnect(); m_graphicsConstructObserver.disconnect(); m_lightConstructObserver.disconnect(); - m_cameraDestroyConnection.release(); - m_graphicsDestroyConnection.release(); - m_lightDestroyConnection.release(); - m_nodeDestroyConnection.release(); } void RenderSystem::Update(float /*elapsedTime*/) @@ -157,6 +159,52 @@ namespace Nz if (m_registry.try_get(entity)) OnLightDestroy(registry, entity); + + if (m_registry.try_get(entity)) + OnSharedSkeletonDestroy(registry, entity); + + if (m_registry.try_get(entity)) + OnSkeletonDestroy(registry, entity); + } + + void RenderSystem::OnSharedSkeletonDestroy(entt::registry& registry, entt::entity entity) + { + assert(&m_registry == ®istry); + + SharedSkeletonComponent& skeletonComponent = registry.get(entity); + Skeleton* skeleton = skeletonComponent.GetSkeleton().get(); + + auto skeletonInstanceIt = m_sharedSkeletonInstances.find(skeleton); + assert(skeletonInstanceIt != m_sharedSkeletonInstances.end()); + + SharedSkeleton& sharedSkeleton = skeletonInstanceIt->second; + assert(sharedSkeleton.useCount > 0); + if (--sharedSkeleton.useCount == 0) + { + m_pipeline->UnregisterSkeleton(sharedSkeleton.skeletonInstanceIndex); + m_sharedSkeletonInstances.erase(skeletonInstanceIt); + } + + auto it = m_graphicsEntities.find(entity); + if (it == m_graphicsEntities.end()) + return; + + GraphicsEntity* graphicsEntity = it->second; + graphicsEntity->skeletonInstanceIndex = NoInstance; + } + + void RenderSystem::OnSkeletonDestroy(entt::registry& registry, entt::entity entity) + { + assert(&m_registry == ®istry); + + auto it = m_graphicsEntities.find(entity); + if (it == m_graphicsEntities.end()) + return; + + GraphicsEntity* graphicsEntity = it->second; + + m_pipeline->UnregisterSkeleton(graphicsEntity->skeletonInstanceIndex); + graphicsEntity->skeletonInstanceIndex = NoInstance; } void RenderSystem::UpdateInstances() @@ -247,6 +295,7 @@ namespace Nz graphicsEntity->entity = entity; graphicsEntity->poolIndex = poolIndex; graphicsEntity->renderableIndices.fill(std::numeric_limits::max()); + graphicsEntity->skeletonInstanceIndex = NoInstance; //< will be set in skeleton observer graphicsEntity->worldInstanceIndex = m_pipeline->RegisterWorldInstance(entityGfx.GetWorldInstance()); graphicsEntity->onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, graphicsEntity](const Node* /*node*/) { @@ -259,7 +308,7 @@ namespace Nz return; const auto& renderableEntry = gfx->GetRenderableEntry(renderableIndex); - graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, gfx->GetScissorBox()); + graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, graphicsEntity->skeletonInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, gfx->GetScissorBox()); }); graphicsEntity->onRenderableDetach.Connect(entityGfx.OnRenderableDetach, [this, graphicsEntity](GraphicsComponent* gfx, std::size_t renderableIndex) @@ -301,16 +350,7 @@ namespace Nz m_invalidatedGfxWorldNode.insert(graphicsEntity); if (entityGfx.IsVisible()) - { - for (std::size_t renderableIndex = 0; renderableIndex < LightComponent::MaxLightCount; ++renderableIndex) - { - const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex); - if (!renderableEntry.renderable) - continue; - - graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, entityGfx.GetScissorBox()); - } - } + m_newlyVisibleGfxEntities.insert(graphicsEntity); assert(m_graphicsEntities.find(entity) == m_graphicsEntities.end()); m_graphicsEntities.emplace(entity, graphicsEntity); @@ -383,6 +423,46 @@ namespace Nz assert(m_lightEntities.find(entity) == m_lightEntities.end()); m_lightEntities.emplace(entity, lightEntity); }); + + m_sharedSkeletonConstructObserver.each([&](entt::entity entity) + { + GraphicsEntity* graphicsEntity = Retrieve(m_graphicsEntities, entity); + + SharedSkeletonComponent& skeletonComponent = m_registry.get(entity); + const std::shared_ptr& skeleton = skeletonComponent.GetSkeleton(); + + if (auto it = m_sharedSkeletonInstances.find(skeleton.get()); it == m_sharedSkeletonInstances.end()) + { + SharedSkeleton& sharedSkeleton = m_sharedSkeletonInstances[skeleton.get()]; + sharedSkeleton.skeletonInstanceIndex = m_pipeline->RegisterSkeleton(std::make_shared(skeleton)); + sharedSkeleton.useCount = 1; + sharedSkeleton.onJointsInvalidated.Connect(skeleton->OnSkeletonJointsInvalidated, [this, instanceIndex = sharedSkeleton.skeletonInstanceIndex](const Skeleton* /*skeleton*/) + { + m_pipeline->InvalidateSkeletalInstance(instanceIndex); + }); + + graphicsEntity->skeletonInstanceIndex = sharedSkeleton.skeletonInstanceIndex; + } + else + { + it->second.useCount++; + graphicsEntity->skeletonInstanceIndex = it->second.skeletonInstanceIndex; + } + }); + + m_skeletonConstructObserver.each([&](entt::entity entity) + { + GraphicsEntity* graphicsEntity = Retrieve(m_graphicsEntities, entity); + + SkeletonComponent& skeletonComponent = m_registry.get(entity); + const std::shared_ptr& skeleton = skeletonComponent.GetSkeleton(); + + graphicsEntity->skeletonInstanceIndex = m_pipeline->RegisterSkeleton(std::make_shared(skeleton)); + graphicsEntity->onSkeletonJointsInvalidated.Connect(skeleton->OnSkeletonJointsInvalidated, [this, instanceIndex = graphicsEntity->skeletonInstanceIndex](const Skeleton* /*skeleton*/) + { + m_pipeline->InvalidateSkeletalInstance(instanceIndex); + }); + }); } void RenderSystem::UpdateVisibility() @@ -414,7 +494,7 @@ namespace Nz if (!renderableEntry.renderable) continue; - graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, entityGfx.GetScissorBox()); + graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, graphicsEntity->skeletonInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, entityGfx.GetScissorBox()); } } m_newlyVisibleGfxEntities.clear(); diff --git a/src/Nazara/Graphics/TextSprite.cpp b/src/Nazara/Graphics/TextSprite.cpp index 080d0d54a..9f394a767 100644 --- a/src/Nazara/Graphics/TextSprite.cpp +++ b/src/Nazara/Graphics/TextSprite.cpp @@ -18,7 +18,7 @@ namespace Nz { } - void TextSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const + void TextSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector>& elements, const Recti& scissorBox) const { const auto& materialPass = m_material->GetPass(passIndex); if (!materialPass) diff --git a/src/Nazara/Physics2D/Systems/Physics2DSystem.cpp b/src/Nazara/Physics2D/Systems/Physics2DSystem.cpp index cdf492550..fe140bc6b 100644 --- a/src/Nazara/Physics2D/Systems/Physics2DSystem.cpp +++ b/src/Nazara/Physics2D/Systems/Physics2DSystem.cpp @@ -31,8 +31,6 @@ namespace Nz auto rigidBodyView = m_registry.view(); for (auto [entity, rigidBodyComponent] : rigidBodyView.each()) rigidBodyComponent.Destroy(); - - m_constructConnection.release(); } void Physics2DSystem::Update(float elapsedTime) diff --git a/src/Nazara/Physics3D/Systems/Physics3DSystem.cpp b/src/Nazara/Physics3D/Systems/Physics3DSystem.cpp index 89ba7144b..48bcf92c4 100644 --- a/src/Nazara/Physics3D/Systems/Physics3DSystem.cpp +++ b/src/Nazara/Physics3D/Systems/Physics3DSystem.cpp @@ -20,8 +20,6 @@ namespace Nz auto rigidBodyView = m_registry.view(); for (auto [entity, rigidBodyComponent] : rigidBodyView.each()) rigidBodyComponent.Destroy(); - - m_constructConnection.release(); } void Physics3DSystem::Update(float elapsedTime) diff --git a/src/Nazara/Utility/Components/SharedSkeletonComponent.cpp b/src/Nazara/Utility/Components/SharedSkeletonComponent.cpp new file mode 100644 index 000000000..c33e61c9a --- /dev/null +++ b/src/Nazara/Utility/Components/SharedSkeletonComponent.cpp @@ -0,0 +1,96 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + SharedSkeletonComponent::SharedSkeletonComponent(std::shared_ptr skeleton) : + SkeletonComponentBase(std::move(skeleton)), + m_skeletonJointInvalidated(true) + { + SetupSkeleton(); + } + + SharedSkeletonComponent::SharedSkeletonComponent(const SharedSkeletonComponent& sharedSkeletalComponent) : + SkeletonComponentBase(sharedSkeletalComponent), + m_attachedSkeleton(sharedSkeletalComponent.m_attachedSkeleton), + m_skeletonJointInvalidated(true) + { + SetupSkeleton(); + } + + SharedSkeletonComponent::SharedSkeletonComponent(SharedSkeletonComponent&& sharedSkeletalComponent) noexcept : + SkeletonComponentBase(std::move(sharedSkeletalComponent)), + m_attachedSkeleton(std::move(sharedSkeletalComponent.m_attachedSkeleton)), + m_skeletonJointInvalidated(sharedSkeletalComponent.m_skeletonJointInvalidated) + { + SetupSkeleton(); + } + + const Joint& SharedSkeletonComponent::GetAttachedJoint(std::size_t jointIndex) const + { + return *m_attachedSkeleton.GetJoint(jointIndex); + } + + SharedSkeletonComponent& SharedSkeletonComponent::operator=(const SharedSkeletonComponent& sharedSkeletalComponent) + { + SkeletonComponentBase::operator=(sharedSkeletalComponent); + + m_attachedSkeleton = sharedSkeletalComponent.m_attachedSkeleton; + m_skeletonJointInvalidated = true; + SetupSkeleton(); + + return *this; + } + + SharedSkeletonComponent& SharedSkeletonComponent::operator=(SharedSkeletonComponent&& sharedSkeletalComponent) noexcept + { + SkeletonComponentBase::operator=(std::move(sharedSkeletalComponent)); + + m_attachedSkeleton = std::move(sharedSkeletalComponent.m_attachedSkeleton); + m_skeletonJointInvalidated = sharedSkeletalComponent.m_skeletonJointInvalidated; + SetupSkeleton(); + + return *this; + } + + void SharedSkeletonComponent::OnReferenceJointsInvalidated(const Skeleton* skeleton) + { + m_skeletonJointInvalidated = true; + } + + void SharedSkeletonComponent::SetSkeletonParent(Node* parent) + { + m_attachedSkeleton.GetRootJoint()->SetParent(parent); + } + + void SharedSkeletonComponent::SetupSkeleton() + { + assert(m_referenceSkeleton); + m_attachedSkeleton = *m_referenceSkeleton; + m_referenceSkeleton->OnSkeletonJointsInvalidated.Connect(this, &SharedSkeletonComponent::OnReferenceJointsInvalidated); + } + + void SharedSkeletonComponent::UpdateAttachedSkeletonJoints() + { + assert(m_referenceSkeleton->GetJointCount() == m_attachedSkeleton.GetJointCount()); + std::size_t jointCount = m_referenceSkeleton->GetJointCount(); + + // TODO: This will trigger a lot of invalidation which can be avoided + for (std::size_t i = 0; i < jointCount; ++i) + { + const Joint* referenceJoint = m_referenceSkeleton->GetJoint(i); + Joint* attachedJoint = m_attachedSkeleton.GetJoint(i); + + attachedJoint->SetPosition(referenceJoint->GetPosition()); + attachedJoint->SetRotation(referenceJoint->GetRotation()); + attachedJoint->SetScale(referenceJoint->GetScale()); + } + + m_skeletonJointInvalidated = false; + } +} diff --git a/src/Nazara/Utility/Components/SkeletonComponent.cpp b/src/Nazara/Utility/Components/SkeletonComponent.cpp new file mode 100644 index 000000000..f690e9660 --- /dev/null +++ b/src/Nazara/Utility/Components/SkeletonComponent.cpp @@ -0,0 +1,24 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + SkeletonComponent::SkeletonComponent(std::shared_ptr skeleton) : + SkeletonComponentBase(std::move(skeleton)) + { + } + + const Joint& SkeletonComponent::GetAttachedJoint(std::size_t jointIndex) const + { + return *m_referenceSkeleton->GetJoint(jointIndex); + } + + Node* SkeletonComponent::GetRootNode() + { + return m_referenceSkeleton->GetRootJoint(); + } +} diff --git a/src/Nazara/Utility/Components/SkeletonComponentBase.cpp b/src/Nazara/Utility/Components/SkeletonComponentBase.cpp new file mode 100644 index 000000000..60b60e7a0 --- /dev/null +++ b/src/Nazara/Utility/Components/SkeletonComponentBase.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} diff --git a/src/Nazara/Utility/Skeleton.cpp b/src/Nazara/Utility/Skeleton.cpp index 6d4a8b276..6cc15136d 100644 --- a/src/Nazara/Utility/Skeleton.cpp +++ b/src/Nazara/Utility/Skeleton.cpp @@ -217,15 +217,15 @@ namespace Nz m_impl->joints = skeleton.m_impl->joints; // Restore parent hierarchy - const Node* firstJoint = skeleton.m_impl->joints.data(); + const Joint* firstJoint = skeleton.m_impl->joints.data(); std::size_t jointCount = skeleton.m_impl->joints.size(); for (std::size_t i = 0; i < jointCount; ++i) { - const Node* parent = skeleton.m_impl->joints[i].GetParent(); + const Joint* parent = SafeCast(skeleton.m_impl->joints[i].GetParent()); if (parent) { - std::size_t parentIndex = SafeCast(firstJoint - parent); + std::size_t parentIndex = SafeCast(parent - firstJoint); m_impl->joints[i].SetParent(m_impl->joints[parentIndex]); } } diff --git a/src/Nazara/Utility/Systems/SkeletonSystem.cpp b/src/Nazara/Utility/Systems/SkeletonSystem.cpp new file mode 100644 index 000000000..5b0a28b87 --- /dev/null +++ b/src/Nazara/Utility/Systems/SkeletonSystem.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + SkeletonSystem::SkeletonSystem(entt::registry& registry) : + m_registry(registry), + m_sharedSkeletonConstructObserver(registry, entt::collector.group(entt::exclude)), + m_skeletonConstructObserver(registry, entt::collector.group(entt::exclude)) + { + } + + SkeletonSystem::~SkeletonSystem() + { + m_sharedSkeletonConstructObserver.disconnect(); + m_skeletonConstructObserver.disconnect(); + } + + void SkeletonSystem::Update(float /*elapsedTime*/) + { + m_sharedSkeletonConstructObserver.each([&](entt::entity entity) + { + NodeComponent& entityNode = m_registry.get(entity); + SharedSkeletonComponent& entitySkeleton = m_registry.get(entity); + + entitySkeleton.SetSkeletonParent(&entityNode); + }); + + m_skeletonConstructObserver.each([&](entt::entity entity) + { + NodeComponent& entityNode = m_registry.get(entity); + SkeletonComponent& entitySkeleton = m_registry.get(entity); + + // TODO: When attaching for the first time, set the skeleton to the position of the node before attaching the node + Node* skeletonRoot = entitySkeleton.GetRootNode(); + + entityNode.SetParent(entitySkeleton.GetRootNode()); + }); + + // Updated attached skeleton joints (TODO: Only do this if necessary) + auto view = m_registry.view(); + for (auto entity : view) + { + auto& sharedSkeletonComponent = view.get(entity); + if (sharedSkeletonComponent.IsAttachedSkeletonOutdated()) + sharedSkeletonComponent.UpdateAttachedSkeletonJoints(); + } + } +}