diff --git a/SDK/include/NDK/Components/GraphicsComponent.hpp b/SDK/include/NDK/Components/GraphicsComponent.hpp index c34d1cf42..28911778d 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.hpp +++ b/SDK/include/NDK/Components/GraphicsComponent.hpp @@ -8,6 +8,7 @@ #define NDK_COMPONENTS_GRAPHICSCOMPONENT_HPP #include +#include #include namespace Ndk @@ -16,22 +17,49 @@ namespace Ndk { public: GraphicsComponent() = default; + inline GraphicsComponent(const GraphicsComponent& graphicsComponent); ~GraphicsComponent() = default; - inline void AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const; + inline void AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const; inline void Attach(NzRenderableRef renderable); + inline void EnsureTransformMatrixUpdate() const; + static ComponentIndex componentIndex; private: + void InvalidateRenderableData(const NzRenderable* renderable, nzUInt32 flags, unsigned int index); + inline void InvalidateTransformMatrix(); + + void OnAttached() override; + void OnComponentAttached(BaseComponent& component) override; + void OnComponentDetached(BaseComponent& component) override; + void OnDetached() override; + void OnNodeInvalidated(const NzNode* node); + + void UpdateTransformMatrix() const; + + NazaraSlot(NzNode, OnNodeInvalidation, m_nodeInvalidationSlot); + struct Renderable { - NzBoundingVolumef volume; + Renderable(NzMatrix4f& transformMatrix) : + data(transformMatrix), + dataUpdated(false) + { + } + + NazaraSlot(NzRenderable, OnRenderableInvalidateInstanceData, renderableInvalidationSlot); + + mutable NzRenderable::InstanceData data; NzRenderableRef renderable; + mutable bool dataUpdated; }; std::vector m_renderables; + mutable NzMatrix4f m_transformMatrix; + mutable bool m_transformMatrixUpdated; }; } diff --git a/SDK/include/NDK/Components/GraphicsComponent.inl b/SDK/include/NDK/Components/GraphicsComponent.inl index 9fa6a81b3..ad145121d 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.inl +++ b/SDK/include/NDK/Components/GraphicsComponent.inl @@ -6,16 +6,48 @@ namespace Ndk { - inline void GraphicsComponent::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const + inline GraphicsComponent::GraphicsComponent(const GraphicsComponent& graphicsComponent) : + Component(graphicsComponent), + m_transformMatrix(graphicsComponent.m_transformMatrix), + m_transformMatrixUpdated(graphicsComponent.m_transformMatrixUpdated) { + m_renderables.reserve(graphicsComponent.m_renderables.size()); + for (const Renderable& r : graphicsComponent.m_renderables) + Attach(r.renderable); + } + + inline void GraphicsComponent::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const + { + EnsureTransformMatrixUpdate(); + for (const Renderable& object : m_renderables) - object.renderable->AddToRenderQueue(renderQueue, transformMatrix); + { + if (!object.dataUpdated) + { + object.renderable->UpdateData(&object.data); + object.dataUpdated = true; + } + + object.renderable->AddToRenderQueue(renderQueue, object.data); + } } inline void GraphicsComponent::Attach(NzRenderableRef renderable) { - m_renderables.resize(m_renderables.size() + 1); + m_renderables.emplace_back(m_transformMatrix); Renderable& r = m_renderables.back(); r.renderable = std::move(renderable); + r.renderableInvalidationSlot.Connect(r.renderable->OnRenderableInvalidateInstanceData, std::bind(InvalidateRenderableData, this, std::placeholders::_1, std::placeholders::_2, m_renderables.size()-1)); + } + + inline void GraphicsComponent::EnsureTransformMatrixUpdate() const + { + if (!m_transformMatrixUpdated) + UpdateTransformMatrix(); + } + + inline void GraphicsComponent::InvalidateTransformMatrix() + { + m_transformMatrixUpdated = false; } } diff --git a/SDK/src/NDK/Components/GraphicsComponent.cpp b/SDK/src/NDK/Components/GraphicsComponent.cpp index 7f6873d95..228bff7d8 100644 --- a/SDK/src/NDK/Components/GraphicsComponent.cpp +++ b/SDK/src/NDK/Components/GraphicsComponent.cpp @@ -3,8 +3,72 @@ // For conditions of distribution and use, see copyright notice in Prerequesites.hpp #include +#include namespace Ndk { + void GraphicsComponent::InvalidateRenderableData(const NzRenderable* renderable, nzUInt32 flags, unsigned int index) + { + NazaraUnused(renderable); + + NazaraAssert(index < m_renderables.size(), "Invalid renderable index"); + + Renderable& r = m_renderables[index]; + r.dataUpdated = false; + r.renderable->InvalidateData(&r.data, flags); + } + + void GraphicsComponent::OnAttached() + { + if (m_entity->HasComponent()) + m_nodeInvalidationSlot.Connect(m_entity->GetComponent().OnNodeInvalidation, this, OnNodeInvalidated); + + InvalidateTransformMatrix(); + } + + void GraphicsComponent::OnComponentAttached(BaseComponent& component) + { + if (IsComponent(component)) + { + NodeComponent& nodeComponent = static_cast(component); + m_nodeInvalidationSlot.Connect(nodeComponent.OnNodeInvalidation, this, OnNodeInvalidated); + + InvalidateTransformMatrix(); + } + } + + void GraphicsComponent::OnComponentDetached(BaseComponent& component) + { + if (IsComponent(component)) + { + m_nodeInvalidationSlot.Disconnect(); + + InvalidateTransformMatrix(); + } + } + + void GraphicsComponent::OnDetached() + { + m_nodeInvalidationSlot.Disconnect(); + + InvalidateTransformMatrix(); + } + + void GraphicsComponent::OnNodeInvalidated(const NzNode* node) + { + NazaraUnused(node); + + // Our view matrix depends on NodeComponent position/rotation + InvalidateTransformMatrix(); + } + + void GraphicsComponent::UpdateTransformMatrix() const + { + NazaraAssert(m_entity && m_entity->HasComponent(), "GraphicsComponent requires NodeComponent"); + + m_transformMatrix = m_entity->GetComponent().GetTransformMatrix(); + m_transformMatrixUpdated = true; + } + ComponentIndex GraphicsComponent::componentIndex; } diff --git a/SDK/src/NDK/Systems/RenderSystem.cpp b/SDK/src/NDK/Systems/RenderSystem.cpp index 3cb48f4bb..9bd53e58a 100644 --- a/SDK/src/NDK/Systems/RenderSystem.cpp +++ b/SDK/src/NDK/Systems/RenderSystem.cpp @@ -29,7 +29,7 @@ namespace Ndk GraphicsComponent& graphicsComponent = drawable->GetComponent(); NodeComponent& drawableNode = drawable->GetComponent(); - graphicsComponent.AddToRenderQueue(renderQueue, drawableNode.GetTransformMatrix()); + graphicsComponent.AddToRenderQueue(renderQueue); } NzColorBackground background; diff --git a/include/Nazara/Graphics/Light.hpp b/include/Nazara/Graphics/Light.hpp index cff58da44..8a23bf73b 100644 --- a/include/Nazara/Graphics/Light.hpp +++ b/include/Nazara/Graphics/Light.hpp @@ -29,12 +29,12 @@ class NAZARA_API NzLight : public NzRenderable NzLight(const NzLight& light) = default; ~NzLight() = default; - void AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const override; + void AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; NzLight* Clone() const; NzLight* Create() const; - bool Cull(const NzFrustumf& frustum, const NzBoundingVolumef& volume, const NzMatrix4f& transformMatrix) const override; + bool Cull(const NzFrustumf& frustum, const InstanceData& instanceData) const override; float GetAmbientFactor() const; float GetAttenuation() const; @@ -58,7 +58,7 @@ class NAZARA_API NzLight : public NzRenderable void SetOuterAngle(float outerAngle); void SetRadius(float radius); - void UpdateBoundingVolume(NzBoundingVolumef* boundingVolume, const NzMatrix4f& transformMatrix) const; + void UpdateBoundingVolume(InstanceData* instanceData) const; NzLight& operator=(const NzLight& light) = default; diff --git a/include/Nazara/Graphics/Model.hpp b/include/Nazara/Graphics/Model.hpp index 44167ab38..4b0d1e1ae 100644 --- a/include/Nazara/Graphics/Model.hpp +++ b/include/Nazara/Graphics/Model.hpp @@ -42,7 +42,7 @@ class NAZARA_API NzModel : public NzRenderable, public NzResource NzModel(NzModel&& model) = default; virtual ~NzModel(); - void AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const override; + void AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; NzMaterial* GetMaterial(const NzString& subMeshName) const; NzMaterial* GetMaterial(unsigned int matIndex) const; diff --git a/include/Nazara/Graphics/Renderable.hpp b/include/Nazara/Graphics/Renderable.hpp index 0d807a7b1..242e1f021 100644 --- a/include/Nazara/Graphics/Renderable.hpp +++ b/include/Nazara/Graphics/Renderable.hpp @@ -27,25 +27,45 @@ using NzRenderableRef = NzObjectRef; class NAZARA_API NzRenderable : public NzRefCounted { public: + struct InstanceData; + NzRenderable() = default; inline NzRenderable(const NzRenderable& renderable); virtual ~NzRenderable(); inline void EnsureBoundingVolumeUpdated() const; - virtual void AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const = 0; - virtual bool Cull(const NzFrustumf& frustum, const NzBoundingVolumef& volume, const NzMatrix4f& transformMatrix) const; + virtual void AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const = 0; + virtual bool Cull(const NzFrustumf& frustum, const InstanceData& instanceData) const; virtual const NzBoundingVolumef& GetBoundingVolume() const; - virtual void UpdateBoundingVolume(NzBoundingVolumef* boundingVolume, const NzMatrix4f& transformMatrix) const; + virtual void InvalidateData(InstanceData* instanceData, nzUInt32 flags) const; + virtual void UpdateBoundingVolume(InstanceData* instanceData) const; + virtual void UpdateData(InstanceData* instanceData) const; inline NzRenderable& operator=(const NzRenderable& renderable); // Signals: + NazaraSignal(OnRenderableInvalidateInstanceData, const NzRenderable*, nzUInt32); //< Args: me, flags NazaraSignal(OnRenderableRelease, const NzRenderable*); //< Args: me + struct InstanceData + { + InstanceData(NzMatrix4f& referenceMatrix) : + transformMatrix(referenceMatrix), + flags(0) + { + } + + std::vector data; + NzBoundingVolumef volume; + NzMatrix4f& transformMatrix; + nzUInt32 flags; + }; + protected: virtual void MakeBoundingVolume() const = 0; void InvalidateBoundingVolume(); + inline void InvalidateInstanceData(nzUInt32 flags); inline void UpdateBoundingVolume() const; mutable NzBoundingVolumef m_boundingVolume; diff --git a/include/Nazara/Graphics/Renderable.inl b/include/Nazara/Graphics/Renderable.inl index cebf623a1..a3035d23b 100644 --- a/include/Nazara/Graphics/Renderable.inl +++ b/include/Nazara/Graphics/Renderable.inl @@ -19,6 +19,11 @@ inline void NzRenderable::InvalidateBoundingVolume() m_boundingVolumeUpdated = false; } +inline void NzRenderable::InvalidateInstanceData(nzUInt32 flags) +{ + OnRenderableInvalidateInstanceData(this, flags); +} + inline NzRenderable& NzRenderable::operator=(const NzRenderable& renderable) { m_boundingVolume = renderable.m_boundingVolume; diff --git a/include/Nazara/Graphics/SkeletalModel.hpp b/include/Nazara/Graphics/SkeletalModel.hpp index bbea076b8..dcc5bd63b 100644 --- a/include/Nazara/Graphics/SkeletalModel.hpp +++ b/include/Nazara/Graphics/SkeletalModel.hpp @@ -38,7 +38,7 @@ class NAZARA_API NzSkeletalModel : public NzModel, NzUpdatable NzSkeletalModel(NzSkeletalModel&& model) = default; ~NzSkeletalModel() = default; - void AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const override; + void AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; void AdvanceAnimation(float elapsedTime); NzSkeletalModel* Clone() const; diff --git a/src/Nazara/Graphics/Light.cpp b/src/Nazara/Graphics/Light.cpp index fd9d4ef61..06a28dfb6 100644 --- a/src/Nazara/Graphics/Light.cpp +++ b/src/Nazara/Graphics/Light.cpp @@ -28,7 +28,7 @@ m_type(type) SetRadius(5.f); } -void NzLight::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const +void NzLight::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const { switch (m_type) { @@ -38,7 +38,7 @@ void NzLight::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatri light.ambientFactor = m_ambientFactor; light.color = m_color; light.diffuseFactor = m_diffuseFactor; - light.direction = transformMatrix.Transform(NzVector3f::Forward(), 0.f); + light.direction = instanceData.transformMatrix.Transform(NzVector3f::Forward(), 0.f); renderQueue->AddDirectionalLight(light); break; @@ -52,7 +52,7 @@ void NzLight::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatri light.color = m_color; light.diffuseFactor = m_diffuseFactor; light.invRadius = m_invRadius; - light.position = transformMatrix.GetTranslation(); + light.position = instanceData.transformMatrix.GetTranslation(); light.radius = m_radius; renderQueue->AddPointLight(light); @@ -66,12 +66,12 @@ void NzLight::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatri light.attenuation = m_attenuation; light.color = m_color; light.diffuseFactor = m_diffuseFactor; - light.direction = transformMatrix.Transform(NzVector3f::Forward(), 0.f); + light.direction = instanceData.transformMatrix.Transform(NzVector3f::Forward(), 0.f); light.innerAngleCosine = m_innerAngleCosine; light.invRadius = m_invRadius; light.outerAngleCosine = m_outerAngleCosine; light.outerAngleTangent = m_outerAngleTangent; - light.position = transformMatrix.GetTranslation(); + light.position = instanceData.transformMatrix.GetTranslation(); light.radius = m_radius; renderQueue->AddSpotLight(light); @@ -94,7 +94,7 @@ NzLight* NzLight::Create() const return new NzLight; } -bool NzLight::Cull(const NzFrustumf& frustum, const NzBoundingVolumef& volume, const NzMatrix4f& transformMatrix) const +bool NzLight::Cull(const NzFrustumf& frustum, const InstanceData& instanceData) const { switch (m_type) { @@ -102,19 +102,19 @@ bool NzLight::Cull(const NzFrustumf& frustum, const NzBoundingVolumef& volume, c return true; // Always visible case nzLightType_Point: - return frustum.Contains(NzSpheref(transformMatrix.GetTranslation(), m_radius)); // A sphere test is much faster (and precise) + return frustum.Contains(NzSpheref(instanceData.transformMatrix.GetTranslation(), m_radius)); // A sphere test is much faster (and precise) case nzLightType_Spot: - return frustum.Contains(volume); + return frustum.Contains(instanceData.volume); } NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')'); return false; } -void NzLight::UpdateBoundingVolume(NzBoundingVolumef* boundingVolume, const NzMatrix4f& transformMatrix) const +void NzLight::UpdateBoundingVolume(InstanceData* instanceData) const { - NazaraAssert(boundingVolume, "Invalid bounding volume"); + NazaraAssert(instanceData, "Invalid data"); switch (m_type) { @@ -122,11 +122,11 @@ void NzLight::UpdateBoundingVolume(NzBoundingVolumef* boundingVolume, const NzMa break; // Nothing to do (bounding volume should be infinite) case nzLightType_Point: - boundingVolume->Update(transformMatrix.GetTranslation()); // The bounding volume only needs to be shifted + instanceData->volume.Update(instanceData->transformMatrix.GetTranslation()); // The bounding volume only needs to be shifted break; case nzLightType_Spot: - boundingVolume->Update(transformMatrix); + instanceData->volume.Update(instanceData->transformMatrix); break; default: diff --git a/src/Nazara/Graphics/Model.cpp b/src/Nazara/Graphics/Model.cpp index abf8a36a5..082e05589 100644 --- a/src/Nazara/Graphics/Model.cpp +++ b/src/Nazara/Graphics/Model.cpp @@ -36,7 +36,7 @@ NzModel::~NzModel() Reset(); } -void NzModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const +void NzModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const { unsigned int submeshCount = m_mesh->GetSubMeshCount(); for (unsigned int i = 0; i < submeshCount; ++i) @@ -49,7 +49,7 @@ void NzModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatri meshData.primitiveMode = mesh->GetPrimitiveMode(); meshData.vertexBuffer = mesh->GetVertexBuffer(); - renderQueue->AddMesh(material, meshData, mesh->GetAABB(), transformMatrix); + renderQueue->AddMesh(material, meshData, mesh->GetAABB(), instanceData.transformMatrix); } } diff --git a/src/Nazara/Graphics/Renderable.cpp b/src/Nazara/Graphics/Renderable.cpp index 59956362b..2d80efb8d 100644 --- a/src/Nazara/Graphics/Renderable.cpp +++ b/src/Nazara/Graphics/Renderable.cpp @@ -10,11 +10,9 @@ NzRenderable::~NzRenderable() OnRenderableRelease(this); } -bool NzRenderable::Cull(const NzFrustumf& frustum, const NzBoundingVolumef& volume, const NzMatrix4f& transformMatrix) const +bool NzRenderable::Cull(const NzFrustumf& frustum, const InstanceData& instanceData) const { - NazaraUnused(transformMatrix); - - return frustum.Contains(volume); + return frustum.Contains(instanceData.volume); } const NzBoundingVolumef& NzRenderable::GetBoundingVolume() const @@ -24,11 +22,22 @@ const NzBoundingVolumef& NzRenderable::GetBoundingVolume() const return m_boundingVolume; } -void NzRenderable::UpdateBoundingVolume(NzBoundingVolumef* boundingVolume, const NzMatrix4f& transformMatrix) const +void NzRenderable::InvalidateData(InstanceData* instanceData, nzUInt32 flags) const { - NazaraAssert(boundingVolume, "Invalid bounding volume"); + instanceData->flags |= flags; +} - boundingVolume->Update(transformMatrix); +void NzRenderable::UpdateBoundingVolume(InstanceData* instanceData) const +{ + NazaraAssert(instanceData, "Invalid instance data"); + NazaraUnused(instanceData); + + instanceData->volume.Update(instanceData->transformMatrix); +} + +void NzRenderable::UpdateData(InstanceData* instanceData) const +{ + NazaraAssert(instanceData, "Invalid instance data"); } NzRenderableLibrary::LibraryMap NzRenderable::s_library; diff --git a/src/Nazara/Graphics/SkeletalModel.cpp b/src/Nazara/Graphics/SkeletalModel.cpp index e2ecee2af..5000484fa 100644 --- a/src/Nazara/Graphics/SkeletalModel.cpp +++ b/src/Nazara/Graphics/SkeletalModel.cpp @@ -31,7 +31,7 @@ m_animationEnabled(true) { } -void NzSkeletalModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const +void NzSkeletalModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const InstanceData& instanceData) const { if (!m_mesh) return; @@ -47,7 +47,7 @@ void NzSkeletalModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const meshData.primitiveMode = mesh->GetPrimitiveMode(); meshData.vertexBuffer = NzSkinningManager::GetBuffer(mesh, &m_skeleton); - renderQueue->AddMesh(material, meshData, m_skeleton.GetAABB(), transformMatrix); + renderQueue->AddMesh(material, meshData, m_skeleton.GetAABB(), instanceData.transformMatrix); } }