// 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, Nz::UInt32(indices.size()), Nz::DataStorage_Hardware, 0); m_boxMeshIndexBuffer->Fill(indices.data(), 0, Nz::UInt32(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), Nz::UInt32(positions.size()), Nz::DataStorage_Hardware, 0); m_boxMeshVertexBuffer->Fill(positions.data(), 0, Nz::UInt32(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); if (renderable) { 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, Nz::UInt32(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), Nz::UInt32(vertices.size()), Nz::DataStorage_Hardware, 0); vertexBuffer->Fill(vertices.data(), 0, Nz::UInt32(vertices.size())); Nz::MeshRef mesh = Nz::Mesh::New(); mesh->CreateStatic(); Nz::StaticMeshRef subMesh = Nz::StaticMesh::New(vertexBuffer, 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; }