Add initial support for skeletal entities / components

This commit is contained in:
SirLynix
2022-08-27 03:07:48 +02:00
parent 01f3f350fe
commit 50ed8b4028
55 changed files with 919 additions and 131 deletions

View File

@@ -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();

View File

@@ -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<const void*>()(&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)

View File

@@ -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();

View File

@@ -22,7 +22,7 @@ namespace Nz
UpdateVertices();
}
void LinearSlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements, const Recti& scissorBox) const
void LinearSlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector<std::unique_ptr<RenderElement>>& elements, const Recti& scissorBox) const
{
const auto& materialPass = m_material->GetPass(passIndex);
if (!materialPass)

View File

@@ -37,7 +37,7 @@ namespace Nz
UpdateAABB(aabb);
}
void Model::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements, const Recti& scissorBox) const
void Model::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector<std::unique_ptr<RenderElement>>& 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<RenderSubmesh>(GetRenderLayer(), materialPass, renderPipeline, worldInstance, indexCount, indexType, indexBuffer, vertexBuffer, scissorBox));
elements.emplace_back(std::make_unique<RenderSubmesh>(GetRenderLayer(), materialPass, renderPipeline, worldInstance, skeletonInstance, indexCount, indexType, indexBuffer, vertexBuffer, scissorBox));
}
}

View File

@@ -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();

View File

@@ -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 <Nazara/Graphics/SkeletonInstance.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Renderer/UploadPool.hpp>
#include <Nazara/Utility/Joint.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
SkeletonInstance::SkeletonInstance(std::shared_ptr<const Skeleton> 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<Matrix4f*>(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;
}
}

View File

@@ -19,7 +19,7 @@ namespace Nz
UpdateVertices();
}
void SlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements, const Recti& scissorBox) const
void SlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector<std::unique_ptr<RenderElement>>& elements, const Recti& scissorBox) const
{
const auto& materialPass = m_material->GetPass(passIndex);
if (!materialPass)

View File

@@ -23,7 +23,7 @@ namespace Nz
UpdateVertices();
}
void Sprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements, const Recti& scissorBox) const
void Sprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector<std::unique_ptr<RenderElement>>& elements, const Recti& scissorBox) const
{
const auto& materialPass = m_material->GetPass(passIndex);
if (!materialPass)

View File

@@ -5,6 +5,7 @@
#include <Nazara/Graphics/SubmeshRenderer.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/RenderSubmesh.hpp>
#include <Nazara/Graphics/SkeletonInstance.hpp>
#include <Nazara/Graphics/ViewerInstance.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Renderer/RenderFrame.hpp>
@@ -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()
};
}

View File

@@ -13,15 +13,19 @@
#include <Nazara/Renderer/RenderWindow.hpp>
#include <Nazara/Renderer/UploadPool.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
#include <Nazara/Utility/Components/SharedSkeletonComponent.hpp>
#include <Nazara/Utility/Components/SkeletonComponent.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
RenderSystem::RenderSystem(entt::registry& registry) :
m_registry(registry),
m_cameraConstructObserver(registry, entt::collector.group<CameraComponent, NodeComponent>()),
m_graphicsConstructObserver(registry, entt::collector.group<GraphicsComponent, NodeComponent>()),
m_lightConstructObserver(registry, entt::collector.group<LightComponent, NodeComponent>()),
m_registry(registry),
m_sharedSkeletonConstructObserver(registry, entt::collector.group<GraphicsComponent, NodeComponent, SharedSkeletonComponent>(entt::exclude<SkeletonComponent>)),
m_skeletonConstructObserver(registry, entt::collector.group<GraphicsComponent, NodeComponent, SkeletonComponent>(entt::exclude<SharedSkeletonComponent>)),
m_cameraEntityPool(8),
m_graphicsEntityPool(1024),
m_lightEntityPool(32)
@@ -30,6 +34,8 @@ namespace Nz
m_graphicsDestroyConnection = registry.on_destroy<GraphicsComponent>().connect<&RenderSystem::OnGraphicsDestroy>(this);
m_lightDestroyConnection = registry.on_destroy<LightComponent>().connect<&RenderSystem::OnLightDestroy>(this);
m_nodeDestroyConnection = registry.on_destroy<NodeComponent>().connect<&RenderSystem::OnNodeDestroy>(this);
m_sharedSkeletonDestroyConnection = registry.on_destroy<SharedSkeletonComponent>().connect<&RenderSystem::OnSharedSkeletonDestroy>(this);
m_skeletonDestroyConnection = registry.on_destroy<SkeletonComponent>().connect<&RenderSystem::OnSkeletonDestroy>(this);
m_pipeline = std::make_unique<ForwardFramePipeline>();
}
@@ -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<LightComponent>(entity))
OnLightDestroy(registry, entity);
if (m_registry.try_get<SharedSkeletonComponent>(entity))
OnSharedSkeletonDestroy(registry, entity);
if (m_registry.try_get<SkeletonComponent>(entity))
OnSkeletonDestroy(registry, entity);
}
void RenderSystem::OnSharedSkeletonDestroy(entt::registry& registry, entt::entity entity)
{
assert(&m_registry == &registry);
SharedSkeletonComponent& skeletonComponent = registry.get<SharedSkeletonComponent>(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 == &registry);
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<std::size_t>::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<SharedSkeletonComponent>(entity);
const std::shared_ptr<Skeleton>& 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<SkeletonInstance>(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<SkeletonComponent>(entity);
const std::shared_ptr<Skeleton>& skeleton = skeletonComponent.GetSkeleton();
graphicsEntity->skeletonInstanceIndex = m_pipeline->RegisterSkeleton(std::make_shared<SkeletonInstance>(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();

View File

@@ -18,7 +18,7 @@ namespace Nz
{
}
void TextSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements, const Recti& scissorBox) const
void TextSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, const SkeletonInstance* skeletonInstance, std::vector<std::unique_ptr<RenderElement>>& elements, const Recti& scissorBox) const
{
const auto& materialPass = m_material->GetPass(passIndex);
if (!materialPass)

View File

@@ -31,8 +31,6 @@ namespace Nz
auto rigidBodyView = m_registry.view<RigidBody2DComponent>();
for (auto [entity, rigidBodyComponent] : rigidBodyView.each())
rigidBodyComponent.Destroy();
m_constructConnection.release();
}
void Physics2DSystem::Update(float elapsedTime)

View File

@@ -20,8 +20,6 @@ namespace Nz
auto rigidBodyView = m_registry.view<RigidBody3DComponent>();
for (auto [entity, rigidBodyComponent] : rigidBodyView.each())
rigidBodyComponent.Destroy();
m_constructConnection.release();
}
void Physics3DSystem::Update(float elapsedTime)

View File

@@ -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 <Nazara/Utility/Components/SharedSkeletonComponent.hpp>
#include <Nazara/Utility/Joint.hpp>
#include <Nazara/Utility/Debug.hpp>
namespace Nz
{
SharedSkeletonComponent::SharedSkeletonComponent(std::shared_ptr<Skeleton> 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;
}
}

View File

@@ -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 <Nazara/Utility/Components/SkeletonComponent.hpp>
#include <Nazara/Utility/Debug.hpp>
namespace Nz
{
SkeletonComponent::SkeletonComponent(std::shared_ptr<Skeleton> 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();
}
}

View File

@@ -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 <Nazara/Utility/Components/SkeletonComponentBase.hpp>
#include <Nazara/Utility/Debug.hpp>
namespace Nz
{
}

View File

@@ -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<const Joint*>(skeleton.m_impl->joints[i].GetParent());
if (parent)
{
std::size_t parentIndex = SafeCast<std::size_t>(firstJoint - parent);
std::size_t parentIndex = SafeCast<std::size_t>(parent - firstJoint);
m_impl->joints[i].SetParent(m_impl->joints[parentIndex]);
}
}

View File

@@ -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 <Nazara/Utility/Systems/SkeletonSystem.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
#include <Nazara/Utility/Components/SharedSkeletonComponent.hpp>
#include <Nazara/Utility/Components/SkeletonComponent.hpp>
#include <Nazara/Utility/Debug.hpp>
namespace Nz
{
SkeletonSystem::SkeletonSystem(entt::registry& registry) :
m_registry(registry),
m_sharedSkeletonConstructObserver(registry, entt::collector.group<NodeComponent, SharedSkeletonComponent>(entt::exclude<SkeletonComponent>)),
m_skeletonConstructObserver(registry, entt::collector.group<NodeComponent, SkeletonComponent>(entt::exclude<SharedSkeletonComponent>))
{
}
SkeletonSystem::~SkeletonSystem()
{
m_sharedSkeletonConstructObserver.disconnect();
m_skeletonConstructObserver.disconnect();
}
void SkeletonSystem::Update(float /*elapsedTime*/)
{
m_sharedSkeletonConstructObserver.each([&](entt::entity entity)
{
NodeComponent& entityNode = m_registry.get<NodeComponent>(entity);
SharedSkeletonComponent& entitySkeleton = m_registry.get<SharedSkeletonComponent>(entity);
entitySkeleton.SetSkeletonParent(&entityNode);
});
m_skeletonConstructObserver.each([&](entt::entity entity)
{
NodeComponent& entityNode = m_registry.get<NodeComponent>(entity);
SkeletonComponent& entitySkeleton = m_registry.get<SkeletonComponent>(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<NodeComponent, SharedSkeletonComponent>();
for (auto entity : view)
{
auto& sharedSkeletonComponent = view.get<SharedSkeletonComponent>(entity);
if (sharedSkeletonComponent.IsAttachedSkeletonOutdated())
sharedSkeletonComponent.UpdateAttachedSkeletonJoints();
}
}
}