From 9468e5afdc20531f6bce8cae2f05a1ac35e7c72a Mon Sep 17 00:00:00 2001 From: apistol78 Date: Wed, 15 May 2024 16:56:58 +0200 Subject: [PATCH] Traktor: GPU occlusion culling moved to World module and are culling all instances in one pass. --- .../Editor/AnimationPreviewControl.cpp | 2 + code/Mesh/Editor/MeshEditorProfile.cpp | 2 - code/Mesh/Instance/InstanceMesh.cpp | 152 +++---------- code/Mesh/Instance/InstanceMesh.h | 67 +++--- code/Mesh/Instance/InstanceMeshComponent.cpp | 56 +++-- code/Mesh/Instance/InstanceMeshComponent.h | 7 +- .../InstanceMeshComponentRenderer.cpp | 84 ------- .../Instance/InstanceMeshComponentRenderer.h | 63 ------ code/Mesh/Instance/InstanceMeshResource.cpp | 7 +- code/Runtime/Impl/WorldServer.cpp | 4 +- code/Scene/Editor/DefaultEditorProfile.cpp | 2 + code/Scene/Editor/SceneEditorContext.cpp | 22 +- code/World/Entity.cpp | 7 + code/World/Entity.h | 7 +- code/World/Entity/CullingComponent.cpp | 209 ++++++++++++++++++ code/World/Entity/CullingComponent.h | 117 ++++++++++ code/World/Entity/CullingRenderer.cpp | 55 +++++ code/World/Entity/CullingRenderer.h | 58 +++++ code/World/Entity/VolumetricFogRenderer.cpp | 3 +- code/World/IEntityComponent.h | 6 + code/World/Shared/WorldRendererShared.cpp | 8 + code/World/World.cpp | 14 +- .../Mesh/Shaders/Instance/Cull/Cull.xdi | 48 ++-- .../Mesh/Shaders/Instance/Cull/Draw.xdi | 83 +++++-- 24 files changed, 672 insertions(+), 411 deletions(-) delete mode 100644 code/Mesh/Instance/InstanceMeshComponentRenderer.cpp delete mode 100644 code/Mesh/Instance/InstanceMeshComponentRenderer.h create mode 100644 code/World/Entity/CullingComponent.cpp create mode 100644 code/World/Entity/CullingComponent.h create mode 100644 code/World/Entity/CullingRenderer.cpp create mode 100644 code/World/Entity/CullingRenderer.h diff --git a/code/Animation/Editor/AnimationPreviewControl.cpp b/code/Animation/Editor/AnimationPreviewControl.cpp index d4ecd6bbb3..a8beb5a449 100644 --- a/code/Animation/Editor/AnimationPreviewControl.cpp +++ b/code/Animation/Editor/AnimationPreviewControl.cpp @@ -46,6 +46,7 @@ #include "World/WorldEntityRenderers.h" #include "World/WorldRenderSettings.h" #include "World/WorldResourceFactory.h" +#include "World/Entity/CullingRenderer.h" #include "World/Entity/GroupComponent.h" #include "World/Entity/LightComponent.h" #include "World/Entity/ProbeRenderer.h" @@ -251,6 +252,7 @@ void AnimationPreviewControl::updateWorldRenderer() Ref< world::WorldEntityRenderers > worldEntityRenderers = new world::WorldEntityRenderers(); worldEntityRenderers->add(new mesh::MeshComponentRenderer()); worldEntityRenderers->add(new weather::SkyRenderer()); + worldEntityRenderers->add(new world::CullingRenderer()); worldEntityRenderers->add(new world::VolumetricFogRenderer()); worldEntityRenderers->add(new world::ProbeRenderer( m_resourceManager, diff --git a/code/Mesh/Editor/MeshEditorProfile.cpp b/code/Mesh/Editor/MeshEditorProfile.cpp index 1d4f0301ae..28c7e89bb0 100644 --- a/code/Mesh/Editor/MeshEditorProfile.cpp +++ b/code/Mesh/Editor/MeshEditorProfile.cpp @@ -14,7 +14,6 @@ #include "Mesh/MeshResourceFactory.h" #include "Mesh/Editor/MeshAsset.h" #include "Mesh/Editor/MeshEditorProfile.h" -#include "Mesh/Instance/InstanceMeshComponentRenderer.h" #include "Resource/Id.h" #include "Scene/Editor/SceneEditorContext.h" #include "Ui/Command.h" @@ -69,7 +68,6 @@ void MeshEditorProfile::createEntityRenderers( ) const { outEntityRenderers.push_back(new mesh::MeshComponentRenderer()); - outEntityRenderers.push_back(new mesh::InstanceMeshComponentRenderer()); } void MeshEditorProfile::createControllerEditorFactories( diff --git a/code/Mesh/Instance/InstanceMesh.cpp b/code/Mesh/Instance/InstanceMesh.cpp index 6f56478018..315c74ff01 100644 --- a/code/Mesh/Instance/InstanceMesh.cpp +++ b/code/Mesh/Instance/InstanceMesh.cpp @@ -18,6 +18,7 @@ #include "Render/Context/RenderContext.h" #include "Render/Mesh/Mesh.h" #include "World/IWorldRenderPass.h" +#include "World/WorldBuildContext.h" #include "World/WorldHandles.h" #include "World/WorldRenderView.h" @@ -28,13 +29,11 @@ namespace traktor::mesh render::Handle s_handleInstanceWorld(L"InstanceWorld"); render::Handle s_handleInstanceWorldLast(L"InstanceWorldLast"); -render::Handle s_handleTargetSize(L"InstanceMesh_TargetSize"); -render::Handle s_handleViewProjection(L"InstanceMesh_ViewProjection"); render::Handle s_handleVisibility(L"InstanceMesh_Visibility"); -render::Handle s_handleCullFrustum(L"InstanceMesh_CullFrustum"); render::Handle s_handleDraw(L"InstanceMesh_Draw"); render::Handle s_handleIndexCount(L"InstanceMesh_IndexCount"); render::Handle s_handleFirstIndex(L"InstanceMesh_FirstIndex"); +render::Handle s_handleInstanceOffset(L"InstanceMesh_InstanceOffset"); } @@ -42,11 +41,9 @@ T_IMPLEMENT_RTTI_CLASS(L"traktor.mesh.InstanceMesh", InstanceMesh, IMesh) InstanceMesh::InstanceMesh( render::IRenderSystem* renderSystem, - const resource::Proxy< render::Shader >& shaderCull, const resource::Proxy< render::Shader >& shaderDraw ) : m_renderSystem(renderSystem) -, m_shaderCull(shaderCull) , m_shaderDraw(shaderDraw) { } @@ -67,113 +64,40 @@ void InstanceMesh::getTechniques(SmallSet< render::handle_t >& outHandles) const outHandles.insert(part.first); } -void InstanceMesh::build( - render::RenderContext* renderContext, +void InstanceMesh::cullableBuild( + const world::WorldBuildContext& context, const world::WorldRenderView& worldRenderView, const world::IWorldRenderPass& worldRenderPass, - render::ProgramParameters* extraParameters + render::Buffer* instanceBuffer, + render::Buffer* visibilityBuffer, + uint32_t start, + uint32_t count ) { - bool haveAlphaBlend = false; - - if (m_instances.empty()) - return; - const auto it = m_parts.find(worldRenderPass.getTechnique()); if (it == m_parts.end()) return; + render::RenderContext* renderContext = context.getRenderContext(); + const AlignedVector< Part >& parts = it->second; const auto& meshParts = m_renderMesh->getParts(); - const uint32_t bufferItemCount = (uint32_t)alignUp(m_instances.size(), 16); - // Lazy create the buffers. - if (!m_instanceBuffer || bufferItemCount > m_instanceAllocatedCount) + if (count > m_allocatedCount) { - m_instanceBuffer = m_renderSystem->createBuffer(render::BufferUsage::BuStructured, bufferItemCount * sizeof(InstanceMeshData), true); - m_visibilityBuffers.resize(0); m_drawBuffers.resize(0); - m_instanceAllocatedCount = bufferItemCount; - m_instanceBufferDirty = true; + m_allocatedCount = count; } - const uint32_t peakCascade = worldRenderView.getCascade(); - const uint32_t vbSize = (uint32_t)m_visibilityBuffers.size(); - for (uint32_t i = vbSize; i < peakCascade + 1; ++i) - m_visibilityBuffers.push_back(m_renderSystem->createBuffer(render::BufferUsage::BuStructured, bufferItemCount * sizeof(float), false)); - + const uint32_t peakCascade = worldRenderView.getCascade(); const uint32_t dbSize = (uint32_t)m_drawBuffers.size(); for (uint32_t i = dbSize; i < (peakCascade + 1) * parts.size(); ++i) - m_drawBuffers.push_back(m_renderSystem->createBuffer(render::BufferUsage::BuStructured | render::BufferUsage::BuIndirect, bufferItemCount * sizeof(render::IndexedIndirectDraw), false)); - - // Update buffer is any instance has moved. - if (m_instanceBufferDirty) - { - auto ptr = (InstanceMeshData*)m_instanceBuffer->lock(); - for (const auto& instance : m_instances) - { - *ptr++ = packInstanceMeshData( - instance->transform, - instance->boundingBox - ); - } - m_instanceBuffer->unlock(); - m_instanceBufferDirty = false; - } - - render::Buffer* visibilityBuffer = m_visibilityBuffers[worldRenderView.getCascade()]; - - // Cull instances. - // #todo Compute blocks are executed before render pass, so for shadow map rendering all cascades - // are culled before being rendered. - { - Vector4 cullFrustum[12]; - - const Frustum& cf = worldRenderView.getCullFrustum(); - T_FATAL_ASSERT(cf.planes.size() <= sizeof_array(cullFrustum)); - for (int32_t i = 0; i < (int32_t)cf.planes.size(); ++i) - cullFrustum[i] = cf.planes[i].normal().xyz0() + Vector4(0.0f, 0.0f, 0.0f, cf.planes[i].distance()); - for (int32_t i = (int32_t)cf.planes.size(); i < sizeof_array(cullFrustum); ++i) - cullFrustum[i] = Vector4::zero(); - - const Vector2 viewSize = worldRenderView.getViewSize(); - - auto renderBlock = renderContext->allocNamed< render::ComputeRenderBlock >( - str(L"InstanceMesh cull %d", worldRenderView.getCascade()) - ); - - render::Shader::Permutation perm; - if (worldRenderPass.getTechnique() == world::s_techniqueDeferredGBufferWrite) - { - // Deferred g-buffer pass has access to HiZ texture. - m_shaderCull->setCombination(render::getParameterHandle(L"InstanceMesh_HiZ"), true, perm); - } - else - { - // All other paths use simple frustum culling only. - m_shaderCull->setCombination(render::getParameterHandle(L"InstanceMesh_HiZ"), false, perm); - } - - renderBlock->program = m_shaderCull->getProgram(perm).program; - - renderBlock->programParams = renderContext->alloc< render::ProgramParameters >(); - renderBlock->programParams->beginParameters(renderContext); - - worldRenderPass.setProgramParameters(renderBlock->programParams); - - renderBlock->programParams->setVectorParameter(s_handleTargetSize, Vector4(viewSize.x, viewSize.y, 0.0f, 0.0f)); - renderBlock->programParams->setMatrixParameter(s_handleViewProjection, worldRenderView.getProjection() * worldRenderView.getView()); - renderBlock->programParams->setVectorArrayParameter(s_handleCullFrustum, cullFrustum, sizeof_array(cullFrustum)); - renderBlock->programParams->setBufferViewParameter(s_handleInstanceWorld, m_instanceBuffer->getBufferView()); - renderBlock->programParams->setBufferViewParameter(s_handleVisibility, visibilityBuffer->getBufferView()); - renderBlock->programParams->endParameters(renderContext); - - renderBlock->workSize[0] = (int32_t)m_instances.size(); - - renderContext->compute(renderBlock); - renderContext->compute< render::BarrierRenderBlock >(render::Stage::Compute, render::Stage::Compute, nullptr, 0); - } + m_drawBuffers.push_back(m_renderSystem->createBuffer( + render::BufferUsage::BuStructured | render::BufferUsage::BuIndirect, + count * sizeof(render::IndexedIndirectDraw), + false + )); // Create draw buffers from visibility buffer. for (uint32_t i = 0; i < parts.size(); ++i) @@ -200,11 +124,12 @@ void InstanceMesh::build( renderBlock->programParams->beginParameters(renderContext); renderBlock->programParams->setFloatParameter(s_handleIndexCount, primitives.getVertexCount() + 0.5f); renderBlock->programParams->setFloatParameter(s_handleFirstIndex, primitives.offset + 0.5f); + renderBlock->programParams->setFloatParameter(s_handleInstanceOffset, start + 0.5f); renderBlock->programParams->setBufferViewParameter(s_handleVisibility, visibilityBuffer->getBufferView()); renderBlock->programParams->setBufferViewParameter(s_handleDraw, drawBuffer->getBufferView()); renderBlock->programParams->endParameters(renderContext); - renderBlock->workSize[0] = (int32_t)m_instances.size(); + renderBlock->workSize[0] = (int32_t)count; renderContext->compute(renderBlock); } @@ -236,12 +161,12 @@ void InstanceMesh::build( renderBlock->vertexLayout = m_renderMesh->getVertexLayout(); renderBlock->primitive = meshParts[part.meshPart].primitives.type; renderBlock->drawBuffer = drawBuffer->getBufferView(); - renderBlock->drawCount = (uint32_t)m_instances.size(); + renderBlock->drawCount = (uint32_t)count; renderBlock->programParams->beginParameters(renderContext); - if (extraParameters) - renderBlock->programParams->attachParameters(extraParameters); + //if (extraParameters) + // renderBlock->programParams->attachParameters(extraParameters); worldRenderPass.setProgramParameters( renderBlock->programParams, @@ -250,38 +175,13 @@ void InstanceMesh::build( ); // #todo Same world buffer - renderBlock->programParams->setBufferViewParameter(s_handleInstanceWorld, m_instanceBuffer->getBufferView()); - renderBlock->programParams->setBufferViewParameter(s_handleInstanceWorldLast, m_instanceBuffer->getBufferView()); + renderBlock->programParams->setFloatParameter(s_handleInstanceOffset, start + 0.5f); + renderBlock->programParams->setBufferViewParameter(s_handleInstanceWorld, instanceBuffer->getBufferView()); + renderBlock->programParams->setBufferViewParameter(s_handleInstanceWorldLast, instanceBuffer->getBufferView()); renderBlock->programParams->endParameters(renderContext); renderContext->draw(sp.priority, renderBlock); } } -InstanceMesh::Instance* InstanceMesh::allocateInstance() -{ - Instance* instance = new Instance(); - instance->mesh = this; - instance->transform = Transform::identity(); - instance->boundingBox = m_renderMesh->getBoundingBox(); - m_instances.push_back(instance); - return instance; -} - -void InstanceMesh::releaseInstance(Instance*& instance) -{ - T_FATAL_ASSERT(instance->mesh == this); - auto it = std::find(m_instances.begin(), m_instances.end(), instance); - m_instances.erase(it); - delete instance; - instance = nullptr; -} - -void InstanceMesh::Instance::setTransform(const Transform& transform) -{ - this->mesh->m_instanceBufferDirty = true; - this->transform = transform; - this->boundingBox = this->mesh->m_renderMesh->getBoundingBox().transform(transform); -} - } diff --git a/code/Mesh/Instance/InstanceMesh.h b/code/Mesh/Instance/InstanceMesh.h index 3883787f47..96ceed5829 100644 --- a/code/Mesh/Instance/InstanceMesh.h +++ b/code/Mesh/Instance/InstanceMesh.h @@ -17,6 +17,7 @@ #include "Mesh/IMesh.h" #include "Render/Shader.h" #include "Resource/Proxy.h" +#include "World/Entity/CullingComponent.h" // import/export mechanism. #undef T_DLLCLASS @@ -29,22 +30,22 @@ namespace traktor::render { -class Buffer; +//class Buffer; class IRenderSystem; class ProgramParameters; -class RenderContext; +//class RenderContext; class Mesh; class Shader; } -namespace traktor::world -{ - -class IWorldRenderPass; -class WorldRenderView; - -} +//namespace traktor::world +//{ +// +//class IWorldRenderPass; +//class WorldRenderView; +// +//} namespace traktor::mesh { @@ -56,13 +57,13 @@ namespace traktor::mesh * automatically by the GPU in any number of instances * using hardware instancing in a single draw call. */ -class T_DLLCLASS InstanceMesh : public IMesh +class T_DLLCLASS InstanceMesh +: public IMesh +, public world::CullingComponent::ICullable { T_RTTI_CLASS; public: - enum { MaxInstanceCount = 60 }; - struct Part { render::handle_t shaderTechnique; @@ -71,7 +72,6 @@ class T_DLLCLASS InstanceMesh : public IMesh explicit InstanceMesh( render::IRenderSystem* renderSystem, - const resource::Proxy< render::Shader >& shaderCull, const resource::Proxy< render::Shader >& shaderDraw ); @@ -81,29 +81,19 @@ class T_DLLCLASS InstanceMesh : public IMesh void getTechniques(SmallSet< render::handle_t >& outHandles) const; - void build( - render::RenderContext* renderContext, - const world::WorldRenderView& worldRenderView, - const world::IWorldRenderPass& worldRenderPass, - render::ProgramParameters* extraParameters - ); + /* world::CullingComponent::ICullable */ - // + virtual const Aabb3& cullableGetBoundingBox() const override final { return getBoundingBox(); } - struct Instance - { - InstanceMesh* mesh; - Transform transform; - Aabb3 boundingBox; - - void setTransform(const Transform& transform); - }; - - Instance* allocateInstance(); - - void releaseInstance(Instance*& instance); - - // + virtual void cullableBuild( + const world::WorldBuildContext& context, + const world::WorldRenderView& worldRenderView, + const world::IWorldRenderPass& worldRenderPass, + render::Buffer* instanceBuffer, + render::Buffer* visibilityBuffer, + uint32_t start, + uint32_t count + ) override final; private: friend class InstanceMeshResource; @@ -113,15 +103,10 @@ class T_DLLCLASS InstanceMesh : public IMesh SmallMap< render::handle_t, AlignedVector< Part > > m_parts; //#todo All instances are bookkeep;ed in InstanceMesh which should be a resource. - Ref< render::IRenderSystem > m_renderSystem; - resource::Proxy< render::Shader > m_shaderCull; + Ref< render::IRenderSystem > m_renderSystem; resource::Proxy< render::Shader > m_shaderDraw; - AlignedVector< Instance* > m_instances; - Ref< render::Buffer > m_instanceBuffer; - RefArray< render::Buffer > m_visibilityBuffers; RefArray< render::Buffer > m_drawBuffers; - uint32_t m_instanceAllocatedCount = 0; - bool m_instanceBufferDirty = false; + uint32_t m_allocatedCount = 0; }; } diff --git a/code/Mesh/Instance/InstanceMeshComponent.cpp b/code/Mesh/Instance/InstanceMeshComponent.cpp index 921756eb61..26b5a62b83 100644 --- a/code/Mesh/Instance/InstanceMeshComponent.cpp +++ b/code/Mesh/Instance/InstanceMeshComponent.cpp @@ -8,7 +8,9 @@ */ #include "Mesh/Instance/InstanceMesh.h" #include "Mesh/Instance/InstanceMeshComponent.h" +#include "World/Entity.h" #include "World/IWorldRenderPass.h" +#include "World/World.h" #include "World/WorldBuildContext.h" #include "World/WorldRenderView.h" @@ -20,7 +22,7 @@ T_IMPLEMENT_RTTI_CLASS(L"traktor.mesh.InstanceMeshComponent", InstanceMeshCompon InstanceMeshComponent::InstanceMeshComponent(const resource::Proxy< InstanceMesh >& mesh) : m_mesh(mesh) { - m_meshInstance = m_mesh->allocateInstance(); + //m_cullingInstance = m_mesh->allocateInstance(); m_mesh.consume(); } @@ -33,28 +35,46 @@ InstanceMeshComponent::~InstanceMeshComponent() void InstanceMeshComponent::destroy() { - // Only release instance if resource hasn't been replaced. - if (!m_mesh.changed()) + //// Only release instance if resource hasn't been replaced. + //if (!m_mesh.changed()) + //{ + // if (m_mesh && m_meshInstance != nullptr) + // m_mesh->releaseInstance(m_meshInstance); + //} + + if (m_owner != nullptr && m_cullingInstance != nullptr) { - if (m_mesh && m_meshInstance != nullptr) - m_mesh->releaseInstance(m_meshInstance); + world::CullingComponent* culling = m_owner->getWorld()->getComponent< world::CullingComponent >(); + culling->releaseInstance(m_cullingInstance); } + m_mesh.clear(); MeshComponent::destroy(); } +void InstanceMeshComponent::setWorld(world::World* world) +{ + T_FATAL_ASSERT(m_cullingInstance == nullptr); + if (world != nullptr) + { + world::CullingComponent* culling = world->getComponent< world::CullingComponent >(); + m_cullingInstance = culling->allocateInstance(m_mesh); + } +} + void InstanceMeshComponent::setTransform(const Transform& transform) { MeshComponent::setTransform(transform); // Re-allocate instance if mesh resource has been replaced. - if (m_mesh.changed()) - { - m_meshInstance = m_mesh->allocateInstance(); - m_mesh.consume(); - } + //if (m_mesh.changed()) + //{ + // m_meshInstance = m_mesh->allocateInstance(); + // m_mesh.consume(); + //} - m_meshInstance->setTransform(transform); + if (m_cullingInstance) + m_cullingInstance->setTransform(transform); } Aabb3 InstanceMeshComponent::getBoundingBox() const @@ -67,17 +87,17 @@ void InstanceMeshComponent::update(const world::UpdateParams& update) MeshComponent::update(update); // Re-allocate instance if mesh resource has been replaced. - if (m_mesh.changed()) - { - m_meshInstance = m_mesh->allocateInstance(); - m_meshInstance->setTransform(getTransform().get0()); - m_mesh.consume(); - } + //if (m_mesh.changed()) + //{ + // m_meshInstance = m_mesh->allocateInstance(); + // m_meshInstance->setTransform(getTransform().get0()); + // m_mesh.consume(); + //} } void InstanceMeshComponent::build(const world::WorldBuildContext& context, const world::WorldRenderView& worldRenderView, const world::IWorldRenderPass& worldRenderPass) { - T_ASSERT_M(0, L"Forgot to register InstanceMeshComponentRenderer?"); + //T_ASSERT_M(0, L"Forgot to register InstanceMeshComponentRenderer?"); } } diff --git a/code/Mesh/Instance/InstanceMeshComponent.h b/code/Mesh/Instance/InstanceMeshComponent.h index 7d6b34d879..12220a2ead 100644 --- a/code/Mesh/Instance/InstanceMeshComponent.h +++ b/code/Mesh/Instance/InstanceMeshComponent.h @@ -1,6 +1,6 @@ /* * TRAKTOR - * Copyright (c) 2022 Anders Pistol. + * Copyright (c) 2022-2024 Anders Pistol. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -10,6 +10,7 @@ #include "Mesh/MeshComponent.h" #include "Resource/Proxy.h" +#include "World/Entity/CullingComponent.h" // import/export mechanism. #undef T_DLLCLASS @@ -38,6 +39,8 @@ class T_DLLCLASS InstanceMeshComponent : public MeshComponent virtual void destroy() override final; + virtual void setWorld(world::World* world) override final; + virtual void setTransform(const Transform& transform) override final; virtual Aabb3 getBoundingBox() const override final; @@ -50,7 +53,7 @@ class T_DLLCLASS InstanceMeshComponent : public MeshComponent private: resource::Proxy< InstanceMesh > m_mesh; - InstanceMesh::Instance* m_meshInstance = nullptr; + world::CullingComponent::Instance* m_cullingInstance = nullptr; }; } diff --git a/code/Mesh/Instance/InstanceMeshComponentRenderer.cpp b/code/Mesh/Instance/InstanceMeshComponentRenderer.cpp deleted file mode 100644 index 99eb8cc06e..0000000000 --- a/code/Mesh/Instance/InstanceMeshComponentRenderer.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * TRAKTOR - * Copyright (c) 2022-2024 Anders Pistol. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -#include "Mesh/Instance/InstanceMesh.h" -#include "Mesh/Instance/InstanceMeshComponent.h" -#include "Mesh/Instance/InstanceMeshComponentRenderer.h" -#include "World/IWorldRenderPass.h" -#include "World/WorldBuildContext.h" -#include "World/WorldRenderView.h" - -namespace traktor::mesh -{ - namespace - { - -static const render::Handle s_techniqueVelocityWrite(L"World_VelocityWrite"); - - } - -T_IMPLEMENT_RTTI_CLASS(L"traktor.mesh.InstanceMeshComponentRenderer", InstanceMeshComponentRenderer, world::IEntityRenderer) - -const TypeInfoSet InstanceMeshComponentRenderer::getRenderableTypes() const -{ - return makeTypeInfoSet< InstanceMeshComponent >(); -} - -void InstanceMeshComponentRenderer::setup( - const world::WorldSetupContext& context, - const world::WorldRenderView& worldRenderView, - Object* renderable -) -{ -} - -void InstanceMeshComponentRenderer::setup( - const world::WorldSetupContext& context -) -{ -} - -void InstanceMeshComponentRenderer::build( - const world::WorldBuildContext& context, - const world::WorldRenderView& worldRenderView, - const world::IWorldRenderPass& worldRenderPass, - Object* renderable -) -{ - auto meshComponent = static_cast< InstanceMeshComponent* >(renderable); - auto mesh = meshComponent->getMesh(); - - //#todo Doesn't support velocity yet. - if (worldRenderPass.getTechnique() == s_techniqueVelocityWrite) - return; - - if (!mesh->supportTechnique(worldRenderPass.getTechnique())) - return; - - m_meshes.insert(mesh); -} - -void InstanceMeshComponentRenderer::build( - const world::WorldBuildContext& context, - const world::WorldRenderView& worldRenderView, - const world::IWorldRenderPass& worldRenderPass -) -{ - for (auto& it : m_meshes) - { - it->build( - context.getRenderContext(), - worldRenderView, - worldRenderPass, - nullptr - ); - } - m_meshes.reset(); -} - -} diff --git a/code/Mesh/Instance/InstanceMeshComponentRenderer.h b/code/Mesh/Instance/InstanceMeshComponentRenderer.h deleted file mode 100644 index 444374f56d..0000000000 --- a/code/Mesh/Instance/InstanceMeshComponentRenderer.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * TRAKTOR - * Copyright (c) 2022-2024 Anders Pistol. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -#pragma once - -#include "Core/Containers/SmallSet.h" -#include "Mesh/Instance/InstanceMesh.h" -#include "World/IEntityRenderer.h" - -// import/export mechanism. -#undef T_DLLCLASS -#if defined(T_MESH_EXPORT) -# define T_DLLCLASS T_DLLEXPORT -#else -# define T_DLLCLASS T_DLLIMPORT -#endif - -namespace traktor::mesh -{ - -/*! Instance mesh component renderer. - * \ingroup Mesh - */ -class T_DLLCLASS InstanceMeshComponentRenderer : public world::IEntityRenderer -{ - T_RTTI_CLASS; - -public: - virtual const TypeInfoSet getRenderableTypes() const override final; - - virtual void setup( - const world::WorldSetupContext& context, - const world::WorldRenderView& worldRenderView, - Object* renderable - ) override final; - - virtual void setup( - const world::WorldSetupContext& context - ) override final; - - virtual void build( - const world::WorldBuildContext& context, - const world::WorldRenderView& worldRenderView, - const world::IWorldRenderPass& worldRenderPass, - Object* renderable - ) override final; - - virtual void build( - const world::WorldBuildContext& context, - const world::WorldRenderView& worldRenderView, - const world::IWorldRenderPass& worldRenderPass - ) override final; - -private: - SmallSet< InstanceMesh* > m_meshes; -}; - -} diff --git a/code/Mesh/Instance/InstanceMeshResource.cpp b/code/Mesh/Instance/InstanceMeshResource.cpp index 99802f0647..5a10c93d08 100644 --- a/code/Mesh/Instance/InstanceMeshResource.cpp +++ b/code/Mesh/Instance/InstanceMeshResource.cpp @@ -24,7 +24,6 @@ namespace traktor::mesh namespace { -const resource::Id< render::Shader > c_shaderInstanceMeshCull(L"{37998131-BDA1-DE45-B175-35B088FEE61C}"); const resource::Id< render::Shader > c_shaderInstanceMeshDraw(L"{A8FDE33C-D75B-4D4E-848F-7D7CF97F11D0}"); } @@ -51,15 +50,11 @@ Ref< IMesh > InstanceMeshResource::createMesh( } } - resource::Proxy< render::Shader > shaderCull; - if (!resourceManager->bind(c_shaderInstanceMeshCull, shaderCull)) - return nullptr; - resource::Proxy< render::Shader > shaderDraw; if (!resourceManager->bind(c_shaderInstanceMeshDraw, shaderDraw)) return nullptr; - Ref< InstanceMesh > instanceMesh = new InstanceMesh(renderSystem, shaderCull, shaderDraw); + Ref< InstanceMesh > instanceMesh = new InstanceMesh(renderSystem, shaderDraw); if (!resourceManager->bind(m_shader, instanceMesh->m_shader)) return nullptr; diff --git a/code/Runtime/Impl/WorldServer.cpp b/code/Runtime/Impl/WorldServer.cpp index f9cd9fff5d..dee66e2329 100644 --- a/code/Runtime/Impl/WorldServer.cpp +++ b/code/Runtime/Impl/WorldServer.cpp @@ -22,7 +22,6 @@ #include "Core/Settings/PropertyInteger.h" #include "Core/Settings/PropertyString.h" #include "Mesh/MeshComponentRenderer.h" -#include "Mesh/Instance/InstanceMeshComponentRenderer.h" #include "Physics/PhysicsManager.h" #include "Render/IRenderSystem.h" #include "Resource/IResourceManager.h" @@ -37,6 +36,7 @@ #include "World/IWorldRenderer.h" #include "World/WorldEntityRenderers.h" #include "World/WorldResourceFactory.h" +#include "World/Entity/CullingRenderer.h" #include "World/Entity/DecalRenderer.h" #include "World/Entity/ProbeRenderer.h" #include "World/Entity/VolumetricFogRenderer.h" @@ -175,11 +175,11 @@ void WorldServer::createEntityRenderers(IEnvironment* environment) bool(m_oceanQuality >= world::Quality::High) ); + m_entityRenderers->add(new world::CullingRenderer()); m_entityRenderers->add(new world::DecalRenderer(renderSystem)); m_entityRenderers->add(new world::ProbeRenderer(resourceManager, renderSystem, *m_worldType)); m_entityRenderers->add(new world::VolumetricFogRenderer()); m_entityRenderers->add(new mesh::MeshComponentRenderer()); - m_entityRenderers->add(new mesh::InstanceMeshComponentRenderer()); m_entityRenderers->add(m_effectEntityRenderer); m_entityRenderers->add(new animation::AnimatedMeshComponentRenderer()); m_entityRenderers->add(new animation::ClothRenderer()); diff --git a/code/Scene/Editor/DefaultEditorProfile.cpp b/code/Scene/Editor/DefaultEditorProfile.cpp index aa3d5d8393..edb79b60d3 100644 --- a/code/Scene/Editor/DefaultEditorProfile.cpp +++ b/code/Scene/Editor/DefaultEditorProfile.cpp @@ -30,6 +30,7 @@ #include "World/Editor/WorldEditorEntityFactory.h" // Entity renderers +#include "World/Entity/CullingRenderer.h" #include "World/Entity/DecalRenderer.h" #include "World/Entity/ProbeRenderer.h" #include "World/Entity/VolumetricFogRenderer.h" @@ -100,6 +101,7 @@ void DefaultEditorProfile::createEntityRenderers( RefArray< world::IEntityRenderer >& outEntityRenderers ) const { + outEntityRenderers.push_back(new world::CullingRenderer()); outEntityRenderers.push_back(new world::DecalRenderer(context->getRenderSystem())); outEntityRenderers.push_back(new world::ProbeRenderer(context->getResourceManager(), context->getRenderSystem(), worldRendererType)); outEntityRenderers.push_back(new world::VolumetricFogRenderer()); diff --git a/code/Scene/Editor/SceneEditorContext.cpp b/code/Scene/Editor/SceneEditorContext.cpp index 0be6c0ae9c..ad30e58dc3 100644 --- a/code/Scene/Editor/SceneEditorContext.cpp +++ b/code/Scene/Editor/SceneEditorContext.cpp @@ -51,6 +51,7 @@ #include "World/EntityEventManager.h" #include "World/EntityFactory.h" #include "World/World.h" +#include "World/Entity/CullingComponent.h" #include "World/Entity/GroupComponent.h" namespace traktor::scene @@ -467,6 +468,18 @@ void SceneEditorContext::buildEntities() Ref< world::World > world = new world::World(); + // #fixme How should we create this? + world->setComponent(new world::CullingComponent(m_resourceManager, m_renderSystem)); + + // Create world components. + for (auto worldComponentData : m_sceneAsset->getWorldComponents()) + { + Ref< EntityAdapterBuilder > entityAdapterBuilder = new EntityAdapterBuilder(this, entityFactory, world, nullptr); + Ref< world::IWorldComponent > worldComponent = entityAdapterBuilder->create(worldComponentData); + if (worldComponent) + world->setComponent(worldComponent); + } + // Create entities from scene layers. RefArray< world::EntityData > layers = m_sceneAsset->getLayers(); layers.erase( @@ -491,15 +504,6 @@ void SceneEditorContext::buildEntities() T_FATAL_ASSERT(m_layerEntityAdapters[i]->getParent() == nullptr); } - // Create world components. - for (auto worldComponentData : m_sceneAsset->getWorldComponents()) - { - Ref< EntityAdapterBuilder > entityAdapterBuilder = new EntityAdapterBuilder(this, entityFactory, world, nullptr); - Ref< world::IWorldComponent > worldComponent = entityAdapterBuilder->create(worldComponentData); - if (worldComponent) - world->setComponent(worldComponent); - } - // Create our scene. m_scene = new Scene( m_sceneAsset->getWorldRenderSettings(), diff --git a/code/World/Entity.cpp b/code/World/Entity.cpp index 00a9c1decf..1e3bed2211 100644 --- a/code/World/Entity.cpp +++ b/code/World/Entity.cpp @@ -44,6 +44,13 @@ void Entity::destroy() m_components.clear(); } +void Entity::setWorld(World* world) +{ + m_world = world; + for (auto component : m_components) + component->setWorld(world); +} + EntityState Entity::setState(const EntityState& state, const EntityState& mask) { const EntityState current = m_state; diff --git a/code/World/Entity.h b/code/World/Entity.h index c3f720fdd8..e03b725cf6 100644 --- a/code/World/Entity.h +++ b/code/World/Entity.h @@ -62,7 +62,10 @@ class T_DLLCLASS Entity : public Object */ virtual void destroy(); - /*! Get world entity is part of. */ + /*! Set world by which entity is part of. */ + void setWorld(World* world); + + /*! Get world by which entity is part of. */ World* getWorld() const { return m_world; } /*! Get entity id. */ @@ -154,8 +157,6 @@ class T_DLLCLASS Entity : public Object } private: - friend class World; - World* m_world = nullptr; Guid m_id; std::wstring m_name; diff --git a/code/World/Entity/CullingComponent.cpp b/code/World/Entity/CullingComponent.cpp new file mode 100644 index 0000000000..b9c7b81984 --- /dev/null +++ b/code/World/Entity/CullingComponent.cpp @@ -0,0 +1,209 @@ +/* + * TRAKTOR + * Copyright (c) 2024 Anders Pistol. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#include "Core/Misc/SafeDestroy.h" +#include "Core/Misc/String.h" +#include "Render/Buffer.h" +#include "Render/IRenderSystem.h" +#include "Render/Shader.h" +#include "Render/Context/RenderContext.h" +#include "Resource/IResourceManager.h" +#include "World/IWorldRenderPass.h" +#include "World/WorldBuildContext.h" +#include "World/WorldHandles.h" +#include "World/WorldRenderView.h" +#include "World/Entity/CullingComponent.h" + +namespace traktor::world +{ + namespace + { + +const resource::Id< render::Shader > c_shaderInstanceMeshCull(L"{37998131-BDA1-DE45-B175-35B088FEE61C}"); + +render::Handle s_handleInstanceWorld(L"InstanceWorld"); +render::Handle s_handleTargetSize(L"InstanceMesh_TargetSize"); +render::Handle s_handleViewProjection(L"InstanceMesh_ViewProjection"); +render::Handle s_handleVisibility(L"InstanceMesh_Visibility"); +render::Handle s_handleCullFrustum(L"InstanceMesh_CullFrustum"); + + } + +T_IMPLEMENT_RTTI_CLASS(L"traktor.world.CullingComponent", CullingComponent, IWorldComponent) + +CullingComponent::CullingComponent(resource::IResourceManager* resourceManager, render::IRenderSystem* renderSystem) +: m_renderSystem(renderSystem) +{ + resourceManager->bind(c_shaderInstanceMeshCull, m_shaderCull); +} + +void CullingComponent::destroy() +{ + T_FATAL_ASSERT_M(m_instances.empty(), L"Culling instances not empty."); + safeDestroy(m_instanceBuffer); + for (auto visibilityBuffer : m_visibilityBuffers) + safeDestroy(visibilityBuffer); + m_visibilityBuffers.resize(0); +} + +void CullingComponent::update(World* world, const UpdateParams& update) +{ +} + +void CullingComponent::build( + const WorldBuildContext& context, + const WorldRenderView& worldRenderView, + const IWorldRenderPass& worldRenderPass +) +{ + if (m_instances.empty()) + return; + + render::RenderContext* renderContext = context.getRenderContext(); + const uint32_t bufferItemCount = (uint32_t)alignUp(m_instances.size(), 16); + + // Lazy create the buffers if necessary. + if (!m_instanceBuffer || bufferItemCount > m_instanceAllocatedCount) + { + m_instanceBuffer = m_renderSystem->createBuffer(render::BufferUsage::BuStructured, bufferItemCount * sizeof(InstanceRenderData), true); + m_visibilityBuffers.resize(0); + m_instanceAllocatedCount = bufferItemCount; + m_instanceBufferDirty = true; + } + + const uint32_t peakCascade = worldRenderView.getCascade(); + const uint32_t vbSize = (uint32_t)m_visibilityBuffers.size(); + for (uint32_t i = vbSize; i < peakCascade + 1; ++i) + m_visibilityBuffers.push_back(m_renderSystem->createBuffer(render::BufferUsage::BuStructured, bufferItemCount * sizeof(float), false)); + + // Update buffer is any instance has moved. + if (m_instanceBufferDirty) + { + auto ptr = (InstanceRenderData*)m_instanceBuffer->lock(); + for (const auto& instance : m_instances) + { + InstanceRenderData& ird = *ptr++; + instance->transform.rotation().e.storeAligned(ird.rotation); + instance->transform.translation().storeAligned(ird.translation); + instance->boundingBox.mn.storeAligned(ird.boundingBoxMin); + instance->boundingBox.mx.storeAligned(ird.boundingBoxMax); + } + m_instanceBuffer->unlock(); + m_instanceBufferDirty = false; + } + + render::Buffer* visibilityBuffer = m_visibilityBuffers[worldRenderView.getCascade()]; + + // Cull instances, output are visibility buffer. + // #todo Compute blocks are executed before render pass, so for shadow map rendering all cascades + // are culled before being rendered. + { + Vector4 cullFrustum[12]; + + const Frustum& cf = worldRenderView.getCullFrustum(); + T_FATAL_ASSERT(cf.planes.size() <= sizeof_array(cullFrustum)); + for (int32_t i = 0; i < (int32_t)cf.planes.size(); ++i) + cullFrustum[i] = cf.planes[i].normal().xyz0() + Vector4(0.0f, 0.0f, 0.0f, cf.planes[i].distance()); + for (int32_t i = (int32_t)cf.planes.size(); i < sizeof_array(cullFrustum); ++i) + cullFrustum[i] = Vector4::zero(); + + const Vector2 viewSize = worldRenderView.getViewSize(); + + auto renderBlock = renderContext->allocNamed< render::ComputeRenderBlock >( + str(L"Mesh cull %d", worldRenderView.getCascade()) + ); + + render::Shader::Permutation perm; + if (worldRenderPass.getTechnique() == s_techniqueDeferredGBufferWrite) + { + // Deferred g-buffer pass has access to HiZ texture. + m_shaderCull->setCombination(render::getParameterHandle(L"InstanceMesh_HiZ"), true, perm); + } + else + { + // All other paths use simple frustum culling only. + m_shaderCull->setCombination(render::getParameterHandle(L"InstanceMesh_HiZ"), false, perm); + } + + renderBlock->program = m_shaderCull->getProgram(perm).program; + + renderBlock->programParams = renderContext->alloc< render::ProgramParameters >(); + renderBlock->programParams->beginParameters(renderContext); + + worldRenderPass.setProgramParameters(renderBlock->programParams); + + renderBlock->programParams->setVectorParameter(s_handleTargetSize, Vector4(viewSize.x, viewSize.y, 0.0f, 0.0f)); + renderBlock->programParams->setMatrixParameter(s_handleViewProjection, worldRenderView.getProjection() * worldRenderView.getView()); + renderBlock->programParams->setVectorArrayParameter(s_handleCullFrustum, cullFrustum, sizeof_array(cullFrustum)); + renderBlock->programParams->setBufferViewParameter(s_handleInstanceWorld, m_instanceBuffer->getBufferView()); + renderBlock->programParams->setBufferViewParameter(s_handleVisibility, visibilityBuffer->getBufferView()); + renderBlock->programParams->endParameters(renderContext); + + renderBlock->workSize[0] = (int32_t)m_instances.size(); + + renderContext->compute(renderBlock); + renderContext->compute< render::BarrierRenderBlock >(render::Stage::Compute, render::Stage::Compute, nullptr, 0); + } + + // Batch draw instances; assumes m_instances are sorted by "cullable". + for (uint32_t i = 0; i < (uint32_t)m_instances.size(); ) + { + uint32_t j = i + 1; + for (; j < (uint32_t)m_instances.size(); ++j) + { + if (m_instances[i]->cullable != m_instances[j]->cullable) + break; + } + + m_instances[i]->cullable->cullableBuild( + context, + worldRenderView, + worldRenderPass, + m_instanceBuffer, + visibilityBuffer, + i, + (j - i) + ); + + i = j; + } +} + +CullingComponent::Instance* CullingComponent::allocateInstance(ICullable* cullable) +{ + Instance* instance = new Instance(); + instance->owner = this; + instance->cullable = cullable; + instance->transform = Transform::identity(); + instance->boundingBox = cullable->cullableGetBoundingBox(); + + // Insert instance sorted by cullable so we can calculate run length when building. + auto it = std::upper_bound(m_instances.begin(), m_instances.end(), instance, [=](Instance* lh, Instance* rh) { + return lh->cullable < rh->cullable; + }); + m_instances.insert(it, instance); + return instance; +} + +void CullingComponent::releaseInstance(Instance*& instance) +{ + T_FATAL_ASSERT(instance->owner == this); + auto it = std::find(m_instances.begin(), m_instances.end(), instance); + m_instances.erase(it); + delete instance; + instance = nullptr; +} + +void CullingComponent::Instance::setTransform(const Transform& transform) +{ + this->transform = transform; + this->boundingBox = this->cullable->cullableGetBoundingBox().transform(transform); + this->owner->m_instanceBufferDirty = true; +} + +} diff --git a/code/World/Entity/CullingComponent.h b/code/World/Entity/CullingComponent.h new file mode 100644 index 0000000000..ed32d1dccb --- /dev/null +++ b/code/World/Entity/CullingComponent.h @@ -0,0 +1,117 @@ +/* + * TRAKTOR + * Copyright (c) 2024 Anders Pistol. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "Core/RefArray.h" +#include "Core/Containers/AlignedVector.h" +#include "Core/Math/Aabb3.h" +#include "Resource/Proxy.h" +#include "World/IWorldComponent.h" + +// import/export mechanism. +#undef T_DLLCLASS +#if defined(T_WORLD_EXPORT) +# define T_DLLCLASS T_DLLEXPORT +#else +# define T_DLLCLASS T_DLLIMPORT +#endif + +namespace traktor::render +{ + +class Buffer; +class IRenderSystem; +class Shader; + +} + +namespace traktor::resource +{ + +class IResourceManager; + +} + +namespace traktor::world +{ + +class IWorldRenderPass; +class WorldBuildContext; +class WorldRenderView; + +/*! + * \ingroup Mesh + */ +class T_DLLCLASS CullingComponent : public IWorldComponent +{ + T_RTTI_CLASS; + +public: +#pragma pack(1) + struct T_MATH_ALIGN16 InstanceRenderData + { + float rotation[4]; + float translation[4]; + float boundingBoxMin[4]; + float boundingBoxMax[4]; + }; +#pragma pack() + + struct T_DLLCLASS ICullable + { + virtual const Aabb3& cullableGetBoundingBox() const = 0; + + virtual void cullableBuild( + const WorldBuildContext& context, + const world::WorldRenderView& worldRenderView, + const world::IWorldRenderPass& worldRenderPass, + render::Buffer* instanceBuffer, + render::Buffer* visibilityBuffer, + uint32_t start, + uint32_t count + ) = 0; + }; + + struct T_DLLCLASS Instance + { + CullingComponent* owner; + ICullable* cullable; + Transform transform; + Aabb3 boundingBox; + + void setTransform(const Transform& transform); + }; + + explicit CullingComponent(resource::IResourceManager* resourceManager, render::IRenderSystem* renderSystem); + + virtual void destroy() override final; + + virtual void update(World* world, const UpdateParams& update) override final; + + void build( + const WorldBuildContext& context, + const WorldRenderView& worldRenderView, + const IWorldRenderPass& worldRenderPass + ); + + Instance* allocateInstance(ICullable* cullable); + + void releaseInstance(Instance*& instance); + +private: + Ref< render::IRenderSystem > m_renderSystem; + resource::Proxy< render::Shader > m_shaderCull; + AlignedVector< Instance* > m_instances; + Ref< render::Buffer > m_instanceBuffer; + RefArray< render::Buffer > m_visibilityBuffers; + uint32_t m_instanceAllocatedCount = 0; + bool m_instanceBufferDirty = false; +}; + +} diff --git a/code/World/Entity/CullingRenderer.cpp b/code/World/Entity/CullingRenderer.cpp new file mode 100644 index 0000000000..2bb0eccc84 --- /dev/null +++ b/code/World/Entity/CullingRenderer.cpp @@ -0,0 +1,55 @@ +/* + * TRAKTOR + * Copyright (c) 2024 Anders Pistol. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#include "World/Entity/CullingComponent.h" +#include "World/Entity/CullingRenderer.h" + +namespace traktor::world +{ + +T_IMPLEMENT_RTTI_CLASS(L"traktor.world.CullingRenderer", CullingRenderer, IEntityRenderer) + +const TypeInfoSet CullingRenderer::getRenderableTypes() const +{ + return makeTypeInfoSet< CullingComponent >(); +} + +void CullingRenderer::setup( + const WorldSetupContext& context, + const WorldRenderView& worldRenderView, + Object* renderable +) +{ +} + +void CullingRenderer::setup( + const WorldSetupContext& context +) +{ +} + +void CullingRenderer::build( + const WorldBuildContext& context, + const WorldRenderView& worldRenderView, + const IWorldRenderPass& worldRenderPass, + Object* renderable +) +{ + auto cullingComponent = static_cast< CullingComponent* >(renderable); + cullingComponent->build(context, worldRenderView, worldRenderPass); +} + +void CullingRenderer::build( + const WorldBuildContext& context, + const WorldRenderView& worldRenderView, + const IWorldRenderPass& worldRenderPass +) +{ +} + +} diff --git a/code/World/Entity/CullingRenderer.h b/code/World/Entity/CullingRenderer.h new file mode 100644 index 0000000000..1384337d31 --- /dev/null +++ b/code/World/Entity/CullingRenderer.h @@ -0,0 +1,58 @@ +/* + * TRAKTOR + * Copyright (c) 2024 Anders Pistol. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "World/IEntityRenderer.h" + +// import/export mechanism. +#undef T_DLLCLASS +#if defined(T_WORLD_EXPORT) +# define T_DLLCLASS T_DLLEXPORT +#else +# define T_DLLCLASS T_DLLIMPORT +#endif + +namespace traktor::world +{ + +/*! + * \ingroup World + */ +class T_DLLCLASS CullingRenderer : public IEntityRenderer +{ + T_RTTI_CLASS; + +public: + virtual const TypeInfoSet getRenderableTypes() const override final; + + virtual void setup( + const WorldSetupContext& context, + const WorldRenderView& worldRenderView, + Object* renderable + ) override final; + + virtual void setup( + const WorldSetupContext& context + ) override final; + + virtual void build( + const WorldBuildContext& context, + const WorldRenderView& worldRenderView, + const IWorldRenderPass& worldRenderPass, + Object* renderable + ) override final; + + virtual void build( + const WorldBuildContext& context, + const WorldRenderView& worldRenderView, + const IWorldRenderPass& worldRenderPass + ) override final; +}; + +} diff --git a/code/World/Entity/VolumetricFogRenderer.cpp b/code/World/Entity/VolumetricFogRenderer.cpp index 5658192048..0d91ec2ec1 100644 --- a/code/World/Entity/VolumetricFogRenderer.cpp +++ b/code/World/Entity/VolumetricFogRenderer.cpp @@ -1,12 +1,11 @@ /* * TRAKTOR - * Copyright (c) 2022 Anders Pistol. + * Copyright (c) 2022-2024 Anders Pistol. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#include "World/WorldBuildContext.h" #include "World/Entity/VolumetricFogComponent.h" #include "World/Entity/VolumetricFogRenderer.h" diff --git a/code/World/IEntityComponent.h b/code/World/IEntityComponent.h index b50e61ad44..4c7449bba1 100644 --- a/code/World/IEntityComponent.h +++ b/code/World/IEntityComponent.h @@ -24,6 +24,7 @@ namespace traktor::world { class Entity; +class World; /*! Entity component. * \ingroup World @@ -40,6 +41,11 @@ class T_DLLCLASS IEntityComponent : public Object */ virtual void setOwner(Entity* owner) = 0; + /*! Called when entity's world is set. + * \param world New world. + */ + virtual void setWorld(World* world) {} + /*! Called when entity's state is modified. * \param state Entity state. */ diff --git a/code/World/Shared/WorldRendererShared.cpp b/code/World/Shared/WorldRendererShared.cpp index 15f5a11faa..8615ea5129 100644 --- a/code/World/Shared/WorldRendererShared.cpp +++ b/code/World/Shared/WorldRendererShared.cpp @@ -21,6 +21,7 @@ #include "World/Entity.h" #include "World/IEntityRenderer.h" #include "World/IrradianceGrid.h" +#include "World/IWorldComponent.h" #include "World/Packer.h" #include "World/World.h" #include "World/WorldBuildContext.h" @@ -221,6 +222,13 @@ void WorldRendererShared::gather(const World* world, const std::function< bool(c m_gatheredView.probes.resize(0); m_gatheredView.fog = nullptr; + for (auto component : world->getComponents()) + { + IEntityRenderer* entityRenderer = m_entityRenderers->find(type_of(component)); + if (entityRenderer) + m_gatheredView.renderables.push_back({ entityRenderer, component, EntityState::All }); + } + for (auto entity : world->getEntities()) { const EntityState state = entity->getState(); diff --git a/code/World/World.cpp b/code/World/World.cpp index 6392c72a62..8064d83d6a 100644 --- a/code/World/World.cpp +++ b/code/World/World.cpp @@ -32,7 +32,7 @@ void World::destroy() for (auto entity : m_entities) { - entity->m_world = nullptr; + entity->setWorld(nullptr); entity->destroy(); } m_entities.clear(); @@ -68,17 +68,17 @@ IWorldComponent* World::getComponent(const TypeInfo& componentType) const void World::addEntity(Entity* entity) { - T_FATAL_ASSERT(entity->m_world == nullptr); + T_FATAL_ASSERT(entity->getWorld() == nullptr); if (m_update) m_deferredAdd.push_back(entity); else m_entities.push_back(entity); - entity->m_world = this; + entity->setWorld(this); } void World::removeEntity(Entity* entity) { - T_FATAL_ASSERT(entity->m_world == this); + T_FATAL_ASSERT(entity->getWorld() == this); if (m_update) m_deferredRemove.push_back(entity); else @@ -86,12 +86,12 @@ void World::removeEntity(Entity* entity) const bool removed = m_entities.remove(entity); T_FATAL_ASSERT(removed); } - entity->m_world = nullptr; + entity->setWorld(nullptr); } bool World::haveEntity(const Entity* entity) const { - if (entity->m_world == this) + if (entity->getWorld() == this) return std::find(m_entities.begin(), m_entities.end(), entity) != m_entities.end(); else return false; @@ -151,7 +151,7 @@ void World::update(const UpdateParams& update) m_update = true; for (auto entity : m_entities) { - if (entity->m_world != nullptr) + if (entity->getWorld() != nullptr) entity->update(update); } m_update = false; diff --git a/data/Source/System/Mesh/Shaders/Instance/Cull/Cull.xdi b/data/Source/System/Mesh/Shaders/Instance/Cull/Cull.xdi index 46f594b987..e750a73628 100644 --- a/data/Source/System/Mesh/Shaders/Instance/Cull/Cull.xdi +++ b/data/Source/System/Mesh/Shaders/Instance/Cull/Cull.xdi @@ -5,8 +5,8 @@ {4BAA3A0D-D779-C141-B519-197EEA8640D6} - -336 - 123 + -398 + 135 Global @@ -14,8 +14,8 @@ {B63DBBD3-72C0-C645-8A24-4893BBF22E30} - -107 - 132 + -169 + 144 x @@ -538,16 +538,6 @@ else {AF60E34F-9E23-4E4B-8874-3FCB33F46C2F} - - - - {ADB4FC1D-3726-4CC5-B4D5-1E2468274325} - - - - {13DDFE53-988C-FE40-B343-7FE23D25E253} - - @@ -598,16 +588,6 @@ else {0FF6511C-0293-41A8-830E-81978BD01F7F} - - - - {ADB4FC1D-3726-4CC5-B4D5-1E2468274325} - - - - {0FF6511C-0293-41A8-840E-81978BD01F7F} - - @@ -778,6 +758,26 @@ else {D2D716D6-C4A1-471F-894A-D718515F6281} + + + + {ADB4FC1D-3726-4CC5-B4D5-1E2468274325} + + + + {0FF6511C-0293-41A8-840E-81978BD01F7F} + + + + + + {ADB4FC1D-3726-4CC5-B4D5-1E2468274325} + + + + {13DDFE53-988C-FE40-B343-7FE23D25E253} + + diff --git a/data/Source/System/Mesh/Shaders/Instance/Cull/Draw.xdi b/data/Source/System/Mesh/Shaders/Instance/Cull/Draw.xdi index 6d5dcf0798..448a2245e7 100644 --- a/data/Source/System/Mesh/Shaders/Instance/Cull/Draw.xdi +++ b/data/Source/System/Mesh/Shaders/Instance/Cull/Draw.xdi @@ -5,8 +5,8 @@ {AA20BF80-C68A-D649-9BF9-23BC5F6257F8} - 156 - 344 + -305 + 333 x @@ -14,8 +14,8 @@ {0C63324D-D49B-7C44-BE53-17ECD5DBE92E} - -87 - 356 + -542 + 319 Global @@ -23,8 +23,8 @@ {9772F569-3912-1B46-AB4B-157B8CC99284} - -4 - 282 + -27 + 269 InstanceMesh_Visibility @@ -38,8 +38,8 @@ {B6ABD193-2075-0442-A1B6-92D4C05F99CB} - 6 - 239 + -17 + 226 InstanceMesh_Draw @@ -121,8 +121,8 @@ $DrawBuffer[index].firstInstance = 0; {3491574B-839F-B74B-8747-0C4A10BECB57} - 32 - 405 + 7 + 434 InstanceMesh_IndexCount Scalar @@ -132,13 +132,32 @@ $DrawBuffer[index].firstInstance = 0; {89C29FEB-0F4E-714E-88DE-0BA91E135B7D} - 56 - 442 + 31 + 471 InstanceMesh_FirstIndex Scalar Frame + + {63F715EF-F327-F04D-8827-705805B04479} + + + -438 + 398 + + InstanceMesh_InstanceOffset + Scalar + Draw + + + {EBB7D461-F10E-FD4E-B98F-27018D533692} + + + -137 + 341 + + @@ -151,16 +170,6 @@ $DrawBuffer[index].firstInstance = 0; {F2E22CA6-DFF3-4B20-A70A-0D7A44EACD8C} - - - - {ADB4FC1D-3726-4CC5-B4D5-1E2468274325} - - - - {89D6EAF6-DAAD-C647-B640-7F86B233A509} - - @@ -201,6 +210,36 @@ $DrawBuffer[index].firstInstance = 0; {788C47C3-E973-CF48-9A12-5A2751107996} + + + + {1E6639B6-8B58-4694-99E7-C058E3583522} + + + + {9F45B2C3-B513-4646-B0C1-663748FD169C} + + + + + + {ADB4FC1D-3726-4CC5-B4D5-1E2468274325} + + + + {3DE04294-4DEA-4A13-A460-2274647357EA} + + + + + + {32FD3DAA-16C1-44C8-8A1E-E9ECF97F31D2} + + + + {89D6EAF6-DAAD-C647-B640-7F86B233A509} + +