From 251b8af03af701cc6eb0fa48a61e24d4675663ea Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 13 Apr 2018 22:09:19 +0200 Subject: [PATCH] SDK: Add DebugComponent @gawaboumga @gawaboumga @gawaboumga @gawaboumga @gawaboumga @gawaboumga @gawaboumga @gawaboumga @gawaboumga @gawaboumga --- ChangeLog.md | 1 + SDK/include/NDK/Components.hpp | 3 +- SDK/include/NDK/Components/DebugComponent.hpp | 80 ++++ SDK/include/NDK/Components/DebugComponent.inl | 74 ++++ SDK/include/NDK/Systems.hpp | 1 + SDK/include/NDK/Systems/DebugSystem.hpp | 51 +++ SDK/include/NDK/Systems/DebugSystem.inl | 6 + SDK/src/NDK/Components/DebugComponent.cpp | 10 + SDK/src/NDK/Sdk.cpp | 4 + SDK/src/NDK/Systems/DebugSystem.cpp | 363 ++++++++++++++++++ SDK/src/NDK/World.cpp | 2 + include/Nazara/Core.hpp | 1 + 12 files changed, 595 insertions(+), 1 deletion(-) create mode 100644 SDK/include/NDK/Components/DebugComponent.hpp create mode 100644 SDK/include/NDK/Components/DebugComponent.inl create mode 100644 SDK/include/NDK/Systems/DebugSystem.hpp create mode 100644 SDK/include/NDK/Systems/DebugSystem.inl create mode 100644 SDK/src/NDK/Components/DebugComponent.cpp create mode 100644 SDK/src/NDK/Systems/DebugSystem.cpp diff --git a/ChangeLog.md b/ChangeLog.md index 7fec5792d..c724b0117 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -129,6 +129,7 @@ Nazara Development Kit: - Fix GraphicsComponent bounding volume not taking local matrix in account - ⚠️ Rewrote all render queue system, which should be more efficient, take scissor box into account - ⚠️ All widgets are now bound to a scissor box when rendering +- Add DebugComponent (a component able to show aabb/obb/collision mesh) # 0.4: diff --git a/SDK/include/NDK/Components.hpp b/SDK/include/NDK/Components.hpp index e099b641b..97c15fc7c 100644 --- a/SDK/include/NDK/Components.hpp +++ b/SDK/include/NDK/Components.hpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -17,6 +19,5 @@ #include #include #include -#include #endif // NDK_COMPONENTS_GLOBAL_HPP diff --git a/SDK/include/NDK/Components/DebugComponent.hpp b/SDK/include/NDK/Components/DebugComponent.hpp new file mode 100644 index 000000000..85e868b5a --- /dev/null +++ b/SDK/include/NDK/Components/DebugComponent.hpp @@ -0,0 +1,80 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#pragma once + +#ifndef NDK_SERVER +#ifndef NDK_COMPONENTS_DEBUGCOMPONENT_HPP +#define NDK_COMPONENTS_DEBUGCOMPONENT_HPP + +#include +#include +#include + +namespace Ndk +{ + enum class DebugDraw + { + //TODO: Collider2D + Collider3D, + GraphicsAABB, + GraphicsOBB, + + Max = GraphicsOBB + }; +} + +namespace Nz +{ + template<> + struct EnumAsFlags + { + static constexpr Ndk::DebugDraw max = Ndk::DebugDraw::GraphicsOBB; + }; +} + +namespace Ndk +{ + using DebugDrawFlags = Nz::Flags; + + constexpr DebugDrawFlags DebugDraw_None = 0; + + class NDK_API DebugComponent : public Component + { + friend class DebugSystem; + + public: + inline DebugComponent(DebugDrawFlags flags = DebugDraw_None); + inline DebugComponent(const DebugComponent& debug); + ~DebugComponent() = default; + + inline void Disable(DebugDrawFlags flags); + inline void Enable(DebugDrawFlags flags); + + inline DebugDrawFlags GetFlags() const; + + inline bool IsEnabled(DebugDrawFlags flags) const; + + inline DebugComponent& operator=(const DebugComponent& debug); + + static ComponentIndex componentIndex; + + private: + inline const Nz::InstancedRenderableRef& GetDebugRenderable(DebugDraw option) const; + inline DebugDrawFlags GetEnabledFlags() const; + inline void UpdateDebugRenderable(DebugDraw option, Nz::InstancedRenderableRef renderable); + inline void UpdateEnabledFlags(DebugDrawFlags flags); + + static constexpr std::size_t DebugModeCount = static_cast(DebugDraw::Max) + 1; + + std::array m_debugRenderables; + DebugDrawFlags m_enabledFlags; + DebugDrawFlags m_flags; + }; +} + +#include + +#endif // NDK_COMPONENTS_DEBUGCOMPONENT_HPP +#endif // NDK_SERVER diff --git a/SDK/include/NDK/Components/DebugComponent.inl b/SDK/include/NDK/Components/DebugComponent.inl new file mode 100644 index 000000000..e0f42a560 --- /dev/null +++ b/SDK/include/NDK/Components/DebugComponent.inl @@ -0,0 +1,74 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include + +namespace Ndk +{ + inline DebugComponent::DebugComponent(DebugDrawFlags flags) : + m_flags(flags) + { + } + + inline DebugComponent::DebugComponent(const DebugComponent& debug) : + m_flags(debug.m_flags) + { + } + + inline void DebugComponent::Disable(DebugDrawFlags flags) + { + m_flags &= ~flags; + + if (m_entity) + m_entity->Invalidate(); + } + + inline void DebugComponent::Enable(DebugDrawFlags flags) + { + m_flags |= flags; + + if (m_entity) + m_entity->Invalidate(); + } + + inline DebugDrawFlags DebugComponent::GetFlags() const + { + return m_flags; + } + + inline bool DebugComponent::IsEnabled(DebugDrawFlags flags) const + { + return (m_flags & flags) == flags; + } + + inline DebugComponent& DebugComponent::operator=(const DebugComponent& debug) + { + m_flags = debug.m_flags; + + if (m_entity) + m_entity->Invalidate(); + + return *this; + } + + inline const Nz::InstancedRenderableRef& DebugComponent::GetDebugRenderable(DebugDraw option) const + { + return m_debugRenderables[static_cast(option)]; + } + + inline DebugDrawFlags DebugComponent::GetEnabledFlags() const + { + return m_enabledFlags; + } + + inline void DebugComponent::UpdateDebugRenderable(DebugDraw option, Nz::InstancedRenderableRef renderable) + { + m_debugRenderables[static_cast(option)] = std::move(renderable); + } + + inline void DebugComponent::UpdateEnabledFlags(DebugDrawFlags flags) + { + m_enabledFlags = flags; + } +} diff --git a/SDK/include/NDK/Systems.hpp b/SDK/include/NDK/Systems.hpp index a0ee23e77..80571ef45 100644 --- a/SDK/include/NDK/Systems.hpp +++ b/SDK/include/NDK/Systems.hpp @@ -5,6 +5,7 @@ #ifndef NDK_SYSTEMS_GLOBAL_HPP #define NDK_SYSTEMS_GLOBAL_HPP +#include #include #include #include diff --git a/SDK/include/NDK/Systems/DebugSystem.hpp b/SDK/include/NDK/Systems/DebugSystem.hpp new file mode 100644 index 000000000..940feb782 --- /dev/null +++ b/SDK/include/NDK/Systems/DebugSystem.hpp @@ -0,0 +1,51 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#pragma once + +#ifndef NDK_SERVER +#ifndef NDK_SYSTEMS_DEBUGSYSTEM_HPP +#define NDK_SYSTEMS_DEBUGSYSTEM_HPP + +#include +#include +#include +#include +#include + +namespace Ndk +{ + class NDK_API DebugSystem : public System + { + public: + DebugSystem(); + ~DebugSystem() = default; + + static SystemIndex systemIndex; + + private: + Nz::InstancedRenderableRef GenerateBox(Nz::Boxf box); + Nz::InstancedRenderableRef GenerateCollision3DMesh(Entity* entity); + + Nz::MaterialRef GetAABBMaterial(); + Nz::MaterialRef GetCollisionMaterial(); + Nz::MaterialRef GetOBBMaterial(); + std::pair GetBoxMesh(); + + void OnEntityValidation(Entity* entity, bool justAdded) override; + + void OnUpdate(float elapsedTime) override; + + Nz::MaterialRef m_aabbMaterial; + Nz::MaterialRef m_collisionMaterial; + Nz::MaterialRef m_obbMaterial; + Nz::IndexBufferRef m_boxMeshIndexBuffer; + Nz::VertexBufferRef m_boxMeshVertexBuffer; + }; +} + +#include + +#endif // NDK_SYSTEMS_DEBUGSYSTEM_HPP +#endif // NDK_SERVER diff --git a/SDK/include/NDK/Systems/DebugSystem.inl b/SDK/include/NDK/Systems/DebugSystem.inl new file mode 100644 index 000000000..b7439fc12 --- /dev/null +++ b/SDK/include/NDK/Systems/DebugSystem.inl @@ -0,0 +1,6 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include + diff --git a/SDK/src/NDK/Components/DebugComponent.cpp b/SDK/src/NDK/Components/DebugComponent.cpp new file mode 100644 index 000000000..6c8ab9f1c --- /dev/null +++ b/SDK/src/NDK/Components/DebugComponent.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include + +namespace Ndk +{ + ComponentIndex DebugComponent::componentIndex; +} diff --git a/SDK/src/NDK/Sdk.cpp b/SDK/src/NDK/Sdk.cpp index e4a7008b7..90d07c738 100644 --- a/SDK/src/NDK/Sdk.cpp +++ b/SDK/src/NDK/Sdk.cpp @@ -28,11 +28,13 @@ #ifndef NDK_SERVER #include +#include #include #include #include #include #include +#include #include #include #include @@ -95,6 +97,7 @@ namespace Ndk #ifndef NDK_SERVER // Client components InitializeComponent("NdkCam"); + InitializeComponent("NdkDebug"); InitializeComponent("NdkLight"); InitializeComponent("NdkList"); InitializeComponent("NdkGfx"); @@ -113,6 +116,7 @@ namespace Ndk #ifndef NDK_SERVER // Client systems + InitializeSystem(); InitializeSystem(); InitializeSystem(); InitializeSystem(); diff --git a/SDK/src/NDK/Systems/DebugSystem.cpp b/SDK/src/NDK/Systems/DebugSystem.cpp new file mode 100644 index 000000000..61dcae0a8 --- /dev/null +++ b/SDK/src/NDK/Systems/DebugSystem.cpp @@ -0,0 +1,363 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ndk +{ + namespace + { + class DebugRenderable : public Nz::InstancedRenderable + { + public: + DebugRenderable(Ndk::Entity* owner, Nz::MaterialRef mat, Nz::IndexBufferRef indexBuffer, Nz::VertexBufferRef vertexBuffer) : + m_entityOwner(owner), + m_material(std::move(mat)), + m_indexBuffer(std::move(indexBuffer)), + m_vertexBuffer(std::move(vertexBuffer)) + { + ResetMaterials(1); + + m_meshData.indexBuffer = m_indexBuffer; + m_meshData.primitiveMode = Nz::PrimitiveMode_LineList; + m_meshData.vertexBuffer = m_vertexBuffer; + } + + void UpdateBoundingVolume(InstanceData* instanceData) const override + { + } + + void MakeBoundingVolume() const override + { + m_boundingVolume.MakeNull(); + } + + protected: + Ndk::EntityHandle m_entityOwner; + Nz::IndexBufferRef m_indexBuffer; + Nz::MaterialRef m_material; + Nz::MeshData m_meshData; + Nz::VertexBufferRef m_vertexBuffer; + }; + + class AABBDebugRenderable : public DebugRenderable + { + public: + using DebugRenderable::DebugRenderable; + + void AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Nz::Recti& scissorRect) const override + { + NazaraAssert(m_entityOwner, "DebugRenderable has no owner"); + + const DebugComponent& entityDebug = m_entityOwner->GetComponent(); + const GraphicsComponent& entityGfx = m_entityOwner->GetComponent(); + + Nz::Matrix4f transformMatrix = Nz::Matrix4f::Identity(); + transformMatrix.SetScale(entityGfx.GetBoundingVolume().aabb.GetLengths()); + transformMatrix.SetTranslation(entityGfx.GetBoundingVolume().aabb.GetCenter()); + + renderQueue->AddMesh(0, m_material, m_meshData, Nz::Boxf::Zero(), transformMatrix, scissorRect); + } + }; + + class OBBDebugRenderable : public DebugRenderable + { + public: + using DebugRenderable::DebugRenderable; + + void AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Nz::Recti& scissorRect) const override + { + NazaraAssert(m_entityOwner, "DebugRenderable has no owner"); + + const DebugComponent& entityDebug = m_entityOwner->GetComponent(); + const GraphicsComponent& entityGfx = m_entityOwner->GetComponent(); + + Nz::Matrix4f transformMatrix = instanceData.transformMatrix; + transformMatrix.ApplyScale(entityGfx.GetBoundingVolume().obb.localBox.GetLengths()); + + renderQueue->AddMesh(0, m_material, m_meshData, Nz::Boxf::Zero(), transformMatrix, scissorRect); + } + }; + } + + /*! + * \ingroup NDK + * \class Ndk::DebugSystem + * \brief NDK class that represents the debug system + * + * \remark This system is enabled if the entity owns the trait: DebugComponent and GraphicsComponent + */ + + /*! + * \brief Constructs an DebugSystem object by default + */ + DebugSystem::DebugSystem() + { + Requires(); + SetUpdateOrder(1000); //< Update last + } + + std::pair DebugSystem::GetBoxMesh() + { + if (!m_boxMeshIndexBuffer) + { + std::array indices = { + { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + + 4, 5, + 5, 6, + 6, 7, + 7, 4, + + 0, 4, + 1, 5, + 2, 6, + 3, 7 + } + }; + + m_boxMeshIndexBuffer = Nz::IndexBuffer::New(false, indices.size(), Nz::DataStorage_Hardware, 0); + m_boxMeshIndexBuffer->Fill(indices.data(), 0, indices.size()); + } + + if (!m_boxMeshVertexBuffer) + { + Nz::Boxf box(-0.5f, -0.5f, -0.5f, 1.f, 1.f, 1.f); + + std::array positions = { + { + box.GetCorner(Nz::BoxCorner_FarLeftBottom), + box.GetCorner(Nz::BoxCorner_NearLeftBottom), + box.GetCorner(Nz::BoxCorner_NearRightBottom), + box.GetCorner(Nz::BoxCorner_FarRightBottom), + box.GetCorner(Nz::BoxCorner_FarLeftTop), + box.GetCorner(Nz::BoxCorner_NearLeftTop), + box.GetCorner(Nz::BoxCorner_NearRightTop), + box.GetCorner(Nz::BoxCorner_FarRightTop) + } + }; + + m_boxMeshVertexBuffer = Nz::VertexBuffer::New(Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ), positions.size(), Nz::DataStorage_Hardware, 0); + m_boxMeshVertexBuffer->Fill(positions.data(), 0, positions.size()); + } + + return { m_boxMeshIndexBuffer, m_boxMeshVertexBuffer }; + } + + void DebugSystem::OnEntityValidation(Entity* entity, bool /*justAdded*/) + { + static constexpr int DebugDrawOrder = 1'000; + + DebugComponent& entityDebug = entity->GetComponent(); + GraphicsComponent& entityGfx = entity->GetComponent(); + + DebugDrawFlags enabledFlags = entityDebug.GetEnabledFlags(); + DebugDrawFlags flags = entityDebug.GetFlags(); + + DebugDrawFlags flagsToEnable = flags & ~enabledFlags; + for (std::size_t i = 0; i <= static_cast(DebugDraw::Max); ++i) + { + DebugDraw option = static_cast(i); + if (flagsToEnable & option) + { + switch (option) + { + case DebugDraw::Collider3D: + { + const Nz::Boxf& obb = entityGfx.GetBoundingVolume().obb.localBox; + + Nz::InstancedRenderableRef renderable = GenerateCollision3DMesh(entity); + renderable->SetPersistent(false); + + entityGfx.Attach(renderable, Nz::Matrix4f::Translate(obb.GetCenter()), DebugDrawOrder); + + entityDebug.UpdateDebugRenderable(option, std::move(renderable)); + break; + } + + case DebugDraw::GraphicsAABB: + { + auto indexVertexBuffers = GetBoxMesh(); + + Nz::InstancedRenderableRef renderable = new AABBDebugRenderable(entity, GetAABBMaterial(), indexVertexBuffers.first, indexVertexBuffers.second); + renderable->SetPersistent(false); + + entityGfx.Attach(renderable, Nz::Matrix4f::Identity(), DebugDrawOrder); + + entityDebug.UpdateDebugRenderable(option, std::move(renderable)); + break; + } + + case DebugDraw::GraphicsOBB: + { + auto indexVertexBuffers = GetBoxMesh(); + + Nz::InstancedRenderableRef renderable = new OBBDebugRenderable(entity, GetOBBMaterial(), indexVertexBuffers.first, indexVertexBuffers.second); + renderable->SetPersistent(false); + + entityGfx.Attach(renderable, Nz::Matrix4f::Identity(), DebugDrawOrder); + + entityDebug.UpdateDebugRenderable(option, std::move(renderable)); + break; + } + + default: + break; + } + } + } + + DebugDrawFlags flagsToDisable = enabledFlags & ~flags; + for (std::size_t i = 0; i <= static_cast(DebugDraw::Max); ++i) + { + DebugDraw option = static_cast(i); + if (flagsToDisable & option) + entityGfx.Detach(entityDebug.GetDebugRenderable(option)); + } + + entityDebug.UpdateEnabledFlags(flags); + } + + void DebugSystem::OnUpdate(float elapsedTime) + { + // Nothing to do + } + + Nz::InstancedRenderableRef DebugSystem::GenerateBox(Nz::Boxf box) + { + Nz::MeshRef mesh = Nz::Mesh::New(); + mesh->CreateStatic(); + + mesh->BuildSubMesh(Nz::Primitive::Box(box.GetLengths())); + mesh->SetMaterialCount(1); + + Nz::ModelRef model = Nz::Model::New(); + model->SetMesh(mesh); + model->SetMaterial(0, GetOBBMaterial()); + + return model; + } + + Nz::InstancedRenderableRef DebugSystem::GenerateCollision3DMesh(Entity* entity) + { + if (entity->HasComponent()) + { + CollisionComponent3D& entityCollision = entity->GetComponent(); + const Nz::Collider3DRef& geom = entityCollision.GetGeom(); + + std::vector vertices; + std::vector indices; + + geom->ForEachPolygon([&](const float* polygonVertices, std::size_t vertexCount) + { + std::size_t firstIndex = vertices.size(); + + for (std::size_t i = 0; i < vertexCount; ++i) + { + const float* vertexData = &polygonVertices[i * 3]; + vertices.emplace_back(vertexData[0], vertexData[1], vertexData[2]); + } + + for (std::size_t i = 0; i < vertexCount - 1; ++i) + { + indices.push_back(firstIndex + i); + indices.push_back(firstIndex + i + 1); + } + + indices.push_back(firstIndex + vertexCount - 1); + indices.push_back(firstIndex); + }); + + Nz::IndexBufferRef indexBuffer = Nz::IndexBuffer::New(vertices.size() > 0xFFFF, indices.size(), Nz::DataStorage_Hardware, 0); + Nz::IndexMapper indexMapper(indexBuffer, Nz::BufferAccess_WriteOnly); + + Nz::IndexIterator indexPtr = indexMapper.begin(); + for (std::size_t index : indices) + *indexPtr++ = static_cast(index); + + indexMapper.Unmap(); + + Nz::VertexBufferRef vertexBuffer = Nz::VertexBuffer::New(Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ), vertices.size(), Nz::DataStorage_Hardware, 0); + vertexBuffer->Fill(vertices.data(), 0, vertices.size()); + + Nz::MeshRef mesh = Nz::Mesh::New(); + mesh->CreateStatic(); + + Nz::StaticMeshRef subMesh = Nz::StaticMesh::New(mesh); + subMesh->Create(vertexBuffer); + subMesh->SetIndexBuffer(indexBuffer); + subMesh->SetPrimitiveMode(Nz::PrimitiveMode_LineList); + subMesh->SetMaterialIndex(0); + subMesh->GenerateAABB(); + + mesh->SetMaterialCount(1); + mesh->AddSubMesh(subMesh); + + Nz::ModelRef model = Nz::Model::New(); + model->SetMesh(mesh); + model->SetMaterial(0, GetCollisionMaterial()); + + return model; + } + else + return nullptr; + } + + Nz::MaterialRef DebugSystem::GetAABBMaterial() + { + if (!m_aabbMaterial) + { + m_aabbMaterial = Nz::Material::New(); + m_aabbMaterial->EnableFaceCulling(false); + m_aabbMaterial->EnableDepthBuffer(true); + m_aabbMaterial->SetDiffuseColor(Nz::Color::Red); + m_aabbMaterial->SetFaceFilling(Nz::FaceFilling_Line); + } + + return m_aabbMaterial; + } + + Nz::MaterialRef DebugSystem::GetCollisionMaterial() + { + if (!m_collisionMaterial) + { + m_collisionMaterial = Nz::Material::New(); + m_collisionMaterial->EnableFaceCulling(false); + m_collisionMaterial->EnableDepthBuffer(true); + m_collisionMaterial->SetDiffuseColor(Nz::Color::Blue); + m_collisionMaterial->SetFaceFilling(Nz::FaceFilling_Line); + } + + return m_collisionMaterial; + } + + Nz::MaterialRef DebugSystem::GetOBBMaterial() + { + if (!m_obbMaterial) + { + m_obbMaterial = Nz::Material::New(); + m_obbMaterial->EnableFaceCulling(false); + m_obbMaterial->EnableDepthBuffer(true); + m_obbMaterial->SetDiffuseColor(Nz::Color::Green); + m_obbMaterial->SetFaceFilling(Nz::FaceFilling_Line); + } + + return m_obbMaterial; + } + + SystemIndex DebugSystem::systemIndex; +} diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index 7582461b9..bfbe0140c 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -11,6 +11,7 @@ #include #ifndef NDK_SERVER +#include #include #include #include @@ -47,6 +48,7 @@ namespace Ndk AddSystem(); #ifndef NDK_SERVER + AddSystem(); AddSystem(); AddSystem(); AddSystem(); diff --git a/include/Nazara/Core.hpp b/include/Nazara/Core.hpp index b4481b1a6..515d8c545 100644 --- a/include/Nazara/Core.hpp +++ b/include/Nazara/Core.hpp @@ -88,6 +88,7 @@ #include #include #include +#include #include #include