From 2da086b7df196e61d2b68f3049bbaad545909eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Wed, 11 Apr 2018 19:36:52 +0200 Subject: [PATCH] New Render queues (#161) * Add new render queues proof of concept + scissoring support (WIP) * Graphics: Adapt basic sprites rendering to new render queue system * Graphics: Fix layers when rendering sprites * Graphics/RenderQueue: Fix sprite default overlay * Graphics: Enable scissor test by default * SDK/Widgets: Enable scissoring on widgets * Graphics: Handle almost everything with the new renderqueues system Todo: - Billboard rendering - Proper model rendering * Graphics/RenderQueue: Billboard drawing now works (WIP) At 1/4 of previous code performances due to individually process of billboards * Add new render queues proof of concept + scissoring support (WIP) * Graphics: Adapt basic sprites rendering to new render queue system * Graphics: Fix layers when rendering sprites * Graphics/RenderQueue: Fix sprite default overlay * Graphics: Enable scissor test by default * SDK/Widgets: Enable scissoring on widgets * Graphics: Handle almost everything with the new renderqueues system Todo: - Billboard rendering - Proper model rendering * Graphics/RenderQueue: Billboard drawing now works (WIP) At 1/4 of previous code performances due to individually process of billboards * Graphics/RenderQueues: Add full support for billboards * Graphics/RenderQueue: Cleanup and improve billboard rendering * Graphics/RenderQueue: Fix model drawing * Examples/Particles: Fix lighting on space station * Graphics: Cleanup forward render queue/technique * Fix compilation under Linux * Graphics/ForwardRenderTechnique: Fix case when scissoring is enabled on material but disabled on element * Add support for Deferred Shading * SDK/Widgets: Fix widget rendering * Graphics: Remove legacy code from render queues * Graphics: Fix some objects sometimes not showing up due to broken scissor box * Fix compilation error * Sdk/GraphicsGraphics: Fix bounding volume * SDK/World: Fix self-assignation * Update changelog for render queues --- ChangeLog.md | 3 + SDK/include/NDK/BaseWidget.hpp | 12 +- SDK/include/NDK/Canvas.inl | 2 +- .../NDK/Components/GraphicsComponent.hpp | 5 +- .../NDK/Components/GraphicsComponent.inl | 14 +- SDK/include/NDK/Widgets/LabelWidget.hpp | 4 +- SDK/include/NDK/World.inl | 2 +- SDK/src/NDK/BaseWidget.cpp | 45 +- SDK/src/NDK/Components/GraphicsComponent.cpp | 4 +- SDK/src/NDK/Widgets/ButtonWidget.cpp | 9 +- SDK/src/NDK/Widgets/CheckboxWidget.cpp | 8 +- SDK/src/NDK/Widgets/ImageWidget.cpp | 2 +- SDK/src/NDK/Widgets/LabelWidget.cpp | 9 +- SDK/src/NDK/Widgets/ProgressBarWidget.cpp | 8 +- SDK/src/NDK/Widgets/TextAreaWidget.cpp | 7 +- examples/Particles/LogoDemo.cpp | 2 +- examples/Particles/SpacebattleDemo.cpp | 10 +- examples/Particles/main.cpp | 2 +- include/Nazara/Graphics.hpp | 5 +- .../Nazara/Graphics/AbstractRenderQueue.hpp | 20 +- include/Nazara/Graphics/BasicRenderQueue.hpp | 142 +++ include/Nazara/Graphics/BasicRenderQueue.inl | 38 + include/Nazara/Graphics/Billboard.hpp | 2 +- .../Nazara/Graphics/DeferredGeometryPass.hpp | 21 + .../Graphics/DeferredProxyRenderQueue.hpp | 58 + .../Graphics/DeferredProxyRenderQueue.inl | 30 + .../Nazara/Graphics/DeferredRenderPass.hpp | 6 +- .../Nazara/Graphics/DeferredRenderQueue.hpp | 89 -- .../Nazara/Graphics/DeferredRenderQueue.inl | 0 .../Graphics/DeferredRenderTechnique.hpp | 7 +- include/Nazara/Graphics/DepthRenderQueue.hpp | 26 +- .../Nazara/Graphics/DepthRenderTechnique.hpp | 14 +- .../Nazara/Graphics/ForwardRenderQueue.hpp | 201 --- .../Graphics/ForwardRenderTechnique.hpp | 16 +- .../Graphics/ForwardRenderTechnique.inl | 2 +- .../Nazara/Graphics/InstancedRenderable.hpp | 2 +- include/Nazara/Graphics/Model.hpp | 5 +- include/Nazara/Graphics/Model.inl | 9 +- include/Nazara/Graphics/RenderQueue.hpp | 96 ++ include/Nazara/Graphics/RenderQueue.inl | 136 ++ include/Nazara/Graphics/SkeletalModel.hpp | 2 +- include/Nazara/Graphics/Sprite.hpp | 2 +- include/Nazara/Graphics/Sprite.inl | 1 + include/Nazara/Graphics/TextSprite.hpp | 2 +- include/Nazara/Graphics/TextSprite.inl | 9 +- include/Nazara/Graphics/TileMap.hpp | 2 +- src/Nazara/Graphics/BasicRenderQueue.cpp | 947 ++++++++++++++ src/Nazara/Graphics/Billboard.cpp | 4 +- src/Nazara/Graphics/DeferredGeometryPass.cpp | 641 ++++++++-- .../Graphics/DeferredPhongLightingPass.cpp | 2 +- .../Graphics/DeferredProxyRenderQueue.cpp | 261 ++++ src/Nazara/Graphics/DeferredRenderPass.cpp | 4 +- src/Nazara/Graphics/DeferredRenderQueue.cpp | 410 ------ .../Graphics/DeferredRenderTechnique.cpp | 26 +- src/Nazara/Graphics/DepthRenderQueue.cpp | 42 +- src/Nazara/Graphics/DepthRenderTechnique.cpp | 783 ++++++------ src/Nazara/Graphics/ForwardRenderQueue.cpp | 931 -------------- .../Graphics/ForwardRenderTechnique.cpp | 1101 ++++++----------- src/Nazara/Graphics/MaterialPipeline.cpp | 7 +- src/Nazara/Graphics/Model.cpp | 4 +- src/Nazara/Graphics/RenderQueue.cpp | 18 + src/Nazara/Graphics/SkeletalModel.cpp | 4 +- src/Nazara/Graphics/Sprite.cpp | 4 +- src/Nazara/Graphics/TextSprite.cpp | 4 +- src/Nazara/Graphics/TileMap.cpp | 4 +- 65 files changed, 3290 insertions(+), 2998 deletions(-) create mode 100644 include/Nazara/Graphics/BasicRenderQueue.hpp create mode 100644 include/Nazara/Graphics/BasicRenderQueue.inl create mode 100644 include/Nazara/Graphics/DeferredProxyRenderQueue.hpp create mode 100644 include/Nazara/Graphics/DeferredProxyRenderQueue.inl delete mode 100644 include/Nazara/Graphics/DeferredRenderQueue.hpp create mode 100644 include/Nazara/Graphics/DeferredRenderQueue.inl delete mode 100644 include/Nazara/Graphics/ForwardRenderQueue.hpp create mode 100644 include/Nazara/Graphics/RenderQueue.hpp create mode 100644 include/Nazara/Graphics/RenderQueue.inl create mode 100644 src/Nazara/Graphics/BasicRenderQueue.cpp create mode 100644 src/Nazara/Graphics/DeferredProxyRenderQueue.cpp delete mode 100644 src/Nazara/Graphics/DeferredRenderQueue.cpp delete mode 100644 src/Nazara/Graphics/ForwardRenderQueue.cpp create mode 100644 src/Nazara/Graphics/RenderQueue.cpp diff --git a/ChangeLog.md b/ChangeLog.md index d40dab079..03a51ba76 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -125,6 +125,9 @@ Nazara Development Kit: - ListenerSystem now handles velocity in a generic way (no longer require a VelocityComponent and is compatible with physics) - World now has const getters for systems - Add World::ForEachSystem method, allowing iteration on every active system on a specific world +- 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 # 0.4: diff --git a/SDK/include/NDK/BaseWidget.hpp b/SDK/include/NDK/BaseWidget.hpp index 0804a5191..a8c3e5cd4 100644 --- a/SDK/include/NDK/BaseWidget.hpp +++ b/SDK/include/NDK/BaseWidget.hpp @@ -83,9 +83,10 @@ namespace Ndk }; protected: - const EntityHandle& CreateEntity(); + const EntityHandle& CreateEntity(bool isContentEntity); void DestroyEntity(Entity* entity); virtual void Layout(); + void InvalidateNode() override; virtual bool IsFocusable() const; @@ -111,11 +112,18 @@ namespace Ndk void RegisterToCanvas(); inline void UpdateCanvasIndex(std::size_t index); void UnregisterFromCanvas(); + void UpdatePositionAndSize(); + + struct WidgetEntity + { + EntityOwner handle; + bool isContent; + }; static constexpr std::size_t InvalidCanvasIndex = std::numeric_limits::max(); std::size_t m_canvasIndex; - std::vector m_entities; + std::vector m_entities; std::vector> m_children; Canvas* m_canvas; EntityOwner m_backgroundEntity; diff --git a/SDK/include/NDK/Canvas.inl b/SDK/include/NDK/Canvas.inl index 251a09fbc..7ac84a45d 100644 --- a/SDK/include/NDK/Canvas.inl +++ b/SDK/include/NDK/Canvas.inl @@ -62,7 +62,7 @@ namespace Ndk WidgetEntry& entry = m_widgetEntries[index]; Nz::Vector3f pos = entry.widget->GetPosition(); - Nz::Vector2f size = entry.widget->GetContentSize(); + Nz::Vector2f size = entry.widget->GetSize(); entry.box.Set(pos.x, pos.y, pos.z, size.x, size.y, 1.f); } diff --git a/SDK/include/NDK/Components/GraphicsComponent.hpp b/SDK/include/NDK/Components/GraphicsComponent.hpp index cd6b90c8c..e5dce0144 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.hpp +++ b/SDK/include/NDK/Components/GraphicsComponent.hpp @@ -28,7 +28,7 @@ namespace Ndk public: using RenderableList = std::vector; - GraphicsComponent() = default; + GraphicsComponent(); inline GraphicsComponent(const GraphicsComponent& graphicsComponent); ~GraphicsComponent() = default; @@ -54,6 +54,8 @@ namespace Ndk inline void RemoveFromCullingList(GraphicsComponentCullingList* cullingList) const; + inline void SetScissorRect(const Nz::Recti& scissorRect); + inline void UpdateLocalMatrix(const Nz::InstancedRenderable* instancedRenderable, const Nz::Matrix4f& localMatrix); inline void UpdateRenderOrder(const Nz::InstancedRenderable* instancedRenderable, int renderOrder); @@ -144,6 +146,7 @@ namespace Ndk std::unordered_map m_materialEntries; mutable Nz::BoundingVolumef m_boundingVolume; mutable Nz::Matrix4f m_transformMatrix; + Nz::Recti m_scissorRect; Nz::TextureRef m_reflectionMap; mutable bool m_boundingVolumeUpdated; mutable bool m_transformMatrixUpdated; diff --git a/SDK/include/NDK/Components/GraphicsComponent.inl b/SDK/include/NDK/Components/GraphicsComponent.inl index da8764d73..71b62f367 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.inl +++ b/SDK/include/NDK/Components/GraphicsComponent.inl @@ -9,12 +9,16 @@ namespace Ndk { + inline GraphicsComponent::GraphicsComponent() : + m_scissorRect(-1, -1) + { + } + /*! * \brief Constructs a GraphicsComponent object by copy semantic * * \param graphicsComponent GraphicsComponent to copy */ - inline GraphicsComponent::GraphicsComponent(const GraphicsComponent& graphicsComponent) : Component(graphicsComponent), HandledObject(graphicsComponent), @@ -177,6 +181,14 @@ namespace Ndk } } + inline void GraphicsComponent::SetScissorRect(const Nz::Recti& scissorRect) + { + m_scissorRect = scissorRect; + + for (VolumeCullingEntry& entry : m_volumeCullingEntries) + entry.listEntry.ForceInvalidation(); //< Invalidate render queues + } + inline void GraphicsComponent::UpdateLocalMatrix(const Nz::InstancedRenderable* instancedRenderable, const Nz::Matrix4f& localMatrix) { for (auto& renderable : m_renderables) diff --git a/SDK/include/NDK/Widgets/LabelWidget.hpp b/SDK/include/NDK/Widgets/LabelWidget.hpp index 5cb01b92a..f4f43bf1e 100644 --- a/SDK/include/NDK/Widgets/LabelWidget.hpp +++ b/SDK/include/NDK/Widgets/LabelWidget.hpp @@ -28,7 +28,7 @@ namespace Ndk //virtual LabelWidget* Clone() const = 0; - void ResizeToContent(); + void ResizeToContent() override; inline void UpdateText(const Nz::AbstractTextDrawer& drawer); @@ -36,6 +36,8 @@ namespace Ndk LabelWidget& operator=(LabelWidget&&) = default; private: + void Layout() override; + EntityHandle m_textEntity; Nz::TextSpriteRef m_textSprite; }; diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index efe8b0480..942ce4d36 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -426,7 +426,7 @@ namespace Ndk m_orderedSystems = std::move(world.m_orderedSystems); m_orderedSystemsUpdated = world.m_orderedSystemsUpdated; m_profilerData = std::move(world.m_profilerData); - m_isProfilerEnabled = m_isProfilerEnabled; + m_isProfilerEnabled = world.m_isProfilerEnabled; m_entities = std::move(world.m_entities); for (EntityBlock& block : m_entities) diff --git a/SDK/src/NDK/BaseWidget.cpp b/SDK/src/NDK/BaseWidget.cpp index d8aa9fbfa..dc33252e2 100644 --- a/SDK/src/NDK/BaseWidget.cpp +++ b/SDK/src/NDK/BaseWidget.cpp @@ -81,7 +81,7 @@ namespace Ndk m_backgroundSprite->SetColor(m_backgroundColor); m_backgroundSprite->SetMaterial(Nz::Material::New((m_backgroundColor.IsOpaque()) ? "Basic2D" : "Translucent2D")); //< TODO: Use a shared material instead of creating one everytime - m_backgroundEntity = CreateEntity(); + m_backgroundEntity = CreateEntity(false); m_backgroundEntity->AddComponent().Attach(m_backgroundSprite, -1); m_backgroundEntity->AddComponent().SetParent(this); @@ -147,26 +147,30 @@ namespace Ndk else UnregisterFromCanvas(); - for (const EntityHandle& entity : m_entities) - entity->Enable(show); + for (WidgetEntity& entity : m_entities) + entity.handle->Enable(show); for (const auto& widgetPtr : m_children) widgetPtr->Show(show); } } - const Ndk::EntityHandle& BaseWidget::CreateEntity() + const Ndk::EntityHandle& BaseWidget::CreateEntity(bool isContentEntity) { const EntityHandle& newEntity = m_world->CreateEntity(); newEntity->Enable(m_visible); - m_entities.emplace_back(newEntity); + m_entities.emplace_back(); + WidgetEntity& widgetEntity = m_entities.back(); + widgetEntity.handle = newEntity; + widgetEntity.isContent = isContentEntity; + return newEntity; } void BaseWidget::DestroyEntity(Entity* entity) { - auto it = std::find(m_entities.begin(), m_entities.end(), entity); + auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; }); NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget"); m_entities.erase(it); @@ -174,19 +178,17 @@ namespace Ndk void BaseWidget::Layout() { - if (IsRegisteredToCanvas()) - m_canvas->NotifyWidgetBoxUpdate(m_canvasIndex); - if (m_backgroundEntity) m_backgroundSprite->SetSize(m_contentSize.x + m_padding.left + m_padding.right, m_contentSize.y + m_padding.top + m_padding.bottom); + + UpdatePositionAndSize(); } void BaseWidget::InvalidateNode() { Node::InvalidateNode(); - if (IsRegisteredToCanvas()) - m_canvas->NotifyWidgetBoxUpdate(m_canvasIndex); + UpdatePositionAndSize(); } bool BaseWidget::IsFocusable() const @@ -271,4 +273,25 @@ namespace Ndk m_canvasIndex = InvalidCanvasIndex; } } + + void BaseWidget::UpdatePositionAndSize() + { + if (IsRegisteredToCanvas()) + m_canvas->NotifyWidgetBoxUpdate(m_canvasIndex); + + Nz::Vector2f widgetPos = Nz::Vector2f(GetPosition()); + Nz::Vector2f widgetSize = GetSize(); + + Nz::Vector2f contentPos = widgetPos + GetContentOrigin(); + Nz::Vector2f contentSize = GetContentSize(); + + Nz::Recti fullBounds(Nz::Rectf(widgetPos.x, widgetPos.y, widgetSize.x, widgetSize.y)); + Nz::Recti contentBounds(Nz::Rectf(contentPos.x, contentPos.y, contentSize.x, contentSize.y)); + for (WidgetEntity& widgetEntity : m_entities) + { + const Ndk::EntityHandle& entity = widgetEntity.handle; + if (entity->HasComponent()) + entity->GetComponent().SetScissorRect((widgetEntity.isContent) ? contentBounds : fullBounds); + } + } } diff --git a/SDK/src/NDK/Components/GraphicsComponent.cpp b/SDK/src/NDK/Components/GraphicsComponent.cpp index a4bc6e439..f8b4e468d 100644 --- a/SDK/src/NDK/Components/GraphicsComponent.cpp +++ b/SDK/src/NDK/Components/GraphicsComponent.cpp @@ -35,7 +35,7 @@ namespace Ndk object.dataUpdated = true; } - object.renderable->AddToRenderQueue(renderQueue, object.data); + object.renderable->AddToRenderQueue(renderQueue, object.data, m_scissorRect); } } @@ -282,7 +282,7 @@ namespace Ndk boundingVolume.Set(Nz::Boxf(newPos.x, newPos.y, newPos.z, newLengths.x, newLengths.y, newLengths.z)); } - m_boundingVolume.ExtendTo(r.renderable->GetBoundingVolume()); + m_boundingVolume.ExtendTo(boundingVolume); } RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem(); diff --git a/SDK/src/NDK/Widgets/ButtonWidget.cpp b/SDK/src/NDK/Widgets/ButtonWidget.cpp index e44348dc6..5d8360635 100644 --- a/SDK/src/NDK/Widgets/ButtonWidget.cpp +++ b/SDK/src/NDK/Widgets/ButtonWidget.cpp @@ -30,13 +30,13 @@ namespace Ndk m_gradientSprite->SetCornerColor(Nz::RectCorner_RightBottom, m_cornerColor); m_gradientSprite->SetMaterial(Nz::Material::New("Basic2D")); - m_gradientEntity = CreateEntity(); + m_gradientEntity = CreateEntity(false); m_gradientEntity->AddComponent().SetParent(this); m_gradientEntity->AddComponent().Attach(m_gradientSprite); m_textSprite = Nz::TextSprite::New(); - m_textEntity = CreateEntity(); + m_textEntity = CreateEntity(true); m_textEntity->AddComponent().SetParent(this); m_textEntity->AddComponent().Attach(m_textSprite, 1); @@ -82,12 +82,11 @@ namespace Ndk { BaseWidget::Layout(); + m_gradientSprite->SetSize(GetSize()); + Nz::Vector2f origin = GetContentOrigin(); const Nz::Vector2f& contentSize = GetContentSize(); - m_gradientEntity->GetComponent().SetPosition(origin); - m_gradientSprite->SetSize(contentSize); - Nz::Boxf textBox = m_textEntity->GetComponent().GetBoundingVolume().obb.localBox; m_textEntity->GetComponent().SetPosition(origin.x + contentSize.x / 2 - textBox.width / 2, origin.y + contentSize.y / 2 - textBox.height / 2); } diff --git a/SDK/src/NDK/Widgets/CheckboxWidget.cpp b/SDK/src/NDK/Widgets/CheckboxWidget.cpp index 237aff415..3f20ff7c6 100644 --- a/SDK/src/NDK/Widgets/CheckboxWidget.cpp +++ b/SDK/src/NDK/Widgets/CheckboxWidget.cpp @@ -28,19 +28,19 @@ namespace Ndk m_checkboxContentSprite = Nz::Sprite::New(Nz::Material::New("Translucent2D")); m_textSprite = Nz::TextSprite::New(); - m_checkboxBorderEntity = CreateEntity(); + m_checkboxBorderEntity = CreateEntity(false); m_checkboxBorderEntity->AddComponent().SetParent(this); m_checkboxBorderEntity->AddComponent().Attach(m_checkboxBorderSprite); - m_checkboxBackgroundEntity = CreateEntity(); + m_checkboxBackgroundEntity = CreateEntity(false); m_checkboxBackgroundEntity->AddComponent().SetParent(this); m_checkboxBackgroundEntity->AddComponent().Attach(m_checkboxBackgroundSprite, 1); - m_checkboxContentEntity = CreateEntity(); + m_checkboxContentEntity = CreateEntity(true); m_checkboxContentEntity->AddComponent().SetParent(this); m_checkboxContentEntity->AddComponent().Attach(m_checkboxContentSprite, 2); - m_textEntity = CreateEntity(); + m_textEntity = CreateEntity(true); m_textEntity->AddComponent().SetParent(this); m_textEntity->AddComponent().Attach(m_textSprite); diff --git a/SDK/src/NDK/Widgets/ImageWidget.cpp b/SDK/src/NDK/Widgets/ImageWidget.cpp index d03c67651..b43d5ba8d 100644 --- a/SDK/src/NDK/Widgets/ImageWidget.cpp +++ b/SDK/src/NDK/Widgets/ImageWidget.cpp @@ -11,7 +11,7 @@ namespace Ndk ImageWidget::ImageWidget(BaseWidget* parent) : BaseWidget(parent) { - m_entity = CreateEntity(); + m_entity = CreateEntity(true); m_entity->AddComponent(); auto& gfx = m_entity->AddComponent(); diff --git a/SDK/src/NDK/Widgets/LabelWidget.cpp b/SDK/src/NDK/Widgets/LabelWidget.cpp index a93503d60..98deb8da3 100644 --- a/SDK/src/NDK/Widgets/LabelWidget.cpp +++ b/SDK/src/NDK/Widgets/LabelWidget.cpp @@ -13,13 +13,20 @@ namespace Ndk { m_textSprite = Nz::TextSprite::New(); - m_textEntity = CreateEntity(); + m_textEntity = CreateEntity(true); m_textEntity->AddComponent().Attach(m_textSprite); m_textEntity->AddComponent().SetParent(this); Layout(); } + void LabelWidget::Layout() + { + BaseWidget::Layout(); + + m_textEntity->GetComponent().SetPosition(GetContentOrigin()); + } + void LabelWidget::ResizeToContent() { SetContentSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); diff --git a/SDK/src/NDK/Widgets/ProgressBarWidget.cpp b/SDK/src/NDK/Widgets/ProgressBarWidget.cpp index 721636ffc..80f01d43a 100644 --- a/SDK/src/NDK/Widgets/ProgressBarWidget.cpp +++ b/SDK/src/NDK/Widgets/ProgressBarWidget.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Samy Bensaid +// Copyright (C) 2017 Samy Bensaid // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequisites.hpp @@ -30,11 +30,11 @@ namespace Ndk SetBarColor(s_barColor, s_barCornerColor); - m_borderEntity = CreateEntity(); + m_borderEntity = CreateEntity(false); m_borderEntity->AddComponent().SetParent(this); m_borderEntity->AddComponent().Attach(m_borderSprite); - m_barEntity = CreateEntity(); + m_barEntity = CreateEntity(true); m_barEntity->AddComponent().SetParent(this); GraphicsComponent& graphics = m_barEntity->AddComponent(); @@ -43,7 +43,7 @@ namespace Ndk m_textSprite = Nz::TextSprite::New(); - m_textEntity = CreateEntity(); + m_textEntity = CreateEntity(true); m_textEntity->AddComponent().SetParent(this); m_textEntity->AddComponent().Attach(m_textSprite); diff --git a/SDK/src/NDK/Widgets/TextAreaWidget.cpp b/SDK/src/NDK/Widgets/TextAreaWidget.cpp index a46089fc3..b750dd2f2 100644 --- a/SDK/src/NDK/Widgets/TextAreaWidget.cpp +++ b/SDK/src/NDK/Widgets/TextAreaWidget.cpp @@ -20,14 +20,14 @@ namespace Ndk m_cursorSprite->SetColor(Nz::Color::Black); m_cursorSprite->SetSize(1.f, float(m_drawer.GetFont()->GetSizeInfo(m_drawer.GetCharacterSize()).lineHeight)); - m_cursorEntity = CreateEntity(); + m_cursorEntity = CreateEntity(true); m_cursorEntity->AddComponent().Attach(m_cursorSprite, 10); m_cursorEntity->AddComponent().SetParent(this); m_cursorEntity->Enable(false); m_textSprite = Nz::TextSprite::New(); - m_textEntity = CreateEntity(); + m_textEntity = CreateEntity(true); m_textEntity->AddComponent().Attach(m_textSprite); m_textEntity->AddComponent().SetParent(this); @@ -236,7 +236,8 @@ namespace Ndk { SetFocus(); - SetCursorPosition(GetHoveredGlyph(float(x), float(y))); + const Padding& padding = GetPadding(); + SetCursorPosition(GetHoveredGlyph(float(x - padding.left), float(y - padding.top))); } } diff --git a/examples/Particles/LogoDemo.cpp b/examples/Particles/LogoDemo.cpp index e5e6c0925..0811191e1 100644 --- a/examples/Particles/LogoDemo.cpp +++ b/examples/Particles/LogoDemo.cpp @@ -113,7 +113,7 @@ class SpriteRenderer : public Nz::ParticleRenderer Nz::SparsePtr sizePtr(&size, 0); Nz::SparsePtr sinCosPtr(nullptr, 0); - renderQueue->AddBillboards(0, m_material, endId - startId + 1, mapper.GetComponentPtr(Nz::ParticleComponent_Position), sizePtr, sinCosPtr, mapper.GetComponentPtr(Nz::ParticleComponent_Color)); + renderQueue->AddBillboards(0, m_material, endId - startId + 1, Nz::Recti(-1, -1), mapper.GetComponentPtr(Nz::ParticleComponent_Position), sizePtr, sinCosPtr, mapper.GetComponentPtr(Nz::ParticleComponent_Color)); } private: diff --git a/examples/Particles/SpacebattleDemo.cpp b/examples/Particles/SpacebattleDemo.cpp index 58d9e1957..8fa72bfff 100644 --- a/examples/Particles/SpacebattleDemo.cpp +++ b/examples/Particles/SpacebattleDemo.cpp @@ -267,6 +267,8 @@ ParticleDemo("Space battle", sharedData) if (!m_spacestationModel.LoadFromFile("resources/SpaceStation/space_station.obj", parameters)) NazaraWarning("Failed to load space_station.obj"); + m_spacestationModel.GetMesh()->GenerateNormalsAndTangents(); + parameters.mesh.texCoordScale.Set(1.f, -1.f); parameters.mesh.matrix.MakeRotation(Nz::EulerAnglesf(0.f, -90.f, 0.f)); @@ -471,7 +473,7 @@ void SpacebattleExample::Enter(Ndk::StateMachine& fsm) auto rotationPtr = mapper.GetComponentPtr(Nz::ParticleComponent_Rotation); auto sizePtr = mapper.GetComponentPtr(Nz::ParticleComponent_Size); - renderQueue->AddBillboards(0, sparkleMat1, endId - startId + 1, positionPtr, sizePtr, rotationPtr); + renderQueue->AddBillboards(0, sparkleMat1, endId - startId + 1, Nz::Recti(-1, -1), positionPtr, sizePtr, rotationPtr); for (unsigned int i = startId; i <= endId; ++i) { Nz::AbstractRenderQueue::PointLight pointLight; @@ -607,6 +609,7 @@ void SpacebattleExample::Enter(Ndk::StateMachine& fsm) fireMat->EnableFaceCulling(true); fireMat->SetDiffuseMap("resources/fire_particle.png"); // Additive blending for fire + fireMat->EnableDepthSorting(false); //< No need for depth sort fireMat->SetDstBlend(Nz::BlendFunc_One); fireMat->SetSrcBlend(Nz::BlendFunc_SrcAlpha); @@ -622,7 +625,7 @@ void SpacebattleExample::Enter(Ndk::StateMachine& fsm) auto rotPtr = mapper.GetComponentPtr(Nz::ParticleComponent_Rotation); auto sizePtr = mapper.GetComponentPtr(Nz::ParticleComponent_Size); - renderQueue->AddBillboards(0, fireMat, endId - startId + 1, posPtr, sizePtr, rotPtr, colorPtr); + renderQueue->AddBillboards(0, fireMat, endId - startId + 1, Nz::Recti(-1, -1), posPtr, sizePtr, rotPtr, colorPtr); })); m_smokeGroup->SetRenderer(Nz::ParticleFunctionRenderer::New([smokeMat] (const Nz::ParticleGroup& /*group*/, const Nz::ParticleMapper& mapper, unsigned int startId, unsigned int endId, Nz::AbstractRenderQueue* renderQueue) @@ -632,7 +635,7 @@ void SpacebattleExample::Enter(Ndk::StateMachine& fsm) auto rotPtr = mapper.GetComponentPtr(Nz::ParticleComponent_Rotation); auto sizePtr = mapper.GetComponentPtr(Nz::ParticleComponent_Size); - renderQueue->AddBillboards(0, smokeMat, endId - startId + 1, posPtr, sizePtr, rotPtr, colorPtr); + renderQueue->AddBillboards(0, smokeMat, endId - startId + 1, Nz::Recti(-1, -1), posPtr, sizePtr, rotPtr, colorPtr); })); ////////////////////////////////////////////////////////////////////////// @@ -647,6 +650,7 @@ void SpacebattleExample::Enter(Ndk::StateMachine& fsm) ////////////////////////////////////////////////////////////////////////// Nz::TextSpriteRef introText = Nz::TextSprite::New(); + introText->SetMaterial(Nz::Material::New("Translucent3D")); introText->Update(Nz::SimpleTextDrawer::Draw("--Tourelle de défense du secteur A407M2--\nLes contrôles ont été adaptés à vos contrôleurs:\nLa souris contrôle l'orientation de la tourelle, cliquez pour tirer.\n", 72)); introText->SetScale(0.5f); diff --git a/examples/Particles/main.cpp b/examples/Particles/main.cpp index da61aad4b..084c33da8 100644 --- a/examples/Particles/main.cpp +++ b/examples/Particles/main.cpp @@ -59,7 +59,7 @@ int main() shared.particleCount->Update(Nz::SimpleTextDrawer::Draw("XXXXX particles", 36)); world2D.GetSystem().SetGlobalUp(Nz::Vector3f::Down()); - //world3D.GetSystem().ChangeRenderTechnique(); + world3D.GetSystem().ChangeRenderTechnique(); Ndk::EntityHandle viewEntity = world2D.CreateEntity(); diff --git a/include/Nazara/Graphics.hpp b/include/Nazara/Graphics.hpp index 3d7edd264..0d14c14a1 100644 --- a/include/Nazara/Graphics.hpp +++ b/include/Nazara/Graphics.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -45,14 +46,13 @@ #include #include #include +#include #include -#include #include #include #include #include #include -#include #include #include #include @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Graphics/AbstractRenderQueue.hpp b/include/Nazara/Graphics/AbstractRenderQueue.hpp index db4139c73..f4d268786 100644 --- a/include/Nazara/Graphics/AbstractRenderQueue.hpp +++ b/include/Nazara/Graphics/AbstractRenderQueue.hpp @@ -37,20 +37,20 @@ namespace Nz // Je ne suis vraiment pas fan du nombre de surcharges pour AddBillboards, // mais je n'ai pas d'autre solution tout aussi performante pour le moment... - virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) = 0; - virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) = 0; - virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) = 0; - virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) = 0; - virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) = 0; - virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) = 0; - virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) = 0; - virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) = 0; + virtual void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) = 0; + virtual void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) = 0; + virtual void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) = 0; + virtual void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) = 0; + virtual void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) = 0; + virtual void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) = 0; + virtual void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) = 0; + virtual void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) = 0; virtual void AddDrawable(int renderOrder, const Drawable* drawable) = 0; virtual void AddDirectionalLight(const DirectionalLight& light); - virtual void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) = 0; + virtual void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix, const Recti& scissorRect) = 0; virtual void AddPointLight(const PointLight& light); virtual void AddSpotLight(const SpotLight& light); - virtual void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) = 0; + virtual void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Recti& scissorRect, const Texture* overlay = nullptr) = 0; virtual void Clear(bool fully = false); diff --git a/include/Nazara/Graphics/BasicRenderQueue.hpp b/include/Nazara/Graphics/BasicRenderQueue.hpp new file mode 100644 index 000000000..1be56b5fd --- /dev/null +++ b/include/Nazara/Graphics/BasicRenderQueue.hpp @@ -0,0 +1,142 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_BASICRENDERQUEUE_HPP +#define NAZARA_BASICRENDERQUEUE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class AbstractViewer; + + class NAZARA_GRAPHICS_API BasicRenderQueue : public AbstractRenderQueue + { + friend class ForwardRenderTechnique; + + public: + struct BillboardData; + + BasicRenderQueue() = default; + ~BasicRenderQueue() = default; + + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; + void AddDrawable(int renderOrder, const Drawable* drawable) override; + void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix, const Recti& scissorRect) override; + void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Recti& scissorRect, const Texture* overlay = nullptr) override; + + void Clear(bool fully = false) override; + + inline const BillboardData* GetBillboardData(std::size_t billboardIndex) const; + + void Sort(const AbstractViewer* viewer); + + struct BillboardData + { + Color color; + Vector3f center; + Vector2f size; + Vector2f sinCos; + }; + + struct Billboard + { + int layerIndex; + MovablePtr material; + Nz::Recti scissorRect; + BillboardData data; + }; + + struct BillboardChain + { + int layerIndex; + MovablePtr material; + Nz::Recti scissorRect; + std::size_t billboardCount; + std::size_t billboardIndex; + }; + + RenderQueue billboards; + RenderQueue depthSortedBillboards; + + struct CustomDrawable + { + int layerIndex; + MovablePtr drawable; + }; + + RenderQueue customDrawables; + + struct Model + { + int layerIndex; + MeshData meshData; + MovablePtr material; + Nz::Matrix4f matrix; + Nz::Recti scissorRect; + Nz::Spheref obbSphere; + }; + + RenderQueue models; + RenderQueue depthSortedModels; + + struct SpriteChain + { + int layerIndex; + std::size_t spriteCount; + MovablePtr material; + MovablePtr overlay; + MovablePtr vertices; + Nz::Recti scissorRect; + }; + + RenderQueue basicSprites; + RenderQueue depthSortedSprites; + + private: + inline Color ComputeColor(float alpha); + inline Vector2f ComputeSinCos(float angle); + inline Vector2f ComputeSize(float size); + + inline void RegisterLayer(int layerIndex); + + std::unordered_map m_pipelineCache; + std::unordered_map m_materialCache; + std::unordered_map m_overlayCache; + std::unordered_map m_shaderCache; + std::unordered_map m_textureCache; + std::unordered_map m_vertexBufferCache; + std::unordered_map m_layerCache; + + std::vector m_billboards; + std::vector m_renderLayers; + }; +} + +#include + +#endif // NAZARA_BASICRENDERQUEUE_HPP diff --git a/include/Nazara/Graphics/BasicRenderQueue.inl b/include/Nazara/Graphics/BasicRenderQueue.inl new file mode 100644 index 000000000..14dde08dc --- /dev/null +++ b/include/Nazara/Graphics/BasicRenderQueue.inl @@ -0,0 +1,38 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline const BasicRenderQueue::BillboardData* BasicRenderQueue::GetBillboardData(std::size_t billboardIndex) const + { + assert(billboardIndex < m_billboards.size()); + return &m_billboards[billboardIndex]; + } + + inline Color BasicRenderQueue::ComputeColor(float alpha) + { + return Color(255, 255, 255, static_cast(255.f * alpha)); + } + + inline Vector2f BasicRenderQueue::ComputeSinCos(float angle) + { + float radians = ToRadians(angle); + return { std::sin(radians), std::cos(radians) }; + } + + inline Vector2f BasicRenderQueue::ComputeSize(float size) + { + return Vector2f(size, size); + } + + inline void BasicRenderQueue::RegisterLayer(int layerIndex) + { + auto it = std::lower_bound(m_renderLayers.begin(), m_renderLayers.end(), layerIndex); + if (it == m_renderLayers.end() || *it != layerIndex) + m_renderLayers.insert(it, layerIndex); + } +} diff --git a/include/Nazara/Graphics/Billboard.hpp b/include/Nazara/Graphics/Billboard.hpp index af77001bd..62f872f39 100644 --- a/include/Nazara/Graphics/Billboard.hpp +++ b/include/Nazara/Graphics/Billboard.hpp @@ -29,7 +29,7 @@ namespace Nz Billboard(Billboard&&) = delete; ~Billboard() = default; - void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; + void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const override; inline const Color& GetColor() const; inline float GetRotation() const; diff --git a/include/Nazara/Graphics/DeferredGeometryPass.hpp b/include/Nazara/Graphics/DeferredGeometryPass.hpp index aaa8f6471..cb38508b8 100644 --- a/include/Nazara/Graphics/DeferredGeometryPass.hpp +++ b/include/Nazara/Graphics/DeferredGeometryPass.hpp @@ -8,6 +8,7 @@ #define NAZARA_DEFERREDGEOMETRYPASS_HPP #include +#include #include #include #include @@ -17,6 +18,8 @@ namespace Nz { class NAZARA_GRAPHICS_API DeferredGeometryPass : public DeferredRenderPass { + friend class DeferredRenderTechnique; + public: DeferredGeometryPass(); virtual ~DeferredGeometryPass(); @@ -27,9 +30,17 @@ namespace Nz protected: struct ShaderUniforms; + void DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const; + void DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const; + void DrawModels(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& models) const; + void DrawSprites(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& sprites) const; + const ShaderUniforms* GetShaderUniforms(const Shader* shader) const; void OnShaderInvalidated(const Shader* shader) const; + static bool Initialize(); + static void Uninitialize(); + struct ShaderUniforms { NazaraSlot(Shader, OnShaderUniformInvalidated, shaderUniformInvalidatedSlot); @@ -41,8 +52,18 @@ namespace Nz }; mutable std::unordered_map m_shaderUniforms; + mutable std::vector> m_spriteChains; + Buffer m_vertexBuffer; RenderStates m_clearStates; ShaderRef m_clearShader; + Texture m_whiteTexture; + VertexBuffer m_billboardPointBuffer; + VertexBuffer m_spriteBuffer; + + static IndexBuffer s_quadIndexBuffer; + static VertexBuffer s_quadVertexBuffer; + static VertexDeclaration s_billboardInstanceDeclaration; + static VertexDeclaration s_billboardVertexDeclaration; }; } diff --git a/include/Nazara/Graphics/DeferredProxyRenderQueue.hpp b/include/Nazara/Graphics/DeferredProxyRenderQueue.hpp new file mode 100644 index 000000000..98155b0fd --- /dev/null +++ b/include/Nazara/Graphics/DeferredProxyRenderQueue.hpp @@ -0,0 +1,58 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_DEFERREDRENDERQUEUE_HPP +#define NAZARA_DEFERREDRENDERQUEUE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class BasicRenderQueue; + + class NAZARA_GRAPHICS_API DeferredProxyRenderQueue final : public AbstractRenderQueue + { + public: + struct BillboardData; + + inline DeferredProxyRenderQueue(BasicRenderQueue* deferredQueue, BasicRenderQueue* forwardQueue); + ~DeferredProxyRenderQueue() = default; + + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; + void AddDrawable(int renderOrder, const Drawable* drawable) override; + void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix, const Recti& scissorRect) override; + void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Recti& scissorRect, const Texture* overlay = nullptr) override; + + void Clear(bool fully = false) override; + + inline BasicRenderQueue* GetDeferredRenderQueue(); + inline BasicRenderQueue* GetForwardRenderQueue(); + + private: + BasicRenderQueue * m_deferredRenderQueue; + BasicRenderQueue* m_forwardRenderQueue; + }; +} + +#include + +#endif // NAZARA_DEFERREDRENDERQUEUE_HPP diff --git a/include/Nazara/Graphics/DeferredProxyRenderQueue.inl b/include/Nazara/Graphics/DeferredProxyRenderQueue.inl new file mode 100644 index 000000000..e56545e0d --- /dev/null +++ b/include/Nazara/Graphics/DeferredProxyRenderQueue.inl @@ -0,0 +1,30 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include + +namespace Nz +{ + /*! + * \brief Constructs a DeferredProxyRenderQueue using a deferred and a forward queues + * + * \param deferredQueue Deferred queue which will be used for non-blended objects + * \param forwardQueue Forward queue which will be used for blended objects + */ + inline DeferredProxyRenderQueue::DeferredProxyRenderQueue(BasicRenderQueue* deferredQueue, BasicRenderQueue* forwardQueue) : + m_deferredRenderQueue(deferredQueue), + m_forwardRenderQueue(forwardQueue) + { + } + + inline BasicRenderQueue* DeferredProxyRenderQueue::GetDeferredRenderQueue() + { + return m_deferredRenderQueue; + } + + inline BasicRenderQueue* DeferredProxyRenderQueue::GetForwardRenderQueue() + { + return m_forwardRenderQueue; + } +} diff --git a/include/Nazara/Graphics/DeferredRenderPass.hpp b/include/Nazara/Graphics/DeferredRenderPass.hpp index 4c54d5777..ca4cdce3d 100644 --- a/include/Nazara/Graphics/DeferredRenderPass.hpp +++ b/include/Nazara/Graphics/DeferredRenderPass.hpp @@ -14,10 +14,10 @@ namespace Nz { class DeferredRenderTechnique; - class DeferredRenderQueue; - struct SceneData; + class DeferredProxyRenderQueue; class RenderTexture; class Texture; + struct SceneData; class NAZARA_GRAPHICS_API DeferredRenderPass { @@ -42,7 +42,7 @@ namespace Nz protected: Vector2ui m_dimensions; DeferredRenderTechnique* m_deferredTechnique; - DeferredRenderQueue* m_renderQueue; + DeferredProxyRenderQueue* m_renderQueue; RenderTexture* m_GBufferRTT; RenderTexture* m_workRTT; Texture* m_depthStencilTexture; diff --git a/include/Nazara/Graphics/DeferredRenderQueue.hpp b/include/Nazara/Graphics/DeferredRenderQueue.hpp deleted file mode 100644 index db43a9980..000000000 --- a/include/Nazara/Graphics/DeferredRenderQueue.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2017 Jérôme Leclercq -// This file is part of the "Nazara Engine - Graphics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_DEFERREDRENDERQUEUE_HPP -#define NAZARA_DEFERREDRENDERQUEUE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - class NAZARA_GRAPHICS_API DeferredRenderQueue : public AbstractRenderQueue - { - public: - DeferredRenderQueue(ForwardRenderQueue* forwardQueue); - ~DeferredRenderQueue() = default; - - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; - void AddDrawable(int renderOrder, const Drawable* drawable) override; - void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; - void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override; - - void Clear(bool fully = false) override; - - struct MeshInstanceEntry - { - NazaraSlot(IndexBuffer, OnIndexBufferRelease, indexBufferReleaseSlot); - NazaraSlot(VertexBuffer, OnVertexBufferRelease, vertexBufferReleaseSlot); - - std::vector instances; - }; - - using MeshInstanceContainer = std::map; - - struct BatchedModelEntry - { - NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot); - - MeshInstanceContainer meshMap; - bool enabled = false; - }; - - using MeshMaterialBatches = std::map; - - struct BatchedMaterialEntry - { - std::size_t maxInstanceCount = 0; - MeshMaterialBatches materialMap; - }; - - using MeshPipelineBatches = std::map; - - struct Layer - { - MeshPipelineBatches opaqueModels; - unsigned int clearCount = 0; - }; - - std::map layers; - - private: - Layer& GetLayer(unsigned int i); ///TODO: Inline - - ForwardRenderQueue* m_forwardQueue; - - void OnIndexBufferInvalidation(const IndexBuffer* indexBuffer); - void OnMaterialInvalidation(const Material* material); - void OnVertexBufferInvalidation(const VertexBuffer* vertexBuffer); - }; -} - -#endif // NAZARA_DEFERREDRENDERQUEUE_HPP diff --git a/include/Nazara/Graphics/DeferredRenderQueue.inl b/include/Nazara/Graphics/DeferredRenderQueue.inl new file mode 100644 index 000000000..e69de29bb diff --git a/include/Nazara/Graphics/DeferredRenderTechnique.hpp b/include/Nazara/Graphics/DeferredRenderTechnique.hpp index 96f7df1fa..fc9588ae5 100644 --- a/include/Nazara/Graphics/DeferredRenderTechnique.hpp +++ b/include/Nazara/Graphics/DeferredRenderTechnique.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -64,8 +64,9 @@ namespace Nz }; std::map>, RenderPassComparator> m_passes; - ForwardRenderTechnique m_forwardTechnique; // Must be initialized before the RenderQueue - DeferredRenderQueue m_renderQueue; + BasicRenderQueue m_deferredRenderQueue; // Must be initialized before the ProxyRenderQueue + ForwardRenderTechnique m_forwardTechnique; // Must be initialized before the ProxyRenderQueue + DeferredProxyRenderQueue m_renderQueue; mutable TextureRef m_depthStencilTexture; mutable RenderTexture m_GBufferRTT; mutable RenderTexture m_workRTT; diff --git a/include/Nazara/Graphics/DepthRenderQueue.hpp b/include/Nazara/Graphics/DepthRenderQueue.hpp index 69f1ceae9..10a00783e 100644 --- a/include/Nazara/Graphics/DepthRenderQueue.hpp +++ b/include/Nazara/Graphics/DepthRenderQueue.hpp @@ -8,33 +8,33 @@ #define NAZARA_DEPTHRENDERQUEUE_HPP #include -#include +#include #include #include namespace Nz { - class NAZARA_GRAPHICS_API DepthRenderQueue : public ForwardRenderQueue + class NAZARA_GRAPHICS_API DepthRenderQueue : public BasicRenderQueue { public: DepthRenderQueue(); ~DepthRenderQueue() = default; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t count, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t count, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t count, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t count, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t count, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t count, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t count, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, std::size_t count, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; void AddDirectionalLight(const DirectionalLight& light) override; - void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; + void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix, const Recti& scissorRect) override; void AddPointLight(const PointLight& light) override; void AddSpotLight(const SpotLight& light) override; - void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override; + void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Recti& scissorRect, const Texture* overlay = nullptr) override; - private: + private: inline bool IsMaterialSuitable(const Material* material) const; MaterialRef m_baseMaterial; diff --git a/include/Nazara/Graphics/DepthRenderTechnique.hpp b/include/Nazara/Graphics/DepthRenderTechnique.hpp index 0e1e6daea..44ef1fa77 100644 --- a/include/Nazara/Graphics/DepthRenderTechnique.hpp +++ b/include/Nazara/Graphics/DepthRenderTechnique.hpp @@ -35,9 +35,12 @@ namespace Nz private: struct ShaderUniforms; - void DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; - void DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; - void DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; + void DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const; + void DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const; + void DrawCustomDrawables(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& customDrawables) const; + void DrawModels(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& models) const; + void DrawSprites(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& sprites) const; + const ShaderUniforms* GetShaderUniforms(const Shader* shader) const; void OnShaderInvalidated(const Shader* shader) const; @@ -59,11 +62,14 @@ namespace Nz }; mutable std::unordered_map m_shaderUniforms; + mutable std::vector> m_spriteChains; Buffer m_vertexBuffer; - mutable DepthRenderQueue m_renderQueue; + RenderStates m_clearStates; + ShaderRef m_clearShader; Texture m_whiteTexture; VertexBuffer m_billboardPointBuffer; VertexBuffer m_spriteBuffer; + mutable DepthRenderQueue m_renderQueue; static IndexBuffer s_quadIndexBuffer; static VertexBuffer s_quadVertexBuffer; diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp deleted file mode 100644 index 37f1db554..000000000 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (C) 2017 Jérôme Leclercq -// This file is part of the "Nazara Engine - Graphics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_FORWARDRENDERQUEUE_HPP -#define NAZARA_FORWARDRENDERQUEUE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - class AbstractViewer; - - class NAZARA_GRAPHICS_API ForwardRenderQueue : public AbstractRenderQueue - { - friend class ForwardRenderTechnique; - - public: - ForwardRenderQueue() = default; - ~ForwardRenderQueue() = default; - - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; - void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; - void AddDrawable(int renderOrder, const Drawable* drawable) override; - void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; - void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override; - - void Clear(bool fully = false) override; - - void Sort(const AbstractViewer* viewer); - - struct MaterialComparator - { - bool operator()(const Material* mat1, const Material* mat2) const; - }; - - struct MaterialPipelineComparator - { - bool operator()(const MaterialPipeline* pipeline1, const MaterialPipeline* pipeline2) const; - }; - - /// Billboards - struct BillboardData - { - Color color; - Vector3f center; - Vector2f size; - Vector2f sinCos; - }; - - struct BatchedBillboardEntry - { - NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot); - - std::vector billboards; - }; - - using BatchedBillboardContainer = std::map; - - struct BatchedBillboardPipelineEntry - { - BatchedBillboardContainer materialMap; - bool enabled = false; - }; - - using BillboardPipelineBatches = std::map; - - /// Sprites - struct SpriteChain_XYZ_Color_UV - { - const VertexStruct_XYZ_Color_UV* vertices; - std::size_t spriteCount; - }; - - struct BatchedSpriteEntry - { - NazaraSlot(Texture, OnTextureRelease, textureReleaseSlot); - - std::vector spriteChains; - }; - - using SpriteOverlayBatches = std::map; - - struct BatchedBasicSpriteEntry - { - NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot); - - SpriteOverlayBatches overlayMap; - bool enabled = false; - }; - - using SpriteMaterialBatches = std::map; - - struct BatchedSpritePipelineEntry - { - SpriteMaterialBatches materialMap; - bool enabled = false; - }; - - using SpritePipelineBatches = std::map; - - /// Meshes - struct MeshDataComparator - { - bool operator()(const MeshData& data1, const MeshData& data2) const; - }; - - struct MeshInstanceEntry - { - NazaraSlot(IndexBuffer, OnIndexBufferRelease, indexBufferReleaseSlot); - NazaraSlot(VertexBuffer, OnVertexBufferRelease, vertexBufferReleaseSlot); - - std::vector instances; - Spheref squaredBoundingSphere; - }; - - using MeshInstanceContainer = std::map; - - struct BatchedModelEntry - { - NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot); - - MeshInstanceContainer meshMap; - bool enabled = false; - }; - - using MeshMaterialBatches = std::map; - - struct BatchedMaterialEntry - { - std::size_t maxInstanceCount = 0; - MeshMaterialBatches materialMap; - }; - - using MeshPipelineBatches = std::map; - - struct UnbatchedModelData - { - Matrix4f transformMatrix; - MeshData meshData; - Spheref obbSphere; - const Material* material; - }; - - struct UnbatchedSpriteData - { - std::size_t spriteCount; - const Material* material; - const Texture* overlay; - const VertexStruct_XYZ_Color_UV* vertices; - }; - - struct Layer - { - BillboardPipelineBatches billboards; - SpritePipelineBatches opaqueSprites; - MeshPipelineBatches opaqueModels; - std::vector depthSortedMeshes; - std::vector depthSortedSprites; - std::vector depthSortedMeshData; - std::vector depthSortedSpriteData; - std::vector otherDrawables; - unsigned int clearCount = 0; - }; - - std::map layers; - - private: - BillboardData* GetBillboardData(int renderOrder, const Material* material, unsigned int count); - Layer& GetLayer(int i); ///TODO: Inline - - void SortBillboards(Layer& layer, const Planef& nearPlane); - void SortForOrthographic(const AbstractViewer* viewer); - void SortForPerspective(const AbstractViewer* viewer); - - void OnIndexBufferInvalidation(const IndexBuffer* indexBuffer); - void OnMaterialInvalidation(const Material* material); - void OnTextureInvalidation(const Texture* texture); - void OnVertexBufferInvalidation(const VertexBuffer* vertexBuffer); - }; -} - -#endif // NAZARA_FORWARDRENDERQUEUE_HPP diff --git a/include/Nazara/Graphics/ForwardRenderTechnique.hpp b/include/Nazara/Graphics/ForwardRenderTechnique.hpp index f5972d974..8fc75fd13 100644 --- a/include/Nazara/Graphics/ForwardRenderTechnique.hpp +++ b/include/Nazara/Graphics/ForwardRenderTechnique.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -40,11 +40,12 @@ namespace Nz struct ShaderUniforms; void ChooseLights(const Spheref& object, bool includeDirectionalLights = true) const; - void DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; - void DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; - void DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; - void DrawOrderedSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; - void DrawTransparentModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; + void DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const; + void DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const; + void DrawCustomDrawables(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& customDrawables) const; + void DrawModels(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& models) const; + void DrawSprites(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& sprites) const; + const ShaderUniforms* GetShaderUniforms(const Shader* shader) const; void OnShaderInvalidated(const Shader* shader) const; void SendLightUniforms(const Shader* shader, const LightUniforms& uniforms, unsigned int index, unsigned int lightIndex, unsigned int uniformOffset) const; @@ -84,8 +85,9 @@ namespace Nz mutable std::unordered_map m_shaderUniforms; mutable std::vector m_lights; + mutable std::vector> m_spriteChains; Buffer m_vertexBuffer; - mutable ForwardRenderQueue m_renderQueue; + mutable BasicRenderQueue m_renderQueue; Texture m_whiteTexture; VertexBuffer m_billboardPointBuffer; VertexBuffer m_spriteBuffer; diff --git a/include/Nazara/Graphics/ForwardRenderTechnique.inl b/include/Nazara/Graphics/ForwardRenderTechnique.inl index ae22668ec..564da3d22 100644 --- a/include/Nazara/Graphics/ForwardRenderTechnique.inl +++ b/include/Nazara/Graphics/ForwardRenderTechnique.inl @@ -2,7 +2,7 @@ // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp -#include +#include namespace Nz { diff --git a/include/Nazara/Graphics/InstancedRenderable.hpp b/include/Nazara/Graphics/InstancedRenderable.hpp index 56e7bc0a1..25f6064a8 100644 --- a/include/Nazara/Graphics/InstancedRenderable.hpp +++ b/include/Nazara/Graphics/InstancedRenderable.hpp @@ -37,7 +37,7 @@ namespace Nz InstancedRenderable(InstancedRenderable&& renderable) = delete; virtual ~InstancedRenderable(); - virtual void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const = 0; + virtual void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const = 0; virtual bool Cull(const Frustumf& frustum, const InstanceData& instanceData) const; diff --git a/include/Nazara/Graphics/Model.hpp b/include/Nazara/Graphics/Model.hpp index 32cfdfada..b37d68a2e 100644 --- a/include/Nazara/Graphics/Model.hpp +++ b/include/Nazara/Graphics/Model.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -44,8 +45,8 @@ namespace Nz Model(Model&& model) = default; virtual ~Model(); - void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; - inline void AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix, unsigned int renderOrder = 0); + void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const override; + inline void AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix, int renderOrder = 0, const Recti& scissorRect = Recti(-1, -1, -1, -1)) const; using InstancedRenderable::GetMaterial; const MaterialRef& GetMaterial(const String& subMeshName) const; diff --git a/include/Nazara/Graphics/Model.inl b/include/Nazara/Graphics/Model.inl index fa1138f78..486665f3a 100644 --- a/include/Nazara/Graphics/Model.inl +++ b/include/Nazara/Graphics/Model.inl @@ -22,14 +22,15 @@ namespace Nz * * \param renderQueue Queue to be added * \param transformMatrix Transform matrix to be used for rendering the model - * \param renderOrder Specify the renderqueue layer to be used + * \param renderOrder Specify the render queue layer to be used + * \param scissorRect The Scissor rect to uses for rendering */ - inline void Model::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix, unsigned int renderOrder) - { + void Model::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix, int renderOrder, const Recti& scissorRect) const +{ InstanceData instanceData(Nz::Matrix4f::Identity()); instanceData.renderOrder = renderOrder; instanceData.transformMatrix = transformMatrix; - return AddToRenderQueue(renderQueue, instanceData); + return AddToRenderQueue(renderQueue, instanceData, scissorRect); } /*! diff --git a/include/Nazara/Graphics/RenderQueue.hpp b/include/Nazara/Graphics/RenderQueue.hpp new file mode 100644 index 000000000..8ddb725ca --- /dev/null +++ b/include/Nazara/Graphics/RenderQueue.hpp @@ -0,0 +1,96 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RENDERQUEUE_HPP +#define NAZARA_RENDERQUEUE_HPP + +#include +#include +#include + +namespace Nz +{ + class RenderQueueInternal + { + public: + using Index = Nz::UInt64; + + RenderQueueInternal() = default; + ~RenderQueueInternal() = default; + + protected: + using RenderDataPair = std::pair; + + void Sort(); + + std::vector m_orderedRenderQueue; + }; + + template + class RenderQueue : public RenderQueueInternal + { + public: + class const_iterator; + friend const_iterator; + using size_type = std::size_t; + + RenderQueue() = default; + RenderQueue(const RenderQueue&) = default; + RenderQueue(RenderQueue&&) = default; + ~RenderQueue() = default; + + void Clear(); + + void Insert(RenderData&& data); + + template void Sort(IndexFunc&& func); + + // STL API + inline const_iterator begin() const; + inline bool empty() const; + inline const_iterator end() const; + inline size_type size() const; + + RenderQueue& operator=(const RenderQueue&) = default; + RenderQueue& operator=(RenderQueue&&) = default; + + private: + const RenderData& GetData(std::size_t i) const; + + std::vector m_data; + }; + + template + class RenderQueue::const_iterator : public std::iterator + { + friend RenderQueue; + + public: + const_iterator(const const_iterator& it); + + const RenderData& operator*() const; + + const_iterator& operator=(const const_iterator& it); + const_iterator& operator++(); + const_iterator operator++(int); + + bool operator==(const const_iterator& rhs) const; + bool operator!=(const const_iterator& rhs) const; + + void swap(const_iterator& rhs); + + private: + const_iterator(const RenderQueue* queue, std::size_t nextId); + + std::size_t m_nextDataId; + const RenderQueue* m_queue; + }; + +} + +#include + +#endif // NAZARA_RENDERQUEUE_HPP diff --git a/include/Nazara/Graphics/RenderQueue.inl b/include/Nazara/Graphics/RenderQueue.inl new file mode 100644 index 000000000..47ec03c98 --- /dev/null +++ b/include/Nazara/Graphics/RenderQueue.inl @@ -0,0 +1,136 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + template + void RenderQueue::Clear() + { + m_orderedRenderQueue.clear(); + m_data.clear(); + } + + template + void RenderQueue::Insert(RenderData&& data) + { + m_data.emplace_back(std::move(data)); + } + + template + template + void RenderQueue::Sort(IndexFunc&& func) + { + m_orderedRenderQueue.clear(); + m_orderedRenderQueue.reserve(m_data.size()); + + std::size_t dataIndex = 0; + for (const RenderData& renderData : m_data) + m_orderedRenderQueue.emplace_back(func(renderData), dataIndex++); + + RenderQueueInternal::Sort(); + } + + template + typename RenderQueue::const_iterator RenderQueue::begin() const + { + return const_iterator(this, 0); + } + + template + bool RenderQueue::empty() const + { + return m_orderedRenderQueue.empty(); + } + + template + typename RenderQueue::const_iterator RenderQueue::end() const + { + return const_iterator(this, m_orderedRenderQueue.size()); + } + + template + typename RenderQueue::size_type RenderQueue::size() const + { + return m_orderedRenderQueue.size(); + } + + template + const RenderData& RenderQueue::GetData(std::size_t i) const + { + NazaraAssert(i < m_orderedRenderQueue.size(), "Cannot dereference post-end iterator"); + + return m_data[m_orderedRenderQueue[i].second]; + } + + + template + RenderQueue::const_iterator::const_iterator(const RenderQueue* queue, std::size_t nextId) : + m_nextDataId(nextId), + m_queue(queue) + { + } + + template + RenderQueue::const_iterator::const_iterator(const const_iterator& it) : + m_nextDataId(it.m_nextDataId), + m_queue(it.m_queue) + { + } + + template + const RenderData& RenderQueue::const_iterator::operator*() const + { + return m_queue->GetData(m_nextDataId); + } + + template + typename RenderQueue::const_iterator& RenderQueue::const_iterator::operator=(const const_iterator& it) + { + m_nextDataId = it.m_nextDataId; + m_queue = it.m_queue; + + return *this; + } + + template + typename RenderQueue::const_iterator& RenderQueue::const_iterator::operator++() + { + ++m_nextDataId; + + return *this; + } + + template + typename RenderQueue::const_iterator RenderQueue::const_iterator::operator++(int) + { + return iterator(m_queue, m_nextDataId++); + } + + template + bool RenderQueue::const_iterator::operator==(const typename RenderQueue::const_iterator& rhs) const + { + NazaraAssert(m_queue == rhs.m_queue, "Cannot compare iterator coming from different queues"); + + return m_nextDataId == rhs.m_nextDataId; + } + + template + bool RenderQueue::const_iterator::operator!=(const typename RenderQueue::const_iterator& rhs) const + { + return !operator==(rhs); + } + + template + void RenderQueue::const_iterator::swap(typename RenderQueue::const_iterator& rhs) + { + NazaraAssert(m_queue == rhs.m_queue, "Cannot swap iterator coming from different queues"); + + using std::swap; + + swap(m_nextDataId, rhs.m_nextDataId); + } +} diff --git a/include/Nazara/Graphics/SkeletalModel.hpp b/include/Nazara/Graphics/SkeletalModel.hpp index 72489d905..86943b676 100644 --- a/include/Nazara/Graphics/SkeletalModel.hpp +++ b/include/Nazara/Graphics/SkeletalModel.hpp @@ -38,7 +38,7 @@ namespace Nz SkeletalModel(SkeletalModel&& model) = default; ~SkeletalModel() = default; - void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; + void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const override; void AdvanceAnimation(float elapsedTime); SkeletalModel* Clone() const; diff --git a/include/Nazara/Graphics/Sprite.hpp b/include/Nazara/Graphics/Sprite.hpp index 34e75f353..bf9edc0d1 100644 --- a/include/Nazara/Graphics/Sprite.hpp +++ b/include/Nazara/Graphics/Sprite.hpp @@ -33,7 +33,7 @@ namespace Nz Sprite(Sprite&&) = delete; ~Sprite() = default; - void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; + void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const override; inline const Color& GetColor() const; inline const Color& GetCornerColor(RectCorner corner) const; diff --git a/include/Nazara/Graphics/Sprite.inl b/include/Nazara/Graphics/Sprite.inl index 7ecf80cf8..a296f5d61 100644 --- a/include/Nazara/Graphics/Sprite.inl +++ b/include/Nazara/Graphics/Sprite.inl @@ -172,6 +172,7 @@ namespace Nz { MaterialRef material = Material::New(); material->EnableFaceCulling(false); + material->EnableScissorTest(true); SetMaterial(std::move(material)); } diff --git a/include/Nazara/Graphics/TextSprite.hpp b/include/Nazara/Graphics/TextSprite.hpp index 5fddf7e99..355d17fe4 100644 --- a/include/Nazara/Graphics/TextSprite.hpp +++ b/include/Nazara/Graphics/TextSprite.hpp @@ -30,7 +30,7 @@ namespace Nz inline TextSprite(const TextSprite& sprite); ~TextSprite() = default; - void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; + void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const override; inline void Clear(); diff --git a/include/Nazara/Graphics/TextSprite.inl b/include/Nazara/Graphics/TextSprite.inl index 2caec3254..c35cd562e 100644 --- a/include/Nazara/Graphics/TextSprite.inl +++ b/include/Nazara/Graphics/TextSprite.inl @@ -110,14 +110,7 @@ namespace Nz inline void TextSprite::SetDefaultMaterial() { - MaterialRef material = Material::New(); - material->EnableBlending(true); - material->EnableDepthWrite(false); - material->EnableFaceCulling(false); - material->SetDstBlend(BlendFunc_InvSrcAlpha); - material->SetSrcBlend(BlendFunc_SrcAlpha); - - SetMaterial(material); + SetMaterial(Material::New("Translucent2D")); } /*! diff --git a/include/Nazara/Graphics/TileMap.hpp b/include/Nazara/Graphics/TileMap.hpp index 4a8499599..3692eb3c2 100644 --- a/include/Nazara/Graphics/TileMap.hpp +++ b/include/Nazara/Graphics/TileMap.hpp @@ -33,7 +33,7 @@ namespace Nz TileMap(TileMap&&) = delete; ~TileMap() = default; - void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; + void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const override; inline void DisableTile(const Vector2ui& tilePos); inline void DisableTiles(); diff --git a/src/Nazara/Graphics/BasicRenderQueue.cpp b/src/Nazara/Graphics/BasicRenderQueue.cpp new file mode 100644 index 000000000..fb358d8a8 --- /dev/null +++ b/src/Nazara/Graphics/BasicRenderQueue.cpp @@ -0,0 +1,947 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +///TODO: Replace sinus/cosinus by a lookup table (which will lead to a speed up about 10x) + +namespace Nz +{ + /*! + * \ingroup graphics + * \class Nz::BasicRenderQueue + * \brief Graphics class that represents a simple rendering queue + */ + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + + Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 + + if (!sinCosPtr) + sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile + + if (!colorPtr) + colorPtr.Reset(&Color::White, 0); // Same + + if (material->IsDepthSortingEnabled()) + { + for (std::size_t i = 0; i < billboardCount; ++i) + { + depthSortedBillboards.Insert({ + renderOrder, + material, + scissorRect, + { + *colorPtr++, + *positionPtr++, + *sizePtr++, + *sinCosPtr++ + } + }); + } + } + else + { + std::size_t billboardIndex = m_billboards.size(); + m_billboards.resize(billboardIndex + billboardCount); + BillboardData* data = &m_billboards[billboardIndex]; + + for (std::size_t i = 0; i < billboardCount; ++i) + { + data->center = *positionPtr++; + data->color = *colorPtr++; + data->sinCos = *sinCosPtr++; + data->size = *sizePtr++; + data++; + } + + billboards.Insert({ + renderOrder, + material, + scissorRect, + billboardCount, + billboardIndex + }); + } + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + + Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 + + if (!sinCosPtr) + sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile + + float defaultAlpha = 1.f; + + if (!alphaPtr) + alphaPtr.Reset(&defaultAlpha, 0); // Same + + if (material->IsDepthSortingEnabled()) + { + for (std::size_t i = 0; i < billboardCount; ++i) + { + depthSortedBillboards.Insert({ + renderOrder, + material, + scissorRect, + { + ComputeColor(*alphaPtr++), + *positionPtr++, + *sizePtr++, + *sinCosPtr++ + } + }); + } + } + else + { + std::size_t billboardIndex = m_billboards.size(); + m_billboards.resize(billboardIndex + billboardCount); + BillboardData* data = &m_billboards[billboardIndex]; + + for (std::size_t i = 0; i < billboardCount; ++i) + { + data->center = *positionPtr++; + data->color = ComputeColor(*alphaPtr++); + data->sinCos = *sinCosPtr++; + data->size = *sizePtr++; + data++; + } + + billboards.Insert({ + renderOrder, + material, + scissorRect, + billboardCount, + billboardIndex + }); + } + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + + float defaultRotation = 0.f; + + if (!anglePtr) + anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile + + if (!colorPtr) + colorPtr.Reset(&Color::White, 0); // Same + + if (material->IsDepthSortingEnabled()) + { + for (std::size_t i = 0; i < billboardCount; ++i) + { + depthSortedBillboards.Insert({ + renderOrder, + material, + scissorRect, + { + *colorPtr++, + *positionPtr++, + *sizePtr++, + ComputeSinCos(*anglePtr++) + } + }); + } + } + else + { + std::size_t billboardIndex = m_billboards.size(); + m_billboards.resize(billboardIndex + billboardCount); + BillboardData* data = &m_billboards[billboardIndex]; + + for (std::size_t i = 0; i < billboardCount; ++i) + { + data->center = *positionPtr++; + data->color = *colorPtr++; + data->sinCos = ComputeSinCos(*anglePtr++); + data->size = *sizePtr++; + data++; + } + + billboards.Insert({ + renderOrder, + material, + scissorRect, + billboardCount, + billboardIndex + }); + } + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + + float defaultRotation = 0.f; + + if (!anglePtr) + anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile + + float defaultAlpha = 1.f; + + if (!alphaPtr) + alphaPtr.Reset(&defaultAlpha, 0); // Same + + if (material->IsDepthSortingEnabled()) + { + for (std::size_t i = 0; i < billboardCount; ++i) + { + depthSortedBillboards.Insert({ + renderOrder, + material, + scissorRect, + { + ComputeColor(*alphaPtr++), + *positionPtr++, + *sizePtr++, + ComputeSinCos(*anglePtr++) + } + }); + } + } + else + { + std::size_t billboardIndex = m_billboards.size(); + m_billboards.resize(billboardIndex + billboardCount); + BillboardData* data = &m_billboards[billboardIndex]; + + for (std::size_t i = 0; i < billboardCount; ++i) + { + data->center = *positionPtr++; + data->color = ComputeColor(*alphaPtr++); + data->sinCos = ComputeSinCos(*anglePtr++); + data->size = *sizePtr++; + data++; + } + + billboards.Insert({ + renderOrder, + material, + scissorRect, + billboardCount, + billboardIndex + }); + } + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + + Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 + + if (!sinCosPtr) + sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile + + if (!colorPtr) + colorPtr.Reset(&Color::White, 0); // Same + + if (material->IsDepthSortingEnabled()) + { + for (std::size_t i = 0; i < billboardCount; ++i) + { + depthSortedBillboards.Insert({ + renderOrder, + material, + scissorRect, + { + *colorPtr++, + *positionPtr++, + ComputeSize(*sizePtr++), + *sinCosPtr++ + } + }); + } + } + else + { + std::size_t billboardIndex = m_billboards.size(); + m_billboards.resize(billboardIndex + billboardCount); + BillboardData* data = &m_billboards[billboardIndex]; + + for (std::size_t i = 0; i < billboardCount; ++i) + { + data->center = *positionPtr++; + data->color = *colorPtr++; + data->sinCos = *sinCosPtr++; + data->size = ComputeSize(*sizePtr++); + data++; + } + + billboards.Insert({ + renderOrder, + material, + scissorRect, + billboardCount, + billboardIndex + }); + } + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + + Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 + + if (!sinCosPtr) + sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile + + float defaultAlpha = 1.f; + + if (!alphaPtr) + alphaPtr.Reset(&defaultAlpha, 0); // Same + + if (material->IsDepthSortingEnabled()) + { + for (std::size_t i = 0; i < billboardCount; ++i) + { + depthSortedBillboards.Insert({ + renderOrder, + material, + scissorRect, + { + ComputeColor(*alphaPtr++), + *positionPtr++, + ComputeSize(*sizePtr++), + *sinCosPtr++ + } + }); + } + } + else + { + std::size_t billboardIndex = m_billboards.size(); + m_billboards.resize(billboardIndex + billboardCount); + BillboardData* data = &m_billboards[billboardIndex]; + + for (std::size_t i = 0; i < billboardCount; ++i) + { + data->center = *positionPtr++; + data->color = ComputeColor(*alphaPtr++); + data->sinCos = *sinCosPtr++; + data->size = ComputeSize(*sizePtr++); + data++; + } + + billboards.Insert({ + renderOrder, + material, + scissorRect, + billboardCount, + billboardIndex + }); + } + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + + float defaultRotation = 0.f; + + if (!anglePtr) + anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile + + if (!colorPtr) + colorPtr.Reset(&Color::White, 0); // Same + + if (material->IsDepthSortingEnabled()) + { + for (std::size_t i = 0; i < billboardCount; ++i) + { + depthSortedBillboards.Insert({ + renderOrder, + material, + scissorRect, + { + *colorPtr++, + *positionPtr++, + ComputeSize(*sizePtr++), + ComputeSinCos(*anglePtr++) + } + }); + } + } + else + { + std::size_t billboardIndex = m_billboards.size(); + m_billboards.resize(billboardIndex + billboardCount); + BillboardData* data = &m_billboards[billboardIndex]; + + for (std::size_t i = 0; i < billboardCount; ++i) + { + data->center = *positionPtr++; + data->color = *colorPtr++; + data->sinCos = ComputeSinCos(*anglePtr++); + data->size = ComputeSize(*sizePtr++); + data++; + } + + billboards.Insert({ + renderOrder, + material, + scissorRect, + billboardCount, + billboardIndex + }); + } + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void BasicRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + + float defaultRotation = 0.f; + + if (!anglePtr) + anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile + + float defaultAlpha = 1.f; + + if (!alphaPtr) + alphaPtr.Reset(&defaultAlpha, 0); // Same + + if (material->IsDepthSortingEnabled()) + { + for (std::size_t i = 0; i < billboardCount; ++i) + { + depthSortedBillboards.Insert({ + renderOrder, + material, + scissorRect, + { + ComputeColor(*alphaPtr++), + *positionPtr++, + ComputeSize(*sizePtr++), + ComputeSinCos(*anglePtr++) + } + }); + } + } + else + { + std::size_t billboardIndex = m_billboards.size(); + m_billboards.resize(billboardIndex + billboardCount); + BillboardData* data = &m_billboards[billboardIndex]; + + for (std::size_t i = 0; i < billboardCount; ++i) + { + data->center = *positionPtr++; + data->color = ComputeColor(*alphaPtr++); + data->sinCos = ComputeSinCos(*anglePtr++); + data->size = ComputeSize(*sizePtr++); + data++; + } + + billboards.Insert({ + renderOrder, + material, + scissorRect, + billboardCount, + billboardIndex + }); + } + } + + /*! + * \brief Adds drawable to the queue + * + * \param renderOrder Order of rendering + * \param drawable Drawable user defined + * + * \remark Produces a NazaraError if drawable is invalid + */ + void BasicRenderQueue::AddDrawable(int renderOrder, const Drawable* drawable) + { + NazaraAssert(drawable, "Invalid material"); + + RegisterLayer(renderOrder); + + customDrawables.Insert({ + renderOrder, + drawable + }); + } + + /*! + * \brief Adds mesh to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the mesh + * \param meshData Data of the mesh + * \param meshAABB Box of the mesh + * \param transformMatrix Matrix of the mesh + * + * \remark Produces a NazaraAssert if material is invalid + */ + void BasicRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix, const Recti& scissorRect) + { + NazaraAssert(material, "Invalid material"); + + RegisterLayer(renderOrder); + + Spheref obbSphere(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius()); + + if (material->IsDepthSortingEnabled()) + { + depthSortedModels.Insert({ + renderOrder, + meshData, + material, + transformMatrix, + scissorRect, + obbSphere + }); + } + else + { + models.Insert({ + renderOrder, + meshData, + material, + transformMatrix, + scissorRect, + obbSphere + }); + } + } + + /*! + * \brief Adds sprites to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the sprites + * \param vertices Buffer of data for the sprites + * \param spriteCount Number of sprites + * \param overlay Texture of the sprites + * + * \remark Produces a NazaraAssert if material is invalid + */ + void BasicRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Recti& scissorRect, const Texture* overlay /*= nullptr*/) + { + NazaraAssert(material, "Invalid material"); + + RegisterLayer(renderOrder); + + if (material->IsDepthSortingEnabled()) + { + depthSortedSprites.Insert({ + renderOrder, + spriteCount, + material, + overlay, + vertices, + scissorRect + }); + } + else + { + basicSprites.Insert({ + renderOrder, + spriteCount, + material, + overlay, + vertices, + scissorRect + }); + } + } + + /*! + * \brief Clears the queue + * + * \param fully Should everything be cleared or we can keep layers + */ + + void BasicRenderQueue::Clear(bool fully) + { + AbstractRenderQueue::Clear(fully); + + basicSprites.Clear(); + billboards.Clear(); + depthSortedBillboards.Clear(); + depthSortedModels.Clear(); + depthSortedSprites.Clear(); + models.Clear(); + + m_pipelineCache.clear(); + m_materialCache.clear(); + m_overlayCache.clear(); + m_shaderCache.clear(); + m_textureCache.clear(); + m_vertexBufferCache.clear(); + + m_billboards.clear(); + m_renderLayers.clear(); + } + + /*! + * \brief Sorts the object according to the viewer position, furthest to nearest + * + * \param viewer Viewer of the scene + */ + + void BasicRenderQueue::Sort(const AbstractViewer* viewer) + { + m_layerCache.clear(); + for (int layer : m_renderLayers) + m_layerCache.emplace(layer, m_layerCache.size()); + + auto GetOrInsert = [](auto& container, auto&& value) + { + auto it = container.find(value); + if (it == container.end()) + it = container.emplace(value, container.size()).first; + + return it->second; + }; + + basicSprites.Sort([&](const SpriteChain& vertices) + { + // RQ index: + // - Layer (4bits) + // - Pipeline (8bits) + // - Material (8bits) + // - Shader? (8bits) + // - Textures (8bits) + // - Overlay (8bits) + // - Scissor (4bits) + // - Depth? (16bits) + + UInt64 layerIndex = m_layerCache[vertices.layerIndex]; + UInt64 pipelineIndex = GetOrInsert(m_pipelineCache, vertices.material->GetPipeline()); + UInt64 materialIndex = GetOrInsert(m_materialCache, vertices.material); + UInt64 shaderIndex = GetOrInsert(m_shaderCache, vertices.material->GetShader()); + UInt64 textureIndex = GetOrInsert(m_textureCache, vertices.material->GetDiffuseMap()); + UInt64 overlayIndex = GetOrInsert(m_overlayCache, vertices.overlay); + UInt64 scissorIndex = 0; //< TODO + UInt64 depthIndex = 0; //< TODO + + UInt64 index = (layerIndex & 0x0F) << 60 | + (pipelineIndex & 0xFF) << 52 | + (materialIndex & 0xFF) << 44 | + (shaderIndex & 0xFF) << 36 | + (textureIndex & 0xFF) << 28 | + (overlayIndex & 0xFF) << 20 | + (scissorIndex & 0x0F) << 16 | + (depthIndex & 0xFFFF) << 0; + + return index; + }); + + billboards.Sort([&](const BillboardChain& billboard) + { + // RQ index: + // - Layer (4bits) + // - Pipeline (8bits) + // - Material (8bits) + // - Shader? (8bits) + // - Textures (8bits) + // - ??? (8bits) + // - Scissor (4bits) + // - Depth? (16bits) + + UInt64 layerIndex = m_layerCache[billboard.layerIndex]; + UInt64 pipelineIndex = GetOrInsert(m_pipelineCache, billboard.material->GetPipeline()); + UInt64 materialIndex = GetOrInsert(m_materialCache, billboard.material); + UInt64 shaderIndex = GetOrInsert(m_shaderCache, billboard.material->GetShader()); + UInt64 textureIndex = GetOrInsert(m_textureCache, billboard.material->GetDiffuseMap()); + UInt64 unknownIndex = 0; //< ??? + UInt64 scissorIndex = 0; //< TODO + UInt64 depthIndex = 0; //< TODO? + + UInt64 index = (layerIndex & 0x0F) << 60 | + (pipelineIndex & 0xFF) << 52 | + (materialIndex & 0xFF) << 44 | + (shaderIndex & 0xFF) << 36 | + (textureIndex & 0xFF) << 28 | + (unknownIndex & 0xFF) << 20 | + (scissorIndex & 0x0F) << 16 | + (depthIndex & 0xFFFF) << 0; + + return index; + }); + + customDrawables.Sort([&](const CustomDrawable& drawable) + { + // RQ index: + // - Layer (4bits) + + UInt64 layerIndex = m_layerCache[drawable.layerIndex]; + + UInt64 index = (layerIndex & 0x0F) << 60; + + return index; + + }); + + models.Sort([&](const Model& renderData) + { + // RQ index: + // - Layer (4bits) + // - Pipeline (8bits) + // - Material (8bits) + // - Shader? (8bits) + // - Textures (8bits) + // - Buffers (8bits) + // - Scissor (4bits) + // - Depth? (16bits) + + UInt64 layerIndex = m_layerCache[renderData.layerIndex]; + UInt64 pipelineIndex = GetOrInsert(m_pipelineCache, renderData.material->GetPipeline()); + UInt64 materialIndex = GetOrInsert(m_materialCache, renderData.material); + UInt64 shaderIndex = GetOrInsert(m_shaderCache, renderData.material->GetShader()); + UInt64 textureIndex = GetOrInsert(m_textureCache, renderData.material->GetDiffuseMap()); + UInt64 bufferIndex = GetOrInsert(m_vertexBufferCache, renderData.meshData.vertexBuffer); + UInt64 scissorIndex = 0; //< TODO + UInt64 depthIndex = 0; //< TODO + + UInt64 index = (layerIndex & 0x0F) << 60 | + (pipelineIndex & 0xFF) << 52 | + (materialIndex & 0xFF) << 44 | + (shaderIndex & 0xFF) << 36 | + (textureIndex & 0xFF) << 28 | + (bufferIndex & 0xFF) << 20 | + (scissorIndex & 0x0F) << 16 | + (depthIndex & 0xFFFF) << 0; + + return index; + }); + + static_assert(std::numeric_limits::is_iec559, "The following sorting functions relies on IEEE 754 floatings-points"); + +#if defined(arm) && \ + ((defined(__MAVERICK__) && defined(NAZARA_BIG_ENDIAN)) || \ + (!defined(__SOFTFP__) && !defined(__VFP_FP__) && !defined(__MAVERICK__))) + #error The following code relies on native-endian IEEE-754 representation, which your platform does not guarantee +#endif + + Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near); + + depthSortedBillboards.Sort([&](const Billboard& billboard) + { + // RQ index: + // - Layer (4bits) + // - Depth (32bits) + // - ?? (28bits) + + // Reinterpret depth as UInt32 (this will work as long as they're all either positive or negative, + // a negative distance may happen with billboard behind the camera which we don't care about since they'll be rendered) + float depth = nearPlane.Distance(billboard.data.center); + + UInt64 layerIndex = m_layerCache[billboard.layerIndex]; + UInt64 depthIndex = ~reinterpret_cast(depth); + + UInt64 index = (layerIndex & 0x0F) << 60 | + (depthIndex & 0xFFFFFFFF) << 28; + + return index; + }); + + if (viewer->GetProjectionType() == ProjectionType_Orthogonal) + { + depthSortedModels.Sort([&](const Model& model) + { + // RQ index: + // - Layer (4bits) + // - Depth (32bits) + // - ?? (28bits) + + float depth = nearPlane.Distance(model.obbSphere.GetPosition()); + + UInt64 layerIndex = m_layerCache[model.layerIndex]; + UInt64 depthIndex = ~reinterpret_cast(depth); + + UInt64 index = (layerIndex & 0x0F) << 60 | + (depthIndex & 0xFFFFFFFF) << 28; + + return index; + }); + + depthSortedSprites.Sort([&](const SpriteChain& spriteChain) + { + // RQ index: + // - Layer (4bits) + // - Depth (32bits) + // - ?? (28bits) + + float depth = nearPlane.Distance(spriteChain.vertices[0].position); + + UInt64 layerIndex = m_layerCache[spriteChain.layerIndex]; + UInt64 depthIndex = ~reinterpret_cast(depth); + + UInt64 index = (layerIndex & 0x0F) << 60 | + (depthIndex & 0xFFFFFFFF) << 28; + + return index; + }); + } + else + { + Vector3f viewerPos = viewer->GetEyePosition(); + + depthSortedModels.Sort([&](const Model& model) + { + // RQ index: + // - Layer (4bits) + // - Depth (32bits) + // - ?? (28bits) + + float depth = viewerPos.SquaredDistance(model.obbSphere.GetPosition()); + + UInt64 layerIndex = m_layerCache[model.layerIndex]; + UInt64 depthIndex = ~reinterpret_cast(depth); + + UInt64 index = (layerIndex & 0x0F) << 60 | + (depthIndex & 0xFFFFFFFF) << 28; + + return index; + }); + + depthSortedSprites.Sort([&](const SpriteChain& sprites) + { + // RQ index: + // - Layer (4bits) + // - Depth (32bits) + // - ?? (28bits) + + float depth = viewerPos.SquaredDistance(sprites.vertices[0].position); + + UInt64 layerIndex = m_layerCache[sprites.layerIndex]; + UInt64 depthIndex = ~reinterpret_cast(depth); + + UInt64 index = (layerIndex & 0x0F) << 60 | + (depthIndex & 0xFFFFFFFF) << 28; + + return index; + }); + } + } +} diff --git a/src/Nazara/Graphics/Billboard.cpp b/src/Nazara/Graphics/Billboard.cpp index 3896cce49..9cc79acce 100644 --- a/src/Nazara/Graphics/Billboard.cpp +++ b/src/Nazara/Graphics/Billboard.cpp @@ -21,10 +21,10 @@ namespace Nz * \param instanceData Data used for instance */ - void Billboard::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const + void Billboard::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const { Nz::Vector3f position = instanceData.transformMatrix.GetTranslation(); - renderQueue->AddBillboards(instanceData.renderOrder, GetMaterial(), 1, &position, &m_size, &m_sinCos, &m_color); + renderQueue->AddBillboards(instanceData.renderOrder, GetMaterial(), 1, scissorRect, &position, &m_size, &m_sinCos, &m_color); } /* diff --git a/src/Nazara/Graphics/DeferredGeometryPass.cpp b/src/Nazara/Graphics/DeferredGeometryPass.cpp index b679c873a..01072bf20 100644 --- a/src/Nazara/Graphics/DeferredGeometryPass.cpp +++ b/src/Nazara/Graphics/DeferredGeometryPass.cpp @@ -5,16 +5,34 @@ #include #include #include +#include #include #include +#include #include #include #include #include +#include #include namespace Nz { + namespace + { + struct BillboardPoint + { + Color color; + Vector3f position; + Vector2f size; + Vector2f sinCos; // must follow `size` (both will be sent as a Vector4f) + Vector2f uv; + }; + + UInt32 s_maxQuads = std::numeric_limits::max() / 6; + UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + } + /*! * \ingroup graphics * \class Nz::DeferredGeometryPass @@ -25,8 +43,20 @@ namespace Nz * \brief Constructs a DeferredGeometryPass object by default */ - DeferredGeometryPass::DeferredGeometryPass() + DeferredGeometryPass::DeferredGeometryPass() : + m_vertexBuffer(BufferType_Vertex) { + ErrorFlags flags(ErrorFlag_ThrowException, true); + + std::array whitePixel = { { 255, 255, 255, 255 } }; + m_whiteTexture.Create(ImageType_2D, PixelFormatType_RGBA8, 1, 1); + m_whiteTexture.Update(whitePixel.data()); + + m_vertexBuffer.Create(s_vertexBufferSize, DataStorage_Hardware, BufferUsage_Dynamic); + + m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer); + m_spriteBuffer.Reset(VertexDeclaration::Get(VertexLayout_XYZ_Color_UV), &m_vertexBuffer); + m_clearShader = ShaderLibrary::Get("DeferredGBufferClear"); m_clearStates.depthBuffer = true; m_clearStates.faceCulling = true; @@ -67,131 +97,27 @@ namespace Nz Renderer::SetMatrix(MatrixType_Projection, sceneData.viewer->GetProjectionMatrix()); Renderer::SetMatrix(MatrixType_View, sceneData.viewer->GetViewMatrix()); - const Shader* lastShader = nullptr; - const ShaderUniforms* shaderUniforms = nullptr; + BasicRenderQueue& renderQueue = *m_renderQueue->GetDeferredRenderQueue(); - for (auto& layerPair : m_renderQueue->layers) - { - for (auto& pipelinePair : layerPair.second.opaqueModels) - { - const MaterialPipeline* pipeline = pipelinePair.first; - auto& pipelineEntry = pipelinePair.second; + renderQueue.Sort(sceneData.viewer); - if (pipelineEntry.maxInstanceCount > 0) - { - bool instancing = instancingEnabled && (pipelineEntry.maxInstanceCount > NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT); + if (!renderQueue.models.empty()) + DrawModels(sceneData, renderQueue, renderQueue.models); - UInt32 flags = ShaderFlags_Deferred; - if (instancing) - flags |= ShaderFlags_Instancing; + if (!renderQueue.basicSprites.empty()) + DrawSprites(sceneData, renderQueue, renderQueue.basicSprites); - const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(flags); + if (!renderQueue.billboards.empty()) + DrawBillboards(sceneData, renderQueue, renderQueue.billboards); - const Shader* shader = pipelineInstance.uberInstance->GetShader(); + if (!renderQueue.depthSortedModels.empty()) + DrawModels(sceneData, renderQueue, renderQueue.depthSortedModels); - // Uniforms are conserved in our program, there's no point to send them back until they change - if (shader != lastShader) - { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); + if (!renderQueue.depthSortedSprites.empty()) + DrawSprites(sceneData, renderQueue, renderQueue.depthSortedSprites); - // Ambiant color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position of the camera - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - - lastShader = shader; - } - - for (auto& materialPair : pipelineEntry.materialMap) - { - const Material* material = materialPair.first; - auto& matEntry = materialPair.second; - - if (matEntry.enabled) - { - DeferredRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; - - if (!meshInstances.empty()) - { - material->Apply(pipelineInstance); - - // Meshes - for (auto& meshIt : meshInstances) - { - const MeshData& meshData = meshIt.first; - auto& meshEntry = meshIt.second; - - std::vector& instances = meshEntry.instances; - if (!instances.empty()) - { - const IndexBuffer* indexBuffer = meshData.indexBuffer; - const VertexBuffer* vertexBuffer = meshData.vertexBuffer; - - // Handle draw call before rendering loop - Renderer::DrawCall drawFunc; - Renderer::DrawCallInstanced instancedDrawFunc; - unsigned int indexCount; - - if (indexBuffer) - { - drawFunc = Renderer::DrawIndexedPrimitives; - instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; - indexCount = indexBuffer->GetIndexCount(); - } - else - { - drawFunc = Renderer::DrawPrimitives; - instancedDrawFunc = Renderer::DrawPrimitivesInstanced; - indexCount = vertexBuffer->GetVertexCount(); - } - - Renderer::SetIndexBuffer(indexBuffer); - Renderer::SetVertexBuffer(vertexBuffer); - - if (instancing) - { - // We get the buffer for instance of Renderer and we configure it to work with matrices - VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); - instanceBuffer->SetVertexDeclaration(VertexDeclaration::Get(VertexLayout_Matrix4)); - - const Matrix4f* instanceMatrices = &instances[0]; - std::size_t instanceCount = instances.size(); - std::size_t maxInstanceCount = instanceBuffer->GetVertexCount(); // The number of matrices that can be hold in the buffer - - while (instanceCount > 0) - { - // We compute the number of instances that we will be able to show this time (Depending on the instance buffer size) - std::size_t renderedInstanceCount = std::min(instanceCount, maxInstanceCount); - instanceCount -= renderedInstanceCount; - - // We fill the instancing buffer with our world matrices - instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount); - instanceMatrices += renderedInstanceCount; - - // And we show - instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); - } - } - else - { - // Without instancing, we must do one draw call for each instance - // This may be faster than instancing under a threshold - // Due to the time to modify the instancing buffer - for (const Matrix4f& matrix : instances) - { - Renderer::SetMatrix(MatrixType_World, matrix); - drawFunc(meshData.primitiveMode, 0, indexCount); - } - } - } - } - } - } - } - } - } - } + if (!renderQueue.depthSortedBillboards.empty()) + DrawBillboards(sceneData, renderQueue, renderQueue.depthSortedBillboards); return false; // We only fill the G-Buffer, the work texture are unchanged } @@ -266,13 +192,409 @@ namespace Nz return false; } } + + void DeferredGeometryPass::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const + { + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); - /*! - * \brief Gets the uniforms of a shader - * \return Uniforms of the shader - * - * \param shader Shader to get uniforms from - */ + Renderer::SetVertexBuffer(&s_quadVertexBuffer); + + Nz::BufferMapper instanceBufferMapper; + std::size_t billboardCount = 0; + std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + + auto Commit = [&]() + { + if (billboardCount > 0) + { + instanceBufferMapper.Unmap(); + + Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4); + + billboardCount = 0; + } + }; + + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + for (const BasicRenderQueue::Billboard& billboard : billboards) + { + const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect; + + if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + Commit(); + + const MaterialPipeline* pipeline = billboard.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Deferred | ShaderFlags_Instancing | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + if (lastMaterial != billboard.material) + { + billboard.material->Apply(*pipelineInstance); + lastMaterial = billboard.material; + } + + if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } + } + + if (!instanceBufferMapper.GetBuffer()) + instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite); + + std::memcpy(static_cast(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, &billboard.data, sizeof(BasicRenderQueue::BillboardData)); + if (++billboardCount >= maxBillboardPerDraw) + Commit(); + } + + Commit(); + } + + void DeferredGeometryPass::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const + { + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); + + Renderer::SetVertexBuffer(&s_quadVertexBuffer); + + Nz::BufferMapper instanceBufferMapper; + std::size_t billboardCount = 0; + std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + + auto Commit = [&]() + { + if (billboardCount > 0) + { + instanceBufferMapper.Unmap(); + + Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4); + + billboardCount = 0; + } + }; + + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + for (const BasicRenderQueue::BillboardChain& billboard : billboards) + { + const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect; + + if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + Commit(); + + const MaterialPipeline* pipeline = billboard.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Deferred | ShaderFlags_Instancing | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + if (lastMaterial != billboard.material) + { + billboard.material->Apply(*pipelineInstance); + lastMaterial = billboard.material; + } + + if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } + } + + std::size_t billboardRemaining = billboard.billboardCount; + const BasicRenderQueue::BillboardData* billboardData = renderQueue.GetBillboardData(billboard.billboardIndex); + do + { + std::size_t renderedBillboardCount = std::min(billboardRemaining, maxBillboardPerDraw - billboardCount); + billboardRemaining -= renderedBillboardCount; + + if (!instanceBufferMapper.GetBuffer()) + instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite); + + std::memcpy(static_cast(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, billboardData, renderedBillboardCount * sizeof(BasicRenderQueue::BillboardData)); + billboardCount += renderedBillboardCount; + billboardData += renderedBillboardCount; + + if (billboardCount >= maxBillboardPerDraw) + Commit(); + } + while (billboardRemaining > 0); + } + + Commit(); + } + + void DeferredGeometryPass::DrawModels(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const Nz::RenderQueue& models) const + { + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + ///TODO: Reimplement instancing + + for (const BasicRenderQueue::Model& model : models) + { + const MaterialPipeline* pipeline = model.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &model.material->GetPipeline()->Apply(ShaderFlags_Deferred); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + if (lastMaterial != model.material) + { + model.material->Apply(*pipelineInstance); + lastMaterial = model.material; + } + + if (model.material->IsScissorTestEnabled()) + { + const Nz::Recti& scissorRect = (model.scissorRect.width > 0) ? model.scissorRect : fullscreenScissorRect; + if (scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } + } + + // Handle draw call before rendering loop + Renderer::DrawCall drawFunc; + Renderer::DrawCallInstanced instancedDrawFunc; + unsigned int indexCount; + + if (model.meshData.indexBuffer) + { + drawFunc = Renderer::DrawIndexedPrimitives; + instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; + indexCount = model.meshData.indexBuffer->GetIndexCount(); + } + else + { + drawFunc = Renderer::DrawPrimitives; + instancedDrawFunc = Renderer::DrawPrimitivesInstanced; + indexCount = model.meshData.vertexBuffer->GetVertexCount(); + } + + Renderer::SetIndexBuffer(model.meshData.indexBuffer); + Renderer::SetVertexBuffer(model.meshData.vertexBuffer); + + Renderer::SetMatrix(MatrixType_World, model.matrix); + drawFunc(model.meshData.primitiveMode, 0, indexCount); + } + } + + void DeferredGeometryPass::DrawSprites(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& spriteList) const + { + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + Renderer::SetIndexBuffer(&s_quadIndexBuffer); + Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); + Renderer::SetVertexBuffer(&m_spriteBuffer); + + const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); + const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); + + m_spriteChains.clear(); + + auto Commit = [&]() + { + std::size_t spriteChainCount = m_spriteChains.size(); + if (spriteChainCount > 0) + { + std::size_t spriteChain = 0; // Which chain of sprites are we treating + std::size_t spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain + + do + { + // We open the buffer in writing mode + BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); + VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); + + std::size_t spriteCount = 0; + + do + { + const VertexStruct_XYZ_Color_UV* currentChain = m_spriteChains[spriteChain].first; + std::size_t currentChainSpriteCount = m_spriteChains[spriteChain].second; + std::size_t count = std::min(maxSpriteCount - spriteCount, currentChainSpriteCount - spriteChainOffset); + + std::memcpy(vertices, currentChain + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += count * 4; + + spriteCount += count; + spriteChainOffset += count; + + // Have we treated the entire chain ? + if (spriteChainOffset == currentChainSpriteCount) + { + spriteChain++; + spriteChainOffset = 0; + } + } + while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); + + vertexMapper.Unmap(); + + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6); + } + while (spriteChain < spriteChainCount); + } + + m_spriteChains.clear(); + }; + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) + { + const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; + + if (basicSprites.material != lastMaterial || basicSprites.overlay != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + Commit(); + + const MaterialPipeline* pipeline = basicSprites.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &basicSprites.material->GetPipeline()->Apply(ShaderFlags_Deferred | ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + // Overlay texture unit + shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + if (lastMaterial != basicSprites.material) + { + basicSprites.material->Apply(*pipelineInstance); + + Renderer::SetTextureSampler(overlayTextureUnit, basicSprites.material->GetDiffuseSampler()); + + lastMaterial = basicSprites.material; + } + + const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : &m_whiteTexture; + if (overlayTexture != lastOverlay) + { + Renderer::SetTexture(overlayTextureUnit, overlayTexture); + lastOverlay = overlayTexture; + } + + if (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } + } + + m_spriteChains.emplace_back(basicSprites.vertices, basicSprites.spriteCount); + } + + Commit(); + } const DeferredGeometryPass::ShaderUniforms* DeferredGeometryPass::GetShaderUniforms(const Shader* shader) const { @@ -303,4 +625,73 @@ namespace Nz { m_shaderUniforms.erase(shader); } + + bool DeferredGeometryPass::Initialize() + { + try + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + + s_quadIndexBuffer.Reset(false, s_maxQuads * 6, DataStorage_Hardware, 0); + + BufferMapper mapper(s_quadIndexBuffer, BufferAccess_WriteOnly); + UInt16* indices = static_cast(mapper.GetPointer()); + + for (unsigned int i = 0; i < s_maxQuads; ++i) + { + *indices++ = i * 4 + 0; + *indices++ = i * 4 + 2; + *indices++ = i * 4 + 1; + + *indices++ = i * 4 + 2; + *indices++ = i * 4 + 3; + *indices++ = i * 4 + 1; + } + + mapper.Unmap(); // No point to keep the buffer open any longer + + // Quad buffer (used for instancing of billboards and sprites) + //Note: UV are computed in the shader + s_quadVertexBuffer.Reset(VertexDeclaration::Get(VertexLayout_XY), 4, DataStorage_Hardware, 0); + + float vertices[2 * 4] = { + -0.5f, -0.5f, + 0.5f, -0.5f, + -0.5f, 0.5f, + 0.5f, 0.5f, + }; + + s_quadVertexBuffer.FillRaw(vertices, 0, sizeof(vertices)); + + // Declaration used when rendering the vertex billboards + s_billboardVertexDeclaration.EnableComponent(VertexComponent_Color, ComponentType_Color, NazaraOffsetOf(BillboardPoint, color)); + s_billboardVertexDeclaration.EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(BillboardPoint, position)); + s_billboardVertexDeclaration.EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(BillboardPoint, uv)); + s_billboardVertexDeclaration.EnableComponent(VertexComponent_Userdata0, ComponentType_Float4, NazaraOffsetOf(BillboardPoint, size)); // Includes sincos + + // Declaration used when rendering the billboards with intancing + // The main advantage is the direct copy (std::memcpy) of data in the RenderQueue to the GPU buffer + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(BasicRenderQueue::BillboardData, center)); + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(BasicRenderQueue::BillboardData, size)); // Englobe sincos + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(BasicRenderQueue::BillboardData, color)); + } + catch (const std::exception& e) + { + NazaraError("Failed to initialise: " + String(e.what())); + return false; + } + + return true; + } + + void DeferredGeometryPass::Uninitialize() + { + s_quadIndexBuffer.Reset(); + s_quadVertexBuffer.Reset(); + } + + IndexBuffer DeferredGeometryPass::s_quadIndexBuffer; + VertexBuffer DeferredGeometryPass::s_quadVertexBuffer; + VertexDeclaration DeferredGeometryPass::s_billboardInstanceDeclaration; + VertexDeclaration DeferredGeometryPass::s_billboardVertexDeclaration; } diff --git a/src/Nazara/Graphics/DeferredPhongLightingPass.cpp b/src/Nazara/Graphics/DeferredPhongLightingPass.cpp index 3902c9d9d..7e4ef2480 100644 --- a/src/Nazara/Graphics/DeferredPhongLightingPass.cpp +++ b/src/Nazara/Graphics/DeferredPhongLightingPass.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/Nazara/Graphics/DeferredProxyRenderQueue.cpp b/src/Nazara/Graphics/DeferredProxyRenderQueue.cpp new file mode 100644 index 000000000..56d2b76d3 --- /dev/null +++ b/src/Nazara/Graphics/DeferredProxyRenderQueue.cpp @@ -0,0 +1,261 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup graphics + * \class Nz::DeferredProxyRenderQueue + * \brief Graphics class sorting the objects into a deferred and forward render queue (depending on blending) + */ + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + */ + + void DeferredProxyRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr); + else + m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + */ + + void DeferredProxyRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr); + else + m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + */ + + void DeferredProxyRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr); + else + m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + */ + + void DeferredProxyRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr); + else + m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + */ + + void DeferredProxyRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr); + else + m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + */ + + void DeferredProxyRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr); + else + m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + */ + + void DeferredProxyRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr); + else + m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + */ + + void DeferredProxyRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr); + else + m_forwardRenderQueue->AddBillboards(renderOrder, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr); + } + + /*! + * \brief Adds drawable to the queue + * + * \param renderOrder Order of rendering + * \param drawable Drawable user defined + * + * \remark Produces a NazaraError if drawable is invalid + */ + + void DeferredProxyRenderQueue::AddDrawable(int renderOrder, const Drawable* drawable) + { + m_forwardRenderQueue->AddDrawable(renderOrder, drawable); + } + + /*! + * \brief Adds mesh to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the mesh + * \param meshData Data of the mesh + * \param meshAABB Box of the mesh + * \param transformMatrix Matrix of the mesh + */ + + void DeferredProxyRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix, const Recti& scissorRect) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix, scissorRect); + else + m_forwardRenderQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix, scissorRect); + } + + /*! + * \brief Adds sprites to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the sprites + * \param vertices Buffer of data for the sprites + * \param spriteCount Number of sprites + * \param overlay Texture of the sprites + */ + + void DeferredProxyRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Recti& scissorRect, const Texture* overlay) + { + NazaraAssert(material, "Invalid material"); + + if (!material->IsBlendingEnabled()) + m_deferredRenderQueue->AddSprites(renderOrder, material, vertices, spriteCount, scissorRect, overlay); + else + m_forwardRenderQueue->AddSprites(renderOrder, material, vertices, spriteCount, scissorRect, overlay); + } + + /*! + * \brief Clears the queue + * + * \param fully Should everything be cleared or we can keep layers + */ + + void DeferredProxyRenderQueue::Clear(bool fully) + { + AbstractRenderQueue::Clear(fully); + + m_deferredRenderQueue->Clear(fully); + m_forwardRenderQueue->Clear(fully); + } +} diff --git a/src/Nazara/Graphics/DeferredRenderPass.cpp b/src/Nazara/Graphics/DeferredRenderPass.cpp index 8b0b7fde5..d3f89e348 100644 --- a/src/Nazara/Graphics/DeferredRenderPass.cpp +++ b/src/Nazara/Graphics/DeferredRenderPass.cpp @@ -3,8 +3,8 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include -#include #include #include @@ -47,7 +47,7 @@ namespace Nz void DeferredRenderPass::Initialize(DeferredRenderTechnique* technique) { m_deferredTechnique = technique; - m_renderQueue = static_cast(technique->GetRenderQueue()); + m_renderQueue = static_cast(technique->GetRenderQueue()); m_depthStencilTexture = technique->GetDepthStencilTexture(); diff --git a/src/Nazara/Graphics/DeferredRenderQueue.cpp b/src/Nazara/Graphics/DeferredRenderQueue.cpp deleted file mode 100644 index 76bc666be..000000000 --- a/src/Nazara/Graphics/DeferredRenderQueue.cpp +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright (C) 2017 Jérôme Leclercq -// This file is part of the "Nazara Engine - Graphics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include - -///TODO: Render billboards using Deferred Shading if possible - -namespace Nz -{ - /*! - * \ingroup graphics - * \class Nz::DeferredRenderQueue - * \brief Graphics class that represents the rendering queue for deferred rendering - */ - - /*! - * \brief Constructs a DeferredRenderQueue object with the rendering queue of forward rendering - * - * \param forwardQueue Queue of data to render - */ - - DeferredRenderQueue::DeferredRenderQueue(ForwardRenderQueue* forwardQueue) : - m_forwardQueue(forwardQueue) - { - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Sizes of the billboards - * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used - * \param colorPtr Color of the billboards if null, Color::White is used - */ - - void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) - { - m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Sizes of the billboards - * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used - * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used - */ - - void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) - { - m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Sizes of the billboards - * \param anglePtr Rotation of the billboards if null, 0.f is used - * \param colorPtr Color of the billboards if null, Color::White is used - */ - - void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) - { - m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, anglePtr, colorPtr); - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Sizes of the billboards - * \param anglePtr Rotation of the billboards if null, 0.f is used - * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used - */ - - void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) - { - m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, anglePtr, alphaPtr); - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Size of the billboards - * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used - * \param colorPtr Color of the billboards if null, Color::White is used - */ - - void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) - { - m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Size of the billboards - * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used - * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used - */ - - void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) - { - m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Size of the billboards - * \param anglePtr Rotation of the billboards if null, 0.f is used - * \param colorPtr Color of the billboards if null, Color::White is used - */ - - void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) - { - m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, anglePtr, colorPtr); - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Size of the billboards - * \param anglePtr Rotation of the billboards if null, 0.f is used - * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used - */ - - void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) - { - m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, anglePtr, alphaPtr); - } - - /*! - * \brief Adds drawable to the queue - * - * \param renderOrder Order of rendering - * \param drawable Drawable user defined - * - * \remark Produces a NazaraError if drawable is invalid - */ - - void DeferredRenderQueue::AddDrawable(int renderOrder, const Drawable* drawable) - { - m_forwardQueue->AddDrawable(renderOrder, drawable); - } - - /*! - * \brief Adds mesh to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the mesh - * \param meshData Data of the mesh - * \param meshAABB Box of the mesh - * \param transformMatrix Matrix of the mesh - */ - - void DeferredRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) - { - if (material->IsBlendingEnabled() || material->IsDepthSortingEnabled()) //< Fixme: Deferred Shading should be able to handle depth sorting - // Deferred Shading cannot handle blended objects, put them in the forward list - m_forwardQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix); - else - { - Layer& currentLayer = GetLayer(renderOrder); - MeshPipelineBatches& opaqueModels = currentLayer.opaqueModels; - - const MaterialPipeline* materialPipeline = material->GetPipeline(); - - auto pipelineIt = opaqueModels.find(materialPipeline); - if (pipelineIt == opaqueModels.end()) - { - BatchedMaterialEntry materialEntry; - pipelineIt = opaqueModels.insert(MeshPipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; - } - - BatchedMaterialEntry& materialEntry = pipelineIt->second; - MeshMaterialBatches& materialMap = materialEntry.materialMap; - - auto materialIt = materialMap.find(material); - if (materialIt == materialMap.end()) - { - BatchedModelEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &DeferredRenderQueue::OnMaterialInvalidation); - - materialIt = materialMap.insert(MeshMaterialBatches::value_type(material, std::move(entry))).first; - } - - BatchedModelEntry& entry = materialIt->second; - entry.enabled = true; - - MeshInstanceContainer& meshMap = entry.meshMap; - - auto it2 = meshMap.find(meshData); - if (it2 == meshMap.end()) - { - MeshInstanceEntry instanceEntry; - if (meshData.indexBuffer) - instanceEntry.indexBufferReleaseSlot.Connect(meshData.indexBuffer->OnIndexBufferRelease, this, &DeferredRenderQueue::OnIndexBufferInvalidation); - - instanceEntry.vertexBufferReleaseSlot.Connect(meshData.vertexBuffer->OnVertexBufferRelease, this, &DeferredRenderQueue::OnVertexBufferInvalidation); - - it2 = meshMap.insert(std::make_pair(meshData, std::move(instanceEntry))).first; - } - - std::vector& instances = it2->second.instances; - instances.push_back(transformMatrix); - - materialEntry.maxInstanceCount = std::max(materialEntry.maxInstanceCount, instances.size()); - } - } - - /*! - * \brief Adds sprites to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the sprites - * \param vertices Buffer of data for the sprites - * \param spriteCount Number of sprites - * \param overlay Texture of the sprites - */ - - void DeferredRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay) - { - m_forwardQueue->AddSprites(renderOrder, material, vertices, spriteCount, overlay); - } - - /*! - * \brief Clears the queue - * - * \param fully Should everything be cleared or we can keep layers - */ - - void DeferredRenderQueue::Clear(bool fully) - { - AbstractRenderQueue::Clear(fully); - - if (fully) - layers.clear(); - else - { - for (auto it = layers.begin(); it != layers.end();) - { - Layer& layer = it->second; - if (layer.clearCount++ >= 100) - it = layers.erase(it); - else - { - for (auto& pipelinePair : layer.opaqueModels) - { - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.maxInstanceCount > 0) - { - for (auto& materialPair : pipelineEntry.materialMap) - { - auto& matEntry = materialPair.second; - - if (matEntry.enabled) - { - MeshInstanceContainer& meshInstances = matEntry.meshMap; - - for (auto& meshIt : meshInstances) - { - auto& meshEntry = meshIt.second; - - meshEntry.instances.clear(); - } - matEntry.enabled = false; - } - } - pipelineEntry.maxInstanceCount = 0; - } - } - - ++it; - } - } - } - - m_forwardQueue->Clear(fully); - } - - /*! - * \brief Gets the ith layer - * \return Reference to the ith layer for the queue - * - * \param i Index of the layer - */ - - DeferredRenderQueue::Layer& DeferredRenderQueue::GetLayer(unsigned int i) - { - auto it = layers.find(i); - if (it == layers.end()) - it = layers.insert(std::make_pair(i, Layer())).first; - - Layer& layer = it->second; - layer.clearCount = 0; - - return layer; - } - - /*! - * \brief Handle the invalidation of an index buffer - * - * \param indexBuffer Index buffer being invalidated - */ - - void DeferredRenderQueue::OnIndexBufferInvalidation(const IndexBuffer* indexBuffer) - { - for (auto& pair : layers) - { - Layer& layer = pair.second; - - for (auto& pipelineEntry : layer.opaqueModels) - { - for (auto& materialEntry : pipelineEntry.second.materialMap) - { - MeshInstanceContainer& meshes = materialEntry.second.meshMap; - for (auto it = meshes.begin(); it != meshes.end();) - { - const MeshData& renderData = it->first; - if (renderData.indexBuffer == indexBuffer) - it = meshes.erase(it); - else - ++it; - } - } - } - } - } - - /*! - * \brief Handle the invalidation of a material - * - * \param material Material being invalidated - */ - - void DeferredRenderQueue::OnMaterialInvalidation(const Material* material) - { - for (auto& pair : layers) - { - Layer& layer = pair.second; - - for (auto& pipelineEntry : layer.opaqueModels) - pipelineEntry.second.materialMap.erase(material); - } - } - - /*! - * \brief Handle the invalidation of a vertex buffer - * - * \param vertexBuffer Vertex buffer being invalidated - */ - - void DeferredRenderQueue::OnVertexBufferInvalidation(const VertexBuffer* vertexBuffer) - { - for (auto& pair : layers) - { - Layer& layer = pair.second; - for (auto& pipelineEntry : layer.opaqueModels) - { - for (auto& materialEntry : pipelineEntry.second.materialMap) - { - MeshInstanceContainer& meshes = materialEntry.second.meshMap; - for (auto it = meshes.begin(); it != meshes.end();) - { - const MeshData& renderData = it->first; - if (renderData.vertexBuffer == vertexBuffer) - it = meshes.erase(it); - else - ++it; - } - } - } - } - } -} diff --git a/src/Nazara/Graphics/DeferredRenderTechnique.cpp b/src/Nazara/Graphics/DeferredRenderTechnique.cpp index f930871fe..34db9e271 100644 --- a/src/Nazara/Graphics/DeferredRenderTechnique.cpp +++ b/src/Nazara/Graphics/DeferredRenderTechnique.cpp @@ -127,7 +127,7 @@ namespace Nz */ DeferredRenderTechnique::DeferredRenderTechnique() : - m_renderQueue(static_cast(m_forwardTechnique.GetRenderQueue())), + m_renderQueue(&m_deferredRenderQueue, static_cast(m_forwardTechnique.GetRenderQueue())), m_GBufferSize(0U) { m_depthStencilTexture = Texture::New(); @@ -455,35 +455,35 @@ namespace Nz switch (renderPass) { case RenderPassType_AA: - smartPtr.reset(new DeferredFXAAPass); + smartPtr = std::make_unique(); break; case RenderPassType_Bloom: - smartPtr.reset(new DeferredBloomPass); + smartPtr = std::make_unique(); break; case RenderPassType_DOF: - smartPtr.reset(new DeferredDOFPass); + smartPtr = std::make_unique(); break; case RenderPassType_Final: - smartPtr.reset(new DeferredFinalPass); + smartPtr = std::make_unique(); break; case RenderPassType_Fog: - smartPtr.reset(new DeferredFogPass); + smartPtr = std::make_unique(); break; case RenderPassType_Forward: - smartPtr.reset(new DeferredForwardPass); + smartPtr = std::make_unique(); break; case RenderPassType_Geometry: - smartPtr.reset(new DeferredGeometryPass); + smartPtr = std::make_unique(); break; case RenderPassType_Lighting: - smartPtr.reset(new DeferredPhongLightingPass); + smartPtr = std::make_unique(); break; case RenderPassType_SSAO: @@ -701,6 +701,12 @@ namespace Nz NazaraWarning("Failed to register gaussian blur shader, certain features will not work: " + error); } + if (!DeferredGeometryPass::Initialize()) + { + NazaraError("Failed to initialize geometry pass"); + return false; + } + return true; } @@ -710,6 +716,8 @@ namespace Nz void DeferredRenderTechnique::Uninitialize() { + DeferredGeometryPass::Uninitialize(); + ShaderLibrary::Unregister("DeferredGBufferClear"); ShaderLibrary::Unregister("DeferredDirectionnalLight"); ShaderLibrary::Unregister("DeferredPointSpotLight"); diff --git a/src/Nazara/Graphics/DepthRenderQueue.cpp b/src/Nazara/Graphics/DepthRenderQueue.cpp index 775276c37..71c985097 100644 --- a/src/Nazara/Graphics/DepthRenderQueue.cpp +++ b/src/Nazara/Graphics/DepthRenderQueue.cpp @@ -43,7 +43,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -56,7 +56,7 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); + BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr); } /*! @@ -73,7 +73,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -86,7 +86,7 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); + BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr); } /*! @@ -103,7 +103,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -116,7 +116,7 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, anglePtr, colorPtr); + BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr); } /*! @@ -133,7 +133,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -146,7 +146,7 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, anglePtr, alphaPtr); + BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr); } /*! @@ -163,7 +163,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -176,7 +176,7 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); + BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, colorPtr); } /*! @@ -193,7 +193,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -206,7 +206,7 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); + BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, sinCosPtr, alphaPtr); } /*! @@ -223,7 +223,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -236,7 +236,7 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, anglePtr, colorPtr); + BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, colorPtr); } /*! @@ -253,7 +253,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, std::size_t billboardCount, const Recti& scissorRect, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -266,11 +266,11 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, anglePtr, alphaPtr); + BasicRenderQueue::AddBillboards(0, material, billboardCount, scissorRect, positionPtr, sizePtr, anglePtr, alphaPtr); } /*! - * \brief Adds a direcitonal light to the queue + * \brief Adds a directional light to the queue * * \param light Light to add * @@ -295,7 +295,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) + void DepthRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix, const Recti& scissorRect) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -309,7 +309,7 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddMesh(0, material, meshData, meshAABB, transformMatrix); + BasicRenderQueue::AddMesh(0, material, meshData, meshAABB, transformMatrix, scissorRect); } /*! @@ -352,7 +352,7 @@ namespace Nz * \remark Produces a NazaraAssert if material is invalid */ - void DepthRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay) + void DepthRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Recti& scissorRect, const Texture* overlay /*= nullptr*/) { NazaraAssert(material, "Invalid material"); NazaraUnused(renderOrder); @@ -366,7 +366,7 @@ namespace Nz else material = m_baseMaterial; - ForwardRenderQueue::AddSprites(0, material, vertices, spriteCount, overlay); + BasicRenderQueue::AddSprites(0, material, vertices, spriteCount, scissorRect, overlay); } } diff --git a/src/Nazara/Graphics/DepthRenderTechnique.cpp b/src/Nazara/Graphics/DepthRenderTechnique.cpp index c58b4e4ee..c536c4c83 100644 --- a/src/Nazara/Graphics/DepthRenderTechnique.cpp +++ b/src/Nazara/Graphics/DepthRenderTechnique.cpp @@ -5,11 +5,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -83,22 +85,28 @@ namespace Nz bool DepthRenderTechnique::Draw(const SceneData& sceneData) const { - for (auto& pair : m_renderQueue.layers) - { - ForwardRenderQueue::Layer& layer = pair.second; + m_renderQueue.Sort(sceneData.viewer); - if (!layer.opaqueModels.empty()) - DrawOpaqueModels(sceneData, layer); + if (!m_renderQueue.models.empty()) + DrawModels(sceneData, m_renderQueue, m_renderQueue.models); - if (!layer.opaqueSprites.empty()) - DrawBasicSprites(sceneData, layer); + if (!m_renderQueue.basicSprites.empty()) + DrawSprites(sceneData, m_renderQueue, m_renderQueue.basicSprites); - if (!layer.billboards.empty()) - DrawBillboards(sceneData, layer); + if (!m_renderQueue.billboards.empty()) + DrawBillboards(sceneData, m_renderQueue, m_renderQueue.billboards); - for (const Drawable* drawable : layer.otherDrawables) - drawable->Draw(); - } + if (!m_renderQueue.depthSortedModels.empty()) + DrawModels(sceneData, m_renderQueue, m_renderQueue.depthSortedModels); + + if (!m_renderQueue.depthSortedSprites.empty()) + DrawSprites(sceneData, m_renderQueue, m_renderQueue.depthSortedSprites); + + if (!m_renderQueue.depthSortedBillboards.empty()) + DrawBillboards(sceneData, m_renderQueue, m_renderQueue.depthSortedBillboards); + + if (!m_renderQueue.customDrawables.empty()) + DrawCustomDrawables(sceneData, m_renderQueue, m_renderQueue.customDrawables); return true; } @@ -175,9 +183,9 @@ namespace Nz // Declaration utilisée lors du rendu des billboards par instancing // L'avantage ici est la copie directe (std::memcpy) des données de la RenderQueue vers le buffer GPU - s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(ForwardRenderQueue::BillboardData, center)); - s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(ForwardRenderQueue::BillboardData, size)); // Englobe sincos - s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(ForwardRenderQueue::BillboardData, color)); + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(BasicRenderQueue::BillboardData, center)); + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(BasicRenderQueue::BillboardData, size)); // Englobe sincos + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(BasicRenderQueue::BillboardData, color)); } catch (const std::exception& e) { @@ -197,411 +205,406 @@ namespace Nz s_quadIndexBuffer.Reset(); s_quadVertexBuffer.Reset(); } - - /*! - * \brief Draws basic sprites - * - * \param sceneData Data of the scene - * \param layer Layer of the rendering - */ - - void DepthRenderTechnique::DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const + + void DepthRenderTechnique::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const { + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); + + Renderer::SetVertexBuffer(&s_quadVertexBuffer); + + Nz::BufferMapper instanceBufferMapper; + std::size_t billboardCount = 0; + std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + + auto Commit = [&]() + { + if (billboardCount > 0) + { + instanceBufferMapper.Unmap(); + + Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4); + + billboardCount = 0; + } + }; + + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; const Shader* lastShader = nullptr; const ShaderUniforms* shaderUniforms = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + for (const BasicRenderQueue::Billboard& billboard : billboards) + { + const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect; + + if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + Commit(); + + const MaterialPipeline* pipeline = billboard.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Deferred | ShaderFlags_Instancing | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + if (lastMaterial != billboard.material) + { + billboard.material->Apply(*pipelineInstance); + lastMaterial = billboard.material; + } + + if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } + } + + if (!instanceBufferMapper.GetBuffer()) + instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite); + + std::memcpy(static_cast(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, &billboard.data, sizeof(BasicRenderQueue::BillboardData)); + if (++billboardCount >= maxBillboardPerDraw) + Commit(); + } + + Commit(); + } + + void DepthRenderTechnique::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const + { + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); + + Renderer::SetVertexBuffer(&s_quadVertexBuffer); + + Nz::BufferMapper instanceBufferMapper; + std::size_t billboardCount = 0; + std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + + auto Commit = [&]() + { + if (billboardCount > 0) + { + instanceBufferMapper.Unmap(); + + Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4); + + billboardCount = 0; + } + }; + + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + for (const BasicRenderQueue::BillboardChain& billboard : billboards) + { + const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect; + + if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + Commit(); + + const MaterialPipeline* pipeline = billboard.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Deferred | ShaderFlags_Instancing | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + if (lastMaterial != billboard.material) + { + billboard.material->Apply(*pipelineInstance); + lastMaterial = billboard.material; + } + + if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } + } + + std::size_t billboardRemaining = billboard.billboardCount; + const BasicRenderQueue::BillboardData* billboardData = renderQueue.GetBillboardData(billboard.billboardIndex); + do + { + std::size_t renderedBillboardCount = std::min(billboardRemaining, maxBillboardPerDraw - billboardCount); + billboardRemaining -= renderedBillboardCount; + + if (!instanceBufferMapper.GetBuffer()) + instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite); + + std::memcpy(static_cast(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, billboardData, renderedBillboardCount * sizeof(BasicRenderQueue::BillboardData)); + billboardCount += renderedBillboardCount; + billboardData += renderedBillboardCount; + + if (billboardCount >= maxBillboardPerDraw) + Commit(); + } + while (billboardRemaining > 0); + } + + Commit(); + } + + void DepthRenderTechnique::DrawCustomDrawables(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& customDrawables) const + { + for (const BasicRenderQueue::CustomDrawable& customDrawable : customDrawables) + customDrawable.drawable->Draw(); + } + + void DepthRenderTechnique::DrawModels(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const Nz::RenderQueue& models) const + { + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + ///TODO: Reimplement instancing + + for (const BasicRenderQueue::Model& model : models) + { + const MaterialPipeline* pipeline = model.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &model.material->GetPipeline()->Apply(ShaderFlags_Deferred); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + if (lastMaterial != model.material) + { + model.material->Apply(*pipelineInstance); + lastMaterial = model.material; + } + + if (model.material->IsScissorTestEnabled()) + { + const Nz::Recti& scissorRect = (model.scissorRect.width > 0) ? model.scissorRect : fullscreenScissorRect; + if (scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } + } + + // Handle draw call before rendering loop + Renderer::DrawCall drawFunc; + Renderer::DrawCallInstanced instancedDrawFunc; + unsigned int indexCount; + + if (model.meshData.indexBuffer) + { + drawFunc = Renderer::DrawIndexedPrimitives; + instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; + indexCount = model.meshData.indexBuffer->GetIndexCount(); + } + else + { + drawFunc = Renderer::DrawPrimitives; + instancedDrawFunc = Renderer::DrawPrimitivesInstanced; + indexCount = model.meshData.vertexBuffer->GetVertexCount(); + } + + Renderer::SetIndexBuffer(model.meshData.indexBuffer); + Renderer::SetVertexBuffer(model.meshData.vertexBuffer); + + Renderer::SetMatrix(MatrixType_World, model.matrix); + drawFunc(model.meshData.primitiveMode, 0, indexCount); + } + } + + void DepthRenderTechnique::DrawSprites(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& spriteList) const + { + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); Renderer::SetIndexBuffer(&s_quadIndexBuffer); Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetVertexBuffer(&m_spriteBuffer); - for (auto& pipelinePair : layer.opaqueSprites) + const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); + const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); + + m_spriteChains.clear(); + + auto Commit = [&]() { - const MaterialPipeline* pipeline = pipelinePair.first; - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.enabled) + std::size_t spriteChainCount = m_spriteChains.size(); + if (spriteChainCount > 0) { - const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + std::size_t spriteChain = 0; // Which chain of sprites are we treating + std::size_t spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain - const Shader* shader = pipelineInstance.uberInstance->GetShader(); - - // Uniforms are conserved in our program, there's no point to send them back until they change - if (shader != lastShader) + do { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); + // We open the buffer in writing mode + BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); + VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); - // Ambiant color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + std::size_t spriteCount = 0; - lastShader = shader; - } - - for (auto& materialPair : pipelineEntry.materialMap) - { - const Material* material = materialPair.first; - auto& matEntry = materialPair.second; - - if (matEntry.enabled) + do { - unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - material->Apply(pipelineInstance); + const VertexStruct_XYZ_Color_UV* currentChain = m_spriteChains[spriteChain].first; + std::size_t currentChainSpriteCount = m_spriteChains[spriteChain].second; + std::size_t count = std::min(maxSpriteCount - spriteCount, currentChainSpriteCount - spriteChainOffset); + std::memcpy(vertices, currentChain + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += count * 4; + + spriteCount += count; + spriteChainOffset += count; + + // Have we treated the entire chain ? + if (spriteChainOffset == currentChainSpriteCount) + { + spriteChain++; + spriteChainOffset = 0; + } + } + while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); + + vertexMapper.Unmap(); + + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6); + } + while (spriteChain < spriteChainCount); + } + + m_spriteChains.clear(); + }; + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) + { + const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; + + if (basicSprites.material != lastMaterial || basicSprites.overlay != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + Commit(); + + const MaterialPipeline* pipeline = basicSprites.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &basicSprites.material->GetPipeline()->Apply(ShaderFlags_Deferred | ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + + // Overlay texture unit shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); - Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler()); - - auto& overlayMap = matEntry.overlayMap; - for (auto& overlayIt : overlayMap) - { - const Texture* overlay = overlayIt.first; - auto& spriteChainVector = overlayIt.second.spriteChains; - - std::size_t spriteChainCount = spriteChainVector.size(); - if (spriteChainCount > 0) - { - Renderer::SetTexture(overlayTextureUnit, (overlay) ? overlay : &m_whiteTexture); - - std::size_t spriteChain = 0; // Which chain of sprites are we treating - std::size_t spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain - - do - { - // We open the buffer in writing mode - BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); - - std::size_t spriteCount = 0; - std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); - - do - { - ForwardRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain]; - std::size_t count = std::min(maxSpriteCount - spriteCount, currentChain.spriteCount - spriteChainOffset); - - std::memcpy(vertices, currentChain.vertices + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); - vertices += count * 4; - - spriteCount += count; - spriteChainOffset += count; - - // Have we treated the entire chain ? - if (spriteChainOffset == currentChain.spriteCount) - { - spriteChain++; - spriteChainOffset = 0; - } - } while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); - - vertexMapper.Unmap(); - - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6); - } while (spriteChain < spriteChainCount); - - spriteChainVector.clear(); - } - } - - // We set it back to zero - matEntry.enabled = false; - } - } - pipelineEntry.enabled = false; - } - } - } - - /*! - * \brief Draws billboards - * - * \param sceneData Data of the scene - * \param layer Layer of the rendering - */ - - void DepthRenderTechnique::DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const - { - const Shader* lastShader = nullptr; - const ShaderUniforms* shaderUniforms = nullptr; - - if (Renderer::HasCapability(RendererCap_Instancing)) - { - VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); - instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); - - Renderer::SetVertexBuffer(&s_quadVertexBuffer); - - for (auto& pipelinePair : layer.billboards) - { - const MaterialPipeline* pipeline = pipelinePair.first; - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.enabled) - { - const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor); - - const Shader* shader = pipelineInstance.uberInstance->GetShader(); - - // Uniforms are conserved in our program, there's no point to send them back until they change - if (shader != lastShader) - { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); - - // Ambiant color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - lastShader = shader; } - for (auto& matIt : pipelinePair.second.materialMap) - { - const Material* material = matIt.first; - auto& entry = matIt.second; - auto& billboardVector = entry.billboards; + lastPipeline = pipeline; + } - std::size_t billboardCount = billboardVector.size(); - if (billboardCount > 0) - { - // We begin to apply the material (and get the shader activated doing so) - material->Apply(pipelineInstance); + if (lastMaterial != basicSprites.material) + { + basicSprites.material->Apply(*pipelineInstance); - const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; - std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount(); - do - { - std::size_t renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); - billboardCount -= renderedBillboardCount; + Renderer::SetTextureSampler(overlayTextureUnit, basicSprites.material->GetDiffuseSampler()); - instanceBuffer->Fill(data, 0, renderedBillboardCount); - data += renderedBillboardCount; + lastMaterial = basicSprites.material; + } - Renderer::DrawPrimitivesInstanced(renderedBillboardCount, PrimitiveMode_TriangleStrip, 0, 4); - } - while (billboardCount > 0); + const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : &m_whiteTexture; + if (overlayTexture != lastOverlay) + { + Renderer::SetTexture(overlayTextureUnit, overlayTexture); + lastOverlay = overlayTexture; + } - billboardVector.clear(); - } - } + if (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; } } + + m_spriteChains.emplace_back(basicSprites.vertices, basicSprites.spriteCount); } - else - { - Renderer::SetIndexBuffer(&s_quadIndexBuffer); - Renderer::SetVertexBuffer(&m_billboardPointBuffer); - for (auto& pipelinePair : layer.billboards) - { - const MaterialPipeline* pipeline = pipelinePair.first; - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.enabled) - { - const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_VertexColor); - - const Shader* shader = pipelineInstance.uberInstance->GetShader(); - - // Uniforms are conserved in our program, there's no point to send them back until they change - if (shader != lastShader) - { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); - - // Ambiant color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - - lastShader = shader; - } - - for (auto& matIt : pipelinePair.second.materialMap) - { - auto& entry = matIt.second; - auto& billboardVector = entry.billboards; - - const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; - std::size_t maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount() / 4); - - std::size_t billboardCount = billboardVector.size(); - do - { - std::size_t renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); - billboardCount -= renderedBillboardCount; - - BufferMapper vertexMapper(m_billboardPointBuffer, BufferAccess_DiscardAndWrite, 0, renderedBillboardCount * 4); - BillboardPoint* vertices = static_cast(vertexMapper.GetPointer()); - - for (unsigned int i = 0; i < renderedBillboardCount; ++i) - { - const ForwardRenderQueue::BillboardData& billboard = *data++; - - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(0.f, 1.f); - vertices++; - - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(1.f, 1.f); - vertices++; - - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(0.f, 0.f); - vertices++; - - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(1.f, 0.f); - vertices++; - } - - vertexMapper.Unmap(); - - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, renderedBillboardCount * 6); - } - while (billboardCount > 0); - - billboardVector.clear(); - } - } - } - } - } - - /*! - * \brief Draws opaques models - * - * \param sceneData Data of the scene - * \param layer Layer of the rendering - */ - - void DepthRenderTechnique::DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const - { - const Shader* lastShader = nullptr; - const ShaderUniforms* shaderUniforms = nullptr; - - for (auto& pipelinePair : layer.opaqueModels) - { - const MaterialPipeline* pipeline = pipelinePair.first; - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.maxInstanceCount > 0) - { - bool instancing = (pipelineEntry.maxInstanceCount > NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT); - const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply((instancing) ? ShaderFlags_Instancing : 0); - - const Shader* shader = pipelineInstance.uberInstance->GetShader(); - - // Uniforms are conserved in our program, there's no point to send them back until they change - if (shader != lastShader) - { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); - - // Ambiant color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - - lastShader = shader; - } - - for (auto& materialPair : pipelineEntry.materialMap) - { - const Material* material = materialPair.first; - auto& matEntry = materialPair.second; - - if (matEntry.enabled) - { - material->Apply(pipelineInstance); - - ForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; - - // Meshes - for (auto& meshIt : meshInstances) - { - const MeshData& meshData = meshIt.first; - auto& meshEntry = meshIt.second; - - std::vector& instances = meshEntry.instances; - if (!instances.empty()) - { - const IndexBuffer* indexBuffer = meshData.indexBuffer; - const VertexBuffer* vertexBuffer = meshData.vertexBuffer; - - // Handle draw call before rendering loop - Renderer::DrawCall drawFunc; - Renderer::DrawCallInstanced instancedDrawFunc; - unsigned int indexCount; - - if (indexBuffer) - { - drawFunc = Renderer::DrawIndexedPrimitives; - instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; - indexCount = indexBuffer->GetIndexCount(); - } - else - { - drawFunc = Renderer::DrawPrimitives; - instancedDrawFunc = Renderer::DrawPrimitivesInstanced; - indexCount = vertexBuffer->GetVertexCount(); - } - - Renderer::SetIndexBuffer(indexBuffer); - Renderer::SetVertexBuffer(vertexBuffer); - - if (instancing) - { - // We compute the number of instances that we will be able to draw this time (depending on the instancing buffer size) - VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); - instanceBuffer->SetVertexDeclaration(VertexDeclaration::Get(VertexLayout_Matrix4)); - - const Matrix4f* instanceMatrices = &instances[0]; - std::size_t instanceCount = instances.size(); - std::size_t maxInstanceCount = instanceBuffer->GetVertexCount(); // Maximum number of instance in one batch - - while (instanceCount > 0) - { - // We compute the number of instances that we will be able to draw this time (depending on the instancing buffer size) - std::size_t renderedInstanceCount = std::min(instanceCount, maxInstanceCount); - instanceCount -= renderedInstanceCount; - - // We fill the instancing buffer with our world matrices - instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount); - instanceMatrices += renderedInstanceCount; - - // And we draw - instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); - } - } - else - { - // Without instancing, we must do a draw call for each instance - // This may be faster than instancing under a certain number - // Due to the time to modify the instancing buffer - for (const Matrix4f& matrix : instances) - { - Renderer::SetMatrix(MatrixType_World, matrix); - drawFunc(meshData.primitiveMode, 0, indexCount); - } - } - instances.clear(); - } - } - - matEntry.enabled = false; - } - } - - pipelineEntry.maxInstanceCount = 0; - } - } + Commit(); } /*! diff --git a/src/Nazara/Graphics/ForwardRenderQueue.cpp b/src/Nazara/Graphics/ForwardRenderQueue.cpp deleted file mode 100644 index d55901de0..000000000 --- a/src/Nazara/Graphics/ForwardRenderQueue.cpp +++ /dev/null @@ -1,931 +0,0 @@ -// Copyright (C) 2017 Jérôme Leclercq -// This file is part of the "Nazara Engine - Graphics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -///TODO: Replace sinus/cosinus by a lookup table (which will lead to a speed up about 10x) - -namespace Nz -{ - /*! - * \ingroup graphics - * \class Nz::ForwardRenderQueue - * \brief Graphics class that represents the rendering queue for forward rendering - */ - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Sizes of the billboards - * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used - * \param colorPtr Color of the billboards if null, Color::White is used - * - * \remark Produces a NazaraAssert if material is invalid - */ - - void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) - { - NazaraAssert(material, "Invalid material"); - - Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 - - if (!sinCosPtr) - sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile - - if (!colorPtr) - colorPtr.Reset(&Color::White, 0); // Same - - BillboardData* billboardData = GetBillboardData(renderOrder, material, count); - for (unsigned int i = 0; i < count; ++i) - { - billboardData->center = *positionPtr++; - billboardData->color = *colorPtr++; - billboardData->sinCos = *sinCosPtr++; - billboardData->size = *sizePtr++; - billboardData++; - } - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Sizes of the billboards - * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used - * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used - * - * \remark Produces a NazaraAssert if material is invalid - */ - - void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) - { - NazaraAssert(material, "Invalid material"); - - Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 - - if (!sinCosPtr) - sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile - - float defaultAlpha = 1.f; - - if (!alphaPtr) - alphaPtr.Reset(&defaultAlpha, 0); // Same - - BillboardData* billboardData = GetBillboardData(renderOrder, material, count); - for (unsigned int i = 0; i < count; ++i) - { - billboardData->center = *positionPtr++; - billboardData->color = Color(255, 255, 255, static_cast(255.f * (*alphaPtr++))); - billboardData->sinCos = *sinCosPtr++; - billboardData->size = *sizePtr++; - billboardData++; - } - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Sizes of the billboards - * \param anglePtr Rotation of the billboards if null, 0.f is used - * \param colorPtr Color of the billboards if null, Color::White is used - * - * \remark Produces a NazaraAssert if material is invalid - */ - - void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) - { - NazaraAssert(material, "Invalid material"); - - float defaultRotation = 0.f; - - if (!anglePtr) - anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile - - if (!colorPtr) - colorPtr.Reset(&Color::White, 0); // Same - - BillboardData* billboardData = GetBillboardData(renderOrder, material, count); - for (unsigned int i = 0; i < count; ++i) - { - float sin = std::sin(ToRadians(*anglePtr)); - float cos = std::cos(ToRadians(*anglePtr)); - anglePtr++; - - billboardData->center = *positionPtr++; - billboardData->color = *colorPtr++; - billboardData->sinCos.Set(sin, cos); - billboardData->size = *sizePtr++; - billboardData++; - } - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Sizes of the billboards - * \param anglePtr Rotation of the billboards if null, 0.f is used - * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used - * - * \remark Produces a NazaraAssert if material is invalid - */ - - void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) - { - NazaraAssert(material, "Invalid material"); - - float defaultRotation = 0.f; - - if (!anglePtr) - anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile - - float defaultAlpha = 1.f; - - if (!alphaPtr) - alphaPtr.Reset(&defaultAlpha, 0); // Same - - BillboardData* billboardData = GetBillboardData(renderOrder, material, count); - for (unsigned int i = 0; i < count; ++i) - { - float sin = std::sin(ToRadians(*anglePtr)); - float cos = std::cos(ToRadians(*anglePtr)); - anglePtr++; - - billboardData->center = *positionPtr++; - billboardData->color = Color(255, 255, 255, static_cast(255.f * (*alphaPtr++))); - billboardData->sinCos.Set(sin, cos); - billboardData->size = *sizePtr++; - billboardData++; - } - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Size of the billboards - * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used - * \param colorPtr Color of the billboards if null, Color::White is used - * - * \remark Produces a NazaraAssert if material is invalid - */ - - void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) - { - NazaraAssert(material, "Invalid material"); - - Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 - - if (!sinCosPtr) - sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile - - if (!colorPtr) - colorPtr.Reset(&Color::White, 0); // Same - - BillboardData* billboardData = GetBillboardData(renderOrder, material, count); - for (unsigned int i = 0; i < count; ++i) - { - billboardData->center = *positionPtr++; - billboardData->color = *colorPtr++; - billboardData->sinCos = *sinCosPtr++; - billboardData->size.Set(*sizePtr++); - billboardData++; - } - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Size of the billboards - * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used - * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used - * - * \remark Produces a NazaraAssert if material is invalid - */ - - void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) - { - NazaraAssert(material, "Invalid material"); - - Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 - - if (!sinCosPtr) - sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile - - float defaultAlpha = 1.f; - - if (!alphaPtr) - alphaPtr.Reset(&defaultAlpha, 0); // Same - - BillboardData* billboardData = GetBillboardData(renderOrder, material, count); - for (unsigned int i = 0; i < count; ++i) - { - billboardData->center = *positionPtr++; - billboardData->color = Color(255, 255, 255, static_cast(255.f * (*alphaPtr++))); - billboardData->sinCos = *sinCosPtr++; - billboardData->size.Set(*sizePtr++); - billboardData++; - } - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Size of the billboards - * \param anglePtr Rotation of the billboards if null, 0.f is used - * \param colorPtr Color of the billboards if null, Color::White is used - * - * \remark Produces a NazaraAssert if material is invalid - */ - - void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) - { - NazaraAssert(material, "Invalid material"); - - float defaultRotation = 0.f; - - if (!anglePtr) - anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile - - if (!colorPtr) - colorPtr.Reset(&Color::White, 0); // Same - - BillboardData* billboardData = GetBillboardData(renderOrder, material, count); - for (unsigned int i = 0; i < count; ++i) - { - float sin = std::sin(ToRadians(*anglePtr)); - float cos = std::cos(ToRadians(*anglePtr)); - anglePtr++; - - billboardData->center = *positionPtr++; - billboardData->color = *colorPtr++; - billboardData->sinCos.Set(sin, cos); - billboardData->size.Set(*sizePtr++); - billboardData++; - } - } - - /*! - * \brief Adds multiple billboards to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the billboards - * \param count Number of billboards - * \param positionPtr Position of the billboards - * \param sizePtr Size of the billboards - * \param anglePtr Rotation of the billboards if null, 0.f is used - * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used - * - * \remark Produces a NazaraAssert if material is invalid - */ - - void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) - { - NazaraAssert(material, "Invalid material"); - - float defaultRotation = 0.f; - - if (!anglePtr) - anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile - - float defaultAlpha = 1.f; - - if (!alphaPtr) - alphaPtr.Reset(&defaultAlpha, 0); // Same - - BillboardData* billboardData = GetBillboardData(renderOrder, material, count); - for (unsigned int i = 0; i < count; ++i) - { - float sin = std::sin(ToRadians(*anglePtr)); - float cos = std::cos(ToRadians(*anglePtr)); - anglePtr++; - - billboardData->center = *positionPtr++; - billboardData->color = Color(255, 255, 255, static_cast(255.f * (*alphaPtr++))); - billboardData->sinCos.Set(sin, cos); - billboardData->size.Set(*sizePtr++); - billboardData++; - } - } - - /*! - * \brief Adds drawable to the queue - * - * \param renderOrder Order of rendering - * \param drawable Drawable user defined - * - * \remark Produces a NazaraError if drawable is invalid - */ - - void ForwardRenderQueue::AddDrawable(int renderOrder, const Drawable* drawable) - { - #if NAZARA_GRAPHICS_SAFE - if (!drawable) - { - NazaraError("Invalid drawable"); - return; - } - #endif - - auto& otherDrawables = GetLayer(renderOrder).otherDrawables; - - otherDrawables.push_back(drawable); - } - - /*! - * \brief Adds mesh to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the mesh - * \param meshData Data of the mesh - * \param meshAABB Box of the mesh - * \param transformMatrix Matrix of the mesh - * - * \remark Produces a NazaraAssert if material is invalid - */ - void ForwardRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) - { - NazaraAssert(material, "Invalid material"); - - if (material->IsDepthSortingEnabled()) - { - Layer& currentLayer = GetLayer(renderOrder); - auto& transparentMeshes = currentLayer.depthSortedMeshes; - auto& transparentData = currentLayer.depthSortedMeshData; - - // The material is marked for depth sorting, we must draw this mesh using another way (after the rendering of opaques objects while sorting them) - std::size_t index = transparentData.size(); - transparentData.resize(index+1); - - UnbatchedModelData& data = transparentData.back(); - data.material = material; - data.meshData = meshData; - data.obbSphere = Spheref(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius()); - data.transformMatrix = transformMatrix; - - transparentMeshes.push_back(index); - } - else - { - Layer& currentLayer = GetLayer(renderOrder); - MeshPipelineBatches& opaqueModels = currentLayer.opaqueModels; - - const MaterialPipeline* materialPipeline = material->GetPipeline(); - - auto pipelineIt = opaqueModels.find(materialPipeline); - if (pipelineIt == opaqueModels.end()) - { - BatchedMaterialEntry materialEntry; - pipelineIt = opaqueModels.insert(MeshPipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; - } - - BatchedMaterialEntry& materialEntry = pipelineIt->second; - MeshMaterialBatches& materialMap = materialEntry.materialMap; - - auto materialIt = materialMap.find(material); - if (materialIt == materialMap.end()) - { - BatchedModelEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - materialIt = materialMap.insert(MeshMaterialBatches::value_type(material, std::move(entry))).first; - } - - BatchedModelEntry& entry = materialIt->second; - entry.enabled = true; - - MeshInstanceContainer& meshMap = entry.meshMap; - - auto it2 = meshMap.find(meshData); - if (it2 == meshMap.end()) - { - MeshInstanceEntry instanceEntry; - instanceEntry.squaredBoundingSphere = meshAABB.GetSquaredBoundingSphere(); - - if (meshData.indexBuffer) - instanceEntry.indexBufferReleaseSlot.Connect(meshData.indexBuffer->OnIndexBufferRelease, this, &ForwardRenderQueue::OnIndexBufferInvalidation); - - instanceEntry.vertexBufferReleaseSlot.Connect(meshData.vertexBuffer->OnVertexBufferRelease, this, &ForwardRenderQueue::OnVertexBufferInvalidation); - - it2 = meshMap.insert(std::make_pair(meshData, std::move(instanceEntry))).first; - } - - std::vector& instances = it2->second.instances; - instances.push_back(transformMatrix); - - materialEntry.maxInstanceCount = std::max(materialEntry.maxInstanceCount, instances.size()); - } - } - - /*! - * \brief Adds sprites to the queue - * - * \param renderOrder Order of rendering - * \param material Material of the sprites - * \param vertices Buffer of data for the sprites - * \param spriteCount Number of sprites - * \param overlay Texture of the sprites - * - * \remark Produces a NazaraAssert if material is invalid - */ - void ForwardRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay) - { - NazaraAssert(material, "Invalid material"); - - Layer& currentLayer = GetLayer(renderOrder); - - if (material->IsDepthSortingEnabled()) - { - auto& transparentSprites = currentLayer.depthSortedSprites; - auto& transparentData = currentLayer.depthSortedSpriteData; - - // The material is marked for depth sorting, we must draw this mesh using another way (after the rendering of opaques objects while sorting them) - std::size_t index = transparentData.size(); - transparentData.resize(index + 1); - - UnbatchedSpriteData& data = transparentData.back(); - data.material = material; - data.overlay = overlay; - data.spriteCount = spriteCount; - data.vertices = vertices; - - transparentSprites.push_back(index); - } - else - { - SpritePipelineBatches& sprites = currentLayer.opaqueSprites; - - const MaterialPipeline* materialPipeline = material->GetPipeline(); - - auto pipelineIt = sprites.find(materialPipeline); - if (pipelineIt == sprites.end()) - { - BatchedSpritePipelineEntry materialEntry; - pipelineIt = sprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; - } - - BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second; - pipelineEntry.enabled = true; - - SpriteMaterialBatches& materialMap = pipelineEntry.materialMap; - - auto matIt = materialMap.find(material); - if (matIt == materialMap.end()) - { - BatchedBasicSpriteEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - matIt = materialMap.insert(SpriteMaterialBatches::value_type(material, std::move(entry))).first; - } - - BatchedBasicSpriteEntry& entry = matIt->second; - entry.enabled = true; - - auto& overlayMap = entry.overlayMap; - - auto overlayIt = overlayMap.find(overlay); - if (overlayIt == overlayMap.end()) - { - BatchedSpriteEntry overlayEntry; - if (overlay) - overlayEntry.textureReleaseSlot.Connect(overlay->OnTextureRelease, this, &ForwardRenderQueue::OnTextureInvalidation); - - overlayIt = overlayMap.insert(std::make_pair(overlay, std::move(overlayEntry))).first; - } - - auto& spriteVector = overlayIt->second.spriteChains; - spriteVector.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount})); - } - } - - /*! - * \brief Clears the queue - * - * \param fully Should everything be cleared or we can keep layers - */ - - void ForwardRenderQueue::Clear(bool fully) - { - AbstractRenderQueue::Clear(fully); - - if (fully) - layers.clear(); - else - { - for (auto it = layers.begin(); it != layers.end();) - { - Layer& layer = it->second; - if (layer.clearCount++ >= 100) - layers.erase(it++); - else - { - for (auto& pipelinePair : layer.billboards) - { - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.enabled) - { - for (auto& matIt : pipelinePair.second.materialMap) - { - auto& entry = matIt.second; - auto& billboardVector = entry.billboards; - - billboardVector.clear(); - } - } - - pipelineEntry.enabled = false; - } - - for (auto& pipelinePair : layer.opaqueSprites) - { - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.enabled) - { - for (auto& materialPair : pipelineEntry.materialMap) - { - auto& matEntry = materialPair.second; - - if (matEntry.enabled) - { - auto& overlayMap = matEntry.overlayMap; - for (auto& overlayIt : overlayMap) - { - auto& spriteChainVector = overlayIt.second.spriteChains; - spriteChainVector.clear(); - } - - matEntry.enabled = false; - } - } - pipelineEntry.enabled = false; - } - } - - for (auto& pipelinePair : layer.opaqueModels) - { - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.maxInstanceCount > 0) - { - for (auto& materialPair : pipelineEntry.materialMap) - { - auto& matEntry = materialPair.second; - if (matEntry.enabled) - { - MeshInstanceContainer& meshInstances = matEntry.meshMap; - - for (auto& meshIt : meshInstances) - { - auto& meshEntry = meshIt.second; - meshEntry.instances.clear(); - } - matEntry.enabled = false; - } - } - pipelineEntry.maxInstanceCount = 0; - } - } - - layer.depthSortedMeshes.clear(); - layer.depthSortedMeshData.clear(); - layer.depthSortedSpriteData.clear(); - layer.depthSortedSprites.clear(); - layer.otherDrawables.clear(); - ++it; - } - } - } - } - - /*! - * \brief Sorts the object according to the viewer position, furthest to nearest - * - * \param viewer Viewer of the scene - */ - - void ForwardRenderQueue::Sort(const AbstractViewer* viewer) - { - if (viewer->GetProjectionType() == ProjectionType_Orthogonal) - SortForOrthographic(viewer); - else - SortForPerspective(viewer); - } - - /*! - * \brief Gets the billboard data - * \return Pointer to the data of the billboards - * - * \param renderOrder Order of rendering - * \param material Material of the billboard - */ - - ForwardRenderQueue::BillboardData* ForwardRenderQueue::GetBillboardData(int renderOrder, const Material* material, unsigned int count) - { - auto& billboards = GetLayer(renderOrder).billboards; - - const MaterialPipeline* materialPipeline = material->GetPipeline(); - - auto pipelineIt = billboards.find(materialPipeline); - if (pipelineIt == billboards.end()) - { - BatchedBillboardPipelineEntry pipelineEntry; - pipelineIt = billboards.insert(BillboardPipelineBatches::value_type(materialPipeline, std::move(pipelineEntry))).first; - } - BatchedBillboardPipelineEntry& pipelineEntry = pipelineIt->second; - pipelineEntry.enabled = true; - - BatchedBillboardContainer& materialMap = pipelineEntry.materialMap; - - auto it = materialMap.find(material); - if (it == materialMap.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = materialMap.insert(BatchedBillboardContainer::value_type(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - std::size_t prevSize = billboardVector.size(); - billboardVector.resize(prevSize + count); - - return &billboardVector[prevSize]; - } - - /*! - * \brief Gets the ith layer - * \return Reference to the ith layer for the queue - * - * \param i Index of the layer - */ - - ForwardRenderQueue::Layer& ForwardRenderQueue::GetLayer(int i) - { - auto it = layers.find(i); - if (it == layers.end()) - it = layers.insert(std::make_pair(i, Layer())).first; - - Layer& layer = it->second; - layer.clearCount = 0; - - return layer; - } - - void ForwardRenderQueue::SortBillboards(Layer& layer, const Planef& nearPlane) - { - for (auto& pipelinePair : layer.billboards) - { - for (auto& matPair : pipelinePair.second.materialMap) - { - const Material* mat = matPair.first; - - if (mat->IsDepthSortingEnabled()) - { - BatchedBillboardEntry& entry = matPair.second; - auto& billboardVector = entry.billboards; - - std::sort(billboardVector.begin(), billboardVector.end(), [&nearPlane] (const BillboardData& data1, const BillboardData& data2) - { - return nearPlane.Distance(data1.center) > nearPlane.Distance(data2.center); - }); - } - } - } - } - - void ForwardRenderQueue::SortForOrthographic(const AbstractViewer * viewer) - { - Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near); - - for (auto& pair : layers) - { - Layer& layer = pair.second; - - std::sort(layer.depthSortedMeshes.begin(), layer.depthSortedMeshes.end(), [&layer, &nearPlane] (std::size_t index1, std::size_t index2) - { - const Spheref& sphere1 = layer.depthSortedMeshData[index1].obbSphere; - const Spheref& sphere2 = layer.depthSortedMeshData[index2].obbSphere; - - return nearPlane.Distance(sphere1.GetPosition()) < nearPlane.Distance(sphere2.GetPosition()); - }); - - std::sort(layer.depthSortedSprites.begin(), layer.depthSortedSprites.end(), [&layer, &nearPlane] (std::size_t index1, std::size_t index2) - { - const Vector3f& pos1 = layer.depthSortedSpriteData[index1].vertices[0].position; - const Vector3f& pos2 = layer.depthSortedSpriteData[index2].vertices[0].position; - - return nearPlane.Distance(pos1) < nearPlane.Distance(pos2); - }); - - SortBillboards(layer, nearPlane); - } - } - - void ForwardRenderQueue::SortForPerspective(const AbstractViewer* viewer) - { - Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near); - Vector3f viewerPos = viewer->GetEyePosition(); - - for (auto& pair : layers) - { - Layer& layer = pair.second; - - std::sort(layer.depthSortedMeshes.begin(), layer.depthSortedMeshes.end(), [&layer, &viewerPos] (std::size_t index1, std::size_t index2) - { - const Spheref& sphere1 = layer.depthSortedMeshData[index1].obbSphere; - const Spheref& sphere2 = layer.depthSortedMeshData[index2].obbSphere; - - return viewerPos.SquaredDistance(sphere1.GetPosition()) > viewerPos.SquaredDistance(sphere2.GetPosition()); - }); - - std::sort(layer.depthSortedSprites.begin(), layer.depthSortedSprites.end(), [&layer, &viewerPos] (std::size_t index1, std::size_t index2) - { - const Vector3f& pos1 = layer.depthSortedSpriteData[index1].vertices[0].position; - const Vector3f& pos2 = layer.depthSortedSpriteData[index2].vertices[0].position; - - return viewerPos.SquaredDistance(pos1) > viewerPos.SquaredDistance(pos2); - }); - - SortBillboards(layer, nearPlane); - } - } - - /*! - * \brief Handle the invalidation of an index buffer - * - * \param indexBuffer Index buffer being invalidated - */ - void ForwardRenderQueue::OnIndexBufferInvalidation(const IndexBuffer* indexBuffer) - { - for (auto& pair : layers) - { - Layer& layer = pair.second; - - for (auto& pipelineEntry : layer.opaqueModels) - { - for (auto& materialEntry : pipelineEntry.second.materialMap) - { - MeshInstanceContainer& meshes = materialEntry.second.meshMap; - for (auto it = meshes.begin(); it != meshes.end();) - { - const MeshData& renderData = it->first; - if (renderData.indexBuffer == indexBuffer) - it = meshes.erase(it); - else - ++it; - } - } - } - } - } - - /*! - * \brief Handle the invalidation of a material - * - * \param material Material being invalidated - */ - - void ForwardRenderQueue::OnMaterialInvalidation(const Material* material) - { - for (auto& pair : layers) - { - Layer& layer = pair.second; - - for (auto& pipelineEntry : layer.opaqueSprites) - pipelineEntry.second.materialMap.erase(material); - - for (auto& pipelineEntry : layer.billboards) - pipelineEntry.second.materialMap.erase(material); - - for (auto& pipelineEntry : layer.opaqueModels) - pipelineEntry.second.materialMap.erase(material); - } - } - - /*! - * \brief Handle the invalidation of a texture - * - * \param texture Texture being invalidated - */ - - void ForwardRenderQueue::OnTextureInvalidation(const Texture* texture) - { - for (auto& pair : layers) - { - Layer& layer = pair.second; - for (auto& pipelineEntry : layer.opaqueSprites) - { - for (auto& materialEntry : pipelineEntry.second.materialMap) - materialEntry.second.overlayMap.erase(texture); - } - } - } - - /*! - * \brief Handle the invalidation of a vertex buffer - * - * \param vertexBuffer Vertex buffer being invalidated - */ - - void ForwardRenderQueue::OnVertexBufferInvalidation(const VertexBuffer* vertexBuffer) - { - for (auto& pair : layers) - { - Layer& layer = pair.second; - for (auto& pipelineEntry : layer.opaqueModels) - { - for (auto& materialEntry : pipelineEntry.second.materialMap) - { - MeshInstanceContainer& meshes = materialEntry.second.meshMap; - for (auto it = meshes.begin(); it != meshes.end();) - { - const MeshData& renderData = it->first; - if (renderData.vertexBuffer == vertexBuffer) - it = meshes.erase(it); - else - ++it; - } - } - } - } - } - - bool ForwardRenderQueue::MaterialComparator::operator()(const Material* mat1, const Material* mat2) const - { - const Texture* diffuseMap1 = mat1->GetDiffuseMap(); - const Texture* diffuseMap2 = mat2->GetDiffuseMap(); - if (diffuseMap1 != diffuseMap2) - return diffuseMap1 < diffuseMap2; - - return mat1 < mat2; - } - - bool ForwardRenderQueue::MaterialPipelineComparator::operator()(const MaterialPipeline* pipeline1, const MaterialPipeline* pipeline2) const - { - const Shader* shader1 = pipeline1->GetInstance().renderPipeline.GetInfo().shader; - const Shader* shader2 = pipeline2->GetInstance().renderPipeline.GetInfo().shader; - if (shader1 != shader2) - return shader1 < shader2; - - return pipeline1 < pipeline2; - } - - /*! - * \brief Functor to compare two mesh data - * \return true If first mesh is "smaller" than the second one - * - * \param data1 First mesh to compare - * \param data2 Second mesh to compare - */ - - bool ForwardRenderQueue::MeshDataComparator::operator()(const MeshData& data1, const MeshData& data2) const - { - const Buffer* buffer1; - const Buffer* buffer2; - - buffer1 = (data1.indexBuffer) ? data1.indexBuffer->GetBuffer() : nullptr; - buffer2 = (data2.indexBuffer) ? data2.indexBuffer->GetBuffer() : nullptr; - if (buffer1 != buffer2) - return buffer1 < buffer2; - - buffer1 = data1.vertexBuffer->GetBuffer(); - buffer2 = data2.vertexBuffer->GetBuffer(); - if (buffer1 != buffer2) - return buffer1 < buffer2; - - return data1.primitiveMode < data2.primitiveMode; - } -} diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index 99452eaa1..904d87dfd 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -92,35 +93,33 @@ namespace Nz m_renderQueue.Sort(sceneData.viewer); - for (auto& pair : m_renderQueue.layers) - { - ForwardRenderQueue::Layer& layer = pair.second; + if (!m_renderQueue.models.empty()) + DrawModels(sceneData, m_renderQueue, m_renderQueue.models); - if (!layer.opaqueModels.empty()) - DrawOpaqueModels(sceneData, layer); + if (!m_renderQueue.basicSprites.empty()) + DrawSprites(sceneData, m_renderQueue, m_renderQueue.basicSprites); - if (!layer.depthSortedMeshes.empty()) - DrawTransparentModels(sceneData, layer); + if (!m_renderQueue.billboards.empty()) + DrawBillboards(sceneData, m_renderQueue, m_renderQueue.billboards); - if (!layer.opaqueSprites.empty()) - DrawBasicSprites(sceneData, layer); + if (!m_renderQueue.depthSortedModels.empty()) + DrawModels(sceneData, m_renderQueue, m_renderQueue.depthSortedModels); - if (!layer.depthSortedSprites.empty()) - DrawOrderedSprites(sceneData, layer); + if (!m_renderQueue.depthSortedSprites.empty()) + DrawSprites(sceneData, m_renderQueue, m_renderQueue.depthSortedSprites); - if (!layer.billboards.empty()) - DrawBillboards(sceneData, layer); + if (!m_renderQueue.depthSortedBillboards.empty()) + DrawBillboards(sceneData, m_renderQueue, m_renderQueue.depthSortedBillboards); - for (const Drawable* drawable : layer.otherDrawables) - drawable->Draw(); - } + if (!m_renderQueue.customDrawables.empty()) + DrawCustomDrawables(sceneData, m_renderQueue, m_renderQueue.customDrawables); return true; } /*! * \brief Gets the maximum number of lights available per pass per object - * \return Maximum number of light simulatenously per object + * \return Maximum number of light simultaneously per object */ unsigned int ForwardRenderTechnique::GetMaxLightPassPerObject() const @@ -211,9 +210,9 @@ namespace Nz // Declaration used when rendering the billboards with intancing // The main advantage is the direct copy (std::memcpy) of data in the RenderQueue to the GPU buffer - s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(ForwardRenderQueue::BillboardData, center)); - s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(ForwardRenderQueue::BillboardData, size)); // Englobe sincos - s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(ForwardRenderQueue::BillboardData, color)); + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(BasicRenderQueue::BillboardData, center)); + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(BasicRenderQueue::BillboardData, size)); // Englobe sincos + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(BasicRenderQueue::BillboardData, color)); s_reflectionSampler.SetFilterMode(SamplerFilter_Bilinear); s_reflectionSampler.SetWrapMode(SamplerWrap_Clamp); @@ -290,41 +289,225 @@ namespace Nz }); } - /*! - * \brief Draws basic sprites - * - * \param sceneData Data of the scene - * \param layer Layer of the rendering - * - * \remark Produces a NazaraAssert is viewer is invalid - */ - - void ForwardRenderTechnique::DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const + void ForwardRenderTechnique::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const { - NazaraAssert(sceneData.viewer, "Invalid viewer"); + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); + Renderer::SetVertexBuffer(&s_quadVertexBuffer); + + Nz::BufferMapper instanceBufferMapper; + std::size_t billboardCount = 0; + std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + + auto Commit = [&]() + { + if (billboardCount > 0) + { + instanceBufferMapper.Unmap(); + + Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4); + + billboardCount = 0; + } + }; + + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; const Shader* lastShader = nullptr; const ShaderUniforms* shaderUniforms = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); - Renderer::SetIndexBuffer(&s_quadIndexBuffer); - Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); - Renderer::SetVertexBuffer(&m_spriteBuffer); + const MaterialPipeline::Instance* pipelineInstance = nullptr; - const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); - - for (auto& pipelinePair : layer.opaqueSprites) + for (const BasicRenderQueue::Billboard& billboard : billboards) { - const MaterialPipeline* pipeline = pipelinePair.first; - auto& pipelineEntry = pipelinePair.second; + const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect; - if (pipelineEntry.enabled) + if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) { - const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + Commit(); - const Shader* shader = pipelineInstance.uberInstance->GetShader(); + const MaterialPipeline* pipeline = billboard.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor); - // Uniforms are conserved in our program, there's no point to send them back until they change + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + if (lastMaterial != billboard.material) + { + billboard.material->Apply(*pipelineInstance); + lastMaterial = billboard.material; + } + + if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } + } + + if (!instanceBufferMapper.GetBuffer()) + instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite); + + std::memcpy(static_cast(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, &billboard.data, sizeof(BasicRenderQueue::BillboardData)); + if (++billboardCount >= maxBillboardPerDraw) + Commit(); + } + + Commit(); + } + + void ForwardRenderTechnique::DrawBillboards(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& billboards) const + { + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); + + Renderer::SetVertexBuffer(&s_quadVertexBuffer); + + Nz::BufferMapper instanceBufferMapper; + std::size_t billboardCount = 0; + std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + + auto Commit = [&]() + { + if (billboardCount > 0) + { + instanceBufferMapper.Unmap(); + + Renderer::DrawPrimitivesInstanced(billboardCount, PrimitiveMode_TriangleStrip, 0, 4); + + billboardCount = 0; + } + }; + + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + for (const BasicRenderQueue::BillboardChain& billboard : billboards) + { + const Nz::Recti& scissorRect = (billboard.scissorRect.width > 0) ? billboard.scissorRect : fullscreenScissorRect; + + if (billboard.material != lastMaterial || (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + Commit(); + + const MaterialPipeline* pipeline = billboard.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &billboard.material->GetPipeline()->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + if (lastMaterial != billboard.material) + { + billboard.material->Apply(*pipelineInstance); + lastMaterial = billboard.material; + } + + if (billboard.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } + } + + std::size_t billboardRemaining = billboard.billboardCount; + const BasicRenderQueue::BillboardData* billboardData = renderQueue.GetBillboardData(billboard.billboardIndex); + do + { + std::size_t renderedBillboardCount = std::min(billboardRemaining, maxBillboardPerDraw - billboardCount); + billboardRemaining -= renderedBillboardCount; + + if (!instanceBufferMapper.GetBuffer()) + instanceBufferMapper.Map(instanceBuffer, BufferAccess_DiscardAndWrite); + + std::memcpy(static_cast(instanceBufferMapper.GetPointer()) + sizeof(BasicRenderQueue::BillboardData) * billboardCount, billboardData, renderedBillboardCount * sizeof(BasicRenderQueue::BillboardData)); + billboardCount += renderedBillboardCount; + billboardData += renderedBillboardCount; + + if (billboardCount >= maxBillboardPerDraw) + Commit(); + } + while (billboardRemaining > 0); + } + + Commit(); + } + + void ForwardRenderTechnique::DrawCustomDrawables(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& customDrawables) const + { + for (const BasicRenderQueue::CustomDrawable& customDrawable : customDrawables) + customDrawable.drawable->Draw(); + } + + void ForwardRenderTechnique::DrawModels(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const Nz::RenderQueue& models) const + { + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + ///TODO: Reimplement instancing + + for (const BasicRenderQueue::Model& model : models) + { + const MaterialPipeline* pipeline = model.material->GetPipeline(); + if (lastPipeline != pipeline) + { + pipelineInstance = &model.material->GetPipeline()->Apply(); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); if (shader != lastShader) { // Index of uniforms in the shader @@ -335,560 +518,196 @@ namespace Nz // Position of the camera shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - // Overlay texture unit - shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); - lastShader = shader; } - for (auto& materialPair : pipelineEntry.materialMap) + lastPipeline = pipeline; + } + + if (lastMaterial != model.material) + { + model.material->Apply(*pipelineInstance); + lastMaterial = model.material; + } + + if (model.material->IsScissorTestEnabled()) + { + const Nz::Recti& scissorRect = (model.scissorRect.width > 0) ? model.scissorRect : fullscreenScissorRect; + if (scissorRect != lastScissorRect) { - const Material* material = materialPair.first; - auto& matEntry = materialPair.second; - - if (matEntry.enabled) - { - material->Apply(pipelineInstance); - - Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler()); - - auto& overlayMap = matEntry.overlayMap; - for (auto& overlayIt : overlayMap) - { - const Texture* overlay = overlayIt.first; - auto& spriteChainVector = overlayIt.second.spriteChains; - - std::size_t spriteChainCount = spriteChainVector.size(); - if (spriteChainCount > 0) - { - Renderer::SetTexture(overlayTextureUnit, (overlay) ? overlay : &m_whiteTexture); - - std::size_t spriteChain = 0; // Which chain of sprites are we treating - std::size_t spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain - - do - { - // We open the buffer in writing mode - BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); - - std::size_t spriteCount = 0; - - do - { - ForwardRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain]; - std::size_t count = std::min(maxSpriteCount - spriteCount, currentChain.spriteCount - spriteChainOffset); - - std::memcpy(vertices, currentChain.vertices + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); - vertices += count * 4; - - spriteCount += count; - spriteChainOffset += count; - - // Have we treated the entire chain ? - if (spriteChainOffset == currentChain.spriteCount) - { - spriteChain++; - spriteChainOffset = 0; - } - } while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); - - vertexMapper.Unmap(); - - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6); - } - while (spriteChain < spriteChainCount); - } - } - } + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; } } + + if (shaderUniforms->reflectionMap != -1) + { + unsigned int textureUnit = Material::GetTextureUnit(TextureMap_ReflectionCube); + + Renderer::SetTexture(textureUnit, sceneData.globalReflectionTexture); + Renderer::SetTextureSampler(textureUnit, s_reflectionSampler); + } + + // Handle draw call before rendering loop + Renderer::DrawCall drawFunc; + Renderer::DrawCallInstanced instancedDrawFunc; + unsigned int indexCount; + + if (model.meshData.indexBuffer) + { + drawFunc = Renderer::DrawIndexedPrimitives; + instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; + indexCount = model.meshData.indexBuffer->GetIndexCount(); + } + else + { + drawFunc = Renderer::DrawPrimitives; + instancedDrawFunc = Renderer::DrawPrimitivesInstanced; + indexCount = model.meshData.vertexBuffer->GetVertexCount(); + } + + Renderer::SetIndexBuffer(model.meshData.indexBuffer); + Renderer::SetVertexBuffer(model.meshData.vertexBuffer); + + if (shaderUniforms->hasLightUniforms) + { + ChooseLights(model.obbSphere); + + std::size_t lightCount = m_lights.size(); + + Nz::Renderer::SetMatrix(Nz::MatrixType_World, model.matrix); + std::size_t lightIndex = 0; + RendererComparison oldDepthFunc = Renderer::GetDepthFunc(); // In the case where we have to change it + + std::size_t passCount = (lightCount == 0) ? 1 : (lightCount - 1) / NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; + for (std::size_t pass = 0; pass < passCount; ++pass) + { + lightCount -= std::min(lightCount, NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS); + + if (pass == 1) + { + // To add the result of light computations + // We won't interfere with materials parameters because we only render opaques objects + // (A.K.A., without blending) + // About the depth function, it must be applied only the first time + Renderer::Enable(RendererParameter_Blend, true); + Renderer::SetBlendFunc(BlendFunc_One, BlendFunc_One); + Renderer::SetDepthFunc(RendererComparison_Equal); + } + + // Sends the light uniforms to the shader + for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) + SendLightUniforms(lastShader, shaderUniforms->lightUniforms, i, lightIndex++, shaderUniforms->lightOffset*i); + + // And we draw + drawFunc(model.meshData.primitiveMode, 0, indexCount); + } + + Renderer::Enable(RendererParameter_Blend, false); + Renderer::SetDepthFunc(oldDepthFunc); + } + else + { + Renderer::SetMatrix(MatrixType_World, model.matrix); + drawFunc(model.meshData.primitiveMode, 0, indexCount); + } } } - /*! - * \brief Draws billboards - * - * \param sceneData Data of the scene - * \param layer Layer of the rendering - * - * \remark Produces a NazaraAssert is viewer is invalid - */ - - void ForwardRenderTechnique::DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const + void ForwardRenderTechnique::DrawSprites(const SceneData& sceneData, const BasicRenderQueue& renderQueue, const RenderQueue& spriteList) const { - NazaraAssert(sceneData.viewer, "Invalid viewer"); - - const Shader* lastShader = nullptr; - const ShaderUniforms* shaderUniforms = nullptr; - - if (m_instancingEnabled && Renderer::HasCapability(RendererCap_Instancing)) - { - VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); - instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); - - Renderer::SetVertexBuffer(&s_quadVertexBuffer); - - for (auto& pipelinePair : layer.billboards) - { - const MaterialPipeline* pipeline = pipelinePair.first; - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.enabled) - { - const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor); - - const Shader* shader = pipelineInstance.uberInstance->GetShader(); - - // Uniforms are conserved in our program, there's no point to send them back until they change - if (shader != lastShader) - { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); - - // Ambiant color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position of the camera - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - - lastShader = shader; - } - - for (auto& matIt : pipelinePair.second.materialMap) - { - const Material* material = matIt.first; - auto& entry = matIt.second; - auto& billboardVector = entry.billboards; - - std::size_t billboardCount = billboardVector.size(); - if (billboardCount > 0) - { - // We begin to apply the material (and get the shader activated doing so) - material->Apply(pipelineInstance); - - const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; - std::size_t maxBillboardPerDraw = instanceBuffer->GetVertexCount(); - do - { - std::size_t renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); - billboardCount -= renderedBillboardCount; - - instanceBuffer->Fill(data, 0, renderedBillboardCount); - data += renderedBillboardCount; - - Renderer::DrawPrimitivesInstanced(renderedBillboardCount, PrimitiveMode_TriangleStrip, 0, 4); - } - while (billboardCount > 0); - - billboardVector.clear(); - } - } - } - } - } - else - { - Renderer::SetIndexBuffer(&s_quadIndexBuffer); - Renderer::SetVertexBuffer(&m_billboardPointBuffer); - - for (auto& pipelinePair : layer.billboards) - { - const MaterialPipeline* pipeline = pipelinePair.first; - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.enabled) - { - const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_VertexColor); - - const Shader* shader = pipelineInstance.uberInstance->GetShader(); - - // Uniforms are conserved in our program, there's no point to send them back until they change - if (shader != lastShader) - { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); - - // Ambiant color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position of the camera - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - - lastShader = shader; - } - - for (auto& matIt : pipelinePair.second.materialMap) - { - auto& entry = matIt.second; - auto& billboardVector = entry.billboards; - - const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; - std::size_t maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount() / 4); - - std::size_t billboardCount = billboardVector.size(); - do - { - std::size_t renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); - billboardCount -= renderedBillboardCount; - - BufferMapper vertexMapper(m_billboardPointBuffer, BufferAccess_DiscardAndWrite, 0, renderedBillboardCount * 4); - BillboardPoint* vertices = static_cast(vertexMapper.GetPointer()); - - for (unsigned int i = 0; i < renderedBillboardCount; ++i) - { - const ForwardRenderQueue::BillboardData& billboard = *data++; - - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(0.f, 1.f); - vertices++; - - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(1.f, 1.f); - vertices++; - - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(0.f, 0.f); - vertices++; - - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(1.f, 0.f); - vertices++; - } - - vertexMapper.Unmap(); - - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, renderedBillboardCount * 6); - } - while (billboardCount > 0); - } - } - } - } - } - - /*! - * \brief Draws opaques models - * - * \param sceneData Data of the scene - * \param layer Layer of the rendering - * - * \remark Produces a NazaraAssert is viewer is invalid - */ - - void ForwardRenderTechnique::DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const - { - NazaraAssert(sceneData.viewer, "Invalid viewer"); - - const Shader* lastShader = nullptr; - const ShaderUniforms* shaderUniforms = nullptr; - - Texture* reflectionMap = sceneData.globalReflectionTexture; - if (!reflectionMap) - reflectionMap = &s_dummyReflection; - - for (auto& pipelinePair : layer.opaqueModels) - { - const MaterialPipeline* pipeline = pipelinePair.first; - auto& pipelineEntry = pipelinePair.second; - - if (pipelineEntry.maxInstanceCount > 0) - { - bool instancing = m_instancingEnabled && (pipelineEntry.maxInstanceCount > NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT); - const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply((instancing) ? ShaderFlags_Instancing : 0); - - const Shader* shader = pipelineInstance.uberInstance->GetShader(); - - // Uniforms are conserved in our program, there's no point to send them back until they change - if (shader != lastShader) - { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); - - // Ambiant color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position of the camera - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - - lastShader = shader; - } - - for (auto& materialPair : pipelineEntry.materialMap) - { - const Material* material = materialPair.first; - auto& matEntry = materialPair.second; - - if (matEntry.enabled) - { - material->Apply(pipelineInstance); - - if (shaderUniforms->reflectionMap != -1) - { - unsigned int textureUnit = Material::GetTextureUnit(TextureMap_ReflectionCube); - - Renderer::SetTexture(textureUnit, reflectionMap); - Renderer::SetTextureSampler(textureUnit, s_reflectionSampler); - } - - ForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; - - // Meshes - for (auto& meshIt : meshInstances) - { - const MeshData& meshData = meshIt.first; - auto& meshEntry = meshIt.second; - - const Spheref& squaredBoundingSphere = meshEntry.squaredBoundingSphere; - std::vector& instances = meshEntry.instances; - - if (!instances.empty()) - { - const IndexBuffer* indexBuffer = meshData.indexBuffer; - const VertexBuffer* vertexBuffer = meshData.vertexBuffer; - - // Handle draw call before rendering loop - Renderer::DrawCall drawFunc; - Renderer::DrawCallInstanced instancedDrawFunc; - unsigned int indexCount; - - if (indexBuffer) - { - drawFunc = Renderer::DrawIndexedPrimitives; - instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; - indexCount = indexBuffer->GetIndexCount(); - } - else - { - drawFunc = Renderer::DrawPrimitives; - instancedDrawFunc = Renderer::DrawPrimitivesInstanced; - indexCount = vertexBuffer->GetVertexCount(); - } - - Renderer::SetIndexBuffer(indexBuffer); - Renderer::SetVertexBuffer(vertexBuffer); - - if (instancing) - { - // We compute the number of instances that we will be able to draw this time (depending on the instancing buffer size) - VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); - instanceBuffer->SetVertexDeclaration(VertexDeclaration::Get(VertexLayout_Matrix4)); - - // With instancing, impossible to select the lights for each object - // So, it's only activated for directional lights - std::size_t lightCount = m_renderQueue.directionalLights.size(); - std::size_t lightIndex = 0; - RendererComparison oldDepthFunc = Renderer::GetDepthFunc(); - - std::size_t passCount = (lightCount == 0) ? 1 : (lightCount - 1) / NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; - for (std::size_t pass = 0; pass < passCount; ++pass) - { - if (shaderUniforms->hasLightUniforms) - { - std::size_t renderedLightCount = std::min(lightCount, NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS); - lightCount -= renderedLightCount; - - if (pass == 1) - { - // To add the result of light computations - // We won't interfeer with materials parameters because we only render opaques objects - // (A.K.A., without blending) - // About the depth function, it must be applied only the first time - Renderer::Enable(RendererParameter_Blend, true); - Renderer::SetBlendFunc(BlendFunc_One, BlendFunc_One); - Renderer::SetDepthFunc(RendererComparison_Equal); - } - - // Sends the uniforms - for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) - SendLightUniforms(shader, shaderUniforms->lightUniforms, i, lightIndex++, shaderUniforms->lightOffset * i); - } - - const Matrix4f* instanceMatrices = &instances[0]; - std::size_t instanceCount = instances.size(); - std::size_t maxInstanceCount = instanceBuffer->GetVertexCount(); // Maximum number of instance in one batch - - while (instanceCount > 0) - { - // We compute the number of instances that we will be able to draw this time (depending on the instancing buffer size) - std::size_t renderedInstanceCount = std::min(instanceCount, maxInstanceCount); - instanceCount -= renderedInstanceCount; - - // We fill the instancing buffer with our world matrices - instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount); - instanceMatrices += renderedInstanceCount; - - // And we draw - instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); - } - } - - // We don't forget to disable the blending to avoid to interferering with the rest of the rendering - Renderer::Enable(RendererParameter_Blend, false); - Renderer::SetDepthFunc(oldDepthFunc); - } - else - { - if (shaderUniforms->hasLightUniforms) - { - for (const Matrix4f& matrix : instances) - { - // Choose the lights depending on an object position and apparent radius - ChooseLights(Spheref(matrix.GetTranslation() + squaredBoundingSphere.GetPosition(), squaredBoundingSphere.radius)); - - std::size_t lightCount = m_lights.size(); - - Renderer::SetMatrix(MatrixType_World, matrix); - std::size_t lightIndex = 0; - RendererComparison oldDepthFunc = Renderer::GetDepthFunc(); // In the case where we have to change it - - std::size_t passCount = (lightCount == 0) ? 1 : (lightCount - 1) / NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; - for (std::size_t pass = 0; pass < passCount; ++pass) - { - lightCount -= std::min(lightCount, NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS); - - if (pass == 1) - { - // To add the result of light computations - // We won't interfeer with materials parameters because we only render opaques objects - // (A.K.A., without blending) - // About the depth function, it must be applied only the first time - Renderer::Enable(RendererParameter_Blend, true); - Renderer::SetBlendFunc(BlendFunc_One, BlendFunc_One); - Renderer::SetDepthFunc(RendererComparison_Equal); - } - - // Sends the light uniforms to the shader - for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) - SendLightUniforms(shader, shaderUniforms->lightUniforms, i, lightIndex++, shaderUniforms->lightOffset*i); - - // And we draw - drawFunc(meshData.primitiveMode, 0, indexCount); - } - - Renderer::Enable(RendererParameter_Blend, false); - Renderer::SetDepthFunc(oldDepthFunc); - } - } - else - { - // Without instancing, we must do a draw call for each instance - // This may be faster than instancing under a certain number - // Due to the time to modify the instancing buffer - for (const Matrix4f& matrix : instances) - { - Renderer::SetMatrix(MatrixType_World, matrix); - drawFunc(meshData.primitiveMode, 0, indexCount); - } - } - } - } - } - } - } - } - } - } - - void ForwardRenderTechnique::DrawOrderedSprites(const SceneData & sceneData, ForwardRenderQueue::Layer & layer) const - { - NazaraAssert(sceneData.viewer, "Invalid viewer"); + const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); + Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); Renderer::SetIndexBuffer(&s_quadIndexBuffer); Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetVertexBuffer(&m_spriteBuffer); + const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); + const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); + + m_spriteChains.clear(); + + auto Commit = [&]() + { + std::size_t spriteChainCount = m_spriteChains.size(); + if (spriteChainCount > 0) + { + std::size_t spriteChain = 0; // Which chain of sprites are we treating + std::size_t spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain + + do + { + // We open the buffer in writing mode + BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); + VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); + + std::size_t spriteCount = 0; + + do + { + const VertexStruct_XYZ_Color_UV* currentChain = m_spriteChains[spriteChain].first; + std::size_t currentChainSpriteCount = m_spriteChains[spriteChain].second; + std::size_t count = std::min(maxSpriteCount - spriteCount, currentChainSpriteCount - spriteChainOffset); + + std::memcpy(vertices, currentChain + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += count * 4; + + spriteCount += count; + spriteChainOffset += count; + + // Have we treated the entire chain ? + if (spriteChainOffset == currentChainSpriteCount) + { + spriteChain++; + spriteChainOffset = 0; + } + } + while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); + + vertexMapper.Unmap(); + + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6); + } + while (spriteChain < spriteChainCount); + } + + m_spriteChains.clear(); + }; + const Material* lastMaterial = nullptr; const MaterialPipeline* lastPipeline = nullptr; const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + const MaterialPipeline::Instance* pipelineInstance = nullptr; - const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - - bool updateVertexBuffer = true; - const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); - - std::size_t alreadyDrawnCount = 0; - std::size_t spriteIndex = 0; - std::size_t spriteChainOffset = 0; - auto splitChainIt = layer.depthSortedSprites.end(); - - for (auto it = layer.depthSortedSprites.begin(); it != layer.depthSortedSprites.end();) + for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) { - if (updateVertexBuffer) + const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; + + if (basicSprites.material != lastMaterial || basicSprites.overlay != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) { - // We open the buffer in writing mode - BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); + Commit(); - std::size_t availableSpriteSpace = maxSpriteCount; - bool split = false; - for (auto it2 = it; it2 != layer.depthSortedSprites.end(); ++it2) + const MaterialPipeline* pipeline = basicSprites.material->GetPipeline(); + if (lastPipeline != pipeline) { - const ForwardRenderQueue::UnbatchedSpriteData& spriteData = layer.depthSortedSpriteData[*it2]; - - std::size_t count = std::min(availableSpriteSpace, spriteData.spriteCount - spriteChainOffset); - - std::memcpy(vertices, spriteData.vertices + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); - vertices += count * 4; - - availableSpriteSpace -= count; - - // Have we treated the entire chain ? - if (count != spriteData.spriteCount) - { - // Oops, not enough space to store current chain - spriteChainOffset += count; - splitChainIt = it2; - split = true; - break; - } - - // Switch to next sprite chain, if any - spriteChainOffset = 0; - } - - spriteIndex = 0; - updateVertexBuffer = false; - - if (!split) - splitChainIt = layer.depthSortedSprites.end(); - } - - std::size_t index = *it; - - const ForwardRenderQueue::UnbatchedSpriteData& spriteData = layer.depthSortedSpriteData[index]; - - const Material* material = spriteData.material; - if (material != lastMaterial) - { - const MaterialPipeline* pipeline = material->GetPipeline(); - if (pipeline != lastPipeline) - { - pipelineInstance = &pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + pipelineInstance = &basicSprites.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); const Shader* shader = pipelineInstance->uberInstance->GetShader(); - - // Uniforms are conserved in our program, there's no point to send them back until they change if (shader != lastShader) { // Index of uniforms in the shader - const ShaderUniforms* shaderUniforms = GetShaderUniforms(shader); + shaderUniforms = GetShaderUniforms(shader); // Ambient color of the scene shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); // Position of the camera shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + // Overlay texture unit shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); @@ -898,150 +717,34 @@ namespace Nz lastPipeline = pipeline; } - material->Apply(*pipelineInstance); - - Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler()); - - lastMaterial = material; - } - - const Texture* overlay = (spriteData.overlay) ? spriteData.overlay : &m_whiteTexture; - if (overlay != lastOverlay) - { - Renderer::SetTexture(overlayTextureUnit, overlay); - lastOverlay = overlay; - } - - std::size_t spriteCount; - if (it != splitChainIt) - { - spriteCount = spriteData.spriteCount - alreadyDrawnCount; - alreadyDrawnCount = 0; - - ++it; - } - else - { - spriteCount = spriteChainOffset; - - alreadyDrawnCount = spriteCount; - updateVertexBuffer = true; - - // Restart at current iterator next time - } - - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, spriteIndex * 6, spriteCount * 6); - spriteIndex += spriteCount; - } - } - - /*! - * \brief Draws transparent models - * - * \param sceneData Data of the scene - * \param layer Layer of the rendering - * - * \remark Produces a NazaraAssert is viewer is invalid - */ - - void ForwardRenderTechnique::DrawTransparentModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const - { - NazaraAssert(sceneData.viewer, "Invalid viewer"); - - const MaterialPipeline* lastPipeline = nullptr; - const MaterialPipeline::Instance* pipelineInstance = nullptr; - const Shader* lastShader = nullptr; - const ShaderUniforms* shaderUniforms = nullptr; - unsigned int lightCount = 0; - - for (std::size_t index : layer.depthSortedMeshes) - { - const ForwardRenderQueue::UnbatchedModelData& modelData = layer.depthSortedMeshData[index]; - - // Material - const Material* material = modelData.material; - - const MaterialPipeline* pipeline = material->GetPipeline(); - if (pipeline != lastPipeline) - { - pipelineInstance = &pipeline->Apply(); - lastPipeline = pipeline; - } - - // We begin to apply the material - material->Apply(*pipelineInstance); - - // Uniforms are conserved in our program, there's no point to send them back until they change - const Shader* shader = pipelineInstance->uberInstance->GetShader(); - if (shader != lastShader) - { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); - - // Ambiant color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position of the camera - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - - // We send the directional lights if there is one (same for all) - if (shaderUniforms->hasLightUniforms) + if (lastMaterial != basicSprites.material) { - lightCount = std::min(m_renderQueue.directionalLights.size(), static_cast(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS)); + basicSprites.material->Apply(*pipelineInstance); - for (std::size_t i = 0; i < lightCount; ++i) - SendLightUniforms(shader, shaderUniforms->lightUniforms, i, i, shaderUniforms->lightOffset * i); + Renderer::SetTextureSampler(overlayTextureUnit, basicSprites.material->GetDiffuseSampler()); + + lastMaterial = basicSprites.material; } - lastShader = shader; + const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : &m_whiteTexture; + if (overlayTexture != lastOverlay) + { + Renderer::SetTexture(overlayTextureUnit, overlayTexture); + lastOverlay = overlayTexture; + } + + if (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + { + Renderer::SetScissorRect(scissorRect); + lastScissorRect = scissorRect; + } } - // Mesh - const Matrix4f& matrix = modelData.transformMatrix; - const MeshData& meshData = modelData.meshData; - - const IndexBuffer* indexBuffer = meshData.indexBuffer; - const VertexBuffer* vertexBuffer = meshData.vertexBuffer; - - // Handle draw call before the rendering loop - Renderer::DrawCall drawFunc; - unsigned int indexCount; - - if (indexBuffer) - { - drawFunc = Renderer::DrawIndexedPrimitives; - indexCount = indexBuffer->GetIndexCount(); - } - else - { - drawFunc = Renderer::DrawPrimitives; - indexCount = vertexBuffer->GetVertexCount(); - } - - Renderer::SetIndexBuffer(indexBuffer); - Renderer::SetVertexBuffer(vertexBuffer); - - if (shaderUniforms->hasLightUniforms && lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS) - { - // Compute the closest lights - Vector3f position = matrix.GetTranslation() + modelData.obbSphere.GetPosition(); - float radius = modelData.obbSphere.radius; - ChooseLights(Spheref(position, radius), false); - - for (std::size_t i = lightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) - SendLightUniforms(shader, shaderUniforms->lightUniforms, i, i, shaderUniforms->lightOffset*i); - } - - Renderer::SetMatrix(MatrixType_World, matrix); - drawFunc(meshData.primitiveMode, 0, indexCount); + m_spriteChains.emplace_back(basicSprites.vertices, basicSprites.spriteCount); } - } - /*! - * \brief Gets the shader uniforms - * \return Uniforms of the shader - * - * \param shader Shader to get uniforms from - */ + Commit(); + } const ForwardRenderTechnique::ShaderUniforms* ForwardRenderTechnique::GetShaderUniforms(const Shader* shader) const { diff --git a/src/Nazara/Graphics/MaterialPipeline.cpp b/src/Nazara/Graphics/MaterialPipeline.cpp index 54d860637..c1313b05a 100644 --- a/src/Nazara/Graphics/MaterialPipeline.cpp +++ b/src/Nazara/Graphics/MaterialPipeline.cpp @@ -188,17 +188,19 @@ namespace Nz MaterialPipelineInfo pipelineInfo; pipelineInfo.uberShader = UberShaderLibrary::Get("Basic"); - // Basic 2D - No depth write/face culling + // Basic 2D - No depth write/face culling with scissoring pipelineInfo.depthWrite = false; pipelineInfo.faceCulling = false; + pipelineInfo.scissorTest = true; MaterialPipelineLibrary::Register("Basic2D", GetPipeline(pipelineInfo)); - // Translucent 2D - Alpha blending with no depth write/face culling + // Translucent 2D - Alpha blending with no depth write/face culling and scissoring pipelineInfo.blending = true; pipelineInfo.depthWrite = false; pipelineInfo.faceCulling = false; pipelineInfo.depthSorting = true; + pipelineInfo.scissorTest = true; pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha; pipelineInfo.srcBlend = BlendFunc_SrcAlpha; @@ -210,6 +212,7 @@ namespace Nz pipelineInfo.depthWrite = false; pipelineInfo.faceCulling = false; pipelineInfo.depthSorting = true; + pipelineInfo.scissorTest = false; pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha; pipelineInfo.srcBlend = BlendFunc_SrcAlpha; diff --git a/src/Nazara/Graphics/Model.cpp b/src/Nazara/Graphics/Model.cpp index 217211f01..05b8bc909 100644 --- a/src/Nazara/Graphics/Model.cpp +++ b/src/Nazara/Graphics/Model.cpp @@ -52,7 +52,7 @@ namespace Nz * \param instanceData Data used for this instance */ - void Model::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const + void Model::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const { unsigned int submeshCount = m_mesh->GetSubMeshCount(); for (unsigned int i = 0; i < submeshCount; ++i) @@ -65,7 +65,7 @@ namespace Nz meshData.primitiveMode = mesh->GetPrimitiveMode(); meshData.vertexBuffer = mesh->GetVertexBuffer(); - renderQueue->AddMesh(instanceData.renderOrder, material, meshData, mesh->GetAABB(), instanceData.transformMatrix); + renderQueue->AddMesh(instanceData.renderOrder, material, meshData, mesh->GetAABB(), instanceData.transformMatrix, scissorRect); } } diff --git a/src/Nazara/Graphics/RenderQueue.cpp b/src/Nazara/Graphics/RenderQueue.cpp new file mode 100644 index 000000000..3baa8ecc1 --- /dev/null +++ b/src/Nazara/Graphics/RenderQueue.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + void RenderQueueInternal::Sort() + { + std::sort(m_orderedRenderQueue.begin(), m_orderedRenderQueue.end(), [](const RenderDataPair& lhs, const RenderDataPair& rhs) + { + return lhs.first < rhs.first; + }); + } +} diff --git a/src/Nazara/Graphics/SkeletalModel.cpp b/src/Nazara/Graphics/SkeletalModel.cpp index 433a748f6..c42b63a83 100644 --- a/src/Nazara/Graphics/SkeletalModel.cpp +++ b/src/Nazara/Graphics/SkeletalModel.cpp @@ -53,7 +53,7 @@ namespace Nz * \param instanceData Data for the instance */ - void SkeletalModel::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const + void SkeletalModel::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const { if (!m_mesh) return; @@ -69,7 +69,7 @@ namespace Nz meshData.primitiveMode = mesh->GetPrimitiveMode(); meshData.vertexBuffer = SkinningManager::GetBuffer(mesh, &m_skeleton); - renderQueue->AddMesh(instanceData.renderOrder, material, meshData, m_skeleton.GetAABB(), instanceData.transformMatrix); + renderQueue->AddMesh(instanceData.renderOrder, material, meshData, m_skeleton.GetAABB(), instanceData.transformMatrix, scissorRect); } } diff --git a/src/Nazara/Graphics/Sprite.cpp b/src/Nazara/Graphics/Sprite.cpp index 5d51a1ae1..70ba21926 100644 --- a/src/Nazara/Graphics/Sprite.cpp +++ b/src/Nazara/Graphics/Sprite.cpp @@ -22,10 +22,10 @@ namespace Nz * \param instanceData Data for the instance */ - void Sprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const + void Sprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const { const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(instanceData.data.data()); - renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(), vertices, 1); + renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(), vertices, 1, scissorRect); } /*! diff --git a/src/Nazara/Graphics/TextSprite.cpp b/src/Nazara/Graphics/TextSprite.cpp index 3d82037d4..883268963 100644 --- a/src/Nazara/Graphics/TextSprite.cpp +++ b/src/Nazara/Graphics/TextSprite.cpp @@ -26,7 +26,7 @@ namespace Nz * \param instanceData Data for the instance */ - void TextSprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const + void TextSprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const { for (auto& pair : m_renderInfos) { @@ -36,7 +36,7 @@ namespace Nz if (indices.count > 0) { const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(instanceData.data.data()); - renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(), &vertices[indices.first * 4], indices.count, overlay); + renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(), &vertices[indices.first * 4], indices.count, scissorRect, overlay); } } } diff --git a/src/Nazara/Graphics/TileMap.cpp b/src/Nazara/Graphics/TileMap.cpp index 192341472..26a146f88 100644 --- a/src/Nazara/Graphics/TileMap.cpp +++ b/src/Nazara/Graphics/TileMap.cpp @@ -23,7 +23,7 @@ namespace Nz * \param renderQueue Queue to be added * \param instanceData Data for the instance */ - void TileMap::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const + void TileMap::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData, const Recti& scissorRect) const { const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(instanceData.data.data()); @@ -31,7 +31,7 @@ namespace Nz std::size_t spriteCount = 0; for (const Layer& layer : m_layers) { - renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(matCount++), &vertices[spriteCount], layer.tiles.size()); + renderQueue->AddSprites(instanceData.renderOrder, GetMaterial(matCount++), &vertices[spriteCount], layer.tiles.size(), scissorRect); spriteCount += layer.tiles.size(); }