Add light support (WIP)
This commit is contained in:
parent
e6951d54a5
commit
8a3a8547dc
|
|
@ -854,12 +854,14 @@ int main()
|
|||
planeModel.BuildElement(forwardPassIndex, planeInstance, elements);
|
||||
|
||||
std::vector<Nz::Pointer<const Nz::RenderElement>> elementPointers;
|
||||
std::vector<Nz::ElementRenderer::RenderStates> renderStates(elements.size());
|
||||
elementPointers.reserve(elements.size());
|
||||
for (const auto& element : elements)
|
||||
elementPointers.emplace_back(element.get());
|
||||
|
||||
submeshRenderer.Prepare(viewerInstance, *submeshRendererData, *currentFrame, {}, elementPointers.data(), elementPointers.size());
|
||||
submeshRenderer.Render(viewerInstance, *submeshRendererData, builder, elementPointers.data(), elementPointers.size());
|
||||
submeshRenderer.Prepare(viewerInstance, *submeshRendererData, *currentFrame, elementPointers.size(), elementPointers.data(), renderStates.data());
|
||||
submeshRenderer.PrepareEnd(*currentFrame, *spriteRendererData);
|
||||
submeshRenderer.Render(viewerInstance, *submeshRendererData, builder, elementPointers.size(), elementPointers.data());
|
||||
});
|
||||
|
||||
Nz::FramePass& lightingPass = graph.AddPass("Lighting pass");
|
||||
|
|
@ -918,12 +920,14 @@ int main()
|
|||
flareSprite.BuildElement(forwardPassIndex, flareInstance, elements);
|
||||
|
||||
std::vector<Nz::Pointer<const Nz::RenderElement>> elementPointers;
|
||||
std::vector<Nz::ElementRenderer::RenderStates> renderStates(elements.size());
|
||||
|
||||
elementPointers.reserve(elements.size());
|
||||
for (const auto& element : elements)
|
||||
elementPointers.emplace_back(element.get());
|
||||
|
||||
spritechainRenderer.Prepare(viewerInstance, *spriteRendererData, *currentFrame, {}, elementPointers.data(), elementPointers.size());
|
||||
spritechainRenderer.Render(viewerInstance, *spriteRendererData, builder, elementPointers.data(), elementPointers.size());
|
||||
spritechainRenderer.Prepare(viewerInstance, *spriteRendererData, *currentFrame, elementPointers.size(), elementPointers.data(), renderStates.data());
|
||||
spritechainRenderer.Render(viewerInstance, *spriteRendererData, builder, elementPointers.size(), elementPointers.data());
|
||||
});
|
||||
forwardPass.SetExecutionCallback([&]
|
||||
{
|
||||
|
|
@ -946,12 +950,15 @@ int main()
|
|||
flareSprite.BuildElement(forwardPassIndex, flareInstance, elements);
|
||||
|
||||
std::vector<Nz::Pointer<const Nz::RenderElement>> elementPointers;
|
||||
std::vector<Nz::ElementRenderer::RenderStates> renderStates(elements.size());
|
||||
|
||||
elementPointers.reserve(elements.size());
|
||||
for (const auto& element : elements)
|
||||
elementPointers.emplace_back(element.get());
|
||||
|
||||
spritechainRenderer.Prepare(viewerInstance, *spriteRendererData, *currentFrame, {}, elementPointers.data(), elementPointers.size());
|
||||
spritechainRenderer.Render(viewerInstance, *spriteRendererData, builder, elementPointers.data(), elementPointers.size());
|
||||
spritechainRenderer.Prepare(viewerInstance, *spriteRendererData, *currentFrame, elementPointers.size(), elementPointers.data(), renderStates.data());
|
||||
spritechainRenderer.PrepareEnd(*currentFrame, *spriteRendererData);
|
||||
spritechainRenderer.Render(viewerInstance, *spriteRendererData, builder, elementPointers.size(), elementPointers.data());
|
||||
});
|
||||
|
||||
occluderPass.AddOutput(occluderTexture);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ int main()
|
|||
Nz::MeshParams meshParams;
|
||||
meshParams.center = true;
|
||||
meshParams.matrix = Nz::Matrix4f::Rotate(Nz::EulerAnglesf(0.f, 90.f, 0.f)) * Nz::Matrix4f::Scale(Nz::Vector3f(0.002f));
|
||||
meshParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_UV);
|
||||
meshParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Normal_UV_Tangent);
|
||||
|
||||
std::shared_ptr<Nz::Mesh> spaceshipMesh = Nz::Mesh::LoadFromFile(resourceDir / "Spaceship/spaceship.obj", meshParams);
|
||||
if (!spaceshipMesh)
|
||||
|
|
@ -71,10 +71,10 @@ int main()
|
|||
|
||||
std::shared_ptr<Nz::MaterialPass> depthPass = std::make_shared<Nz::MaterialPass>(Nz::DepthMaterial::GetSettings());
|
||||
depthPass->EnableDepthBuffer(true);
|
||||
depthPass->EnableDepthClamp(true);
|
||||
//depthPass->EnableDepthClamp(true);
|
||||
depthPass->EnableFaceCulling(true);
|
||||
|
||||
std::shared_ptr<Nz::MaterialPass> materialPass = std::make_shared<Nz::MaterialPass>(Nz::BasicMaterial::GetSettings());
|
||||
std::shared_ptr<Nz::MaterialPass> materialPass = std::make_shared<Nz::MaterialPass>(Nz::PhongLightingMaterial::GetSettings());
|
||||
materialPass->EnableDepthBuffer(true);
|
||||
materialPass->EnableDepthClamp(true);
|
||||
materialPass->EnableFaceCulling(true);
|
||||
|
|
@ -91,7 +91,7 @@ int main()
|
|||
|
||||
Nz::BasicMaterial basicMat(*materialPass);
|
||||
basicMat.EnableAlphaTest(false);
|
||||
basicMat.SetAlphaMap(Nz::Texture::LoadFromFile(resourceDir / "alphatile.png", texParams));
|
||||
//basicMat.SetAlphaMap(Nz::Texture::LoadFromFile(resourceDir / "alphatile.png", texParams));
|
||||
basicMat.SetDiffuseMap(Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/diffuse.png", texParams));
|
||||
basicMat.SetDiffuseSampler(samplerInfo);
|
||||
|
||||
|
|
@ -143,37 +143,12 @@ int main()
|
|||
Nz::Physics3DSystem physSytem(registry);
|
||||
Nz::RenderSystem renderSystem(registry);
|
||||
|
||||
Nz::Canvas canvas2D(registry, window.GetEventHandler(), window.GetCursorController().CreateHandle(), 2);
|
||||
canvas2D.Resize(Nz::Vector2f(window.GetSize()));
|
||||
|
||||
Nz::LabelWidget* labelWidget = canvas2D.Add<Nz::LabelWidget>();
|
||||
labelWidget->SetPosition(0.f, 300.f, 0.f);
|
||||
labelWidget->EnableBackground(true);
|
||||
labelWidget->UpdateText(Nz::SimpleTextDrawer::Draw("Bonjour Paris !", 72));
|
||||
|
||||
Nz::ButtonWidget* buttonWidget = canvas2D.Add<Nz::ButtonWidget>();
|
||||
buttonWidget->SetPosition(200.f, 400.f);
|
||||
buttonWidget->UpdateText(Nz::SimpleTextDrawer::Draw("Press me senpai", 72));
|
||||
buttonWidget->Resize(buttonWidget->GetPreferredSize());
|
||||
buttonWidget->OnButtonTrigger.Connect([](const Nz::ButtonWidget*)
|
||||
{
|
||||
std::cout << "Coucou !" << std::endl;
|
||||
});
|
||||
|
||||
entt::entity viewer2D = registry.create();
|
||||
{
|
||||
registry.emplace<Nz::NodeComponent>(viewer2D);
|
||||
auto& cameraComponent = registry.emplace<Nz::CameraComponent>(viewer2D, window.GetRenderTarget(), Nz::ProjectionType::Orthographic);
|
||||
cameraComponent.UpdateClearColor(Nz::Color(0, 0, 0, 0));
|
||||
cameraComponent.UpdateRenderOrder(1);
|
||||
cameraComponent.UpdateRenderMask(2);
|
||||
}
|
||||
|
||||
entt::entity viewer = registry.create();
|
||||
{
|
||||
registry.emplace<Nz::NodeComponent>(viewer);
|
||||
auto& cameraComponent = registry.emplace<Nz::CameraComponent>(viewer, window.GetRenderTarget());
|
||||
cameraComponent.UpdateRenderMask(1);
|
||||
//cameraComponent.UpdateClearColor(Nz::Color(127, 127, 127));
|
||||
}
|
||||
|
||||
auto shipCollider = std::make_shared<Nz::ConvexCollider3D>(vertices, vertexMapper.GetVertexCount(), 0.01f);
|
||||
|
|
@ -211,6 +186,9 @@ int main()
|
|||
|
||||
entt::entity headingEntity = registry.create();
|
||||
{
|
||||
auto& entityLight = registry.emplace<Nz::LightComponent>(playerEntity);
|
||||
entityLight.AttachLight(std::make_shared<Nz::DirectionalLight>(), 1);
|
||||
|
||||
auto& entityGfx = registry.emplace<Nz::GraphicsComponent>(playerEntity);
|
||||
entityGfx.AttachRenderable(model, 1);
|
||||
|
||||
|
|
@ -287,6 +265,7 @@ int main()
|
|||
case Nz::WindowEventType::KeyPressed:
|
||||
if (event.key.virtualKey == Nz::Keyboard::VKey::A)
|
||||
{
|
||||
//canvas2D.Resize({ 1920.f, 1080.f });
|
||||
basicMat.EnableAlphaTest(!basicMat.IsAlphaTestEnabled());
|
||||
basicMatDepth.EnableAlphaTest(!basicMatDepth.IsAlphaTestEnabled());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,172 @@
|
|||
#include <Nazara/Core.hpp>
|
||||
#include <Nazara/Core/ECS.hpp>
|
||||
#include <Nazara/Platform.hpp>
|
||||
#include <Nazara/Graphics.hpp>
|
||||
#include <Nazara/Graphics/TextSprite.hpp>
|
||||
#include <Nazara/Graphics/Components.hpp>
|
||||
#include <Nazara/Graphics/Systems.hpp>
|
||||
#include <Nazara/Math/PidController.hpp>
|
||||
#include <Nazara/Physics3D.hpp>
|
||||
#include <Nazara/Physics3D/Components.hpp>
|
||||
#include <Nazara/Physics3D/Systems.hpp>
|
||||
#include <Nazara/Renderer.hpp>
|
||||
#include <Nazara/Shader.hpp>
|
||||
#include <Nazara/Shader/SpirvConstantCache.hpp>
|
||||
#include <Nazara/Shader/SpirvPrinter.hpp>
|
||||
#include <Nazara/Utility.hpp>
|
||||
#include <Nazara/Utility/Components.hpp>
|
||||
#include <Nazara/Widgets.hpp>
|
||||
#include <entt/entt.hpp>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
NAZARA_REQUEST_DEDICATED_GPU()
|
||||
|
||||
int main()
|
||||
{
|
||||
std::filesystem::path resourceDir = "resources";
|
||||
if (!std::filesystem::is_directory(resourceDir) && std::filesystem::is_directory(".." / resourceDir))
|
||||
resourceDir = ".." / resourceDir;
|
||||
|
||||
Nz::Renderer::Config rendererConfig;
|
||||
std::cout << "Run using Vulkan? (y/n)" << std::endl;
|
||||
if (std::getchar() != 'n')
|
||||
rendererConfig.preferredAPI = Nz::RenderAPI::Vulkan;
|
||||
else
|
||||
rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL;
|
||||
|
||||
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||
|
||||
Nz::Modules<Nz::Graphics, Nz::Physics3D, Nz::ECS, Nz::Widgets> nazara(rendererConfig);
|
||||
|
||||
Nz::RenderWindow window;
|
||||
|
||||
std::shared_ptr<Nz::RenderDevice> device = Nz::Graphics::Instance()->GetRenderDevice();
|
||||
|
||||
std::string windowTitle = "Widget Test";
|
||||
if (!window.Create(device, Nz::VideoMode(1920, 1080, 32), windowTitle))
|
||||
{
|
||||
std::cout << "Failed to create Window" << std::endl;
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
entt::registry registry;
|
||||
Nz::RenderSystem renderSystem(registry);
|
||||
|
||||
Nz::Canvas canvas2D(registry, window.GetEventHandler(), window.GetCursorController().CreateHandle(), 0xFFFFFFFF);
|
||||
canvas2D.Resize(Nz::Vector2f(window.GetSize()));
|
||||
|
||||
Nz::LabelWidget* labelWidget = canvas2D.Add<Nz::LabelWidget>();
|
||||
labelWidget->SetPosition(0.f, 200.f, 0.f);
|
||||
labelWidget->UpdateText(Nz::SimpleTextDrawer::Draw("Je suis un LabelWidget !", 72));
|
||||
|
||||
Nz::ButtonWidget* buttonWidget = canvas2D.Add<Nz::ButtonWidget>();
|
||||
buttonWidget->SetPosition(200.f, 400.f);
|
||||
buttonWidget->UpdateText(Nz::SimpleTextDrawer::Draw("Press me senpai", 72));
|
||||
buttonWidget->Resize(buttonWidget->GetPreferredSize());
|
||||
buttonWidget->OnButtonTrigger.Connect([=](const Nz::ButtonWidget*)
|
||||
{
|
||||
buttonWidget->Destroy();
|
||||
});
|
||||
|
||||
std::shared_ptr<Nz::Material> material = std::make_shared<Nz::Material>();
|
||||
|
||||
std::shared_ptr<Nz::MaterialPass> materialPass = std::make_shared<Nz::MaterialPass>(Nz::BasicMaterial::GetSettings());
|
||||
material->AddPass("ForwardPass", materialPass);
|
||||
|
||||
Nz::TextureSamplerInfo samplerInfo;
|
||||
samplerInfo.anisotropyLevel = 8;
|
||||
|
||||
Nz::TextureParams texParams;
|
||||
texParams.renderDevice = device;
|
||||
texParams.loadFormat = Nz::PixelFormat::RGBA8_SRGB;
|
||||
|
||||
Nz::BasicMaterial basicMat(*materialPass);
|
||||
basicMat.SetDiffuseMap(Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/diffuse.png", texParams));
|
||||
basicMat.SetDiffuseSampler(samplerInfo);
|
||||
|
||||
Nz::ImageWidget* imageWidget = canvas2D.Add<Nz::ImageWidget>();
|
||||
imageWidget->SetPosition(1200.f, 200.f);
|
||||
imageWidget->SetMaterial(material);
|
||||
imageWidget->Resize(imageWidget->GetPreferredSize() / 4.f);
|
||||
|
||||
Nz::TextAreaWidget* textAreaWidget = canvas2D.Add<Nz::TextAreaWidget>();
|
||||
textAreaWidget->SetPosition(800.f, 500.f);
|
||||
textAreaWidget->SetText("Je suis un TextAreaWidget !");
|
||||
textAreaWidget->Resize(Nz::Vector2f(400.f, textAreaWidget->GetPreferredHeight() * 5.f));
|
||||
textAreaWidget->SetBackgroundColor(Nz::Color::White);
|
||||
textAreaWidget->SetTextColor(Nz::Color::Black);
|
||||
textAreaWidget->EnableMultiline(true);
|
||||
|
||||
Nz::CheckboxWidget* checkboxWidget = canvas2D.Add<Nz::CheckboxWidget>();
|
||||
//checkboxWidget->EnableTristate(true);
|
||||
checkboxWidget->SetPosition(800.f, 800.f);
|
||||
checkboxWidget->Resize({ 256.f, 256.f });
|
||||
checkboxWidget->SetState(true);
|
||||
|
||||
/*Nz::TextAreaWidget* textAreaWidget2 = canvas2D.Add<Nz::TextAreaWidget>();
|
||||
textAreaWidget2->SetPosition(800.f, 700.f);
|
||||
textAreaWidget2->SetText("Je suis un autre TextAreaWidget !");
|
||||
textAreaWidget2->Resize(Nz::Vector2f(500.f, textAreaWidget2->GetPreferredHeight()));
|
||||
textAreaWidget2->SetBackgroundColor(Nz::Color::White);
|
||||
textAreaWidget2->SetTextColor(Nz::Color::Black);*/
|
||||
|
||||
entt::entity viewer2D = registry.create();
|
||||
{
|
||||
registry.emplace<Nz::NodeComponent>(viewer2D);
|
||||
auto& cameraComponent = registry.emplace<Nz::CameraComponent>(viewer2D, window.GetRenderTarget(), Nz::ProjectionType::Orthographic);
|
||||
cameraComponent.UpdateClearColor(Nz::Color(173, 216, 230, 255));
|
||||
}
|
||||
|
||||
window.EnableEventPolling(true);
|
||||
|
||||
Nz::Clock updateClock;
|
||||
Nz::Clock secondClock;
|
||||
unsigned int fps = 0;
|
||||
|
||||
float elapsedTime = 0.f;
|
||||
Nz::UInt64 time = Nz::GetElapsedMicroseconds();
|
||||
|
||||
while (window.IsOpen())
|
||||
{
|
||||
Nz::UInt64 now = Nz::GetElapsedMicroseconds();
|
||||
elapsedTime = (now - time) / 1'000'000.f;
|
||||
time = now;
|
||||
|
||||
Nz::WindowEvent event;
|
||||
while (window.PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case Nz::WindowEventType::Quit:
|
||||
window.Close();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Nz::RenderFrame frame = window.AcquireFrame();
|
||||
if (!frame)
|
||||
continue;
|
||||
|
||||
renderSystem.Render(registry, frame);
|
||||
|
||||
frame.Present();
|
||||
|
||||
fps++;
|
||||
|
||||
if (secondClock.GetMilliseconds() >= 1000)
|
||||
{
|
||||
window.SetTitle(windowTitle + " - " + Nz::NumberToString(fps) + " FPS" + " - " + Nz::NumberToString(registry.alive()) + " entities");
|
||||
|
||||
fps = 0;
|
||||
|
||||
secondClock.Restart();
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
target("WidgetDemo")
|
||||
set_group("Examples")
|
||||
set_kind("binary")
|
||||
add_deps("NazaraGraphics", "NazaraPhysics3D", "NazaraWidgets")
|
||||
add_packages("entt")
|
||||
add_files("main.cpp")
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
#include <Nazara/Graphics/Camera.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/DepthMaterial.hpp>
|
||||
#include <Nazara/Graphics/DirectionalLight.hpp>
|
||||
#include <Nazara/Graphics/ElementRenderer.hpp>
|
||||
#include <Nazara/Graphics/Enums.hpp>
|
||||
#include <Nazara/Graphics/ForwardFramePipeline.hpp>
|
||||
|
|
@ -47,6 +48,7 @@
|
|||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Graphics/GuillotineTextureAtlas.hpp>
|
||||
#include <Nazara/Graphics/InstancedRenderable.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/Material.hpp>
|
||||
#include <Nazara/Graphics/MaterialPass.hpp>
|
||||
#include <Nazara/Graphics/MaterialPassRegistry.hpp>
|
||||
|
|
@ -54,6 +56,7 @@
|
|||
#include <Nazara/Graphics/MaterialSettings.hpp>
|
||||
#include <Nazara/Graphics/Model.hpp>
|
||||
#include <Nazara/Graphics/PhongLightingMaterial.hpp>
|
||||
#include <Nazara/Graphics/PointLight.hpp>
|
||||
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
|
||||
#include <Nazara/Graphics/RenderElement.hpp>
|
||||
#include <Nazara/Graphics/RenderQueue.hpp>
|
||||
|
|
@ -61,6 +64,7 @@
|
|||
#include <Nazara/Graphics/RenderSpriteChain.hpp>
|
||||
#include <Nazara/Graphics/RenderSubmesh.hpp>
|
||||
#include <Nazara/Graphics/SlicedSprite.hpp>
|
||||
#include <Nazara/Graphics/SpotLight.hpp>
|
||||
#include <Nazara/Graphics/Sprite.hpp>
|
||||
#include <Nazara/Graphics/SpriteChainRenderer.hpp>
|
||||
#include <Nazara/Graphics/SubmeshRenderer.hpp>
|
||||
|
|
|
|||
|
|
@ -31,5 +31,6 @@
|
|||
|
||||
#include <Nazara/Graphics/Components/CameraComponent.hpp>
|
||||
#include <Nazara/Graphics/Components/GraphicsComponent.hpp>
|
||||
#include <Nazara/Graphics/Components/LightComponent.hpp>
|
||||
|
||||
#endif // NAZARA_GRAPHICS_COMPONENTS_HPP
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// 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_GRAPHICS_COMPONENTS_LIGHTCOMPONENT_HPP
|
||||
#define NAZARA_GRAPHICS_COMPONENTS_LIGHTCOMPONENT_HPP
|
||||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Core/ECS.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/WorldInstance.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class NAZARA_GRAPHICS_API LightComponent
|
||||
{
|
||||
public:
|
||||
struct LightEntry;
|
||||
|
||||
inline LightComponent(bool initialyVisible = true);
|
||||
LightComponent(const LightComponent&) = default;
|
||||
LightComponent(LightComponent&&) = default;
|
||||
~LightComponent() = default;
|
||||
|
||||
inline void AttachLight(std::shared_ptr<Light> renderable, UInt32 renderMask);
|
||||
|
||||
inline void Clear();
|
||||
|
||||
inline void DetachLight(const std::shared_ptr<Light>& renderable);
|
||||
|
||||
inline const std::vector<LightEntry>& GetLights() const;
|
||||
|
||||
inline void Hide();
|
||||
|
||||
inline bool IsVisible() const;
|
||||
|
||||
inline void Show(bool show = true);
|
||||
|
||||
LightComponent& operator=(const LightComponent&) = default;
|
||||
LightComponent& operator=(LightComponent&&) = default;
|
||||
|
||||
NazaraSignal(OnLightAttached, LightComponent* /*graphicsComponent*/, const LightEntry& /*lightEntry*/);
|
||||
NazaraSignal(OnLightDetach, LightComponent* /*graphicsComponent*/, const LightEntry& /*lightEntry*/);
|
||||
NazaraSignal(OnVisibilityUpdate, LightComponent* /*graphicsComponent*/, bool /*newVisibilityState*/);
|
||||
|
||||
struct LightEntry
|
||||
{
|
||||
std::shared_ptr<Light> light;
|
||||
UInt32 renderMask;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<LightEntry> m_lightEntries;
|
||||
bool m_isVisible;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/Components/LightComponent.inl>
|
||||
|
||||
#endif // NAZARA_GRAPHICS_COMPONENTS_LIGHTCOMPONENT_HPP
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Components/LightComponent.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline LightComponent::LightComponent(bool initialyVisible) :
|
||||
m_isVisible(initialyVisible)
|
||||
{
|
||||
}
|
||||
|
||||
inline void LightComponent::AttachLight(std::shared_ptr<Light> light, UInt32 renderMask)
|
||||
{
|
||||
auto& entry = m_lightEntries.emplace_back();
|
||||
entry.light = std::move(light);
|
||||
entry.renderMask = renderMask;
|
||||
|
||||
OnLightAttached(this, m_lightEntries.back());
|
||||
}
|
||||
|
||||
inline void LightComponent::Clear()
|
||||
{
|
||||
for (const auto& lightEntry : m_lightEntries)
|
||||
OnLightDetach(this, lightEntry);
|
||||
|
||||
m_lightEntries.clear();
|
||||
}
|
||||
|
||||
inline void LightComponent::DetachLight(const std::shared_ptr<Light>& light)
|
||||
{
|
||||
auto it = std::find_if(m_lightEntries.begin(), m_lightEntries.end(), [&](const auto& lightEntry) { return lightEntry.light == light; });
|
||||
if (it != m_lightEntries.end())
|
||||
{
|
||||
OnLightDetach(this, *it);
|
||||
|
||||
m_lightEntries.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
inline auto LightComponent::GetLights() const -> const std::vector<LightEntry>&
|
||||
{
|
||||
return m_lightEntries;
|
||||
}
|
||||
|
||||
inline void LightComponent::Hide()
|
||||
{
|
||||
return Show(false);
|
||||
}
|
||||
|
||||
inline bool LightComponent::IsVisible() const
|
||||
{
|
||||
return m_isVisible;
|
||||
}
|
||||
|
||||
inline void LightComponent::Show(bool show)
|
||||
{
|
||||
if (m_isVisible != show)
|
||||
{
|
||||
OnVisibilityUpdate(this, show);
|
||||
m_isVisible = show;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// 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_GRAPHICS_DIRECTIONALLIGHT_HPP
|
||||
#define NAZARA_GRAPHICS_DIRECTIONALLIGHT_HPP
|
||||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Color.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Math/Angle.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class NAZARA_GRAPHICS_API DirectionalLight : public Light
|
||||
{
|
||||
public:
|
||||
DirectionalLight();
|
||||
DirectionalLight(const DirectionalLight&) = delete;
|
||||
DirectionalLight(DirectionalLight&&) noexcept = default;
|
||||
~DirectionalLight() = default;
|
||||
|
||||
float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override;
|
||||
|
||||
void FillLightData(void* data) override;
|
||||
|
||||
inline float GetAmbientFactor() const;
|
||||
inline float GetDiffuseFactor() const;
|
||||
inline Color GetColor() const;
|
||||
inline const Vector3f& GetDirection() const;
|
||||
inline const Quaternionf& GetRotation() const;
|
||||
|
||||
inline void UpdateAmbientFactor(float factor);
|
||||
inline void UpdateColor(Color color);
|
||||
inline void UpdateDiffuseFactor(float factor);
|
||||
inline void UpdateDirection(const Vector3f& direction);
|
||||
inline void UpdateRotation(const Quaternionf& rotation);
|
||||
|
||||
void UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& scale) override;
|
||||
|
||||
DirectionalLight& operator=(const DirectionalLight&) = delete;
|
||||
DirectionalLight& operator=(DirectionalLight&&) noexcept = default;
|
||||
|
||||
private:
|
||||
inline void UpdateBoundingVolume();
|
||||
|
||||
Color m_color;
|
||||
Quaternionf m_rotation;
|
||||
Vector3f m_direction;
|
||||
float m_ambientFactor;
|
||||
float m_diffuseFactor;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DirectionalLight.inl>
|
||||
|
||||
#endif // NAZARA_GRAPHICS_DIRECTIONALLIGHT_HPP
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/DirectionalLight.hpp>
|
||||
#include <Nazara/Graphics/DirectionalLight.hpp>
|
||||
#include <cassert>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline DirectionalLight::DirectionalLight() :
|
||||
m_color(Color::White),
|
||||
m_ambientFactor(0.2f),
|
||||
m_diffuseFactor(1.f)
|
||||
{
|
||||
UpdateRotation(Quaternionf::Identity());
|
||||
}
|
||||
|
||||
inline float DirectionalLight::GetAmbientFactor() const
|
||||
{
|
||||
return m_ambientFactor;
|
||||
}
|
||||
|
||||
inline Color DirectionalLight::GetColor() const
|
||||
{
|
||||
return m_color;
|
||||
}
|
||||
|
||||
inline const Vector3f& DirectionalLight::GetDirection() const
|
||||
{
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
inline const Quaternionf& DirectionalLight::GetRotation() const
|
||||
{
|
||||
return m_rotation;
|
||||
}
|
||||
|
||||
inline float DirectionalLight::GetDiffuseFactor() const
|
||||
{
|
||||
return m_diffuseFactor;
|
||||
}
|
||||
|
||||
inline void DirectionalLight::UpdateAmbientFactor(float factor)
|
||||
{
|
||||
m_ambientFactor = factor;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
|
||||
inline void DirectionalLight::UpdateColor(Color color)
|
||||
{
|
||||
m_color = color;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
|
||||
inline void DirectionalLight::UpdateDiffuseFactor(float factor)
|
||||
{
|
||||
m_diffuseFactor = factor;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
|
||||
inline void DirectionalLight::UpdateDirection(const Vector3f& direction)
|
||||
{
|
||||
UpdateRotation(Quaternionf::RotationBetween(Vector3f::Forward(), direction));
|
||||
}
|
||||
|
||||
inline void DirectionalLight::UpdateRotation(const Quaternionf& rotation)
|
||||
{
|
||||
m_rotation = rotation;
|
||||
m_direction = rotation * Vector3f::Forward();
|
||||
|
||||
UpdateBoundingVolume();
|
||||
}
|
||||
|
||||
inline void DirectionalLight::UpdateBoundingVolume()
|
||||
{
|
||||
Light::UpdateBoundingVolume(BoundingVolumef::Infinite()); //< will trigger OnLightDataInvalided
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/Enums.hpp>
|
||||
#include <Nazara/Renderer/RenderBufferView.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
|
@ -18,7 +19,6 @@
|
|||
namespace Nz
|
||||
{
|
||||
class CommandBufferBuilder;
|
||||
class RenderBuffer;
|
||||
class RenderElement;
|
||||
class RenderFrame;
|
||||
class ViewerInstance;
|
||||
|
|
@ -33,13 +33,14 @@ namespace Nz
|
|||
virtual ~ElementRenderer();
|
||||
|
||||
virtual std::unique_ptr<ElementRendererData> InstanciateData() = 0;
|
||||
virtual void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, const RenderStates& renderStates, const Pointer<const RenderElement>* elements, std::size_t elementCount);
|
||||
virtual void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, const Pointer<const RenderElement>* elements, std::size_t elementCount) = 0;
|
||||
virtual void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates);
|
||||
virtual void PrepareEnd(RenderFrame& currentFrame, ElementRendererData& rendererData);
|
||||
virtual void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t elementCount, const Pointer<const RenderElement>* elements) = 0;
|
||||
virtual void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame);
|
||||
|
||||
struct RenderStates
|
||||
{
|
||||
std::optional<RenderBufferView> lightData;
|
||||
RenderBufferView lightData;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@
|
|||
#include <Nazara/Graphics/ElementRenderer.hpp>
|
||||
#include <Nazara/Graphics/FramePipeline.hpp>
|
||||
#include <Nazara/Graphics/InstancedRenderable.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/MaterialPass.hpp>
|
||||
#include <Nazara/Graphics/RenderElement.hpp>
|
||||
#include <Nazara/Graphics/RenderQueue.hpp>
|
||||
#include <Nazara/Graphics/RenderQueueRegistry.hpp>
|
||||
#include <Nazara/Renderer/ShaderBinding.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
|
@ -25,6 +27,7 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
class PointLight;
|
||||
class RenderFrame;
|
||||
class RenderTarget;
|
||||
|
||||
|
|
@ -40,16 +43,20 @@ namespace Nz
|
|||
void InvalidateWorldInstance(WorldInstance* worldInstance) override;
|
||||
|
||||
void RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable, UInt32 renderMask) override;
|
||||
void RegisterLight(std::shared_ptr<Light> light, UInt32 renderMask) override;
|
||||
void RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) override;
|
||||
|
||||
void Render(RenderFrame& renderFrame) override;
|
||||
|
||||
void UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable) override;
|
||||
void UnregisterLight(Light* light) override;
|
||||
void UnregisterViewer(AbstractViewer* viewerInstance) override;
|
||||
|
||||
ForwardFramePipeline& operator=(const ForwardFramePipeline&) = delete;
|
||||
ForwardFramePipeline& operator=(ForwardFramePipeline&&) = delete;
|
||||
|
||||
static constexpr std::size_t MaxLightCountPerDraw = 3;
|
||||
|
||||
private:
|
||||
BakedFrameGraph BuildFrameGraph();
|
||||
|
||||
|
|
@ -59,6 +66,28 @@ namespace Nz
|
|||
|
||||
struct ViewerData;
|
||||
|
||||
struct LightData
|
||||
{
|
||||
std::shared_ptr<Light> light;
|
||||
UInt32 renderMask;
|
||||
|
||||
NazaraSlot(Light, OnLightDataInvalided, onLightInvalidated);
|
||||
};
|
||||
|
||||
using LightKey = std::array<const Light*, MaxLightCountPerDraw>;
|
||||
|
||||
struct LightKeyHasher
|
||||
{
|
||||
inline std::size_t operator()(const LightKey& lightKey) const;
|
||||
};
|
||||
|
||||
struct LightDataUbo
|
||||
{
|
||||
std::shared_ptr<RenderBuffer> renderBuffer;
|
||||
std::size_t offset = 0;
|
||||
UploadPool::Allocation* allocation = nullptr;
|
||||
};
|
||||
|
||||
struct MaterialData
|
||||
{
|
||||
std::size_t usedCount = 0;
|
||||
|
|
@ -92,6 +121,8 @@ namespace Nz
|
|||
std::size_t colorAttachment;
|
||||
std::size_t depthStencilAttachment;
|
||||
std::size_t visibilityHash = 0;
|
||||
std::unordered_map<const RenderElement*, RenderBufferView> lightPerRenderElement;
|
||||
std::unordered_map<LightKey, RenderBufferView, LightKeyHasher> lightBufferPerLights;
|
||||
std::vector<std::unique_ptr<RenderElement>> depthPrepassRenderElements;
|
||||
std::vector<std::unique_ptr<RenderElement>> forwardRenderElements;
|
||||
std::vector<std::unique_ptr<ElementRendererData>> elementRendererData;
|
||||
|
|
@ -108,8 +139,8 @@ namespace Nz
|
|||
|
||||
std::size_t m_depthPassIndex;
|
||||
std::size_t m_forwardPassIndex;
|
||||
std::shared_ptr<RenderBuffer> m_lightDataBuffer;
|
||||
std::unordered_map<AbstractViewer*, ViewerData> m_viewers;
|
||||
std::unordered_map<Light*, LightData> m_lights;
|
||||
std::unordered_map<MaterialPass*, MaterialData> m_materials;
|
||||
std::unordered_map<WorldInstancePtr, std::unordered_map<const InstancedRenderable*, RenderableData>> m_renderables;
|
||||
std::unordered_map<const RenderTarget*, RenderTargetData> m_renderTargets;
|
||||
|
|
@ -118,6 +149,9 @@ namespace Nz
|
|||
std::unordered_set<WorldInstance*> m_invalidatedWorldInstances;
|
||||
std::unordered_set<WorldInstancePtr> m_removedWorldInstances;
|
||||
std::vector<std::unique_ptr<ElementRenderer>> m_elementRenderers;
|
||||
std::vector<ElementRenderer::RenderStates> m_renderStates;
|
||||
std::vector<Light*> m_visibleLights;
|
||||
std::vector<LightDataUbo> m_lightDataBuffers;
|
||||
std::vector<VisibleRenderable> m_visibleRenderables;
|
||||
BakedFrameGraph m_bakedFrameGraph;
|
||||
RenderFrame* m_currentRenderFrame;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,25 @@
|
|||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/ForwardFramePipeline.hpp>
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline std::size_t ForwardFramePipeline::LightKeyHasher::operator()(const LightKey& lightKey) const
|
||||
{
|
||||
std::size_t lightHash = 5;
|
||||
auto CombineHash = [](std::size_t currentHash, std::size_t newHash)
|
||||
{
|
||||
return currentHash * 23 + newHash;
|
||||
};
|
||||
|
||||
std::hash<const Light*> lightPtrHasher;
|
||||
for (std::size_t i = 0; i < lightKey.size(); ++i)
|
||||
lightHash = CombineHash(lightHash, lightPtrHasher(lightKey[i]));
|
||||
|
||||
return lightHash;
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace Nz
|
|||
{
|
||||
class AbstractViewer;
|
||||
class InstancedRenderable;
|
||||
class Light;
|
||||
class RenderFrame;
|
||||
|
||||
class NAZARA_GRAPHICS_API FramePipeline
|
||||
|
|
@ -29,11 +30,13 @@ namespace Nz
|
|||
virtual void InvalidateWorldInstance(WorldInstance* worldInstance) = 0;
|
||||
|
||||
virtual void RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable, UInt32 renderMask) = 0;
|
||||
virtual void RegisterLight(std::shared_ptr<Light> light, UInt32 renderMask) = 0;
|
||||
virtual void RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) = 0;
|
||||
|
||||
virtual void Render(RenderFrame& renderFrame) = 0;
|
||||
|
||||
virtual void UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable) = 0;
|
||||
virtual void UnregisterLight(Light* light) = 0;
|
||||
virtual void UnregisterViewer(AbstractViewer* viewerInstance) = 0;
|
||||
|
||||
FramePipeline& operator=(const FramePipeline&) = delete;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// 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_GRAPHICS_LIGHT_HPP
|
||||
#define NAZARA_GRAPHICS_LIGHT_HPP
|
||||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Signal.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Math/BoundingVolume.hpp>
|
||||
#include <Nazara/Math/Quaternion.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class CommandBufferBuilder;
|
||||
class RenderBuffer;
|
||||
class RenderFrame;
|
||||
|
||||
class NAZARA_GRAPHICS_API Light
|
||||
{
|
||||
public:
|
||||
inline Light();
|
||||
Light(const Light&) = delete;
|
||||
Light(Light&&) noexcept = default;
|
||||
virtual ~Light();
|
||||
|
||||
virtual float ComputeContributionScore(const BoundingVolumef& boundingVolume) const = 0;
|
||||
|
||||
virtual void FillLightData(void* data) = 0;
|
||||
|
||||
inline const BoundingVolumef& GetBoundingVolume() const;
|
||||
|
||||
virtual void UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& scale) = 0;
|
||||
|
||||
Light& operator=(const Light&) = delete;
|
||||
Light& operator=(Light&&) noexcept = default;
|
||||
|
||||
NazaraSignal(OnLightDataInvalided, Light* /*emitter*/);
|
||||
|
||||
protected:
|
||||
inline void UpdateBoundingVolume(const BoundingVolumef& boundingVolume);
|
||||
|
||||
private:
|
||||
BoundingVolumef m_boundingVolume;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/Light.inl>
|
||||
|
||||
#endif // NAZARA_GRAPHICS_LIGHT_HPP
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <cassert>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline Light::Light() :
|
||||
m_boundingVolume(BoundingVolumef::Null())
|
||||
{
|
||||
}
|
||||
|
||||
inline const BoundingVolumef& Light::GetBoundingVolume() const
|
||||
{
|
||||
return m_boundingVolume;
|
||||
}
|
||||
|
||||
inline void Light::UpdateBoundingVolume(const BoundingVolumef& boundingVolume)
|
||||
{
|
||||
m_boundingVolume = boundingVolume;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// 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_GRAPHICS_POINTLIGHT_HPP
|
||||
#define NAZARA_GRAPHICS_POINTLIGHT_HPP
|
||||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Color.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class NAZARA_GRAPHICS_API PointLight : public Light
|
||||
{
|
||||
public:
|
||||
PointLight();
|
||||
PointLight(const PointLight&) = delete;
|
||||
PointLight(PointLight&&) noexcept = default;
|
||||
~PointLight() = default;
|
||||
|
||||
float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override;
|
||||
|
||||
void FillLightData(void* data) override;
|
||||
|
||||
inline float GetAmbientFactor() const;
|
||||
inline float GetDiffuseFactor() const;
|
||||
inline Color GetColor() const;
|
||||
inline const Vector3f& GetPosition() const;
|
||||
inline float GetRadius() const;
|
||||
|
||||
inline void UpdateAmbientFactor(float factor);
|
||||
inline void UpdateColor(Color color);
|
||||
inline void UpdateDiffuseFactor(float factor);
|
||||
inline void UpdatePosition(const Vector3f& position);
|
||||
inline void UpdateRadius(float radius);
|
||||
|
||||
void UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& scale) override;
|
||||
|
||||
PointLight& operator=(const PointLight&) = delete;
|
||||
PointLight& operator=(PointLight&&) noexcept = default;
|
||||
|
||||
private:
|
||||
inline void UpdateBoundingVolume();
|
||||
|
||||
Color m_color;
|
||||
Vector3f m_position;
|
||||
float m_ambientFactor;
|
||||
float m_diffuseFactor;
|
||||
float m_invRadius;
|
||||
float m_radius;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/PointLight.inl>
|
||||
|
||||
#endif // NAZARA_GRAPHICS_POINTLIGHT_HPP
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/PointLight.hpp>
|
||||
#include <cassert>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline PointLight::PointLight() :
|
||||
m_color(Color::White),
|
||||
m_position(Vector3f::Zero()),
|
||||
m_ambientFactor(0.2f),
|
||||
m_diffuseFactor(1.f)
|
||||
{
|
||||
UpdateRadius(5.f);
|
||||
}
|
||||
|
||||
inline float PointLight::GetAmbientFactor() const
|
||||
{
|
||||
return m_ambientFactor;
|
||||
}
|
||||
|
||||
inline Color PointLight::GetColor() const
|
||||
{
|
||||
return m_color;
|
||||
}
|
||||
|
||||
inline const Vector3f& PointLight::GetPosition() const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
inline float PointLight::GetDiffuseFactor() const
|
||||
{
|
||||
return m_diffuseFactor;
|
||||
}
|
||||
|
||||
inline float PointLight::GetRadius() const
|
||||
{
|
||||
return m_radius;
|
||||
}
|
||||
|
||||
inline void PointLight::UpdateAmbientFactor(float factor)
|
||||
{
|
||||
m_ambientFactor = factor;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
|
||||
inline void PointLight::UpdateColor(Color color)
|
||||
{
|
||||
m_color = color;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
|
||||
inline void PointLight::UpdateDiffuseFactor(float factor)
|
||||
{
|
||||
m_diffuseFactor = factor;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
|
||||
inline void PointLight::UpdatePosition(const Vector3f& position)
|
||||
{
|
||||
m_position = position;
|
||||
|
||||
UpdateBoundingVolume();
|
||||
}
|
||||
|
||||
inline void PointLight::UpdateRadius(float radius)
|
||||
{
|
||||
m_radius = radius;
|
||||
m_invRadius = 1.f / m_radius;
|
||||
|
||||
UpdateBoundingVolume();
|
||||
}
|
||||
|
||||
inline void PointLight::UpdateBoundingVolume()
|
||||
{
|
||||
Vector3f extent = Vector3f(m_radius, m_radius, m_radius) * Sqrt3<float>;
|
||||
BoundingVolumef boundingVolume(Boxf(-extent, extent));
|
||||
boundingVolume.Update(m_position);
|
||||
|
||||
Light::UpdateBoundingVolume(boundingVolume); //< will trigger OnLightDataInvalided
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
@ -25,7 +25,7 @@ namespace Nz
|
|||
inline RenderElement(UInt8 elementType);
|
||||
virtual ~RenderElement();
|
||||
|
||||
virtual UInt64 ComputeSortingScore(const Nz::Frustumf& frustum, const RenderQueueRegistry& registry) const = 0;
|
||||
virtual UInt64 ComputeSortingScore(const Frustumf& frustum, const RenderQueueRegistry& registry) const = 0;
|
||||
|
||||
inline UInt8 GetElementType() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// 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_GRAPHICS_SPOTLIGHT_HPP
|
||||
#define NAZARA_GRAPHICS_SPOTLIGHT_HPP
|
||||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Color.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Math/Angle.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class NAZARA_GRAPHICS_API SpotLight : public Light
|
||||
{
|
||||
public:
|
||||
SpotLight();
|
||||
SpotLight(const SpotLight&) = delete;
|
||||
SpotLight(SpotLight&&) noexcept = default;
|
||||
~SpotLight() = default;
|
||||
|
||||
float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override;
|
||||
|
||||
void FillLightData(void* data) override;
|
||||
|
||||
inline float GetAmbientFactor() const;
|
||||
inline float GetDiffuseFactor() const;
|
||||
inline Color GetColor() const;
|
||||
inline const Vector3f& GetDirection() const;
|
||||
inline RadianAnglef GetInnerAngle() const;
|
||||
inline RadianAnglef GetOuterAngle() const;
|
||||
inline const Vector3f& GetPosition() const;
|
||||
inline const Quaternionf& GetRotation() const;
|
||||
inline float GetRadius() const;
|
||||
|
||||
inline void UpdateAmbientFactor(float factor);
|
||||
inline void UpdateColor(Color color);
|
||||
inline void UpdateDiffuseFactor(float factor);
|
||||
inline void UpdateDirection(const Vector3f& direction);
|
||||
inline void UpdateInnerAngle(RadianAnglef innerAngle);
|
||||
inline void UpdateOuterAngle(RadianAnglef outerAngle);
|
||||
inline void UpdatePosition(const Vector3f& position);
|
||||
inline void UpdateRadius(float radius);
|
||||
inline void UpdateRotation(const Quaternionf& rotation);
|
||||
|
||||
void UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& scale) override;
|
||||
|
||||
SpotLight& operator=(const SpotLight&) = delete;
|
||||
SpotLight& operator=(SpotLight&&) noexcept = default;
|
||||
|
||||
private:
|
||||
inline void UpdateBoundingVolume();
|
||||
|
||||
Color m_color;
|
||||
Quaternionf m_rotation;
|
||||
RadianAnglef m_innerAngle;
|
||||
RadianAnglef m_outerAngle;
|
||||
Vector3f m_direction;
|
||||
Vector3f m_position;
|
||||
float m_ambientFactor;
|
||||
float m_diffuseFactor;
|
||||
float m_invRadius;
|
||||
float m_radius;
|
||||
float m_innerAngleCos;
|
||||
float m_outerAngleCos;
|
||||
float m_outerAngleTan;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/SpotLight.inl>
|
||||
|
||||
#endif // NAZARA_GRAPHICS_SPOTLIGHT_HPP
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/SpotLight.hpp>
|
||||
#include <cassert>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline SpotLight::SpotLight() :
|
||||
m_color(Color::White),
|
||||
m_position(Vector3f::Zero()),
|
||||
m_ambientFactor(0.2f),
|
||||
m_diffuseFactor(1.f)
|
||||
{
|
||||
UpdateInnerAngle(DegreeAnglef(30.f));
|
||||
UpdateOuterAngle(DegreeAnglef(45.f));
|
||||
UpdateRadius(5.f);
|
||||
UpdateRotation(Quaternionf::Identity());
|
||||
}
|
||||
|
||||
inline float SpotLight::GetAmbientFactor() const
|
||||
{
|
||||
return m_ambientFactor;
|
||||
}
|
||||
|
||||
inline Color SpotLight::GetColor() const
|
||||
{
|
||||
return m_color;
|
||||
}
|
||||
|
||||
inline const Vector3f& SpotLight::GetDirection() const
|
||||
{
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
inline RadianAnglef SpotLight::GetInnerAngle() const
|
||||
{
|
||||
return m_innerAngle;
|
||||
}
|
||||
|
||||
inline RadianAnglef SpotLight::GetOuterAngle() const
|
||||
{
|
||||
return m_outerAngle;
|
||||
}
|
||||
|
||||
inline const Vector3f& SpotLight::GetPosition() const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
inline const Quaternionf& SpotLight::GetRotation() const
|
||||
{
|
||||
return m_rotation;
|
||||
}
|
||||
|
||||
inline float SpotLight::GetDiffuseFactor() const
|
||||
{
|
||||
return m_diffuseFactor;
|
||||
}
|
||||
|
||||
inline float SpotLight::GetRadius() const
|
||||
{
|
||||
return m_radius;
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateAmbientFactor(float factor)
|
||||
{
|
||||
m_ambientFactor = factor;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateColor(Color color)
|
||||
{
|
||||
m_color = color;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateDiffuseFactor(float factor)
|
||||
{
|
||||
m_diffuseFactor = factor;
|
||||
|
||||
OnLightDataInvalided(this);
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateDirection(const Vector3f& direction)
|
||||
{
|
||||
UpdateRotation(Quaternionf::RotationBetween(Vector3f::Forward(), direction));
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateInnerAngle(RadianAnglef innerAngle)
|
||||
{
|
||||
m_innerAngle = innerAngle;
|
||||
m_innerAngleCos = m_innerAngle.GetCos();
|
||||
|
||||
UpdateBoundingVolume();
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateOuterAngle(RadianAnglef outerAngle)
|
||||
{
|
||||
m_outerAngle = outerAngle;
|
||||
m_outerAngleCos = m_outerAngle.GetCos();
|
||||
m_outerAngleTan = m_outerAngle.GetTan();
|
||||
|
||||
UpdateBoundingVolume();
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdatePosition(const Vector3f& position)
|
||||
{
|
||||
m_position = position;
|
||||
|
||||
UpdateBoundingVolume();
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateRadius(float radius)
|
||||
{
|
||||
m_radius = radius;
|
||||
m_invRadius = 1.f / m_radius;
|
||||
|
||||
UpdateBoundingVolume();
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateRotation(const Quaternionf& rotation)
|
||||
{
|
||||
m_rotation = rotation;
|
||||
m_direction = rotation * Vector3f::Forward();
|
||||
|
||||
UpdateBoundingVolume();
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateBoundingVolume()
|
||||
{
|
||||
// We make a box center in the origin
|
||||
Boxf box(Vector3f::Zero());
|
||||
|
||||
// We compute the other points
|
||||
Vector3f base(Vector3f::Forward() * m_radius);
|
||||
|
||||
// Now we need the radius of the projected circle depending on the distance
|
||||
// Tangent = Opposite/Adjacent <=> Opposite = Adjacent * Tangent
|
||||
float radius = m_radius * m_outerAngleTan;
|
||||
Vector3f lExtend = Vector3f::Left() * radius;
|
||||
Vector3f uExtend = Vector3f::Up() * radius;
|
||||
|
||||
// And we add the four extremities of our pyramid
|
||||
box.ExtendTo(base + lExtend + uExtend);
|
||||
box.ExtendTo(base + lExtend - uExtend);
|
||||
box.ExtendTo(base - lExtend + uExtend);
|
||||
box.ExtendTo(base - lExtend - uExtend);
|
||||
|
||||
BoundingVolumef boundingVolume(box);
|
||||
boundingVolume.Update(Matrix4f::Transform(m_position, m_rotation));
|
||||
|
||||
Light::UpdateBoundingVolume(boundingVolume); //< will trigger OnLightDataInvalided
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
@ -18,44 +18,15 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
class MaterialPass;
|
||||
class RenderDevice;
|
||||
class RenderPipeline;
|
||||
class RenderSpriteChain;
|
||||
class ShaderBinding;
|
||||
|
||||
class NAZARA_GRAPHICS_API SpriteChainRenderer : public ElementRenderer
|
||||
{
|
||||
public:
|
||||
SpriteChainRenderer(RenderDevice& device, std::size_t maxVertexBufferSize = 32 * 1024);
|
||||
~SpriteChainRenderer() = default;
|
||||
|
||||
std::unique_ptr<ElementRendererData> InstanciateData() override;
|
||||
void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, const RenderStates& renderStates, const Pointer<const RenderElement>* elements, std::size_t elementCount) override;
|
||||
void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, const Pointer<const RenderElement>* elements, std::size_t elementCount) override;
|
||||
void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame) override;
|
||||
|
||||
private:
|
||||
struct BufferCopy
|
||||
{
|
||||
RenderBuffer* targetBuffer;
|
||||
UploadPool::Allocation* allocation;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
struct VertexBufferPool
|
||||
{
|
||||
std::vector<std::shared_ptr<RenderBuffer>> vertexBuffers;
|
||||
};
|
||||
|
||||
std::shared_ptr<RenderBuffer> m_indexBuffer;
|
||||
std::shared_ptr<VertexBufferPool> m_vertexBufferPool;
|
||||
std::size_t m_maxVertexBufferSize;
|
||||
std::size_t m_maxVertexCount;
|
||||
std::vector<BufferCopy> m_pendingCopies;
|
||||
std::vector<ShaderBinding::Binding> m_bindingCache;
|
||||
RenderDevice& m_device;
|
||||
};
|
||||
|
||||
class Texture;
|
||||
class VertexDeclaration;
|
||||
class WorldInstance;
|
||||
|
||||
struct SpriteChainRendererData : public ElementRendererData
|
||||
{
|
||||
struct DrawCall
|
||||
|
|
@ -79,6 +50,62 @@ namespace Nz
|
|||
std::vector<std::shared_ptr<RenderBuffer>> vertexBuffers;
|
||||
std::vector<ShaderBindingPtr> shaderBindings;
|
||||
};
|
||||
|
||||
class NAZARA_GRAPHICS_API SpriteChainRenderer final : public ElementRenderer
|
||||
{
|
||||
public:
|
||||
SpriteChainRenderer(RenderDevice& device, std::size_t maxVertexBufferSize = 32 * 1024);
|
||||
~SpriteChainRenderer() = default;
|
||||
|
||||
std::unique_ptr<ElementRendererData> InstanciateData() override;
|
||||
void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates) override;
|
||||
void PrepareEnd(RenderFrame& currentFrame, ElementRendererData& rendererData) override;
|
||||
void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t elementCount, const Pointer<const RenderElement>* elements) override;
|
||||
void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame) override;
|
||||
|
||||
private:
|
||||
void Flush();
|
||||
void FlushDrawCall();
|
||||
void FlushDrawData();
|
||||
|
||||
struct BufferCopy
|
||||
{
|
||||
RenderBuffer* targetBuffer;
|
||||
UploadPool::Allocation* allocation;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
struct PendingData
|
||||
{
|
||||
std::size_t firstQuadIndex = 0;
|
||||
SpriteChainRendererData::DrawCall* currentDrawCall = nullptr;
|
||||
UploadPool::Allocation* currentAllocation = nullptr;
|
||||
UInt8* currentAllocationMemPtr = nullptr;
|
||||
const VertexDeclaration* currentVertexDeclaration = nullptr;
|
||||
RenderBuffer* currentVertexBuffer = nullptr;
|
||||
const MaterialPass* currentMaterialPass = nullptr;
|
||||
const RenderPipeline* currentPipeline = nullptr;
|
||||
const ShaderBinding* currentShaderBinding = nullptr;
|
||||
const Texture* currentTextureOverlay = nullptr;
|
||||
const WorldInstance* currentWorldInstance = nullptr;
|
||||
RenderBufferView currentLightData;
|
||||
Recti currentScissorBox = Recti(-1, -1, -1, -1);
|
||||
};
|
||||
|
||||
struct VertexBufferPool
|
||||
{
|
||||
std::vector<std::shared_ptr<RenderBuffer>> vertexBuffers;
|
||||
};
|
||||
|
||||
std::shared_ptr<RenderBuffer> m_indexBuffer;
|
||||
std::shared_ptr<VertexBufferPool> m_vertexBufferPool;
|
||||
std::size_t m_maxVertexBufferSize;
|
||||
std::size_t m_maxVertexCount;
|
||||
std::vector<BufferCopy> m_pendingCopies;
|
||||
std::vector<ShaderBinding::Binding> m_bindingCache;
|
||||
PendingData m_pendingData;
|
||||
RenderDevice& m_device;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/SpriteChainRenderer.inl>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
namespace Nz
|
||||
{
|
||||
class RenderPipeline;
|
||||
class RenderSubmesh;
|
||||
class ShaderBinding;
|
||||
|
||||
class NAZARA_GRAPHICS_API SubmeshRenderer : public ElementRenderer
|
||||
|
|
@ -24,8 +25,8 @@ namespace Nz
|
|||
~SubmeshRenderer() = default;
|
||||
|
||||
std::unique_ptr<ElementRendererData> InstanciateData() override;
|
||||
void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, const RenderStates& renderStates, const Pointer<const RenderElement>* elements, std::size_t elementCount) override;
|
||||
void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, const Pointer<const RenderElement>* elements, std::size_t elementCount) override;
|
||||
void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates) override;
|
||||
void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t elementCount, const Pointer<const RenderElement>* elements) override;
|
||||
void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame) override;
|
||||
|
||||
private:
|
||||
|
|
@ -40,10 +41,18 @@ namespace Nz
|
|||
const RenderBuffer* vertexBuffer;
|
||||
const RenderPipeline* renderPipeline;
|
||||
const ShaderBinding* shaderBinding;
|
||||
std::size_t firstIndex;
|
||||
std::size_t indexCount;
|
||||
Recti scissorBox;
|
||||
};
|
||||
|
||||
struct DrawCallIndices
|
||||
{
|
||||
std::size_t start;
|
||||
std::size_t count;
|
||||
};
|
||||
|
||||
std::unordered_map<const RenderSubmesh*, DrawCallIndices> drawCallPerElement;
|
||||
std::vector<DrawCall> drawCalls;
|
||||
std::vector<ShaderBindingPtr> shaderBindings;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <Nazara/Core/ECS.hpp>
|
||||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Graphics/Components/GraphicsComponent.hpp>
|
||||
#include <Nazara/Graphics/Components/LightComponent.hpp>
|
||||
#include <Nazara/Utility/Node.hpp>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
|
@ -39,6 +40,7 @@ namespace Nz
|
|||
private:
|
||||
void OnCameraDestroy(entt::registry& registry, entt::entity entity);
|
||||
void OnGraphicsDestroy(entt::registry& registry, entt::entity entity);
|
||||
void OnLightDestroy(entt::registry& registry, entt::entity entity);
|
||||
void OnNodeDestroy(entt::registry& registry, entt::entity entity);
|
||||
void UpdateInstances(entt::registry& registry);
|
||||
void UpdateVisibility(entt::registry& registry);
|
||||
|
|
@ -56,18 +58,32 @@ namespace Nz
|
|||
NazaraSlot(Node, OnNodeInvalidation, onNodeInvalidation);
|
||||
};
|
||||
|
||||
struct LightEntity
|
||||
{
|
||||
NazaraSlot(LightComponent, OnLightAttached, onLightAttached);
|
||||
NazaraSlot(LightComponent, OnLightDetach, onLightDetach);
|
||||
NazaraSlot(LightComponent, OnVisibilityUpdate, onVisibilityUpdate);
|
||||
NazaraSlot(Node, OnNodeInvalidation, onNodeInvalidation);
|
||||
};
|
||||
|
||||
entt::connection m_cameraDestroyConnection;
|
||||
entt::connection m_graphicsDestroyConnection;
|
||||
entt::connection m_lightDestroyConnection;
|
||||
entt::connection m_nodeDestroyConnection;
|
||||
entt::observer m_cameraConstructObserver;
|
||||
entt::observer m_graphicsConstructObserver;
|
||||
entt::observer m_lightConstructObserver;
|
||||
std::set<entt::entity> m_invalidatedCameraNode;
|
||||
std::set<entt::entity> m_invalidatedWorldNode;
|
||||
std::set<entt::entity> m_invalidatedGfxWorldNode;
|
||||
std::set<entt::entity> m_invalidatedLightWorldNode;
|
||||
std::unique_ptr<FramePipeline> m_pipeline;
|
||||
std::unordered_map<entt::entity, CameraEntity> m_cameraEntities;
|
||||
std::unordered_map<entt::entity, GraphicsEntity> m_graphicsEntities;
|
||||
std::unordered_set<entt::entity> m_newlyHiddenEntities;
|
||||
std::unordered_set<entt::entity> m_newlyVisibleEntities;
|
||||
std::unordered_map<entt::entity, LightEntity> m_lightEntities;
|
||||
std::unordered_set<entt::entity> m_newlyHiddenGfxEntities;
|
||||
std::unordered_set<entt::entity> m_newlyVisibleGfxEntities;
|
||||
std::unordered_set<entt::entity> m_newlyHiddenLightEntities;
|
||||
std::unordered_set<entt::entity> m_newlyVisibleLightEntities;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ namespace Nz
|
|||
|
||||
BoundingVolume& ExtendTo(const BoundingVolume& volume);
|
||||
|
||||
bool Intersect(const Box<T>& box) const;
|
||||
|
||||
bool IsFinite() const;
|
||||
bool IsInfinite() const;
|
||||
bool IsNull() const;
|
||||
|
|
|
|||
|
|
@ -169,11 +169,28 @@ namespace Nz
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool BoundingVolume<T>::Intersect(const Box<T>& box) const
|
||||
{
|
||||
switch (extend)
|
||||
{
|
||||
case Extend::Infinite:
|
||||
return true;
|
||||
|
||||
case Extend::Finite:
|
||||
return aabb.Intersect(box);
|
||||
|
||||
case Extend::Null:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Checks whether the volume is finite
|
||||
* \return true if extend is Extend::Finite
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
bool BoundingVolume<T>::IsFinite() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -459,17 +459,12 @@ namespace Nz
|
|||
{
|
||||
T left = std::max(x, box.x);
|
||||
T right = std::min(x + width, box.x + box.width);
|
||||
if (left >= right)
|
||||
return false;
|
||||
|
||||
T top = std::max(y, box.y);
|
||||
T bottom = std::min(y + height, box.y + box.height);
|
||||
if (top >= bottom)
|
||||
return false;
|
||||
|
||||
T up = std::max(z, box.z);
|
||||
T down = std::min(z + depth, box.z + box.depth);
|
||||
if (up >= down)
|
||||
|
||||
if (left >= right || top >= bottom || up >= down)
|
||||
return false;
|
||||
|
||||
if (intersection)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace Nz
|
|||
class RenderBufferView
|
||||
{
|
||||
public:
|
||||
inline RenderBufferView();
|
||||
inline RenderBufferView(RenderBuffer* buffer);
|
||||
inline RenderBufferView(RenderBuffer* buffer, UInt64 offset, UInt64 size);
|
||||
RenderBufferView(const RenderBufferView&) = default;
|
||||
|
|
@ -26,6 +27,11 @@ namespace Nz
|
|||
inline UInt64 GetOffset() const;
|
||||
inline UInt64 GetSize() const;
|
||||
|
||||
inline explicit operator bool() const;
|
||||
|
||||
inline bool operator==(const RenderBufferView& rhs) const;
|
||||
inline bool operator!=(const RenderBufferView& rhs) const;
|
||||
|
||||
RenderBufferView& operator=(const RenderBufferView&) = default;
|
||||
RenderBufferView& operator=(RenderBufferView&&) = default;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
inline RenderBufferView::RenderBufferView() :
|
||||
m_offset(0),
|
||||
m_size(0),
|
||||
m_buffer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
inline RenderBufferView::RenderBufferView(RenderBuffer* buffer) :
|
||||
RenderBufferView(buffer, 0, buffer->GetSize())
|
||||
{
|
||||
|
|
@ -34,6 +41,21 @@ namespace Nz
|
|||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
inline RenderBufferView::operator bool() const
|
||||
{
|
||||
return m_buffer != nullptr;
|
||||
}
|
||||
|
||||
inline bool RenderBufferView::operator==(const RenderBufferView& rhs) const
|
||||
{
|
||||
return m_buffer == rhs.m_buffer && m_offset == rhs.m_offset && m_size == rhs.m_size;
|
||||
}
|
||||
|
||||
inline bool RenderBufferView::operator!=(const RenderBufferView& rhs) const
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Renderer/DebugOff.hpp>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ namespace Nz
|
|||
|
||||
NAZARA_UTILITY_API Boxf ComputeAABB(SparsePtr<const Vector3f> positionPtr, std::size_t vertexCount);
|
||||
NAZARA_UTILITY_API void ComputeBoxIndexVertexCount(const Vector3ui& subdivision, std::size_t* indexCount, std::size_t* vertexCount);
|
||||
NAZARA_UTILITY_API UInt64 ComputeCacheMissCount(IndexIterator indices, std::size_t indexCount);
|
||||
NAZARA_UTILITY_API UInt64 ComputeCacheMissCount(IndexIterator indices, UInt32 indexCount);
|
||||
NAZARA_UTILITY_API void ComputeConeIndexVertexCount(unsigned int subdivision, std::size_t* indexCount, std::size_t* vertexCount);
|
||||
NAZARA_UTILITY_API void ComputeCubicSphereIndexVertexCount(unsigned int subdivision, std::size_t* indexCount, std::size_t* vertexCount);
|
||||
NAZARA_UTILITY_API void ComputeIcoSphereIndexVertexCount(unsigned int recursionLevel, std::size_t* indexCount, std::size_t* vertexCount);
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ namespace Nz
|
|||
UInt32 channelCount = 0;
|
||||
UInt64 frameCount = 0;
|
||||
UInt64 sampleCount = 0;
|
||||
UInt64 sampleRate = 0;
|
||||
UInt32 sampleRate = 0;
|
||||
|
||||
ud.metadataCallback = [&](const FLAC__StreamDecoder* /*decoder*/, const FLAC__StreamMetadata* meta)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Components/LightComponent.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/DirectionalLight.hpp>
|
||||
#include <Nazara/Graphics/Enums.hpp>
|
||||
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
|
||||
#include <Nazara/Math/Vector2.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Math/Vector4.hpp>
|
||||
#include <limits>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
float DirectionalLight::ComputeContributionScore(const BoundingVolumef& /*boundingVolume*/) const
|
||||
{
|
||||
return -std::numeric_limits<float>::infinity();
|
||||
}
|
||||
|
||||
void DirectionalLight::FillLightData(void* data)
|
||||
{
|
||||
auto lightOffset = PredefinedLightData::GetOffsets();
|
||||
|
||||
AccessByOffset<UInt32&>(data, lightOffset.lightMemberOffsets.type) = UnderlyingCast(BasicLightType::Directional);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.color) = Vector4f(m_color.r / 255.f, m_color.g / 255.f, m_color.b / 255.f, m_color.a / 255.f);
|
||||
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.factor) = Vector2f(m_ambientFactor, m_diffuseFactor);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter1) = Vector4f(m_direction.x, m_direction.y, m_direction.z, 0.f);
|
||||
AccessByOffset<UInt8&>(data, lightOffset.lightMemberOffsets.shadowMappingFlag) = 0;
|
||||
}
|
||||
|
||||
void DirectionalLight::UpdateTransform(const Vector3f& /*position*/, const Quaternionf& rotation, const Vector3f& /*scale*/)
|
||||
{
|
||||
UpdateRotation(rotation);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,11 @@ namespace Nz
|
|||
{
|
||||
ElementRenderer::~ElementRenderer() = default;
|
||||
|
||||
void ElementRenderer::Prepare(const ViewerInstance& /*viewerInstance*/, ElementRendererData& /*rendererData*/, RenderFrame& /*currentFrame*/, const RenderStates& /*renderStates*/, const Pointer<const RenderElement>* /*elements*/, std::size_t /*elementCount*/)
|
||||
void ElementRenderer::Prepare(const ViewerInstance& /*viewerInstance*/, ElementRendererData& /*rendererData*/, RenderFrame& /*currentFrame*/, std::size_t /*elementCount*/, const Pointer<const RenderElement>* /*elements*/, const RenderStates* /*renderStates*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ElementRenderer::PrepareEnd(RenderFrame& /*currentFrame*/, ElementRendererData& /*rendererData*/)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Graphics/InstancedRenderable.hpp>
|
||||
#include <Nazara/Graphics/Material.hpp>
|
||||
#include <Nazara/Graphics/PointLight.hpp>
|
||||
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
|
||||
#include <Nazara/Graphics/RenderElement.hpp>
|
||||
#include <Nazara/Graphics/SpriteChainRenderer.hpp>
|
||||
|
|
@ -37,37 +38,6 @@ namespace Nz
|
|||
m_elementRenderers.resize(BasicRenderElementCount);
|
||||
m_elementRenderers[UnderlyingCast(BasicRenderElement::SpriteChain)] = std::make_unique<SpriteChainRenderer>(*Graphics::Instance()->GetRenderDevice());
|
||||
m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique<SubmeshRenderer>();
|
||||
|
||||
auto lightOffset = PredefinedLightData::GetOffsets();
|
||||
|
||||
m_lightDataBuffer = Graphics::Instance()->GetRenderDevice()->InstantiateBuffer(BufferType::Uniform, lightOffset.totalSize, BufferUsage::DeviceLocal | BufferUsage::Write);
|
||||
|
||||
std::vector<UInt8> staticLightData(lightOffset.totalSize);
|
||||
/*AccessByOffset<UInt32&>(staticLightData.data(), lightOffset.lightCountOffset) = 1;
|
||||
AccessByOffset<UInt32&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.type) = 0;
|
||||
AccessByOffset<Vector4f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.color) = Vector4f(1.f, 1.f, 1.f, 1.f);
|
||||
AccessByOffset<Vector2f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.factor) = Vector2f(0.2f, 1.f);
|
||||
AccessByOffset<Vector4f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.parameter1) = Vector4f(0.f, 0.f, -1.f, 1.f);
|
||||
AccessByOffset<UInt8&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.shadowMappingFlag) = 0;*/
|
||||
|
||||
AccessByOffset<UInt32&>(staticLightData.data(), lightOffset.lightCountOffset) = 1;
|
||||
AccessByOffset<UInt32&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.type) = 1;
|
||||
AccessByOffset<Vector4f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.color) = Vector4f(1.f, 1.f, 1.f, 1.f);
|
||||
AccessByOffset<Vector2f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.factor) = Vector2f(0.2f, 1.f);
|
||||
AccessByOffset<Vector4f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.parameter1) = Vector4f(0.f, 0.f, 0.f, 1.f / 3.f);
|
||||
AccessByOffset<UInt8&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.shadowMappingFlag) = 0;
|
||||
|
||||
/*AccessByOffset<UInt32&>(staticLightData.data(), lightOffset.lightCountOffset) = 1;
|
||||
AccessByOffset<UInt32&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.type) = 2;
|
||||
AccessByOffset<Vector4f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.color) = Vector4f(1.f, 1.f, 1.f, 1.f);
|
||||
AccessByOffset<Vector2f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.factor) = Vector2f(0.2f, 1.f);
|
||||
AccessByOffset<Vector4f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.parameter1) = Vector4f(0.f, 0.f, 0.f, 1.f / 3.f);
|
||||
AccessByOffset<Vector4f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.parameter2) = Vector4f(0.f, 0.f, -1.f, 0.f);
|
||||
AccessByOffset<Vector4f&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.parameter3) = Vector4f(DegreeAnglef(15.f).GetCos(), DegreeAnglef(20.f).GetCos(), 0.f, 0.f);
|
||||
AccessByOffset<UInt8&>(staticLightData.data(), lightOffset.lightsOffset + lightOffset.lightMemberOffsets.shadowMappingFlag) = 0;*/
|
||||
|
||||
if (!m_lightDataBuffer->Fill(staticLightData.data(), 0, staticLightData.size()))
|
||||
throw std::runtime_error("failed to fill light data buffer");
|
||||
}
|
||||
|
||||
void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance)
|
||||
|
|
@ -159,6 +129,21 @@ namespace Nz
|
|||
}
|
||||
}
|
||||
|
||||
void ForwardFramePipeline::RegisterLight(std::shared_ptr<Light> light, UInt32 renderMask)
|
||||
{
|
||||
auto& lightData = m_lights[light.get()];
|
||||
lightData.light = std::move(light);
|
||||
lightData.renderMask = renderMask;
|
||||
lightData.onLightInvalidated.Connect(lightData.light->OnLightDataInvalided, [this](Light*)
|
||||
{
|
||||
for (auto&& [viewer, viewerData] : m_viewers)
|
||||
{
|
||||
viewerData.rebuildForwardPass = true;
|
||||
viewerData.prepare = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder)
|
||||
{
|
||||
auto& viewerData = m_viewers.emplace(viewerInstance, ViewerData{}).first->second;
|
||||
|
|
@ -226,6 +211,9 @@ namespace Nz
|
|||
return currentHash * 23 + newHash;
|
||||
};
|
||||
|
||||
PredefinedLightData lightOffsets = PredefinedLightData::GetOffsets();
|
||||
std::size_t lightUboAlignedSize = Align(lightOffsets.totalSize, graphics->GetRenderDevice()->GetDeviceInfo().limits.minUniformBufferOffsetAlignment);
|
||||
|
||||
// Render queues handling
|
||||
for (auto&& [viewer, data] : m_viewers)
|
||||
{
|
||||
|
|
@ -307,12 +295,104 @@ namespace Nz
|
|||
{
|
||||
renderFrame.PushForRelease(std::move(viewerData.forwardRenderElements));
|
||||
viewerData.forwardRenderElements.clear();
|
||||
|
||||
for (const auto& renderableData : m_visibleRenderables)
|
||||
renderableData.instancedRenderable->BuildElement(m_forwardPassIndex, *renderableData.worldInstance, viewerData.forwardRenderElements);
|
||||
|
||||
viewerData.forwardRegistry.Clear();
|
||||
viewerData.forwardRenderQueue.Clear();
|
||||
viewerData.lightBufferPerLights.clear();
|
||||
viewerData.lightPerRenderElement.clear();
|
||||
|
||||
for (auto& lightDataUbo : m_lightDataBuffers)
|
||||
{
|
||||
lightDataUbo.allocation = nullptr;
|
||||
lightDataUbo.offset = 0;
|
||||
}
|
||||
|
||||
for (const auto& renderableData : m_visibleRenderables)
|
||||
{
|
||||
BoundingVolumef renderableBoundingVolume(renderableData.instancedRenderable->GetAABB());
|
||||
renderableBoundingVolume.Update(renderableData.worldInstance->GetWorldMatrix());
|
||||
|
||||
// Select lights (TODO: Cull lights in frustum)
|
||||
m_visibleLights.clear();
|
||||
for (auto&& [light, lightData] : m_lights)
|
||||
{
|
||||
const BoundingVolumef& boundingVolume = light->GetBoundingVolume();
|
||||
if ((renderMask & lightData.renderMask) && boundingVolume.Intersect(renderableBoundingVolume.aabb))
|
||||
m_visibleLights.push_back(light);
|
||||
}
|
||||
|
||||
// Sort lights
|
||||
std::sort(m_visibleLights.begin(), m_visibleLights.end(), [&](Light* lhs, Light* rhs)
|
||||
{
|
||||
return lhs->ComputeContributionScore(renderableBoundingVolume) < rhs->ComputeContributionScore(renderableBoundingVolume);
|
||||
});
|
||||
|
||||
std::size_t lightCount = std::min(m_visibleLights.size(), MaxLightCountPerDraw);
|
||||
|
||||
LightKey lightKey;
|
||||
lightKey.fill(nullptr);
|
||||
for (std::size_t i = 0; i < lightCount; ++i)
|
||||
lightKey[i] = m_visibleLights[i];
|
||||
|
||||
RenderBufferView lightUboView;
|
||||
|
||||
auto it = viewerData.lightBufferPerLights.find(lightKey);
|
||||
if (it == viewerData.lightBufferPerLights.end())
|
||||
{
|
||||
// Prepare light ubo upload
|
||||
|
||||
// Find light ubo
|
||||
LightDataUbo* targetLightData = nullptr;
|
||||
for (auto& lightUboData : m_lightDataBuffers)
|
||||
{
|
||||
if (lightUboData.offset + lightUboAlignedSize <= lightUboData.renderBuffer->GetSize())
|
||||
{
|
||||
targetLightData = &lightUboData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetLightData)
|
||||
{
|
||||
// Allocate a new light UBO
|
||||
auto& lightUboData = m_lightDataBuffers.emplace_back();
|
||||
lightUboData.renderBuffer = graphics->GetRenderDevice()->InstantiateBuffer(BufferType::Uniform, 256 * lightUboAlignedSize, BufferUsage::DeviceLocal | BufferUsage::Dynamic | BufferUsage::Write);
|
||||
|
||||
targetLightData = &lightUboData;
|
||||
}
|
||||
|
||||
assert(targetLightData);
|
||||
if (!targetLightData->allocation)
|
||||
targetLightData->allocation = &uploadPool.Allocate(targetLightData->renderBuffer->GetSize());
|
||||
|
||||
void* lightDataPtr = static_cast<UInt8*>(targetLightData->allocation->mappedPtr) + targetLightData->offset;
|
||||
AccessByOffset<UInt32&>(lightDataPtr, lightOffsets.lightCountOffset) = SafeCast<UInt32>(lightCount);
|
||||
|
||||
UInt8* lightPtr = static_cast<UInt8*>(lightDataPtr) + lightOffsets.lightsOffset;
|
||||
for (std::size_t i = 0; i < lightCount; ++i)
|
||||
{
|
||||
m_visibleLights[i]->FillLightData(lightPtr);
|
||||
lightPtr += lightOffsets.lightSize;
|
||||
}
|
||||
|
||||
// Associate render element with light ubo
|
||||
lightUboView = RenderBufferView(targetLightData->renderBuffer.get(), targetLightData->offset, lightUboAlignedSize);
|
||||
|
||||
targetLightData->offset += lightUboAlignedSize;
|
||||
|
||||
viewerData.lightBufferPerLights.emplace(lightKey, lightUboView);
|
||||
}
|
||||
else
|
||||
lightUboView = it->second;
|
||||
|
||||
std::size_t previousCount = viewerData.forwardRenderElements.size();
|
||||
renderableData.instancedRenderable->BuildElement(m_forwardPassIndex, *renderableData.worldInstance, viewerData.forwardRenderElements);
|
||||
for (std::size_t i = previousCount; i < viewerData.forwardRenderElements.size(); ++i)
|
||||
{
|
||||
const RenderElement* element = viewerData.forwardRenderElements[i].get();
|
||||
viewerData.lightPerRenderElement.emplace(element, lightUboView);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& renderElement : viewerData.forwardRenderElements)
|
||||
{
|
||||
renderElement->Register(viewerData.forwardRegistry);
|
||||
|
|
@ -320,6 +400,25 @@ namespace Nz
|
|||
}
|
||||
|
||||
viewerData.forwardRegistry.Finalize();
|
||||
|
||||
renderFrame.Execute([&](CommandBufferBuilder& builder)
|
||||
{
|
||||
builder.BeginDebugRegion("Light UBO Update", Color::Yellow);
|
||||
{
|
||||
builder.PreTransferBarrier();
|
||||
|
||||
for (auto& lightUboData : m_lightDataBuffers)
|
||||
{
|
||||
if (!lightUboData.allocation)
|
||||
continue;
|
||||
|
||||
builder.CopyBuffer(*lightUboData.allocation, RenderBufferView(lightUboData.renderBuffer.get(), 0, lightUboData.offset));
|
||||
}
|
||||
|
||||
builder.PostTransferBarrier();
|
||||
}
|
||||
builder.EndDebugRegion();
|
||||
}, QueueType::Transfer);
|
||||
}
|
||||
|
||||
viewerData.forwardRenderQueue.Sort([&](const RenderElement* element)
|
||||
|
|
@ -353,20 +452,41 @@ namespace Nz
|
|||
|
||||
const auto& viewerInstance = viewer->GetViewerInstance();
|
||||
|
||||
ElementRenderer::RenderStates renderStates;
|
||||
renderStates.lightData = m_lightDataBuffer;
|
||||
|
||||
ProcessRenderQueue(viewerData.depthPrepassRenderQueue, [&](std::size_t elementType, const Pointer<const RenderElement>* elements, std::size_t elementCount)
|
||||
{
|
||||
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
|
||||
elementRenderer.Prepare(viewerInstance, *rendererData[elementType], renderFrame, renderStates, elements, elementCount);
|
||||
|
||||
m_renderStates.clear();
|
||||
m_renderStates.resize(elementCount);
|
||||
|
||||
elementRenderer.Prepare(viewerInstance, *rendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data());
|
||||
});
|
||||
|
||||
for (std::size_t i = 0; i < m_elementRenderers.size(); ++i)
|
||||
m_elementRenderers[i]->PrepareEnd(renderFrame, *rendererData[i]);
|
||||
|
||||
auto& lightPerRenderElement = viewerData.lightPerRenderElement;
|
||||
ProcessRenderQueue(viewerData.forwardRenderQueue, [&](std::size_t elementType, const Pointer<const RenderElement>* elements, std::size_t elementCount)
|
||||
{
|
||||
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
|
||||
elementRenderer.Prepare(viewerInstance, *rendererData[elementType], renderFrame, renderStates, elements, elementCount);
|
||||
|
||||
m_renderStates.clear();
|
||||
|
||||
m_renderStates.reserve(elementCount);
|
||||
for (std::size_t i = 0; i < elementCount; ++i)
|
||||
{
|
||||
auto it = lightPerRenderElement.find(elements[i]);
|
||||
assert(it != lightPerRenderElement.end());
|
||||
|
||||
auto& renderStates = m_renderStates.emplace_back();
|
||||
renderStates.lightData = it->second;
|
||||
}
|
||||
|
||||
elementRenderer.Prepare(viewerInstance, *rendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data());
|
||||
});
|
||||
|
||||
for (std::size_t i = 0; i < m_elementRenderers.size(); ++i)
|
||||
m_elementRenderers[i]->PrepareEnd(renderFrame, *rendererData[i]);
|
||||
}
|
||||
|
||||
if (m_bakedFrameGraph.Resize(renderFrame))
|
||||
|
|
@ -493,6 +613,17 @@ namespace Nz
|
|||
}
|
||||
}
|
||||
|
||||
void ForwardFramePipeline::UnregisterLight(Light* light)
|
||||
{
|
||||
m_lights.erase(light);
|
||||
|
||||
for (auto&& [viewer, viewerData] : m_viewers)
|
||||
{
|
||||
viewerData.rebuildForwardPass = true;
|
||||
viewerData.prepare = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ForwardFramePipeline::UnregisterViewer(AbstractViewer* viewerInstance)
|
||||
{
|
||||
m_viewers.erase(viewerInstance);
|
||||
|
|
@ -544,7 +675,7 @@ namespace Nz
|
|||
ProcessRenderQueue(viewerData.depthPrepassRenderQueue, [&](std::size_t elementType, const Pointer<const RenderElement>* elements, std::size_t elementCount)
|
||||
{
|
||||
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
|
||||
elementRenderer.Render(viewerInstance, *viewerData.elementRendererData[elementType], builder, elements, elementCount);
|
||||
elementRenderer.Render(viewerInstance, *viewerData.elementRendererData[elementType], builder, elementCount, elements);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -576,7 +707,7 @@ namespace Nz
|
|||
ProcessRenderQueue(viewerData.forwardRenderQueue, [&](std::size_t elementType, const Pointer<const RenderElement>* elements, std::size_t elementCount)
|
||||
{
|
||||
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
|
||||
elementRenderer.Render(viewerInstance , *viewerData.elementRendererData[elementType], builder, elements, elementCount);
|
||||
elementRenderer.Render(viewerInstance, *viewerData.elementRendererData[elementType], builder, elementCount, elements);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
Light::~Light() = default;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/PointLight.hpp>
|
||||
#include <Nazara/Graphics/Enums.hpp>
|
||||
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
|
||||
#include <Nazara/Math/Vector2.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Math/Vector4.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
float PointLight::ComputeContributionScore(const BoundingVolumef& boundingVolume) const
|
||||
{
|
||||
// TODO: take luminosity/radius into account
|
||||
return Vector3f::SquaredDistance(m_position, boundingVolume.aabb.GetCenter());
|
||||
}
|
||||
|
||||
void PointLight::FillLightData(void* data)
|
||||
{
|
||||
auto lightOffset = PredefinedLightData::GetOffsets();
|
||||
|
||||
AccessByOffset<UInt32&>(data, lightOffset.lightMemberOffsets.type) = UnderlyingCast(BasicLightType::Point);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.color) = Vector4f(m_color.r / 255.f, m_color.g / 255.f, m_color.b / 255.f, m_color.a / 255.f);
|
||||
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.factor) = Vector2f(m_ambientFactor, m_diffuseFactor);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter1) = Vector4f(m_position.x, m_position.y, m_position.z, m_invRadius);
|
||||
AccessByOffset<UInt8&>(data, lightOffset.lightMemberOffsets.shadowMappingFlag) = 0;
|
||||
}
|
||||
|
||||
void PointLight::UpdateTransform(const Vector3f& position, const Quaternionf& /*rotation*/, const Vector3f& /*scale*/)
|
||||
{
|
||||
UpdatePosition(position);
|
||||
}
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ struct Light
|
|||
struct LightData
|
||||
{
|
||||
lights: array[Light, MaxLightCount],
|
||||
lightCount: u32
|
||||
lightCount: u32,
|
||||
}
|
||||
|
||||
[layout(std140)]
|
||||
|
|
@ -250,7 +250,7 @@ fn main(input: VertToFrag) -> FragOut
|
|||
else
|
||||
{
|
||||
let output: FragOut;
|
||||
output.RenderTarget0 = diffuseColor.w;
|
||||
output.RenderTarget0 = diffuseColor;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/SpotLight.hpp>
|
||||
#include <Nazara/Graphics/Enums.hpp>
|
||||
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
|
||||
#include <Nazara/Math/Vector2.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Math/Vector4.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
float SpotLight::ComputeContributionScore(const BoundingVolumef& boundingVolume) const
|
||||
{
|
||||
// TODO: take luminosity/radius/direction into account
|
||||
return Vector3f::SquaredDistance(m_position, boundingVolume.aabb.GetCenter());
|
||||
}
|
||||
|
||||
void SpotLight::FillLightData(void* data)
|
||||
{
|
||||
auto lightOffset = PredefinedLightData::GetOffsets();
|
||||
|
||||
AccessByOffset<UInt32&>(data, lightOffset.lightMemberOffsets.type) = UnderlyingCast(BasicLightType::Spot);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.color) = Vector4f(m_color.r / 255.f, m_color.g / 255.f, m_color.b / 255.f, m_color.a / 255.f);
|
||||
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.factor) = Vector2f(m_ambientFactor, m_diffuseFactor);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter1) = Vector4f(m_position.x, m_position.y, m_position.z, m_invRadius);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter2) = Vector4f(m_direction.x, m_direction.y, m_direction.z, 0.f);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter3) = Vector4f(m_innerAngleCos, m_outerAngleCos, 0.f, 0.f);
|
||||
AccessByOffset<UInt8&>(data, lightOffset.lightMemberOffsets.shadowMappingFlag) = 0;
|
||||
}
|
||||
|
||||
void SpotLight::UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& /*scale*/)
|
||||
{
|
||||
UpdatePosition(position);
|
||||
UpdateRotation(rotation);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
|
||||
#include <Nazara/Renderer/RenderFrame.hpp>
|
||||
#include <Nazara/Renderer/UploadPool.hpp>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
|
|
@ -49,7 +50,7 @@ namespace Nz
|
|||
return std::make_unique<SpriteChainRendererData>();
|
||||
}
|
||||
|
||||
void SpriteChainRenderer::Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, const RenderStates& renderStates, const Pointer<const RenderElement>* elements, std::size_t elementCount)
|
||||
void SpriteChainRenderer::Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates)
|
||||
{
|
||||
Graphics* graphics = Graphics::Instance();
|
||||
|
||||
|
|
@ -57,103 +58,65 @@ namespace Nz
|
|||
|
||||
Recti invalidScissorBox(-1, -1, -1, -1);
|
||||
|
||||
std::size_t firstQuadIndex = 0;
|
||||
SpriteChainRendererData::DrawCall* currentDrawCall = nullptr;
|
||||
UploadPool::Allocation* currentAllocation = nullptr;
|
||||
UInt8* currentAllocationMemPtr = nullptr;
|
||||
const VertexDeclaration* currentVertexDeclaration = nullptr;
|
||||
RenderBuffer* currentVertexBuffer = nullptr;
|
||||
const MaterialPass* currentMaterialPass = nullptr;
|
||||
const RenderPipeline* currentPipeline = nullptr;
|
||||
const ShaderBinding* currentShaderBinding = nullptr;
|
||||
const Texture* currentTextureOverlay = nullptr;
|
||||
const WorldInstance* currentWorldInstance = nullptr;
|
||||
Recti currentScissorBox = invalidScissorBox;
|
||||
|
||||
auto FlushDrawCall = [&]()
|
||||
{
|
||||
currentDrawCall = nullptr;
|
||||
};
|
||||
|
||||
auto FlushDrawData = [&]()
|
||||
{
|
||||
FlushDrawCall();
|
||||
|
||||
currentShaderBinding = nullptr;
|
||||
};
|
||||
|
||||
auto Flush = [&]()
|
||||
{
|
||||
// changing vertex buffer always mean we have to switch draw calls
|
||||
FlushDrawCall();
|
||||
|
||||
if (currentAllocation)
|
||||
{
|
||||
std::size_t size = currentAllocationMemPtr - static_cast<UInt8*>(currentAllocation->mappedPtr);
|
||||
|
||||
m_pendingCopies.emplace_back(BufferCopy{
|
||||
currentVertexBuffer,
|
||||
currentAllocation,
|
||||
size
|
||||
});
|
||||
|
||||
firstQuadIndex = 0;
|
||||
currentAllocation = nullptr;
|
||||
currentVertexBuffer = nullptr;
|
||||
}
|
||||
};
|
||||
const auto& defaultSampler = graphics->GetSamplerCache().Get({});
|
||||
|
||||
std::size_t oldDrawCallCount = data.drawCalls.size();
|
||||
const auto& defaultSampler = graphics->GetSamplerCache().Get({});
|
||||
|
||||
for (std::size_t i = 0; i < elementCount; ++i)
|
||||
{
|
||||
assert(elements[i]->GetElementType() == UnderlyingCast(BasicRenderElement::SpriteChain));
|
||||
const RenderSpriteChain& spriteChain = static_cast<const RenderSpriteChain&>(*elements[i]);
|
||||
const RenderStates& renderState = renderStates[i];
|
||||
|
||||
const VertexDeclaration* vertexDeclaration = spriteChain.GetVertexDeclaration();
|
||||
std::size_t stride = vertexDeclaration->GetStride();
|
||||
|
||||
if (currentVertexDeclaration != vertexDeclaration)
|
||||
if (m_pendingData.currentVertexDeclaration != vertexDeclaration)
|
||||
{
|
||||
// TODO: It's be possible to use another vertex declaration with the same vertex buffer but currently very complicated
|
||||
// Wait until buffer rewrite
|
||||
Flush();
|
||||
currentVertexDeclaration = vertexDeclaration;
|
||||
m_pendingData.currentVertexDeclaration = vertexDeclaration;
|
||||
}
|
||||
|
||||
if (const RenderPipeline* pipeline = &spriteChain.GetRenderPipeline(); currentPipeline != pipeline)
|
||||
if (const RenderPipeline* pipeline = &spriteChain.GetRenderPipeline(); m_pendingData.currentPipeline != pipeline)
|
||||
{
|
||||
FlushDrawCall();
|
||||
currentPipeline = pipeline;
|
||||
m_pendingData.currentPipeline = pipeline;
|
||||
}
|
||||
|
||||
if (const MaterialPass* materialPass = &spriteChain.GetMaterialPass(); currentMaterialPass != materialPass)
|
||||
if (const MaterialPass* materialPass = &spriteChain.GetMaterialPass(); m_pendingData.currentMaterialPass != materialPass)
|
||||
{
|
||||
FlushDrawData();
|
||||
currentMaterialPass = materialPass;
|
||||
m_pendingData.currentMaterialPass = materialPass;
|
||||
}
|
||||
|
||||
if (const WorldInstance* worldInstance = &spriteChain.GetWorldInstance(); currentWorldInstance != worldInstance)
|
||||
if (const WorldInstance* worldInstance = &spriteChain.GetWorldInstance(); m_pendingData.currentWorldInstance != worldInstance)
|
||||
{
|
||||
// TODO: Flushing draw calls on instance binding means we can have e.g. 1000 sprites rendered using a draw call for each one
|
||||
// which is far from being efficient, using some bindless could help (or at least instancing?)
|
||||
FlushDrawData();
|
||||
currentWorldInstance = worldInstance;
|
||||
m_pendingData.currentWorldInstance = worldInstance;
|
||||
}
|
||||
|
||||
if (const Texture* textureOverlay = spriteChain.GetTextureOverlay(); currentTextureOverlay != textureOverlay)
|
||||
if (const Texture* textureOverlay = spriteChain.GetTextureOverlay(); m_pendingData.currentTextureOverlay != textureOverlay)
|
||||
{
|
||||
FlushDrawData();
|
||||
currentTextureOverlay = textureOverlay;
|
||||
m_pendingData.currentTextureOverlay = textureOverlay;
|
||||
}
|
||||
|
||||
if (m_pendingData.currentLightData != renderState.lightData)
|
||||
{
|
||||
FlushDrawData();
|
||||
m_pendingData.currentLightData = renderState.lightData;
|
||||
}
|
||||
|
||||
const Recti& scissorBox = spriteChain.GetScissorBox();
|
||||
const Recti& targetScissorBox = (scissorBox.width >= 0) ? scissorBox : invalidScissorBox;
|
||||
if (currentScissorBox != targetScissorBox)
|
||||
if (m_pendingData.currentScissorBox != targetScissorBox)
|
||||
{
|
||||
FlushDrawData();
|
||||
currentScissorBox = targetScissorBox;
|
||||
FlushDrawCall();
|
||||
m_pendingData.currentScissorBox = targetScissorBox;
|
||||
}
|
||||
|
||||
std::size_t remainingQuads = spriteChain.GetSpriteCount();
|
||||
|
|
@ -161,10 +124,10 @@ namespace Nz
|
|||
|
||||
while (remainingQuads > 0)
|
||||
{
|
||||
if (!currentAllocation)
|
||||
if (!m_pendingData.currentAllocation)
|
||||
{
|
||||
currentAllocation = ¤tFrame.GetUploadPool().Allocate(m_maxVertexBufferSize);
|
||||
currentAllocationMemPtr = static_cast<UInt8*>(currentAllocation->mappedPtr);
|
||||
m_pendingData.currentAllocation = ¤tFrame.GetUploadPool().Allocate(m_maxVertexBufferSize);
|
||||
m_pendingData.currentAllocationMemPtr = static_cast<UInt8*>(m_pendingData.currentAllocation->mappedPtr);
|
||||
|
||||
std::shared_ptr<RenderBuffer> vertexBuffer;
|
||||
|
||||
|
|
@ -177,12 +140,12 @@ namespace Nz
|
|||
else
|
||||
vertexBuffer = m_device.InstantiateBuffer(BufferType::Vertex, m_maxVertexBufferSize, BufferUsage::DeviceLocal | BufferUsage::Dynamic | BufferUsage::Write);
|
||||
|
||||
currentVertexBuffer = vertexBuffer.get();
|
||||
m_pendingData.currentVertexBuffer = vertexBuffer.get();
|
||||
|
||||
data.vertexBuffers.emplace_back(std::move(vertexBuffer));
|
||||
}
|
||||
|
||||
if (!currentShaderBinding)
|
||||
if (!m_pendingData.currentShaderBinding)
|
||||
{
|
||||
m_bindingCache.clear();
|
||||
|
||||
|
|
@ -193,7 +156,7 @@ namespace Nz
|
|||
const auto& matSettings = materialPass.GetSettings();
|
||||
if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::InstanceDataUbo); bindingIndex != MaterialSettings::InvalidIndex)
|
||||
{
|
||||
const auto& instanceBuffer = currentWorldInstance->GetInstanceBuffer();
|
||||
const auto& instanceBuffer = m_pendingData.currentWorldInstance->GetInstanceBuffer();
|
||||
|
||||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
|
|
@ -203,13 +166,13 @@ namespace Nz
|
|||
};
|
||||
}
|
||||
|
||||
if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::LightDataUbo); bindingIndex != MaterialSettings::InvalidIndex)
|
||||
if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::LightDataUbo); bindingIndex != MaterialSettings::InvalidIndex && m_pendingData.currentLightData)
|
||||
{
|
||||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
bindingEntry.content = ShaderBinding::UniformBufferBinding{
|
||||
renderStates.lightData.get(),
|
||||
0, renderStates.lightData->GetSize()
|
||||
m_pendingData.currentLightData.GetBuffer(),
|
||||
m_pendingData.currentLightData.GetOffset(), m_pendingData.currentLightData.GetSize()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -230,33 +193,33 @@ namespace Nz
|
|||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
bindingEntry.content = ShaderBinding::TextureBinding{
|
||||
currentTextureOverlay, defaultSampler.get()
|
||||
m_pendingData.currentTextureOverlay, defaultSampler.get()
|
||||
};
|
||||
}
|
||||
|
||||
ShaderBindingPtr drawDataBinding = currentPipeline->GetPipelineInfo().pipelineLayout->AllocateShaderBinding(0);
|
||||
ShaderBindingPtr drawDataBinding = m_pendingData.currentPipeline->GetPipelineInfo().pipelineLayout->AllocateShaderBinding(0);
|
||||
drawDataBinding->Update(m_bindingCache.data(), m_bindingCache.size());
|
||||
|
||||
currentShaderBinding = drawDataBinding.get();
|
||||
m_pendingData.currentShaderBinding = drawDataBinding.get();
|
||||
|
||||
data.shaderBindings.emplace_back(std::move(drawDataBinding));
|
||||
}
|
||||
|
||||
if (!currentDrawCall)
|
||||
if (!m_pendingData.currentDrawCall)
|
||||
{
|
||||
data.drawCalls.push_back(SpriteChainRendererData::DrawCall{
|
||||
currentVertexBuffer,
|
||||
currentPipeline,
|
||||
currentShaderBinding,
|
||||
6 * firstQuadIndex,
|
||||
m_pendingData.currentVertexBuffer,
|
||||
m_pendingData.currentPipeline,
|
||||
m_pendingData.currentShaderBinding,
|
||||
6 * m_pendingData.firstQuadIndex,
|
||||
0,
|
||||
currentScissorBox
|
||||
m_pendingData.currentScissorBox
|
||||
});
|
||||
|
||||
currentDrawCall = &data.drawCalls.back();
|
||||
m_pendingData.currentDrawCall = &data.drawCalls.back();
|
||||
}
|
||||
|
||||
std::size_t remainingSpace = m_maxVertexBufferSize - (currentAllocationMemPtr - static_cast<UInt8*>(currentAllocation->mappedPtr));
|
||||
std::size_t remainingSpace = m_maxVertexBufferSize - (m_pendingData.currentAllocationMemPtr - static_cast<UInt8*>(m_pendingData.currentAllocation->mappedPtr));
|
||||
std::size_t maxQuads = remainingSpace / (4 * stride);
|
||||
if (maxQuads == 0)
|
||||
{
|
||||
|
|
@ -267,12 +230,12 @@ namespace Nz
|
|||
std::size_t copiedQuadCount = std::min(maxQuads, remainingQuads);
|
||||
std::size_t copiedSize = 4 * copiedQuadCount * stride;
|
||||
|
||||
std::memcpy(currentAllocationMemPtr, spriteData, copiedSize);
|
||||
currentAllocationMemPtr += copiedSize;
|
||||
std::memcpy(m_pendingData.currentAllocationMemPtr, spriteData, copiedSize);
|
||||
m_pendingData.currentAllocationMemPtr += copiedSize;
|
||||
spriteData += copiedSize;
|
||||
|
||||
firstQuadIndex += copiedQuadCount;
|
||||
currentDrawCall->quadCount += copiedQuadCount;
|
||||
m_pendingData.firstQuadIndex += copiedQuadCount;
|
||||
m_pendingData.currentDrawCall->quadCount += copiedQuadCount;
|
||||
remainingQuads -= copiedQuadCount;
|
||||
|
||||
// If there's still data to copy, it means buffer is full, flush it
|
||||
|
|
@ -281,12 +244,16 @@ namespace Nz
|
|||
}
|
||||
}
|
||||
|
||||
//TODO: Add Finish()/PrepareEnd() call to allow to reuse buffers/draw calls for multiple Prepare calls
|
||||
Flush();
|
||||
FlushDrawCall();
|
||||
|
||||
const RenderSpriteChain* firstSpriteChain = static_cast<const RenderSpriteChain*>(elements[0]);
|
||||
std::size_t drawCallCount = data.drawCalls.size() - oldDrawCallCount;
|
||||
data.drawCallPerElement[firstSpriteChain] = SpriteChainRendererData::DrawCallIndices{ oldDrawCallCount, drawCallCount };
|
||||
}
|
||||
|
||||
void SpriteChainRenderer::PrepareEnd(RenderFrame& currentFrame, ElementRendererData& /*rendererData*/)
|
||||
{
|
||||
Flush();
|
||||
|
||||
if (!m_pendingCopies.empty())
|
||||
{
|
||||
|
|
@ -300,9 +267,11 @@ namespace Nz
|
|||
|
||||
m_pendingCopies.clear();
|
||||
}
|
||||
|
||||
m_pendingData = PendingData{};
|
||||
}
|
||||
|
||||
void SpriteChainRenderer::Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, const Pointer<const RenderElement>* elements, std::size_t /*elementCount*/)
|
||||
void SpriteChainRenderer::Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t /*elementCount*/, const Pointer<const RenderElement>* elements)
|
||||
{
|
||||
auto& data = static_cast<SpriteChainRendererData&>(rendererData);
|
||||
|
||||
|
|
@ -374,4 +343,37 @@ namespace Nz
|
|||
|
||||
data.drawCalls.clear();
|
||||
}
|
||||
|
||||
void SpriteChainRenderer::Flush()
|
||||
{
|
||||
// changing vertex buffer always mean we have to switch draw calls
|
||||
FlushDrawCall();
|
||||
|
||||
if (m_pendingData.currentAllocation)
|
||||
{
|
||||
std::size_t size = m_pendingData.currentAllocationMemPtr - static_cast<UInt8*>(m_pendingData.currentAllocation->mappedPtr);
|
||||
|
||||
m_pendingCopies.emplace_back(BufferCopy{
|
||||
m_pendingData.currentVertexBuffer,
|
||||
m_pendingData.currentAllocation,
|
||||
size
|
||||
});
|
||||
|
||||
m_pendingData.firstQuadIndex = 0;
|
||||
m_pendingData.currentAllocation = nullptr;
|
||||
m_pendingData.currentVertexBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteChainRenderer::FlushDrawCall()
|
||||
{
|
||||
m_pendingData.currentDrawCall = nullptr;
|
||||
}
|
||||
|
||||
void SpriteChainRenderer::FlushDrawData()
|
||||
{
|
||||
FlushDrawCall();
|
||||
|
||||
m_pendingData.currentShaderBinding = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Nz
|
|||
return std::make_unique<SubmeshRendererData>();
|
||||
}
|
||||
|
||||
void SubmeshRenderer::Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& /*currentFrame*/, const RenderStates& renderStates, const Pointer<const RenderElement>* elements, std::size_t elementCount)
|
||||
void SubmeshRenderer::Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& /*currentFrame*/, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates)
|
||||
{
|
||||
Graphics* graphics = Graphics::Instance();
|
||||
|
||||
|
|
@ -32,6 +32,7 @@ namespace Nz
|
|||
const ShaderBinding* currentShaderBinding = nullptr;
|
||||
const WorldInstance* currentWorldInstance = nullptr;
|
||||
Recti currentScissorBox = invalidScissorBox;
|
||||
RenderBufferView currentLightData;
|
||||
|
||||
auto FlushDrawCall = [&]()
|
||||
{
|
||||
|
|
@ -48,10 +49,13 @@ namespace Nz
|
|||
const auto& whiteTexture = Graphics::Instance()->GetDefaultTextures().whiteTextures[UnderlyingCast(ImageType::E2D)];
|
||||
const auto& defaultSampler = graphics->GetSamplerCache().Get({});
|
||||
|
||||
std::size_t oldDrawCallCount = data.drawCalls.size();
|
||||
|
||||
for (std::size_t i = 0; i < elementCount; ++i)
|
||||
{
|
||||
assert(elements[i]->GetElementType() == UnderlyingCast(BasicRenderElement::Submesh));
|
||||
const RenderSubmesh& submesh = static_cast<const RenderSubmesh&>(*elements[i]);
|
||||
const RenderStates& renderState = renderStates[i];
|
||||
|
||||
if (const RenderPipeline* pipeline = submesh.GetRenderPipeline(); currentPipeline != pipeline)
|
||||
{
|
||||
|
|
@ -85,6 +89,12 @@ namespace Nz
|
|||
currentWorldInstance = worldInstance;
|
||||
}
|
||||
|
||||
if (currentLightData != renderState.lightData)
|
||||
{
|
||||
FlushDrawData();
|
||||
currentLightData = renderState.lightData;
|
||||
}
|
||||
|
||||
const Recti& scissorBox = submesh.GetScissorBox();
|
||||
const Recti& targetScissorBox = (scissorBox.width >= 0) ? scissorBox : invalidScissorBox;
|
||||
if (currentScissorBox != targetScissorBox)
|
||||
|
|
@ -114,13 +124,13 @@ namespace Nz
|
|||
};
|
||||
}
|
||||
|
||||
if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::LightDataUbo); bindingIndex != MaterialSettings::InvalidIndex)
|
||||
if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::LightDataUbo); bindingIndex != MaterialSettings::InvalidIndex && currentLightData)
|
||||
{
|
||||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
bindingEntry.content = ShaderBinding::UniformBufferBinding{
|
||||
renderStates.lightData.get(),
|
||||
0, renderStates.lightData->GetSize()
|
||||
currentLightData.GetBuffer(),
|
||||
currentLightData.GetOffset(), currentLightData.GetSize()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -154,6 +164,7 @@ namespace Nz
|
|||
}
|
||||
|
||||
auto& drawCall = data.drawCalls.emplace_back();
|
||||
drawCall.firstIndex = 0;
|
||||
drawCall.indexBuffer = currentIndexBuffer;
|
||||
drawCall.indexCount = submesh.GetIndexCount();
|
||||
drawCall.renderPipeline = currentPipeline;
|
||||
|
|
@ -161,9 +172,13 @@ namespace Nz
|
|||
drawCall.shaderBinding = currentShaderBinding;
|
||||
drawCall.vertexBuffer = currentVertexBuffer;
|
||||
}
|
||||
|
||||
const RenderSubmesh* firstSubmesh = static_cast<const RenderSubmesh*>(elements[0]);
|
||||
std::size_t drawCallCount = data.drawCalls.size() - oldDrawCallCount;
|
||||
data.drawCallPerElement[firstSubmesh] = SubmeshRendererData::DrawCallIndices{ oldDrawCallCount, drawCallCount };
|
||||
}
|
||||
|
||||
void SubmeshRenderer::Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, const Pointer<const RenderElement>* /*elements*/, std::size_t /*elementCount*/)
|
||||
void SubmeshRenderer::Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t /*elementCount*/, const Pointer<const RenderElement>* elements)
|
||||
{
|
||||
auto& data = static_cast<SubmeshRendererData&>(rendererData);
|
||||
|
||||
|
|
@ -176,8 +191,16 @@ namespace Nz
|
|||
const ShaderBinding* currentShaderBinding = nullptr;
|
||||
Recti currentScissorBox(-1, -1, -1, -1);
|
||||
|
||||
for (const auto& drawData : data.drawCalls)
|
||||
const RenderSubmesh* firstSubmesh = static_cast<const RenderSubmesh*>(elements[0]);
|
||||
auto it = data.drawCallPerElement.find(firstSubmesh);
|
||||
assert(it != data.drawCallPerElement.end());
|
||||
|
||||
const auto& indices = it->second;
|
||||
|
||||
for (std::size_t i = 0; i < indices.count; ++i)
|
||||
{
|
||||
const auto& drawData = data.drawCalls[indices.start + i];
|
||||
|
||||
if (currentPipeline != drawData.renderPipeline)
|
||||
{
|
||||
commandBuffer.BindPipeline(*drawData.renderPipeline);
|
||||
|
|
@ -210,9 +233,9 @@ namespace Nz
|
|||
}
|
||||
|
||||
if (currentIndexBuffer)
|
||||
commandBuffer.DrawIndexed(SafeCast<UInt32>(drawData.indexCount));
|
||||
commandBuffer.DrawIndexed(SafeCast<UInt32>(drawData.indexCount), 1U, SafeCast<UInt32>(drawData.firstIndex));
|
||||
else
|
||||
commandBuffer.Draw(SafeCast<UInt32>(drawData.indexCount));
|
||||
commandBuffer.Draw(SafeCast<UInt32>(drawData.indexCount), 1U, SafeCast<UInt32>(drawData.firstIndex));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,10 +18,12 @@ namespace Nz
|
|||
{
|
||||
RenderSystem::RenderSystem(entt::registry& registry) :
|
||||
m_cameraConstructObserver(registry, entt::collector.group<CameraComponent, NodeComponent>()),
|
||||
m_graphicsConstructObserver(registry, entt::collector.group<GraphicsComponent, NodeComponent>())
|
||||
m_graphicsConstructObserver(registry, entt::collector.group<GraphicsComponent, NodeComponent>()),
|
||||
m_lightConstructObserver(registry, entt::collector.group<LightComponent, NodeComponent>())
|
||||
{
|
||||
m_cameraDestroyConnection = registry.on_destroy<CameraComponent>().connect<&RenderSystem::OnCameraDestroy>(this);
|
||||
m_graphicsDestroyConnection = registry.on_destroy<GraphicsComponent>().connect<&RenderSystem::OnGraphicsDestroy>(this);
|
||||
m_lightDestroyConnection = registry.on_destroy<LightComponent>().connect<&RenderSystem::OnLightDestroy>(this);
|
||||
m_nodeDestroyConnection = registry.on_destroy<NodeComponent>().connect<&RenderSystem::OnNodeDestroy>(this);
|
||||
|
||||
m_pipeline = std::make_unique<ForwardFramePipeline>();
|
||||
|
|
@ -31,8 +33,10 @@ namespace Nz
|
|||
{
|
||||
m_cameraConstructObserver.disconnect();
|
||||
m_graphicsConstructObserver.disconnect();
|
||||
m_lightConstructObserver.disconnect();
|
||||
m_cameraDestroyConnection.release();
|
||||
m_graphicsDestroyConnection.release();
|
||||
m_lightDestroyConnection.release();
|
||||
m_nodeDestroyConnection.release();
|
||||
}
|
||||
|
||||
|
|
@ -67,13 +71,13 @@ namespace Nz
|
|||
m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask);
|
||||
}
|
||||
|
||||
m_invalidatedWorldNode.insert(entity);
|
||||
m_invalidatedGfxWorldNode.insert(entity);
|
||||
|
||||
assert(m_graphicsEntities.find(entity) == m_graphicsEntities.end());
|
||||
auto& graphicsEntity = m_graphicsEntities[entity];
|
||||
graphicsEntity.onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, entity](const Node* /*node*/)
|
||||
{
|
||||
m_invalidatedWorldNode.insert(entity);
|
||||
m_invalidatedGfxWorldNode.insert(entity);
|
||||
});
|
||||
|
||||
graphicsEntity.onRenderableAttached.Connect(entityGfx.OnRenderableAttached, [this](GraphicsComponent* gfx, const GraphicsComponent::Renderable& renderableEntry)
|
||||
|
|
@ -98,13 +102,64 @@ namespace Nz
|
|||
{
|
||||
if (isVisible)
|
||||
{
|
||||
m_newlyHiddenEntities.erase(entity);
|
||||
m_newlyVisibleEntities.insert(entity);
|
||||
m_newlyHiddenGfxEntities.erase(entity);
|
||||
m_newlyVisibleGfxEntities.insert(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_newlyHiddenEntities.insert(entity);
|
||||
m_newlyVisibleEntities.erase(entity);
|
||||
m_newlyHiddenGfxEntities.insert(entity);
|
||||
m_newlyVisibleGfxEntities.erase(entity);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
m_lightConstructObserver.each([&](entt::entity entity)
|
||||
{
|
||||
LightComponent& entityLight = registry.get<LightComponent>(entity);
|
||||
NodeComponent& entityNode = registry.get<NodeComponent>(entity);
|
||||
|
||||
if (entityLight.IsVisible())
|
||||
{
|
||||
for (const auto& lightEntry : entityLight.GetLights())
|
||||
m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
|
||||
}
|
||||
|
||||
m_invalidatedLightWorldNode.insert(entity);
|
||||
|
||||
assert(m_lightEntities.find(entity) == m_lightEntities.end());
|
||||
auto& lightEntity = m_lightEntities[entity];
|
||||
lightEntity.onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, entity](const Node* /*node*/)
|
||||
{
|
||||
m_invalidatedLightWorldNode.insert(entity);
|
||||
});
|
||||
|
||||
lightEntity.onLightAttached.Connect(entityLight.OnLightAttached, [this](LightComponent* light, const LightComponent::LightEntry& lightEntry)
|
||||
{
|
||||
if (!light->IsVisible())
|
||||
return;
|
||||
|
||||
m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
|
||||
});
|
||||
|
||||
lightEntity.onLightDetach.Connect(entityLight.OnLightDetach, [this](LightComponent* light, const LightComponent::LightEntry& lightEntry)
|
||||
{
|
||||
if (!light->IsVisible())
|
||||
return;
|
||||
|
||||
m_pipeline->UnregisterLight(lightEntry.light.get());
|
||||
});
|
||||
|
||||
lightEntity.onVisibilityUpdate.Connect(entityLight.OnVisibilityUpdate, [this, entity](LightComponent* /*light*/, bool isVisible)
|
||||
{
|
||||
if (isVisible)
|
||||
{
|
||||
m_newlyHiddenLightEntities.erase(entity);
|
||||
m_newlyVisibleLightEntities.insert(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_newlyHiddenLightEntities.insert(entity);
|
||||
m_newlyVisibleLightEntities.erase(entity);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -127,9 +182,22 @@ namespace Nz
|
|||
void RenderSystem::OnGraphicsDestroy(entt::registry& registry, entt::entity entity)
|
||||
{
|
||||
m_graphicsEntities.erase(entity);
|
||||
m_invalidatedWorldNode.erase(entity);
|
||||
m_newlyHiddenEntities.erase(entity);
|
||||
m_newlyVisibleEntities.erase(entity);
|
||||
m_invalidatedGfxWorldNode.erase(entity);
|
||||
m_newlyHiddenGfxEntities.erase(entity);
|
||||
m_newlyVisibleGfxEntities.erase(entity);
|
||||
|
||||
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
|
||||
const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance();
|
||||
for (const auto& renderableEntry : entityGfx.GetRenderables())
|
||||
m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get());
|
||||
}
|
||||
|
||||
void RenderSystem::OnLightDestroy(entt::registry& registry, entt::entity entity)
|
||||
{
|
||||
m_lightEntities.erase(entity);
|
||||
m_invalidatedLightWorldNode.erase(entity);
|
||||
m_newlyHiddenLightEntities.erase(entity);
|
||||
m_newlyVisibleLightEntities.erase(entity);
|
||||
|
||||
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
|
||||
const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance();
|
||||
|
|
@ -139,14 +207,19 @@ namespace Nz
|
|||
|
||||
void RenderSystem::OnNodeDestroy(entt::registry& registry, entt::entity entity)
|
||||
{
|
||||
m_newlyHiddenEntities.erase(entity);
|
||||
m_newlyVisibleEntities.erase(entity);
|
||||
m_newlyHiddenGfxEntities.erase(entity);
|
||||
m_newlyVisibleGfxEntities.erase(entity);
|
||||
m_newlyHiddenLightEntities.erase(entity);
|
||||
m_newlyVisibleLightEntities.erase(entity);
|
||||
|
||||
if (registry.try_get<CameraComponent>(entity))
|
||||
OnCameraDestroy(registry, entity);
|
||||
|
||||
if (registry.try_get<GraphicsComponent>(entity))
|
||||
OnGraphicsDestroy(registry, entity);
|
||||
|
||||
if (registry.try_get<LightComponent>(entity))
|
||||
OnLightDestroy(registry, entity);
|
||||
}
|
||||
|
||||
void RenderSystem::UpdateInstances(entt::registry& registry)
|
||||
|
|
@ -166,7 +239,7 @@ namespace Nz
|
|||
}
|
||||
m_invalidatedCameraNode.clear();
|
||||
|
||||
for (entt::entity entity : m_invalidatedWorldNode)
|
||||
for (entt::entity entity : m_invalidatedGfxWorldNode)
|
||||
{
|
||||
const NodeComponent& entityNode = registry.get<const NodeComponent>(entity);
|
||||
GraphicsComponent& entityGraphics = registry.get<GraphicsComponent>(entity);
|
||||
|
|
@ -176,13 +249,27 @@ namespace Nz
|
|||
|
||||
m_pipeline->InvalidateWorldInstance(worldInstance.get());
|
||||
}
|
||||
m_invalidatedWorldNode.clear();
|
||||
m_invalidatedGfxWorldNode.clear();
|
||||
|
||||
for (entt::entity entity : m_invalidatedLightWorldNode)
|
||||
{
|
||||
const NodeComponent& entityNode = registry.get<const NodeComponent>(entity);
|
||||
LightComponent& entityLight = registry.get<LightComponent>(entity);
|
||||
|
||||
const Vector3f& position = entityNode.GetPosition(CoordSys::Global);
|
||||
const Quaternionf& rotation = entityNode.GetRotation(CoordSys::Global);
|
||||
const Vector3f& scale = entityNode.GetScale(CoordSys::Global);
|
||||
|
||||
for (const auto& lightEntry : entityLight.GetLights())
|
||||
lightEntry.light->UpdateTransform(position, rotation, scale);
|
||||
}
|
||||
m_invalidatedLightWorldNode.clear();
|
||||
}
|
||||
|
||||
void RenderSystem::UpdateVisibility(entt::registry& registry)
|
||||
{
|
||||
// Unregister drawables for hidden entities
|
||||
for (entt::entity entity : m_newlyHiddenEntities)
|
||||
// Unregister drawable for hidden entities
|
||||
for (entt::entity entity : m_newlyHiddenGfxEntities)
|
||||
{
|
||||
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
|
||||
|
||||
|
|
@ -190,10 +277,10 @@ namespace Nz
|
|||
for (const auto& renderableEntry : entityGfx.GetRenderables())
|
||||
m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get());
|
||||
}
|
||||
m_newlyHiddenEntities.clear();
|
||||
m_newlyHiddenGfxEntities.clear();
|
||||
|
||||
// Register drawables for newly visible entities
|
||||
for (entt::entity entity : m_newlyVisibleEntities)
|
||||
// Register drawable for newly visible entities
|
||||
for (entt::entity entity : m_newlyVisibleGfxEntities)
|
||||
{
|
||||
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
|
||||
|
||||
|
|
@ -201,6 +288,6 @@ namespace Nz
|
|||
for (const auto& renderableEntry : entityGfx.GetRenderables())
|
||||
m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask);
|
||||
}
|
||||
m_newlyVisibleEntities.clear();
|
||||
m_newlyVisibleGfxEntities.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace Nz
|
|||
NazaraAssert(handle != InvalidHandle, "Invalid handle");
|
||||
|
||||
IpAddressImpl::SockAddrBuffer nameBuffer;
|
||||
std::fill(nameBuffer.begin(), nameBuffer.end(), 0);
|
||||
nameBuffer.fill(0);
|
||||
|
||||
int bufferLength = static_cast<int>(nameBuffer.size());
|
||||
|
||||
|
|
|
|||
|
|
@ -665,7 +665,7 @@ namespace Nz
|
|||
*vertexCount = xVertexCount*2 + yVertexCount*2 + zVertexCount*2;
|
||||
}
|
||||
|
||||
UInt64 ComputeCacheMissCount(IndexIterator indices, std::size_t indexCount)
|
||||
UInt64 ComputeCacheMissCount(IndexIterator indices, UInt32 indexCount)
|
||||
{
|
||||
VertexCache cache(indices, indexCount);
|
||||
return cache.GetMissCount();
|
||||
|
|
|
|||
Loading…
Reference in New Issue