diff --git a/examples/GraphicsTest/main.cpp b/examples/GraphicsTest/main.cpp index 831bf8682..6b6e81cf4 100644 --- a/examples/GraphicsTest/main.cpp +++ b/examples/GraphicsTest/main.cpp @@ -41,26 +41,65 @@ int main() return __LINE__; } - std::shared_ptr spaceshipMesh = Nz::Mesh::LoadFromFile(resourceDir / "Spaceship/spaceship.obj", meshParams); - if (!spaceshipMesh) + std::shared_ptr sphereMesh = std::make_shared(); + sphereMesh->CreateStatic(); + + std::shared_ptr sphereSubmesh = sphereMesh->BuildSubMesh(Nz::Primitive::UVSphere(1.f, 50, 50)); + sphereMesh->SetMaterialCount(1); + sphereMesh->GenerateNormalsAndTangents(); + + std::shared_ptr debugMesh = std::make_shared(); + debugMesh->CreateStatic(); { - NazaraError("Failed to load model"); - return __LINE__; + Nz::VertexMapper sphereMapper(*sphereSubmesh); + std::size_t vertexCount = sphereMapper.GetVertexCount(); + + Nz::SparsePtr positionPtr = sphereMapper.GetComponentPtr(Nz::VertexComponent::Position); + Nz::SparsePtr normalPtr = sphereMapper.GetComponentPtr(Nz::VertexComponent::Normal); + Nz::SparsePtr tangentPtr = sphereMapper.GetComponentPtr(Nz::VertexComponent::Tangent); + + std::shared_ptr debugDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Color); + + std::vector debugVertices(vertexCount * 6); + for (std::size_t i = 0; i < vertexCount; ++i) + { + debugVertices[i * 6 + 0].position = positionPtr[i]; + debugVertices[i * 6 + 0].color = Nz::Color::Red; + + debugVertices[i * 6 + 1].position = positionPtr[i] + normalPtr[i] * 0.05f; + debugVertices[i * 6 + 1].color = Nz::Color::Red; + + debugVertices[i * 6 + 2].position = positionPtr[i]; + debugVertices[i * 6 + 2].color = Nz::Color::Blue; + + debugVertices[i * 6 + 3].position = positionPtr[i] + tangentPtr[i] * 0.05f; + debugVertices[i * 6 + 3].color = Nz::Color::Blue; + + Nz::Vector3f bitangent = Nz::Vector3f::CrossProduct(normalPtr[i], tangentPtr[i]); + + debugVertices[i * 6 + 4].position = positionPtr[i]; + debugVertices[i * 6 + 4].color = Nz::Color::Cyan; + + debugVertices[i * 6 + 5].position = positionPtr[i] + bitangent * 0.05f; + debugVertices[i * 6 + 5].color = Nz::Color::Cyan; + } + + std::shared_ptr normalBuffer = std::make_shared(debugDeclaration, vertexCount * 6, Nz::BufferUsage::Write, Nz::SoftwareBufferFactory, debugVertices.data()); + + std::shared_ptr staticMesh = std::make_shared(normalBuffer, nullptr); + staticMesh->GenerateAABB(); + staticMesh->SetPrimitiveMode(Nz::PrimitiveMode::LineList); + + debugMesh->AddSubMesh(std::move(staticMesh)); } + debugMesh->SetMaterialCount(1); - std::shared_ptr gfxMesh = Nz::GraphicalMesh::BuildFromMesh(*spaceshipMesh); - - // Texture - std::shared_ptr diffuseImage = Nz::Image::LoadFromFile(resourceDir / "Spaceship/Texture/diffuse.png"); - if (!diffuseImage || !diffuseImage->Convert(Nz::PixelFormat::RGBA8_SRGB)) - { - NazaraError("Failed to load image"); - return __LINE__; - } + std::shared_ptr gfxMesh = Nz::GraphicalMesh::BuildFromMesh(*sphereMesh); + std::shared_ptr gfxDebugMesh = Nz::GraphicalMesh::BuildFromMesh(*debugMesh); + // Textures Nz::TextureParams texParams; texParams.renderDevice = device; - texParams.loadFormat = Nz::PixelFormat::RGBA8_SRGB; std::shared_ptr material = std::make_shared(); @@ -70,18 +109,35 @@ int main() material->AddPass("ForwardPass", forwardPass); - std::shared_ptr normalMap = Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/normal.png", texParams); + std::shared_ptr normalMap = Nz::Texture::LoadFromFile(resourceDir / "Rusty/rustediron2_normal.png", texParams); + + texParams.loadFormat = Nz::PixelFormat::RGBA8_SRGB; + + std::shared_ptr debugMaterial = std::make_shared(); + std::shared_ptr debugMaterialPass = std::make_shared(Nz::BasicMaterial::GetSettings()); + debugMaterialPass->EnableDepthBuffer(true); + debugMaterialPass->SetPrimitiveMode(Nz::PrimitiveMode::LineList); + + debugMaterial->AddPass("ForwardPass", debugMaterialPass); + + Nz::BasicMaterial debugMat(*std::make_shared(Nz::BasicMaterial::GetSettings())); Nz::PhongLightingMaterial phongMat(*forwardPass); phongMat.EnableAlphaTest(false); phongMat.SetAlphaMap(Nz::Texture::LoadFromFile(resourceDir / "alphatile.png", texParams)); - phongMat.SetDiffuseMap(Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/diffuse.png", texParams)); - phongMat.SetNormalMap(Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/normal.png", texParams)); + phongMat.SetDiffuseMap(Nz::Texture::LoadFromFile(resourceDir / "Rusty/rustediron2_basecolor.png", texParams)); + //pbrMat.SetMetallicMap(Nz::Texture::LoadFromFile(resourceDir / "Rusty/rustediron2_metallic.png", texParams)); + //pbrMat.SetRoughnessMap(Nz::Texture::LoadFromFile(resourceDir / "Rusty/rustediron2_roughness.png", texParams)); + phongMat.SetNormalMap(normalMap); - Nz::Model model(std::move(gfxMesh), spaceshipMesh->GetAABB()); + Nz::Model model(std::move(gfxMesh), sphereMesh->GetAABB()); for (std::size_t i = 0; i < model.GetSubMeshCount(); ++i) model.SetMaterial(i, material); + Nz::Model debugModel(std::move(gfxDebugMesh), debugMesh->GetAABB()); + for (std::size_t i = 0; i < debugModel.GetSubMeshCount(); ++i) + debugModel.SetMaterial(i, debugMaterial); + Nz::Vector2ui windowSize = window.GetSize(); Nz::Camera camera(window.GetRenderTarget()); @@ -104,11 +160,12 @@ int main() std::size_t worldInstanceIndex1 = framePipeline.RegisterWorldInstance(modelInstance); std::size_t worldInstanceIndex2 = framePipeline.RegisterWorldInstance(modelInstance2); framePipeline.RegisterRenderable(worldInstanceIndex1, &model, 0xFFFFFFFF, scissorBox); + //framePipeline.RegisterRenderable(worldInstanceIndex1, &debugModel, 0xFFFFFFFF, scissorBox); framePipeline.RegisterRenderable(worldInstanceIndex2, &model, 0xFFFFFFFF, scissorBox); + //framePipeline.RegisterRenderable(worldInstanceIndex2, &debugModel, 0xFFFFFFFF, scissorBox); - std::shared_ptr light = std::make_shared(); - light->UpdateInnerAngle(Nz::DegreeAnglef(15.f)); - light->UpdateOuterAngle(Nz::DegreeAnglef(20.f)); + std::shared_ptr light = std::make_shared(); + light->UpdateRotation(Nz::EulerAnglesf(-45.f, 0.f, 0.f)); framePipeline.RegisterLight(light, 0xFFFFFFFF); @@ -167,7 +224,7 @@ int main() camAngles.pitch = Nz::Clamp(camAngles.pitch - event.mouseMove.deltaY*sensitivity, -89.f, 89.f); camQuat = camAngles; - light->UpdateRotation(camQuat); + //light->UpdateRotation(camQuat); break; } @@ -212,7 +269,7 @@ int main() if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LControl) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RControl)) viewerPos += Nz::Vector3f::Down() * cameraSpeed; - light->UpdatePosition(viewerPos); + //light->UpdatePosition(viewerPos); } Nz::RenderFrame frame = window.AcquireFrame(); diff --git a/examples/Physics2DDemo/main.cpp b/examples/Physics2DDemo/main.cpp index cac2a745a..0ce112334 100644 --- a/examples/Physics2DDemo/main.cpp +++ b/examples/Physics2DDemo/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -40,23 +41,20 @@ int main() Nz::Modules nazara(rendererConfig); - Nz::RenderWindow window; - std::shared_ptr device = Nz::Graphics::Instance()->GetRenderDevice(); - std::string windowTitle = "Graphics 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::Physics2DSystem physSytem(registry); - physSytem.GetPhysWorld().SetGravity({ 0.f, -9.81f }); + Nz::SystemGraph systemGraph(registry); + Nz::Physics2DSystem& physSytem = systemGraph.AddSystem(); + Nz::RenderSystem& renderSystem = systemGraph.AddSystem(); - Nz::RenderSystem renderSystem(registry); + std::string windowTitle = "Graphics Test"; + Nz::RenderWindow& window = renderSystem.CreateWindow(device, Nz::VideoMode(1920, 1080), windowTitle); + + Nz::Vector2ui windowSize = window.GetSize(); + + physSytem.GetPhysWorld().SetGravity({ 0.f, -9.81f }); entt::entity viewer = registry.create(); { @@ -166,19 +164,10 @@ int main() { float updateTime = updateClock.Restart() / 1'000'000.f; - physSytem.Update(registry, 1000.f / 60.f); + physSytem.Update(1000.f / 60.f); } - Nz::RenderFrame frame = window.AcquireFrame(); - if (!frame) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - - renderSystem.Render(registry, frame); - - frame.Present(); + systemGraph.Update(); fps++; diff --git a/examples/PhysicsDemo/main.cpp b/examples/PhysicsDemo/main.cpp index 4ece8a578..97c978542 100644 --- a/examples/PhysicsDemo/main.cpp +++ b/examples/PhysicsDemo/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -39,17 +40,8 @@ int main() Nz::Modules nazara(rendererConfig); - Nz::RenderWindow window; - std::shared_ptr device = Nz::Graphics::Instance()->GetRenderDevice(); - std::string windowTitle = "Graphics Test"; - if (!window.Create(device, Nz::VideoMode(1920, 1080, 32), windowTitle)) - { - std::cout << "Failed to create Window" << std::endl; - return __LINE__; - } - 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)); @@ -119,15 +111,19 @@ int main() std::shared_ptr sprite = std::make_shared(spriteMaterial); sprite->Update(Nz::SimpleTextDrawer::Draw("Voix ambiguë d'un cœur qui, au zéphyr, préfère les jattes de kiwis", 72), 0.01f); - Nz::Vector2ui windowSize = window.GetSize(); - Nz::VertexMapper vertexMapper(*spaceshipMesh->GetSubMesh(0)); Nz::SparsePtr vertices = vertexMapper.GetComponentPtr(Nz::VertexComponent::Position); entt::registry registry; - Nz::Physics3DSystem physSytem(registry); - Nz::RenderSystem renderSystem(registry); + Nz::SystemGraph systemGraph(registry); + Nz::Physics3DSystem& physSytem = systemGraph.AddSystem(); + Nz::RenderSystem& renderSystem = systemGraph.AddSystem(); + + std::string windowTitle = "Graphics Test"; + Nz::RenderWindow& window = renderSystem.CreateWindow(device, Nz::VideoMode(1920, 1080, 32), windowTitle); + + Nz::Vector2ui windowSize = window.GetSize(); entt::entity viewer = registry.create(); { @@ -187,10 +183,10 @@ int main() auto& headingNode = registry.emplace(headingEntity); headingNode.SetInheritRotation(false); - headingNode.SetParent(registry, playerEntity); + headingNode.SetParent(entityNode); } - registry.get(viewer).SetParent(registry, headingEntity); + registry.get(viewer).SetParent(entt::handle(registry, headingEntity)); registry.get(viewer).SetPosition(Nz::Vector3f::Backward() * 2.5f + Nz::Vector3f::Up() * 1.f); for (std::size_t x = 0; x < 3; ++x) @@ -310,10 +306,6 @@ int main() if (updateClock.GetMilliseconds() > 1000 / 60) { - float updateTime = updateClock.Restart() / 1'000'000.f; - - physSytem.Update(registry, 1000.f / 60.f); - auto spaceshipView = registry.view(); for (auto&& [entity, node, _] : spaceshipView.each()) { @@ -360,16 +352,7 @@ int main() playerShipBody.AddForce(Nz::Vector3f::Down() * 3.f * mass, Nz::CoordSys::Local); } - Nz::RenderFrame frame = window.AcquireFrame(); - if (!frame) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - - renderSystem.Render(registry, frame); - - frame.Present(); + systemGraph.Update(); fps++; diff --git a/examples/Tut01/main.cpp b/examples/Tut01/main.cpp index 1c44e72f1..70c79b443 100644 --- a/examples/Tut01/main.cpp +++ b/examples/Tut01/main.cpp @@ -1,5 +1,6 @@ // Sources pour https://github.com/NazaraEngine/NazaraEngine/wiki/(FR)-Tutoriel:-%5B01%5D-Hello-World +#include #include #include #include @@ -15,10 +16,10 @@ int main() { Nz::Modules nazara; - Nz::RenderWindow mainWindow(Nz::Graphics::Instance()->GetRenderDevice(), Nz::VideoMode(1280, 720, 32), "Test"); - entt::registry registry; - Nz::RenderSystem renderSystem(registry); + Nz::SystemGraph systemGraph(registry); + Nz::RenderSystem& renderSystem = systemGraph.AddSystem(); + Nz::RenderWindow& mainWindow = renderSystem.CreateWindow(Nz::Graphics::Instance()->GetRenderDevice(), Nz::VideoMode(1280, 720), "Tut01 - Hello world"); entt::entity cameraEntity = registry.create(); { @@ -62,17 +63,7 @@ int main() while (mainWindow.IsOpen()) { mainWindow.ProcessEvents(); - - Nz::RenderFrame renderFrame = mainWindow.AcquireFrame(); - if (!renderFrame) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - - renderSystem.Render(registry, renderFrame); - - renderFrame.Present(); + systemGraph.Update(); } return EXIT_SUCCESS; diff --git a/examples/Tut02/main.cpp b/examples/Tut02/main.cpp index 93bd158a5..2ab8343ba 100644 --- a/examples/Tut02/main.cpp +++ b/examples/Tut02/main.cpp @@ -1,5 +1,6 @@ // Sources pour https://github.com/NazaraEngine/NazaraEngine/wiki/(FR)-Tutoriel:-%5B02%5D-Gestion-des-événements +#include #include #include #include @@ -15,10 +16,10 @@ int main() { Nz::Modules nazara; - Nz::RenderWindow mainWindow(Nz::Graphics::Instance()->GetRenderDevice(), Nz::VideoMode(1280, 720, 32), "Test"); - entt::registry registry; - Nz::RenderSystem renderSystem(registry); + Nz::SystemGraph systemGraph(registry); + Nz::RenderSystem& renderSystem = systemGraph.AddSystem(); + Nz::RenderWindow& mainWindow = renderSystem.CreateWindow(Nz::Graphics::Instance()->GetRenderDevice(), Nz::VideoMode(1280, 720), "Tut02 - Events"); entt::entity cameraEntity = registry.create(); { @@ -79,17 +80,7 @@ int main() while (mainWindow.IsOpen()) { mainWindow.ProcessEvents(); - - Nz::RenderFrame renderFrame = mainWindow.AcquireFrame(); - if (!renderFrame) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - - renderSystem.Render(registry, renderFrame); - - renderFrame.Present(); + systemGraph.Update(); } return EXIT_SUCCESS; diff --git a/examples/WidgetDemo/main.cpp b/examples/WidgetDemo/main.cpp index d6ef47501..d9dad4b43 100644 --- a/examples/WidgetDemo/main.cpp +++ b/examples/WidgetDemo/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -43,14 +44,11 @@ int main() std::shared_ptr 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::SystemGraph systemGraph(registry); + Nz::RenderSystem& renderSystem = systemGraph.AddSystem(); + Nz::RenderWindow& mainWindow = renderSystem.CreateWindow(device, Nz::VideoMode(1920, 1080), windowTitle); Nz::Canvas canvas2D(registry, window.GetEventHandler(), window.GetCursorController().CreateHandle(), 0xFFFFFFFF); canvas2D.Resize(Nz::Vector2f(window.GetSize())); @@ -148,13 +146,7 @@ int main() } } - Nz::RenderFrame frame = window.AcquireFrame(); - if (!frame) - continue; - - renderSystem.Render(registry, frame); - - frame.Present(); + systemGraph.Update(); fps++; diff --git a/include/Nazara/Core/Systems.hpp b/include/Nazara/Core/Systems.hpp index 269bf0b46..6e8908062 100644 --- a/include/Nazara/Core/Systems.hpp +++ b/include/Nazara/Core/Systems.hpp @@ -30,5 +30,6 @@ #define NAZARA_CORE_SYSTEMS_HPP #include +#include #endif // NAZARA_CORE_SYSTEMS_HPP diff --git a/include/Nazara/Core/Systems/SystemGraph.hpp b/include/Nazara/Core/Systems/SystemGraph.hpp new file mode 100644 index 000000000..66972a34c --- /dev/null +++ b/include/Nazara/Core/Systems/SystemGraph.hpp @@ -0,0 +1,70 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CORE_SYSTEMS_SYSTEMGRAPH_HPP +#define NAZARA_CORE_SYSTEMS_SYSTEMGRAPH_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_CORE_API SystemGraph + { + public: + inline SystemGraph(entt::registry& registry); + SystemGraph(const SystemGraph&) = delete; + SystemGraph(SystemGraph&&) = delete; + ~SystemGraph() = default; + + template T& AddSystem(Args&&... args); + + template T& GetSystem() const; + + void Update(); + void Update(float elapsedTime); + + SystemGraph& operator=(const SystemGraph&) = delete; + SystemGraph& operator=(SystemGraph&&) = delete; + + private: + struct NAZARA_CORE_API NodeBase + { + virtual ~NodeBase(); + + virtual void Update(float elapsedTime) = 0; + + Int64 executionOrder; + }; + + template + struct Node : NodeBase + { + template Node(Args&&... args); + + void Update(float elapsedTime) override; + + T system; + }; + + std::unordered_map m_systemToNodes; + std::vector m_orderedNodes; + std::vector> m_nodes; + entt::registry& m_registry; + Nz::Clock m_clock; + bool m_systemOrderUpdated; + }; +} + +#include + +#endif // NAZARA_CORE_SYSTEMS_SYSTEMGRAPH_HPP diff --git a/include/Nazara/Core/Systems/SystemGraph.inl b/include/Nazara/Core/Systems/SystemGraph.inl new file mode 100644 index 000000000..6db8d5409 --- /dev/null +++ b/include/Nazara/Core/Systems/SystemGraph.inl @@ -0,0 +1,74 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + namespace Detail + { + template + struct SystemGraphAllowConcurrent : std::bool_constant {}; + + template + struct SystemGraphAllowConcurrent> : std::bool_constant {}; + + template + struct SystemGraphExecutionOrder : std::integral_constant {}; + + template + struct SystemGraphExecutionOrder> : std::integral_constant {}; + } + + template + template + SystemGraph::Node::Node(Args&&... args) : + system(std::forward(args)...) + { + } + + template + void SystemGraph::Node::Update(float elapsedTime) + { + system.Update(elapsedTime); + } + + inline SystemGraph::SystemGraph(entt::registry& registry) : + m_registry(registry), + m_systemOrderUpdated(true) + { + } + + template + T& SystemGraph::AddSystem(Args&&... args) + { + NazaraAssert(m_systemToNodes.find(entt::type_hash()) == m_systemToNodes.end(), "this system already exists"); + + auto nodePtr = std::make_unique>(m_registry, std::forward(args)...); + nodePtr->executionOrder = Detail::SystemGraphExecutionOrder(); + + T& system = nodePtr->system; + + m_nodes.emplace_back(std::move(nodePtr)); + m_systemOrderUpdated = false; + + return system; + } + + template + T& SystemGraph::GetSystem() const + { + auto it = m_systemToNodes.find(entt::type_hash()); + if (it == m_systemToNodes.end()) + throw std::runtime_error("this system is not part of the graph"); + + auto& node = static_cast&>(*m_nodes[it->second]); + return node.system; + } +} + +#include diff --git a/include/Nazara/Graphics/GraphicalMesh.hpp b/include/Nazara/Graphics/GraphicalMesh.hpp index 8b16d745b..2fab270cf 100644 --- a/include/Nazara/Graphics/GraphicalMesh.hpp +++ b/include/Nazara/Graphics/GraphicalMesh.hpp @@ -8,11 +8,11 @@ #define NAZARA_GRAPHICS_GRAPHICALMESH_HPP #include -#include #include #include #include #include +#include #include namespace Nz diff --git a/include/Nazara/Graphics/Systems/RenderSystem.hpp b/include/Nazara/Graphics/Systems/RenderSystem.hpp index 2c1ae8f3a..f2406b8b7 100644 --- a/include/Nazara/Graphics/Systems/RenderSystem.hpp +++ b/include/Nazara/Graphics/Systems/RenderSystem.hpp @@ -24,17 +24,23 @@ namespace Nz class CommandBufferBuilder; class FramePipeline; class RenderFrame; + class RenderWindow; class UploadPool; class NAZARA_GRAPHICS_API RenderSystem { public: + static constexpr bool AllowConcurrent = false; + static constexpr Int64 ExecutionOrder = 1'000; + RenderSystem(entt::registry& registry); RenderSystem(const RenderSystem&) = delete; RenderSystem(RenderSystem&&) = delete; ~RenderSystem(); - void Render(entt::registry& registry, RenderFrame& renderFrame); + template T& CreateWindow(Args&&... args); + + void Update(float elapsedTime); RenderSystem& operator=(const RenderSystem&) = delete; RenderSystem& operator=(RenderSystem&&) = delete; @@ -44,8 +50,9 @@ namespace Nz 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); + void UpdateInstances(); + void UpdateObservers(); + void UpdateVisibility(); struct CameraEntity { @@ -89,6 +96,7 @@ namespace Nz entt::observer m_cameraConstructObserver; entt::observer m_graphicsConstructObserver; entt::observer m_lightConstructObserver; + entt::registry& m_registry; std::set m_invalidatedCameraNode; std::set m_invalidatedGfxWorldNode; std::set m_invalidatedLightWorldNode; @@ -100,6 +108,7 @@ namespace Nz std::unordered_set m_newlyVisibleGfxEntities; std::unordered_set m_newlyHiddenLightEntities; std::unordered_set m_newlyVisibleLightEntities; + std::vector> m_renderWindows; MemoryPool m_cameraEntityPool; MemoryPool m_graphicsEntityPool; MemoryPool m_lightEntityPool; diff --git a/include/Nazara/Graphics/Systems/RenderSystem.inl b/include/Nazara/Graphics/Systems/RenderSystem.inl index 3b781451d..0c8165220 100644 --- a/include/Nazara/Graphics/Systems/RenderSystem.inl +++ b/include/Nazara/Graphics/Systems/RenderSystem.inl @@ -3,10 +3,23 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include namespace Nz { + template + T& RenderSystem::CreateWindow(Args&& ...args) + { + static_assert(std::is_base_of_v, "T must inherit RenderWindow"); + + auto windowPtr = std::make_unique(std::forward(args)...); + T& windowRef = *windowPtr; + + m_renderWindows.emplace_back(std::move(windowPtr)); + + return windowRef; + } } #include diff --git a/include/Nazara/Physics2D/Systems/Physics2DSystem.hpp b/include/Nazara/Physics2D/Systems/Physics2DSystem.hpp index fd83534bc..71136ea3a 100644 --- a/include/Nazara/Physics2D/Systems/Physics2DSystem.hpp +++ b/include/Nazara/Physics2D/Systems/Physics2DSystem.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace Nz @@ -17,6 +18,9 @@ namespace Nz class NAZARA_PHYSICS2D_API Physics2DSystem { public: + static constexpr Int64 ExecutionOrder = 0; + using Components = TypeList; + Physics2DSystem(entt::registry& registry); Physics2DSystem(const Physics2DSystem&) = delete; Physics2DSystem(Physics2DSystem&&) = delete; @@ -27,7 +31,7 @@ namespace Nz inline PhysWorld2D& GetPhysWorld(); inline const PhysWorld2D& GetPhysWorld() const; - void Update(entt::registry& registry, float elapsedTime); + void Update(float elapsedTime); Physics2DSystem& operator=(const Physics2DSystem&) = delete; Physics2DSystem& operator=(Physics2DSystem&&) = delete; diff --git a/include/Nazara/Physics3D/Systems/Physics3DSystem.hpp b/include/Nazara/Physics3D/Systems/Physics3DSystem.hpp index 3777688b4..4a73eac5b 100644 --- a/include/Nazara/Physics3D/Systems/Physics3DSystem.hpp +++ b/include/Nazara/Physics3D/Systems/Physics3DSystem.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace Nz @@ -17,6 +18,9 @@ namespace Nz class NAZARA_PHYSICS3D_API Physics3DSystem { public: + static constexpr Int64 ExecutionOrder = 0; + using Components = TypeList; + Physics3DSystem(entt::registry& registry); Physics3DSystem(const Physics3DSystem&) = delete; Physics3DSystem(Physics3DSystem&&) = delete; @@ -27,7 +31,7 @@ namespace Nz inline PhysWorld3D& GetPhysWorld(); inline const PhysWorld3D& GetPhysWorld() const; - void Update(entt::registry& registry, float elapsedTime); + void Update(float elapsedTime); Physics3DSystem& operator=(const Physics3DSystem&) = delete; Physics3DSystem& operator=(Physics3DSystem&&) = delete; diff --git a/src/Nazara/Core/Systems/SystemGraph.cpp b/src/Nazara/Core/Systems/SystemGraph.cpp new file mode 100644 index 000000000..1730e1941 --- /dev/null +++ b/src/Nazara/Core/Systems/SystemGraph.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + SystemGraph::NodeBase::~NodeBase() = default; + + void SystemGraph::Update() + { + return Update(m_clock.Restart() / 1'000'000.f); + } + + void SystemGraph::Update(float elapsedTime) + { + if (!m_systemOrderUpdated) + { + m_orderedNodes.clear(); + m_orderedNodes.reserve(m_nodes.size()); + for (auto& nodePtr : m_nodes) + m_orderedNodes.emplace_back(nodePtr.get()); + + std::sort(m_orderedNodes.begin(), m_orderedNodes.end(), [](const NodeBase* a, const NodeBase* b) + { + return a->executionOrder < b->executionOrder; + }); + + m_systemOrderUpdated = true; + } + + for (NodeBase* node : m_orderedNodes) + node->Update(elapsedTime); + } +} diff --git a/src/Nazara/Graphics/Systems/RenderSystem.cpp b/src/Nazara/Graphics/Systems/RenderSystem.cpp index c7b46e2f7..a82a13f74 100644 --- a/src/Nazara/Graphics/Systems/RenderSystem.cpp +++ b/src/Nazara/Graphics/Systems/RenderSystem.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ namespace Nz m_cameraConstructObserver(registry, entt::collector.group()), m_graphicsConstructObserver(registry, entt::collector.group()), m_lightConstructObserver(registry, entt::collector.group()), + m_registry(registry), m_cameraEntityPool(8), m_graphicsEntityPool(1024), m_lightEntityPool(32) @@ -43,12 +45,181 @@ namespace Nz m_nodeDestroyConnection.release(); } - void RenderSystem::Render(entt::registry& registry, RenderFrame& renderFrame) + void RenderSystem::Update(float /*elapsedTime*/) + { + UpdateObservers(); + UpdateVisibility(); + UpdateInstances(); + + for (auto& windowPtr : m_renderWindows) + { + RenderFrame frame = windowPtr->AcquireFrame(); + if (!frame) + continue; + + if (!m_cameraEntities.empty()) + m_pipeline->Render(frame); + + frame.Present(); + } + } + + void RenderSystem::OnCameraDestroy([[maybe_unused]] entt::registry& registry, entt::entity entity) + { + assert(&m_registry == ®istry); + + auto it = m_cameraEntities.find(entity); + if (it == m_cameraEntities.end()) + return; + + CameraEntity* cameraEntity = it->second; + + m_cameraEntities.erase(it); + m_invalidatedCameraNode.erase(cameraEntity); + m_pipeline->UnregisterViewer(cameraEntity->viewerIndex); + + m_cameraEntityPool.Free(cameraEntity->poolIndex); + } + + void RenderSystem::OnGraphicsDestroy([[maybe_unused]] entt::registry& registry, entt::entity entity) + { + assert(&m_registry == ®istry); + + auto it = m_graphicsEntities.find(entity); + if (it == m_graphicsEntities.end()) + return; + + GraphicsEntity* graphicsEntity = it->second; + + m_graphicsEntities.erase(entity); + m_invalidatedGfxWorldNode.erase(graphicsEntity); + m_newlyHiddenGfxEntities.erase(graphicsEntity); + m_newlyVisibleGfxEntities.erase(graphicsEntity); + + GraphicsComponent& entityGfx = m_registry.get(entity); + if (entityGfx.IsVisible()) + { + for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex) + { + const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex); + if (!renderableEntry.renderable) + continue; + + m_pipeline->UnregisterRenderable(graphicsEntity->renderableIndices[renderableIndex]); + } + } + + m_pipeline->UnregisterWorldInstance(graphicsEntity->worldInstanceIndex); + + m_graphicsEntityPool.Free(graphicsEntity->poolIndex); + } + + void RenderSystem::OnLightDestroy([[maybe_unused]] entt::registry& registry, entt::entity entity) + { + assert(&m_registry == ®istry); + + auto it = m_lightEntities.find(entity); + if (it == m_lightEntities.end()) + return; + + LightEntity* lightEntity = it->second; + + m_lightEntities.erase(entity); + m_invalidatedLightWorldNode.erase(lightEntity); + m_newlyHiddenLightEntities.erase(lightEntity); + m_newlyVisibleLightEntities.erase(lightEntity); + + LightComponent& entityLight = m_registry.get(entity); + if (entityLight.IsVisible()) + { + for (std::size_t lightIndex = 0; lightIndex < LightComponent::MaxLightCount; ++lightIndex) + { + const auto& lightEntry = entityLight.GetLightEntry(lightIndex); + if (!lightEntry.light) + continue; + + m_pipeline->UnregisterLight(lightEntity->lightIndices[lightIndex]); + } + } + + m_lightEntityPool.Free(lightEntity->poolIndex); + } + + void RenderSystem::OnNodeDestroy(entt::registry& registry, entt::entity entity) + { + assert(&m_registry == ®istry); + + if (m_registry.try_get(entity)) + OnCameraDestroy(registry, entity); + + if (m_registry.try_get(entity)) + OnGraphicsDestroy(registry, entity); + + if (m_registry.try_get(entity)) + OnLightDestroy(registry, entity); + } + + void RenderSystem::UpdateInstances() + { + for (CameraEntity* cameraEntity : m_invalidatedCameraNode) + { + entt::entity entity = cameraEntity->entity; + + const NodeComponent& entityNode = m_registry.get(entity); + CameraComponent& entityCamera = m_registry.get(entity); + + Vector3f cameraPosition = entityNode.GetPosition(CoordSys::Global); + + ViewerInstance& viewerInstance = entityCamera.GetViewerInstance(); + viewerInstance.UpdateEyePosition(cameraPosition); + viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(cameraPosition, entityNode.GetRotation(CoordSys::Global))); + + m_pipeline->InvalidateViewer(cameraEntity->viewerIndex); + } + m_invalidatedCameraNode.clear(); + + for (GraphicsEntity* graphicsEntity : m_invalidatedGfxWorldNode) + { + entt::entity entity = graphicsEntity->entity; + + const NodeComponent& entityNode = m_registry.get(entity); + GraphicsComponent& entityGraphics = m_registry.get(entity); + + const WorldInstancePtr& worldInstance = entityGraphics.GetWorldInstance(); + worldInstance->UpdateWorldMatrix(entityNode.GetTransformMatrix()); + + m_pipeline->InvalidateWorldInstance(graphicsEntity->worldInstanceIndex); + } + m_invalidatedGfxWorldNode.clear(); + + for (LightEntity* lightEntity : m_invalidatedLightWorldNode) + { + entt::entity entity = lightEntity->entity; + + const NodeComponent& entityNode = m_registry.get(entity); + LightComponent& entityLight = m_registry.get(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()) + { + if (!lightEntry.light) + continue; + + lightEntry.light->UpdateTransform(position, rotation, scale); + } + } + m_invalidatedLightWorldNode.clear(); + } + + void RenderSystem::UpdateObservers() { m_cameraConstructObserver.each([&](entt::entity entity) { - CameraComponent& entityCamera = registry.get(entity); - NodeComponent& entityNode = registry.get(entity); + CameraComponent& entityCamera = m_registry.get(entity); + NodeComponent& entityNode = m_registry.get(entity); std::size_t poolIndex; CameraEntity* cameraEntity = m_cameraEntityPool.Allocate(poolIndex); @@ -68,8 +239,8 @@ namespace Nz m_graphicsConstructObserver.each([&](entt::entity entity) { - GraphicsComponent& entityGfx = registry.get(entity); - NodeComponent& entityNode = registry.get(entity); + GraphicsComponent& entityGfx = m_registry.get(entity); + NodeComponent& entityNode = m_registry.get(entity); std::size_t poolIndex; GraphicsEntity* graphicsEntity = m_graphicsEntityPool.Allocate(poolIndex); @@ -147,8 +318,8 @@ namespace Nz m_lightConstructObserver.each([&](entt::entity entity) { - LightComponent& entityLight = registry.get(entity); - NodeComponent& entityNode = registry.get(entity); + LightComponent& entityLight = m_registry.get(entity); + NodeComponent& entityNode = m_registry.get(entity); std::size_t poolIndex; LightEntity* lightEntity = m_lightEntityPool.Allocate(poolIndex); @@ -212,162 +383,14 @@ namespace Nz assert(m_lightEntities.find(entity) == m_lightEntities.end()); m_lightEntities.emplace(entity, lightEntity); }); - - UpdateVisibility(registry); - UpdateInstances(registry); - - if (!m_cameraEntities.empty()) - m_pipeline->Render(renderFrame); } - void RenderSystem::OnCameraDestroy(entt::registry& /*registry*/, entt::entity entity) - { - auto it = m_cameraEntities.find(entity); - if (it == m_cameraEntities.end()) - return; - - CameraEntity* cameraEntity = it->second; - - m_cameraEntities.erase(it); - m_invalidatedCameraNode.erase(cameraEntity); - m_pipeline->UnregisterViewer(cameraEntity->viewerIndex); - - m_cameraEntityPool.Free(cameraEntity->poolIndex); - } - - void RenderSystem::OnGraphicsDestroy(entt::registry& registry, entt::entity entity) - { - auto it = m_graphicsEntities.find(entity); - if (it == m_graphicsEntities.end()) - return; - - GraphicsEntity* graphicsEntity = it->second; - - m_graphicsEntities.erase(entity); - m_invalidatedGfxWorldNode.erase(graphicsEntity); - m_newlyHiddenGfxEntities.erase(graphicsEntity); - m_newlyVisibleGfxEntities.erase(graphicsEntity); - - GraphicsComponent& entityGfx = registry.get(entity); - if (entityGfx.IsVisible()) - { - for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex) - { - const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex); - if (!renderableEntry.renderable) - continue; - - m_pipeline->UnregisterRenderable(graphicsEntity->renderableIndices[renderableIndex]); - } - } - - m_pipeline->UnregisterWorldInstance(graphicsEntity->worldInstanceIndex); - - m_graphicsEntityPool.Free(graphicsEntity->poolIndex); - } - - void RenderSystem::OnLightDestroy(entt::registry& registry, entt::entity entity) - { - auto it = m_lightEntities.find(entity); - if (it == m_lightEntities.end()) - return; - - LightEntity* lightEntity = it->second; - - m_lightEntities.erase(entity); - m_invalidatedLightWorldNode.erase(lightEntity); - m_newlyHiddenLightEntities.erase(lightEntity); - m_newlyVisibleLightEntities.erase(lightEntity); - - LightComponent& entityLight = registry.get(entity); - if (entityLight.IsVisible()) - { - for (std::size_t lightIndex = 0; lightIndex < LightComponent::MaxLightCount; ++lightIndex) - { - const auto& lightEntry = entityLight.GetLightEntry(lightIndex); - if (!lightEntry.light) - continue; - - m_pipeline->UnregisterLight(lightEntity->lightIndices[lightIndex]); - } - } - - m_lightEntityPool.Free(lightEntity->poolIndex); - } - - void RenderSystem::OnNodeDestroy(entt::registry& registry, entt::entity entity) - { - if (registry.try_get(entity)) - OnCameraDestroy(registry, entity); - - if (registry.try_get(entity)) - OnGraphicsDestroy(registry, entity); - - if (registry.try_get(entity)) - OnLightDestroy(registry, entity); - } - - void RenderSystem::UpdateInstances(entt::registry& registry) - { - for (CameraEntity* cameraEntity : m_invalidatedCameraNode) - { - entt::entity entity = cameraEntity->entity; - - const NodeComponent& entityNode = registry.get(entity); - CameraComponent& entityCamera = registry.get(entity); - - Vector3f cameraPosition = entityNode.GetPosition(CoordSys::Global); - - ViewerInstance& viewerInstance = entityCamera.GetViewerInstance(); - viewerInstance.UpdateEyePosition(cameraPosition); - viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(cameraPosition, entityNode.GetRotation(CoordSys::Global))); - - m_pipeline->InvalidateViewer(cameraEntity->viewerIndex); - } - m_invalidatedCameraNode.clear(); - - for (GraphicsEntity* graphicsEntity : m_invalidatedGfxWorldNode) - { - entt::entity entity = graphicsEntity->entity; - - const NodeComponent& entityNode = registry.get(entity); - GraphicsComponent& entityGraphics = registry.get(entity); - - const WorldInstancePtr& worldInstance = entityGraphics.GetWorldInstance(); - worldInstance->UpdateWorldMatrix(entityNode.GetTransformMatrix()); - - m_pipeline->InvalidateWorldInstance(graphicsEntity->worldInstanceIndex); - } - m_invalidatedGfxWorldNode.clear(); - - for (LightEntity* lightEntity : m_invalidatedLightWorldNode) - { - entt::entity entity = lightEntity->entity; - - const NodeComponent& entityNode = registry.get(entity); - LightComponent& entityLight = registry.get(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()) - { - if (!lightEntry.light) - continue; - - lightEntry.light->UpdateTransform(position, rotation, scale); - } - } - m_invalidatedLightWorldNode.clear(); - } - - void RenderSystem::UpdateVisibility(entt::registry& registry) + void RenderSystem::UpdateVisibility() { // Unregister drawable for hidden entities for (GraphicsEntity* graphicsEntity : m_newlyHiddenGfxEntities) { - GraphicsComponent& entityGfx = registry.get(graphicsEntity->entity); + GraphicsComponent& entityGfx = m_registry.get(graphicsEntity->entity); for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex) { @@ -383,7 +406,7 @@ namespace Nz // Register drawable for newly visible entities for (GraphicsEntity* graphicsEntity : m_newlyVisibleGfxEntities) { - GraphicsComponent& entityGfx = registry.get(graphicsEntity->entity); + GraphicsComponent& entityGfx = m_registry.get(graphicsEntity->entity); for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex) { @@ -399,7 +422,7 @@ namespace Nz // Unregister lights for hidden entities for (LightEntity* lightEntity : m_newlyHiddenLightEntities) { - LightComponent& entityLights = registry.get(lightEntity->entity); + LightComponent& entityLights = m_registry.get(lightEntity->entity); for (std::size_t lightIndex = 0; lightIndex < LightComponent::MaxLightCount; ++lightIndex) { @@ -415,7 +438,7 @@ namespace Nz // Register lights for newly visible entities for (LightEntity* lightEntity : m_newlyVisibleLightEntities) { - LightComponent& entityLights = registry.get(lightEntity->entity); + LightComponent& entityLights = m_registry.get(lightEntity->entity); for (std::size_t renderableIndex = 0; renderableIndex < LightComponent::MaxLightCount; ++renderableIndex) { diff --git a/src/Nazara/Physics2D/Systems/Physics2DSystem.cpp b/src/Nazara/Physics2D/Systems/Physics2DSystem.cpp index 6946a1e8d..cdf492550 100644 --- a/src/Nazara/Physics2D/Systems/Physics2DSystem.cpp +++ b/src/Nazara/Physics2D/Systems/Physics2DSystem.cpp @@ -35,12 +35,12 @@ namespace Nz m_constructConnection.release(); } - void Physics2DSystem::Update(entt::registry& registry, float elapsedTime) + void Physics2DSystem::Update(float elapsedTime) { m_physWorld.Step(elapsedTime); // Replicate rigid body position to their node components - auto view = registry.view(); + auto view = m_registry.view(); for (auto [entity, nodeComponent, rigidBodyComponent] : view.each()) { if (rigidBodyComponent.IsSleeping()) diff --git a/src/Nazara/Physics3D/Systems/Physics3DSystem.cpp b/src/Nazara/Physics3D/Systems/Physics3DSystem.cpp index 2da7120f1..89ba7144b 100644 --- a/src/Nazara/Physics3D/Systems/Physics3DSystem.cpp +++ b/src/Nazara/Physics3D/Systems/Physics3DSystem.cpp @@ -24,12 +24,12 @@ namespace Nz m_constructConnection.release(); } - void Physics3DSystem::Update(entt::registry& registry, float elapsedTime) + void Physics3DSystem::Update(float elapsedTime) { m_physWorld.Step(elapsedTime); // Replicate rigid body position to their node components - auto view = registry.view(); + auto view = m_registry.view(); for (auto [entity, nodeComponent, rigidBodyComponent] : view.each()) { if (rigidBodyComponent.IsSleeping())