diff --git a/.gitattributes b/.gitattributes index dfe077042..a788acb7e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ # Auto detect text files and perform LF normalization * text=auto +extlibs/* linguist-vendored +NazaraModuleTemplate/* linguist-vendored diff --git a/.gitignore b/.gitignore index 258594792..463291bc4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,13 @@ # Nazara build +build/config.lua examples/bin/*.exe examples/bin/*.pdb examples/bin/*.dll examples/bin/*.so tests/*.exe tests/*.pdb +tests/*.dll +tests/*.so lib/* # Feature page @@ -35,6 +38,7 @@ build/**/*.vcxprojResolveAssemblyReference.cache build/**/*.nativecodeanalysis.all.xml build/**/*.nativecodeanalysis.xml build/**/*.VC.opendb +build/**/*.VC.db # Compiled Object files build/**/*.slo diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..aa1920df5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jérôme Leclercq + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/SDK/include/NDK/Application.hpp b/SDK/include/NDK/Application.hpp index 5d918ef8e..6a375776c 100644 --- a/SDK/include/NDK/Application.hpp +++ b/SDK/include/NDK/Application.hpp @@ -33,6 +33,10 @@ namespace Ndk bool Run(); + #ifndef NDK_SERVER + inline void MakeExitOnLastWindowClosed(bool exitOnClosedWindows); + #endif + inline void Quit(); Application& operator=(const Application&) = delete; diff --git a/SDK/include/NDK/Application.inl b/SDK/include/NDK/Application.inl index f44ff9e8c..96e8768ce 100644 --- a/SDK/include/NDK/Application.inl +++ b/SDK/include/NDK/Application.inl @@ -61,6 +61,13 @@ namespace Ndk return m_updateTime; } + #ifndef NDK_SERVER + inline void Application::MakeExitOnLastWindowClosed(bool exitOnClosedWindows) + { + m_exitOnClosedWindows = exitOnClosedWindows; + } + #endif + inline void Application::Quit() { m_shouldQuit = true; diff --git a/SDK/include/NDK/BaseComponent.inl b/SDK/include/NDK/BaseComponent.inl index 71f8ee2c1..341ddb651 100644 --- a/SDK/include/NDK/BaseComponent.inl +++ b/SDK/include/NDK/BaseComponent.inl @@ -26,7 +26,7 @@ namespace Ndk inline ComponentIndex BaseComponent::RegisterComponent(ComponentId id, Factory factoryFunc) { // Nous allons rajouter notre composant à la fin - ComponentIndex index = s_entries.size(); + ComponentIndex index = static_cast(s_entries.size()); s_entries.resize(index + 1); // On récupère et on affecte diff --git a/SDK/include/NDK/Component.inl b/SDK/include/NDK/Component.inl index e39091224..833395957 100644 --- a/SDK/include/NDK/Component.inl +++ b/SDK/include/NDK/Component.inl @@ -28,13 +28,11 @@ namespace Ndk template ComponentIndex Component::RegisterComponent(ComponentId id) { - // Il faut que notre composant possède un constructeur par défaut (pour la factory) - static_assert(std::is_default_constructible::value, "ComponentType must be default-constructible"); - // On utilise les lambda pour créer une fonction factory auto factory = []() -> BaseComponent* { - return new ComponentType; + return nullptr; //< Temporary workaround to allow non-default-constructed components, will be updated for serialization + //return new ComponentType; }; return BaseComponent::RegisterComponent(id, factory); diff --git a/SDK/include/NDK/Components.hpp b/SDK/include/NDK/Components.hpp index 46392ab7c..79bc196f7 100644 --- a/SDK/include/NDK/Components.hpp +++ b/SDK/include/NDK/Components.hpp @@ -1,20 +1,19 @@ -// This file was automatically generated on 03 Mar 2016 at 14:09:12 +// This file was automatically generated on 30 Jul 2016 at 15:29:16 #pragma once #ifndef NDK_COMPONENTS_GLOBAL_HPP #define NDK_COMPONENTS_GLOBAL_HPP -#include -#include -#include -#include - -#ifndef NDK_SERVER #include +#include #include #include #include -#endif +#include +#include +#include +#include +#include #endif // NDK_COMPONENTS_GLOBAL_HPP diff --git a/SDK/include/NDK/Components/CameraComponent.hpp b/SDK/include/NDK/Components/CameraComponent.hpp index d5951109b..af2245b11 100644 --- a/SDK/include/NDK/Components/CameraComponent.hpp +++ b/SDK/include/NDK/Components/CameraComponent.hpp @@ -4,6 +4,7 @@ #pragma once +#ifndef NDK_SERVER #ifndef NDK_COMPONENTS_CAMERACOMPONENT_HPP #define NDK_COMPONENTS_CAMERACOMPONENT_HPP @@ -108,3 +109,4 @@ namespace Ndk #include #endif // NDK_COMPONENTS_CAMERACOMPONENT_HPP +#endif // NDK_SERVER diff --git a/SDK/include/NDK/Components/CameraComponent.inl b/SDK/include/NDK/Components/CameraComponent.inl index 7ff31d623..d91c357eb 100644 --- a/SDK/include/NDK/Components/CameraComponent.inl +++ b/SDK/include/NDK/Components/CameraComponent.inl @@ -2,17 +2,17 @@ // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequesites.hpp +#include #include #include -#include "CameraComponent.hpp" namespace Ndk { inline CameraComponent::CameraComponent() : m_projectionType(Nz::ProjectionType_Perspective), m_targetRegion(0.f, 0.f, 1.f, 1.f), - m_size(0.f), m_target(nullptr), + m_size(0.f), m_frustumUpdated(false), m_projectionMatrixUpdated(false), m_viewMatrixUpdated(false), @@ -30,8 +30,8 @@ namespace Ndk AbstractViewer(camera), m_projectionType(camera.m_projectionType), m_targetRegion(camera.m_targetRegion), - m_size(camera.m_size), m_target(nullptr), + m_size(camera.m_size), m_frustumUpdated(false), m_projectionMatrixUpdated(false), m_viewMatrixUpdated(false), diff --git a/SDK/include/NDK/Components/GraphicsComponent.hpp b/SDK/include/NDK/Components/GraphicsComponent.hpp index f8fa2bf66..92aeeed2f 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.hpp +++ b/SDK/include/NDK/Components/GraphicsComponent.hpp @@ -4,6 +4,7 @@ #pragma once +#ifndef NDK_SERVER #ifndef NDK_COMPONENTS_GRAPHICSCOMPONENT_HPP #define NDK_COMPONENTS_GRAPHICSCOMPONENT_HPP @@ -22,6 +23,8 @@ namespace Ndk friend class RenderSystem; public: + using RenderableList = std::vector; + GraphicsComponent() = default; inline GraphicsComponent(const GraphicsComponent& graphicsComponent); ~GraphicsComponent() = default; @@ -30,16 +33,23 @@ namespace Ndk inline void Attach(Nz::InstancedRenderableRef renderable, int renderOrder = 0); + inline void Clear(); + + inline void Detach(const Nz::InstancedRenderableRef& renderable); + inline void EnsureBoundingVolumeUpdate() const; inline void EnsureTransformMatrixUpdate() const; + inline void GetAttachedRenderables(RenderableList* renderables) const; + inline std::size_t GetAttachedRenderableCount() const; + inline const Nz::BoundingVolumef& GetBoundingVolume() const; static ComponentIndex componentIndex; private: inline void InvalidateBoundingVolume(); - void InvalidateRenderableData(const Nz::InstancedRenderable* renderable, Nz::UInt32 flags, unsigned int index); + void InvalidateRenderableData(const Nz::InstancedRenderable* renderable, Nz::UInt32 flags, std::size_t index); inline void InvalidateRenderables(); inline void InvalidateTransformMatrix(); @@ -56,12 +66,28 @@ namespace Ndk struct Renderable { - Renderable(Nz::Matrix4f& transformMatrix) : + Renderable(const Nz::Matrix4f& transformMatrix) : data(transformMatrix), dataUpdated(false) { } + Renderable(Renderable&& renderable) noexcept : + data(std::move(renderable.data)), + renderable(std::move(renderable.renderable)), + dataUpdated(renderable.dataUpdated) + { + } + + Renderable& operator=(Renderable&& r) noexcept + { + data = std::move(r.data); + dataUpdated = r.dataUpdated; + renderable = std::move(r.renderable); + + return *this; + } + NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableInvalidateData, renderableInvalidationSlot); mutable Nz::InstancedRenderable::InstanceData data; @@ -80,3 +106,4 @@ namespace Ndk #include #endif // NDK_COMPONENTS_GRAPHICSCOMPONENT_HPP +#endif // NDK_SERVER \ No newline at end of file diff --git a/SDK/include/NDK/Components/GraphicsComponent.inl b/SDK/include/NDK/Components/GraphicsComponent.inl index 205eb9462..b38ce6dd6 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.inl +++ b/SDK/include/NDK/Components/GraphicsComponent.inl @@ -3,11 +3,13 @@ // For conditions of distribution and use, see copyright notice in Prerequesites.hpp #include +#include "GraphicsComponent.hpp" namespace Ndk { inline GraphicsComponent::GraphicsComponent(const GraphicsComponent& graphicsComponent) : Component(graphicsComponent), + HandledObject(graphicsComponent), m_boundingVolume(graphicsComponent.m_boundingVolume), m_transformMatrix(graphicsComponent.m_transformMatrix), m_boundingVolumeUpdated(graphicsComponent.m_boundingVolumeUpdated), @@ -41,10 +43,30 @@ namespace Ndk r.data.renderOrder = renderOrder; r.renderable = std::move(renderable); r.renderableInvalidationSlot.Connect(r.renderable->OnInstancedRenderableInvalidateData, std::bind(&GraphicsComponent::InvalidateRenderableData, this, std::placeholders::_1, std::placeholders::_2, m_renderables.size()-1)); - + InvalidateBoundingVolume(); } + inline void GraphicsComponent::Clear() + { + m_renderables.clear(); + + InvalidateBoundingVolume(); + } + + inline void GraphicsComponent::Detach(const Nz::InstancedRenderableRef& renderable) + { + for (auto it = m_renderables.begin(); it != m_renderables.end(); ++it) + { + if (it->renderable == renderable) + { + InvalidateBoundingVolume(); + m_renderables.erase(it); + break; + } + } + } + inline void GraphicsComponent::EnsureBoundingVolumeUpdate() const { if (!m_boundingVolumeUpdated) @@ -57,6 +79,20 @@ namespace Ndk UpdateTransformMatrix(); } + inline void GraphicsComponent::GetAttachedRenderables(RenderableList* renderables) const + { + NazaraAssert(renderables, "Invalid renderable list"); + + renderables->reserve(renderables->size() + m_renderables.size()); + for (const Renderable& r : m_renderables) + renderables->push_back(r.renderable); + } + + inline std::size_t GraphicsComponent::GetAttachedRenderableCount() const + { + return m_renderables.size(); + } + inline const Nz::BoundingVolumef& GraphicsComponent::GetBoundingVolume() const { EnsureBoundingVolumeUpdate(); diff --git a/SDK/include/NDK/Components/LightComponent.hpp b/SDK/include/NDK/Components/LightComponent.hpp index c55ae57bb..80f84a759 100644 --- a/SDK/include/NDK/Components/LightComponent.hpp +++ b/SDK/include/NDK/Components/LightComponent.hpp @@ -4,6 +4,7 @@ #pragma once +#ifndef NDK_SERVER #ifndef NDK_COMPONENTS_LIGHTCOMPONENT_HPP #define NDK_COMPONENTS_LIGHTCOMPONENT_HPP @@ -28,3 +29,4 @@ namespace Ndk #include #endif // NDK_COMPONENTS_LIGHTCOMPONENT_HPP +#endif // NDK_SERVER diff --git a/SDK/include/NDK/Components/ListenerComponent.hpp b/SDK/include/NDK/Components/ListenerComponent.hpp index bee1ac14a..a2b9d59d2 100644 --- a/SDK/include/NDK/Components/ListenerComponent.hpp +++ b/SDK/include/NDK/Components/ListenerComponent.hpp @@ -4,6 +4,7 @@ #pragma once +#ifndef NDK_SERVER #ifndef NDK_COMPONENTS_LISTENERCOMPONENT_HPP #define NDK_COMPONENTS_LISTENERCOMPONENT_HPP @@ -14,11 +15,11 @@ namespace Ndk class NDK_API ListenerComponent : public Component { public: - ListenerComponent(); + inline ListenerComponent(); ~ListenerComponent() = default; - bool IsActive() const; - void SetActive(bool active = true); + inline bool IsActive() const; + inline void SetActive(bool active = true); static ComponentIndex componentIndex; @@ -30,3 +31,4 @@ namespace Ndk #include #endif // NDK_COMPONENTS_LISTENERCOMPONENT_HPP +#endif // NDK_SERVER \ No newline at end of file diff --git a/SDK/include/NDK/Components/ParticleEmitterComponent.hpp b/SDK/include/NDK/Components/ParticleEmitterComponent.hpp new file mode 100644 index 000000000..6ad3e3981 --- /dev/null +++ b/SDK/include/NDK/Components/ParticleEmitterComponent.hpp @@ -0,0 +1,46 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#pragma once + +#ifndef NDK_SERVER +#ifndef NDK_COMPONENTS_PARTICLEEMITTERCOMPONENT_HPP +#define NDK_COMPONENTS_PARTICLEEMITTERCOMPONENT_HPP + +#include +#include +#include + +namespace Ndk +{ + class NDK_API ParticleEmitterComponent : public Component, public Nz::ParticleEmitter + { + public: + using SetupFunc = std::function; + + inline ParticleEmitterComponent(); + ParticleEmitterComponent(const ParticleEmitterComponent& emitter) = default; + ParticleEmitterComponent(ParticleEmitterComponent&& emitter) = default; + ~ParticleEmitterComponent() = default; + + void Enable(bool active = true); + + inline bool IsActive() const; + + inline void SetSetupFunc(SetupFunc func); + + static ComponentIndex componentIndex; + + private: + void SetupParticles(Nz::ParticleMapper& mapper, unsigned int count) const override; + + SetupFunc m_setupFunc; + bool m_isActive; + }; +} + +#include + +#endif // NDK_COMPONENTS_PARTICLEEMITTERCOMPONENT_HPP +#endif // NDK_SERVER \ No newline at end of file diff --git a/SDK/include/NDK/Components/ParticleEmitterComponent.inl b/SDK/include/NDK/Components/ParticleEmitterComponent.inl new file mode 100644 index 000000000..342b5e56f --- /dev/null +++ b/SDK/include/NDK/Components/ParticleEmitterComponent.inl @@ -0,0 +1,28 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include + +namespace Ndk +{ + inline ParticleEmitterComponent::ParticleEmitterComponent() : + m_isActive(true) + { + } + + inline void Ndk::ParticleEmitterComponent::Enable(bool active) + { + m_isActive = active; + } + + inline bool ParticleEmitterComponent::IsActive() const + { + return m_isActive; + } + + inline void Ndk::ParticleEmitterComponent::SetSetupFunc(SetupFunc func) + { + m_setupFunc = std::move(func); + } +} diff --git a/SDK/include/NDK/Components/ParticleGroupComponent.hpp b/SDK/include/NDK/Components/ParticleGroupComponent.hpp new file mode 100644 index 000000000..638f877d3 --- /dev/null +++ b/SDK/include/NDK/Components/ParticleGroupComponent.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#pragma once + +#ifndef NDK_SERVER +#ifndef NDK_COMPONENTS_PARTICLEGROUPCOMPONENT_HPP +#define NDK_COMPONENTS_PARTICLEGROUPCOMPONENT_HPP + +#include +#include + +namespace Ndk +{ + class ParticleGroupComponent; + + using ParticleGroupComponentHandle = Nz::ObjectHandle; + + class NDK_API ParticleGroupComponent : public Component, public Nz::ParticleGroup + { + public: + inline ParticleGroupComponent(unsigned int maxParticleCount, Nz::ParticleLayout layout); + inline ParticleGroupComponent(unsigned int maxParticleCount, Nz::ParticleDeclarationConstRef declaration); + ParticleGroupComponent(const ParticleGroupComponent&) = default; + ~ParticleGroupComponent() = default; + + static ComponentIndex componentIndex; + }; +} + +#include + +#endif // NDK_COMPONENTS_PARTICLEGROUPCOMPONENT_HPP +#endif // NDK_SERVER \ No newline at end of file diff --git a/SDK/include/NDK/Components/ParticleGroupComponent.inl b/SDK/include/NDK/Components/ParticleGroupComponent.inl new file mode 100644 index 000000000..7aefdad56 --- /dev/null +++ b/SDK/include/NDK/Components/ParticleGroupComponent.inl @@ -0,0 +1,17 @@ +#include "ParticleGroupComponent.hpp" +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +namespace Ndk +{ + inline ParticleGroupComponent::ParticleGroupComponent(unsigned int maxParticleCount, Nz::ParticleLayout layout) : + ParticleGroup(maxParticleCount, layout) + { + } + + inline ParticleGroupComponent::ParticleGroupComponent(unsigned int maxParticleCount, Nz::ParticleDeclarationConstRef declaration) : + ParticleGroup(maxParticleCount, std::move(declaration)) + { + } +} diff --git a/SDK/include/NDK/LuaAPI.inl b/SDK/include/NDK/LuaAPI.inl index cbd4e7c4a..a09d6c459 100644 --- a/SDK/include/NDK/LuaAPI.inl +++ b/SDK/include/NDK/LuaAPI.inl @@ -84,8 +84,12 @@ namespace Nz inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, FontParams* params, TypeTag) { + NazaraUnused(params); + instance.CheckType(index, Nz::LuaType_Table); + // Structure is empty for now + return 1; } @@ -96,8 +100,8 @@ namespace Nz params->animated = instance.CheckField("Animated", params->animated); params->center = instance.CheckField("Center", params->center); params->flipUVs = instance.CheckField("FlipUVs", params->flipUVs); + //params->matrix = instance.CheckField("Matrix", params->matrix); params->optimizeIndexBuffers = instance.CheckField("OptimizeIndexBuffers", params->optimizeIndexBuffers); - params->scale = instance.CheckField("Scale", params->scale); return 1; } @@ -255,14 +259,14 @@ namespace Nz inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Ndk::EntityHandle* handle, TypeTag) { - *handle = std::move(*static_cast(instance.CheckUserdata(index, "Entity"))); + *handle = *static_cast(instance.CheckUserdata(index, "Entity")); return 1; } inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Ndk::WorldHandle* handle, TypeTag) { - *handle = std::move(*static_cast(instance.CheckUserdata(index, "World"))); + *handle = *static_cast(instance.CheckUserdata(index, "World")); return 1; } diff --git a/SDK/include/NDK/State.hpp b/SDK/include/NDK/State.hpp index 613a504ab..621227fbe 100644 --- a/SDK/include/NDK/State.hpp +++ b/SDK/include/NDK/State.hpp @@ -13,11 +13,11 @@ namespace Ndk { class StateMachine; - class State + class NDK_API State { public: State() = default; - ~State() = default; + virtual ~State(); virtual void Enter(StateMachine& fsm) = 0; virtual void Leave(StateMachine& fsm) = 0; diff --git a/SDK/include/NDK/StateMachine.hpp b/SDK/include/NDK/StateMachine.hpp index 8e7ad2f64..c36464fea 100644 --- a/SDK/include/NDK/StateMachine.hpp +++ b/SDK/include/NDK/StateMachine.hpp @@ -23,6 +23,8 @@ namespace Ndk inline void ChangeState(std::shared_ptr state); + inline const std::shared_ptr& GetCurrentState() const; + inline bool Update(float elapsedTime); inline StateMachine& operator=(StateMachine&& fsm) = default; diff --git a/SDK/include/NDK/StateMachine.inl b/SDK/include/NDK/StateMachine.inl index fc4c7f787..7b5109ab5 100644 --- a/SDK/include/NDK/StateMachine.inl +++ b/SDK/include/NDK/StateMachine.inl @@ -26,6 +26,11 @@ namespace Ndk m_nextState = std::move(state); } + inline const std::shared_ptr& StateMachine::GetCurrentState() const + { + return m_currentState; + } + inline bool StateMachine::Update(float elapsedTime) { if (m_nextState) diff --git a/SDK/include/NDK/Systems.hpp b/SDK/include/NDK/Systems.hpp index 193438b5e..269bead6f 100644 --- a/SDK/include/NDK/Systems.hpp +++ b/SDK/include/NDK/Systems.hpp @@ -1,16 +1,14 @@ -// This file was automatically generated on 03 Mar 2016 at 14:09:12 +// This file was automatically generated on 30 Jul 2016 at 15:29:16 #pragma once #ifndef NDK_SYSTEMS_GLOBAL_HPP #define NDK_SYSTEMS_GLOBAL_HPP +#include +#include #include +#include #include -#ifndef NDK_SERVER -#include -#include -#endif - #endif // NDK_SYSTEMS_GLOBAL_HPP diff --git a/SDK/include/NDK/Systems/ListenerSystem.hpp b/SDK/include/NDK/Systems/ListenerSystem.hpp index 6f40fcdf9..bb166a4c1 100644 --- a/SDK/include/NDK/Systems/ListenerSystem.hpp +++ b/SDK/include/NDK/Systems/ListenerSystem.hpp @@ -4,6 +4,7 @@ #pragma once +#ifndef NDK_SERVER #ifndef NDK_SYSTEMS_LISTENERSYSTEM_HPP #define NDK_SYSTEMS_LISTENERSYSTEM_HPP @@ -27,3 +28,4 @@ namespace Ndk #include #endif // NDK_SYSTEMS_LISTENERSYSTEM_HPP +#endif // NDK_SERVER diff --git a/SDK/include/NDK/Systems/ParticleSystem.hpp b/SDK/include/NDK/Systems/ParticleSystem.hpp new file mode 100644 index 000000000..3ae00a995 --- /dev/null +++ b/SDK/include/NDK/Systems/ParticleSystem.hpp @@ -0,0 +1,29 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#pragma once + +#ifndef NDK_SYSTEMS_PARTICLESYSTEM_HPP +#define NDK_SYSTEMS_PARTICLESYSTEM_HPP + +#include + +namespace Ndk +{ + class NDK_API ParticleSystem : public System + { + public: + ParticleSystem(); + ~ParticleSystem() = default; + + static SystemIndex systemIndex; + + private: + void OnUpdate(float elapsedTime) override; + }; +} + +#include + +#endif // NDK_SYSTEMS_PARTICLESYSTEM_HPP diff --git a/SDK/include/NDK/Systems/ParticleSystem.inl b/SDK/include/NDK/Systems/ParticleSystem.inl new file mode 100644 index 000000000..e5f296d27 --- /dev/null +++ b/SDK/include/NDK/Systems/ParticleSystem.inl @@ -0,0 +1,3 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp diff --git a/SDK/include/NDK/Systems/RenderSystem.hpp b/SDK/include/NDK/Systems/RenderSystem.hpp index 4b65de11d..82e558773 100644 --- a/SDK/include/NDK/Systems/RenderSystem.hpp +++ b/SDK/include/NDK/Systems/RenderSystem.hpp @@ -4,11 +4,14 @@ #pragma once +#ifndef NDK_SERVER #ifndef NDK_SYSTEMS_RENDERSYSTEM_HPP #define NDK_SYSTEMS_RENDERSYSTEM_HPP #include -#include +#include +#include +#include #include #include #include @@ -48,13 +51,20 @@ namespace Ndk void OnEntityRemoved(Entity* entity) override; void OnEntityValidation(Entity* entity, bool justAdded) override; void OnUpdate(float elapsedTime) override; + void UpdateDirectionalShadowMaps(const Nz::AbstractViewer& viewer); + void UpdatePointSpotShadowMaps(); std::unique_ptr m_renderTechnique; EntityList m_cameras; EntityList m_drawables; + EntityList m_directionalLights; EntityList m_lights; + EntityList m_pointSpotLights; + EntityList m_particleGroups; Nz::BackgroundRef m_background; + Nz::DepthRenderTechnique m_shadowTechnique; Nz::Matrix4f m_coordinateSystemMatrix; + Nz::RenderTexture m_shadowRT; bool m_coordinateSystemInvalidated; }; } @@ -62,3 +72,4 @@ namespace Ndk #include #endif // NDK_SYSTEMS_RENDERSYSTEM_HPP +#endif // NDK_SERVER diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index 91ebc3922..ba2b33bd4 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -44,7 +44,7 @@ namespace Ndk static_assert(std::is_base_of::value, "SystemType is not a component"); // Allocation et affectation du component - std::unique_ptr ptr(new SystemType(std::forward(args)...)); + std::unique_ptr ptr(new SystemType(std::forward(args)...)); return static_cast(AddSystem(std::move(ptr))); } diff --git a/SDK/src/NDK/Components/GraphicsComponent.cpp b/SDK/src/NDK/Components/GraphicsComponent.cpp index e345bdb85..88ccd4ca1 100644 --- a/SDK/src/NDK/Components/GraphicsComponent.cpp +++ b/SDK/src/NDK/Components/GraphicsComponent.cpp @@ -9,7 +9,7 @@ namespace Ndk { - void GraphicsComponent::InvalidateRenderableData(const Nz::InstancedRenderable* renderable, Nz::UInt32 flags, unsigned int index) + void GraphicsComponent::InvalidateRenderableData(const Nz::InstancedRenderable* renderable, Nz::UInt32 flags, std::size_t index) { NazaraAssert(index < m_renderables.size(), "Invalid renderable index"); NazaraUnused(renderable); diff --git a/SDK/src/NDK/Components/ParticleEmitterComponent.cpp b/SDK/src/NDK/Components/ParticleEmitterComponent.cpp new file mode 100644 index 000000000..2ae4365e6 --- /dev/null +++ b/SDK/src/NDK/Components/ParticleEmitterComponent.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include +#include + +namespace Ndk +{ + void ParticleEmitterComponent::SetupParticles(Nz::ParticleMapper& mapper, unsigned int count) const + { + if (m_isActive && m_setupFunc) + m_setupFunc(m_entity, mapper, count); + } + + ComponentIndex ParticleEmitterComponent::componentIndex; +} diff --git a/SDK/src/NDK/Components/ParticleGroupComponent.cpp b/SDK/src/NDK/Components/ParticleGroupComponent.cpp new file mode 100644 index 000000000..4e0845871 --- /dev/null +++ b/SDK/src/NDK/Components/ParticleGroupComponent.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include + +namespace Ndk +{ + ComponentIndex ParticleGroupComponent::componentIndex; +} diff --git a/SDK/src/NDK/Console.cpp b/SDK/src/NDK/Console.cpp index 24a5d8df2..d157d7dbe 100644 --- a/SDK/src/NDK/Console.cpp +++ b/SDK/src/NDK/Console.cpp @@ -28,8 +28,8 @@ namespace Ndk m_characterSize(24) { Nz::MaterialRef backgroundMaterial = Nz::Material::New(); - backgroundMaterial->Enable(Nz::RendererParameter_Blend, true); - backgroundMaterial->Enable(Nz::RendererParameter_DepthBuffer, false); + backgroundMaterial->EnableBlending(true); + backgroundMaterial->EnableDepthBuffer(false); backgroundMaterial->SetDstBlend(Nz::BlendFunc_InvSrcAlpha); backgroundMaterial->SetSrcBlend(Nz::BlendFunc_SrcAlpha); @@ -145,6 +145,7 @@ namespace Ndk { case Nz::Keyboard::Down: case Nz::Keyboard::Up: + { if (event.key.code == Nz::Keyboard::Up) m_historyPosition = std::min(m_commandHistory.size(), m_historyPosition + 1); else @@ -159,6 +160,10 @@ namespace Ndk m_inputDrawer.SetText(s_inputPrefix + text); m_inputTextSprite->Update(m_inputDrawer); break; + } + + default: + break; } break; } diff --git a/SDK/src/NDK/Entity.cpp b/SDK/src/NDK/Entity.cpp index 17730a234..f037e5f9a 100644 --- a/SDK/src/NDK/Entity.cpp +++ b/SDK/src/NDK/Entity.cpp @@ -51,7 +51,7 @@ namespace Ndk BaseComponent& component = *m_components[index].get(); component.SetEntity(this); - for (unsigned int i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) + for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) { if (i != index) m_components[i]->OnComponentAttached(component); @@ -73,8 +73,8 @@ namespace Ndk void Entity::RemoveAllComponents() { - for (unsigned int i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) - RemoveComponent(i); + for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) + RemoveComponent(static_cast(i)); NazaraAssert(m_componentBits.TestNone(), "All components should be gone"); @@ -90,7 +90,7 @@ namespace Ndk { // On récupère le component et on informe les composants du détachement BaseComponent& component = *m_components[index].get(); - for (unsigned int i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) + for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) { if (i != index) m_components[i]->OnComponentDetached(component); @@ -114,7 +114,7 @@ namespace Ndk void Entity::Destroy() { // On informe chaque système - for (SystemIndex index = m_systemBits.FindFirst(); index != m_systemBits.npos; index = m_systemBits.FindNext(index)) + for (std::size_t index = m_systemBits.FindFirst(); index != m_systemBits.npos; index = m_systemBits.FindNext(index)) { if (m_world->HasSystem(index)) { diff --git a/SDK/src/NDK/LuaBinding.cpp b/SDK/src/NDK/LuaBinding.cpp index 33c70dbe1..6acc05a46 100644 --- a/SDK/src/NDK/LuaBinding.cpp +++ b/SDK/src/NDK/LuaBinding.cpp @@ -29,8 +29,8 @@ namespace Ndk // SDK application("Application"), - nodeComponent("NodeComponent"), entityClass("Entity"), + nodeComponent("NodeComponent"), velocityComponent("VelocityComponent"), worldClass("World") @@ -39,9 +39,9 @@ namespace Ndk // Audio musicClass("Music"), + soundClass("Sound"), soundBuffer("SoundBuffer"), soundEmitter("SoundEmitter"), - soundClass("Sound"), // Graphics instancedRenderable("InstancedRenderable"), diff --git a/SDK/src/NDK/LuaBinding_Audio.cpp b/SDK/src/NDK/LuaBinding_Audio.cpp index d75c8215e..89452d720 100644 --- a/SDK/src/NDK/LuaBinding_Audio.cpp +++ b/SDK/src/NDK/LuaBinding_Audio.cpp @@ -76,6 +76,8 @@ namespace Ndk /*********************************** Nz::SoundBuffer **********************************/ soundBuffer.SetConstructor([] (Nz::LuaInstance& lua, Nz::SoundBufferRef* instance) { + NazaraUnused(lua); + Nz::PlacementNew(instance, Nz::SoundBuffer::New()); return true; }); @@ -115,16 +117,16 @@ namespace Ndk return 1; }); - soundBuffer.BindMethod("__tostring", [] (Nz::LuaInstance& lua, Nz::SoundBufferRef& soundBuffer) -> int + soundBuffer.BindMethod("__tostring", [] (Nz::LuaInstance& lua, Nz::SoundBufferRef& instance) -> int { Nz::StringStream stream("SoundBuffer("); - if (soundBuffer->IsValid()) + if (instance->IsValid()) { - Nz::String filePath = soundBuffer->GetFilePath(); + Nz::String filePath = instance->GetFilePath(); if (!filePath.IsEmpty()) stream << "File: " << filePath << ", "; - - stream << "Duration: " << soundBuffer->GetDuration() / 1000.f << "s"; + + stream << "Duration: " << instance->GetDuration() / 1000.f << "s"; } stream << ')'; @@ -148,17 +150,17 @@ namespace Ndk soundEmitter.BindMethod("IsLooping", &Nz::SoundEmitter::IsLooping); soundEmitter.BindMethod("IsSpatialized", &Nz::SoundEmitter::IsSpatialized); - + soundEmitter.BindMethod("Pause", &Nz::SoundEmitter::Pause); soundEmitter.BindMethod("Play", &Nz::SoundEmitter::Play); - + soundEmitter.BindMethod("SetAttenuation", &Nz::SoundEmitter::SetAttenuation); soundEmitter.BindMethod("SetMinDistance", &Nz::SoundEmitter::SetMinDistance); soundEmitter.BindMethod("SetPitch", &Nz::SoundEmitter::SetPitch); soundEmitter.BindMethod("SetPosition", (void(Nz::SoundEmitter::*)(const Nz::Vector3f&)) &Nz::SoundEmitter::SetPosition); soundEmitter.BindMethod("SetVelocity", (void(Nz::SoundEmitter::*)(const Nz::Vector3f&)) &Nz::SoundEmitter::SetVelocity); soundEmitter.BindMethod("SetVolume", &Nz::SoundEmitter::SetVolume); - + soundEmitter.BindMethod("Stop", &Nz::SoundEmitter::Stop); } diff --git a/SDK/src/NDK/LuaBinding_Math.cpp b/SDK/src/NDK/LuaBinding_Math.cpp index cf5454450..c1d7b487e 100644 --- a/SDK/src/NDK/LuaBinding_Math.cpp +++ b/SDK/src/NDK/LuaBinding_Math.cpp @@ -205,7 +205,7 @@ namespace Ndk { case Nz::LuaType_Number: { - long long index = lua.CheckInteger(1); + auto index = lua.CheckBoundInteger(1); if (index < 1 || index > 4) return false; @@ -238,9 +238,15 @@ namespace Ndk case 'h': lua.Push(instance.height); return true; + + default: + break; } break; } + + default: + break; } return false; @@ -252,7 +258,7 @@ namespace Ndk { case Nz::LuaType_Number: { - long long index = lua.CheckInteger(1); + auto index = lua.CheckBoundInteger(1); if (index < 1 || index > 4) return false; @@ -290,6 +296,9 @@ namespace Ndk } break; } + + default: + break; } return false; @@ -313,7 +322,7 @@ namespace Ndk Nz::PlacementNew(quaternion, *static_cast(lua.ToUserdata(1))); else break; - + return true; } @@ -389,6 +398,9 @@ namespace Ndk case 'z': instance.z = value; return true; + + default: + break; } return false; @@ -455,9 +467,15 @@ namespace Ndk case 'y': lua.Push(instance.y); return true; + + default: + break; } break; } + + default: + break; } return false; @@ -496,9 +514,15 @@ namespace Ndk case 'y': instance.y = value; return true; + + default: + break; } break; } + + default: + break; } return false; @@ -513,11 +537,12 @@ namespace Ndk case 0: case 3: Nz::PlacementNew(vector, lua.CheckNumber(1, 0.0), lua.CheckNumber(2, 0.0), lua.CheckNumber(3, 0.0)); + return true; case 1: { if (lua.IsOfType(1, Nz::LuaType_Number)) - Nz::PlacementNew(vector, lua.CheckNumber(1), *static_cast(lua.ToUserdata(1))); + Nz::PlacementNew(vector, lua.CheckNumber(1), *static_cast(lua.CheckUserdata(1, "Vector2"))); else if (lua.IsOfType(1, "Vector2")) Nz::PlacementNew(vector, *static_cast(lua.ToUserdata(1))); else if (lua.IsOfType(1, "Vector3")) @@ -582,9 +607,15 @@ namespace Ndk case 'z': lua.Push(instance.z); return true; + + default: + break; } break; } + + default: + break; } return false; @@ -627,9 +658,15 @@ namespace Ndk case 'z': instance.z = value; return true; + + default: + break; } break; } + + default: + break; } return false; diff --git a/SDK/src/NDK/Sdk.cpp b/SDK/src/NDK/Sdk.cpp index 21bfd236f..964913bf8 100644 --- a/SDK/src/NDK/Sdk.cpp +++ b/SDK/src/NDK/Sdk.cpp @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include #include #endif @@ -71,6 +74,8 @@ namespace Ndk InitializeComponent("NdkLight"); InitializeComponent("NdkList"); InitializeComponent("NdkGfx"); + InitializeComponent("NdkPaEmi"); + InitializeComponent("NdkPaGrp"); #endif // Systems @@ -84,6 +89,7 @@ namespace Ndk #ifndef NDK_SERVER // Client systems InitializeSystem(); + InitializeSystem(); InitializeSystem(); #endif @@ -112,6 +118,12 @@ namespace Ndk // Uninitialize the SDK s_referenceCounter = 0; + // Components + BaseComponent::Uninitialize(); + + // Systems + BaseSystem::Uninitialize(); + // Uninitialize the engine #ifndef NDK_SERVER diff --git a/SDK/src/NDK/State.cpp b/SDK/src/NDK/State.cpp new file mode 100644 index 000000000..e256a7f1c --- /dev/null +++ b/SDK/src/NDK/State.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include + +namespace Ndk +{ + State::~State() = default; +} diff --git a/SDK/src/NDK/Systems/ParticleSystem.cpp b/SDK/src/NDK/Systems/ParticleSystem.cpp new file mode 100644 index 000000000..a9f63cb6c --- /dev/null +++ b/SDK/src/NDK/Systems/ParticleSystem.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include +#include + +namespace Ndk +{ + ParticleSystem::ParticleSystem() + { + Requires(); + } + + void ParticleSystem::OnUpdate(float elapsedTime) + { + for (const Ndk::EntityHandle& entity : GetEntities()) + { + ParticleGroupComponent& group = entity->GetComponent(); + + group.Update(elapsedTime); + } + } + + SystemIndex ParticleSystem::systemIndex; +} diff --git a/SDK/src/NDK/Systems/RenderSystem.cpp b/SDK/src/NDK/Systems/RenderSystem.cpp index 3237bbe52..fcfd5c6a5 100644 --- a/SDK/src/NDK/Systems/RenderSystem.cpp +++ b/SDK/src/NDK/Systems/RenderSystem.cpp @@ -4,10 +4,13 @@ #include #include +#include +#include #include #include #include #include +#include namespace Ndk { @@ -23,8 +26,11 @@ namespace Ndk void RenderSystem::OnEntityRemoved(Entity* entity) { m_cameras.Remove(entity); + m_directionalLights.Remove(entity); m_drawables.Remove(entity); m_lights.Remove(entity); + m_particleGroups.Remove(entity); + m_pointSpotLights.Remove(entity); } void RenderSystem::OnEntityValidation(Entity* entity, bool justAdded) @@ -48,9 +54,32 @@ namespace Ndk m_drawables.Remove(entity); if (entity->HasComponent() && entity->HasComponent()) + { + LightComponent& lightComponent = entity->GetComponent(); + if (lightComponent.GetLightType() == Nz::LightType_Directional) + { + m_directionalLights.Insert(entity); + m_pointSpotLights.Remove(entity); + } + else + { + m_directionalLights.Remove(entity); + m_pointSpotLights.Insert(entity); + } + m_lights.Insert(entity); + } else + { + m_directionalLights.Remove(entity); m_lights.Remove(entity); + m_pointSpotLights.Remove(entity); + } + + if (entity->HasComponent()) + m_particleGroups.Insert(entity); + else + m_particleGroups.Remove(entity); } void RenderSystem::OnUpdate(float elapsedTime) @@ -69,10 +98,13 @@ namespace Ndk m_coordinateSystemInvalidated = false; } + UpdatePointSpotShadowMaps(); + for (const Ndk::EntityHandle& camera : m_cameras) { CameraComponent& camComponent = camera->GetComponent(); - camComponent.ApplyView(); + + //UpdateDirectionalShadowMaps(camComponent); Nz::AbstractRenderQueue* renderQueue = m_renderTechnique->GetRenderQueue(); renderQueue->Clear(); @@ -89,12 +121,21 @@ namespace Ndk for (const Ndk::EntityHandle& light : m_lights) { LightComponent& lightComponent = light->GetComponent(); - NodeComponent& drawableNode = light->GetComponent(); + NodeComponent& lightNode = light->GetComponent(); ///TODO: Cache somehow? - lightComponent.AddToRenderQueue(renderQueue, Nz::Matrix4f::ConcatenateAffine(m_coordinateSystemMatrix, drawableNode.GetTransformMatrix())); + lightComponent.AddToRenderQueue(renderQueue, Nz::Matrix4f::ConcatenateAffine(m_coordinateSystemMatrix, lightNode.GetTransformMatrix())); } + for (const Ndk::EntityHandle& particleGroup : m_particleGroups) + { + ParticleGroupComponent& groupComponent = particleGroup->GetComponent(); + + groupComponent.AddToRenderQueue(renderQueue, Nz::Matrix4f::Identity()); //< ParticleGroup doesn't use Matrix4f + } + + camComponent.ApplyView(); + Nz::SceneData sceneData; sceneData.ambientColor = Nz::Color(25, 25, 25); sceneData.background = m_background; @@ -105,5 +146,146 @@ namespace Ndk } } + void RenderSystem::UpdateDirectionalShadowMaps(const Nz::AbstractViewer& viewer) + { + if (!m_shadowRT.IsValid()) + m_shadowRT.Create(); + + Nz::SceneData dummySceneData; + dummySceneData.ambientColor = Nz::Color(0, 0, 0); + dummySceneData.background = nullptr; + dummySceneData.viewer = nullptr; //< Depth technique doesn't require any viewer + + for (const Ndk::EntityHandle& light : m_directionalLights) + { + LightComponent& lightComponent = light->GetComponent(); + NodeComponent& lightNode = light->GetComponent(); + + if (!lightComponent.IsShadowCastingEnabled()) + continue; + + Nz::Vector2ui shadowMapSize(lightComponent.GetShadowMap()->GetSize()); + + m_shadowRT.AttachTexture(Nz::AttachmentPoint_Depth, 0, lightComponent.GetShadowMap()); + Nz::Renderer::SetTarget(&m_shadowRT); + Nz::Renderer::SetViewport(Nz::Recti(0, 0, shadowMapSize.x, shadowMapSize.y)); + + Nz::AbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue(); + renderQueue->Clear(); + + ///TODO: Culling + for (const Ndk::EntityHandle& drawable : m_drawables) + { + GraphicsComponent& graphicsComponent = drawable->GetComponent(); + NodeComponent& drawableNode = drawable->GetComponent(); + + graphicsComponent.AddToRenderQueue(renderQueue); + } + + ///TODO: Cache the matrices in the light? + Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, Nz::Matrix4f::Ortho(0.f, 100.f, 100.f, 0.f, 1.f, 100.f)); + Nz::Renderer::SetMatrix(Nz::MatrixType_View, Nz::Matrix4f::ViewMatrix(lightNode.GetRotation() * Nz::Vector3f::Forward() * 100.f, lightNode.GetRotation())); + + m_shadowTechnique.Clear(dummySceneData); + m_shadowTechnique.Draw(dummySceneData); + } + } + + void RenderSystem::UpdatePointSpotShadowMaps() + { + if (!m_shadowRT.IsValid()) + m_shadowRT.Create(); + + Nz::SceneData dummySceneData; + dummySceneData.ambientColor = Nz::Color(0, 0, 0); + dummySceneData.background = nullptr; + dummySceneData.viewer = nullptr; //< Depth technique doesn't require any viewer + + for (const Ndk::EntityHandle& light : m_pointSpotLights) + { + LightComponent& lightComponent = light->GetComponent(); + NodeComponent& lightNode = light->GetComponent(); + + if (!lightComponent.IsShadowCastingEnabled()) + continue; + + Nz::Vector2ui shadowMapSize(lightComponent.GetShadowMap()->GetSize()); + + switch (lightComponent.GetLightType()) + { + case Nz::LightType_Directional: + NazaraInternalError("Directional lights included in point/spot light list"); + break; + + case Nz::LightType_Point: + { + static Nz::Quaternionf rotations[6] = + { + Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::UnitX()), // nzCubemapFace_PositiveX + Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), -Nz::Vector3f::UnitX()), // nzCubemapFace_NegativeX + Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), -Nz::Vector3f::UnitY()), // nzCubemapFace_PositiveY + Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::UnitY()), // nzCubemapFace_NegativeY + Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), -Nz::Vector3f::UnitZ()), // nzCubemapFace_PositiveZ + Nz::Quaternionf::RotationBetween(Nz::Vector3f::Forward(), Nz::Vector3f::UnitZ()) // nzCubemapFace_NegativeZ + }; + + for (unsigned int face = 0; face < 6; ++face) + { + m_shadowRT.AttachTexture(Nz::AttachmentPoint_Depth, 0, lightComponent.GetShadowMap(), face); + Nz::Renderer::SetTarget(&m_shadowRT); + Nz::Renderer::SetViewport(Nz::Recti(0, 0, shadowMapSize.x, shadowMapSize.y)); + + ///TODO: Cache the matrices in the light? + Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, Nz::Matrix4f::Perspective(Nz::FromDegrees(90.f), 1.f, 0.1f, lightComponent.GetRadius())); + Nz::Renderer::SetMatrix(Nz::MatrixType_View, Nz::Matrix4f::ViewMatrix(lightNode.GetPosition(), rotations[face])); + + Nz::AbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue(); + renderQueue->Clear(); + + ///TODO: Culling + for (const Ndk::EntityHandle& drawable : m_drawables) + { + GraphicsComponent& graphicsComponent = drawable->GetComponent(); + NodeComponent& drawableNode = drawable->GetComponent(); + + graphicsComponent.AddToRenderQueue(renderQueue); + } + + m_shadowTechnique.Clear(dummySceneData); + m_shadowTechnique.Draw(dummySceneData); + } + break; + } + + case Nz::LightType_Spot: + { + m_shadowRT.AttachTexture(Nz::AttachmentPoint_Depth, 0, lightComponent.GetShadowMap()); + Nz::Renderer::SetTarget(&m_shadowRT); + Nz::Renderer::SetViewport(Nz::Recti(0, 0, shadowMapSize.x, shadowMapSize.y)); + + ///TODO: Cache the matrices in the light? + Nz::Renderer::SetMatrix(Nz::MatrixType_Projection, Nz::Matrix4f::Perspective(lightComponent.GetOuterAngle()*2.f, 1.f, 0.1f, lightComponent.GetRadius())); + Nz::Renderer::SetMatrix(Nz::MatrixType_View, Nz::Matrix4f::ViewMatrix(lightNode.GetPosition(), lightNode.GetRotation())); + + Nz::AbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue(); + renderQueue->Clear(); + + ///TODO: Culling + for (const Ndk::EntityHandle& drawable : m_drawables) + { + GraphicsComponent& graphicsComponent = drawable->GetComponent(); + NodeComponent& drawableNode = drawable->GetComponent(); + + graphicsComponent.AddToRenderQueue(renderQueue); + } + + m_shadowTechnique.Clear(dummySceneData); + m_shadowTechnique.Draw(dummySceneData); + break; + } + } + } + } + SystemIndex RenderSystem::systemIndex; } diff --git a/SDK/src/NDK/Systems/VelocitySystem.cpp b/SDK/src/NDK/Systems/VelocitySystem.cpp index 882b45728..2d3264039 100644 --- a/SDK/src/NDK/Systems/VelocitySystem.cpp +++ b/SDK/src/NDK/Systems/VelocitySystem.cpp @@ -22,7 +22,7 @@ namespace Ndk NodeComponent& node = entity->GetComponent(); const VelocityComponent& velocity = entity->GetComponent(); - node.Move(velocity.linearVelocity * elapsedTime); + node.Move(velocity.linearVelocity * elapsedTime, Nz::CoordSys_Global); } } diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index fc53143dd..5d0e4900b 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -9,6 +9,7 @@ #ifndef NDK_SERVER #include +#include #include #endif @@ -27,6 +28,7 @@ namespace Ndk #ifndef NDK_SERVER AddSystem(); + AddSystem(); AddSystem(); #endif } @@ -94,7 +96,7 @@ namespace Ndk void World::Update() { // Gestion des entités tuées depuis le dernier appel - for (unsigned int i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i)) + for (std::size_t i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i)) { EntityBlock& block = m_entities[i]; Entity& entity = block.entity; @@ -127,7 +129,7 @@ namespace Ndk m_killedEntities.Reset(); // Gestion des entités nécessitant une mise à jour de leurs systèmes - for (unsigned int i = m_dirtyEntities.FindFirst(); i != m_dirtyEntities.npos; i = m_dirtyEntities.FindNext(i)) + for (std::size_t i = m_dirtyEntities.FindFirst(); i != m_dirtyEntities.npos; i = m_dirtyEntities.FindNext(i)) { NazaraAssert(i < m_entities.size(), "Entity index out of range"); diff --git a/build/Build_CodeBlocks.bat b/build/Build_CodeBlocks.bat index 983c7ae80..439c5efac 100644 --- a/build/Build_CodeBlocks.bat +++ b/build/Build_CodeBlocks.bat @@ -1 +1 @@ -premake4 --with-extlibs --with-examples codeblocks \ No newline at end of file +premake4 codeblocks \ No newline at end of file diff --git a/build/Build_CodeBlocks_OneLibrary.bat b/build/Build_CodeBlocks_OneLibrary.bat deleted file mode 100644 index b7c5ab4c9..000000000 --- a/build/Build_CodeBlocks_OneLibrary.bat +++ /dev/null @@ -1 +0,0 @@ -premake4 --united --with-extlibs --with-examples codeblocks \ No newline at end of file diff --git a/build/Build_CodeLite.bat b/build/Build_CodeLite.bat index 8584e6d9b..170e7a451 100644 --- a/build/Build_CodeLite.bat +++ b/build/Build_CodeLite.bat @@ -1 +1 @@ -premake5 --with-extlibs --with-examples codelite \ No newline at end of file +premake5 codelite \ No newline at end of file diff --git a/build/Build_CodeLite_OneLibrary.bat b/build/Build_CodeLite_OneLibrary.bat deleted file mode 100644 index 52a80f504..000000000 --- a/build/Build_CodeLite_OneLibrary.bat +++ /dev/null @@ -1 +0,0 @@ -premake5 --united --with-extlibs --with-examples codelite \ No newline at end of file diff --git a/build/Build_VS2015.bat b/build/Build_VS2015.bat index 2dd6e578a..6d0726faf 100644 --- a/build/Build_VS2015.bat +++ b/build/Build_VS2015.bat @@ -1 +1 @@ -premake5 --with-extlibs --with-examples vs2015 \ No newline at end of file +premake5 vs2015 \ No newline at end of file diff --git a/build/Build_VS2015_OneLibrary.bat b/build/Build_VS2015_OneLibrary.bat deleted file mode 100644 index aaab1938c..000000000 --- a/build/Build_VS2015_OneLibrary.bat +++ /dev/null @@ -1 +0,0 @@ -premake5 --united --with-extlibs --with-examples vs2015 \ No newline at end of file diff --git a/build/config.lua b/build/config.lua new file mode 100644 index 000000000..8370331c7 --- /dev/null +++ b/build/config.lua @@ -0,0 +1,17 @@ +-- This file contains special configurations values, such as directories to extern libraries (Qt) +-- Editing this file is not required to use/compile the engine, as default values should be enough + +-- Builds Nazara extern libraries (such as lua/STB) +BuildDependencies = true + +-- Builds Nazara examples +BuildExamples = true + +-- Setup additionnals install directories, separated by a semi-colon ; (library binaries will be copied there) +--InstallDir = "/usr/local/lib64" + +-- Excludes client-only modules/tools/examples +ServerMode = false + +-- Builds modules as one united library (useless on POSIX systems) +UniteModules = false diff --git a/build/scripts/common.lua b/build/scripts/common.lua index 405b66b60..b467eb9c5 100644 --- a/build/scripts/common.lua +++ b/build/scripts/common.lua @@ -1,16 +1,32 @@ NazaraBuild = {} -- L'équivalent d'un namespace en Lua est une table +function NazaraBuild:AddExecutablePath(path) + self.ExecutableDir[path] = true + self.InstallDir[path] = true +end + +function NazaraBuild:AddInstallPath(path) + self.InstallDir[path] = true +end + function NazaraBuild:Execute() if (_ACTION == nil) then -- Si aucune action n'est spécifiée return -- Alors l'utilisateur voulait probablement savoir comment utiliser le programme, on ne fait rien end + local platformData + if (os.is64bit()) then + platformData = {"x64", "x32"} + else + platformData = {"x32", "x64"} + end + if (self.Actions[_ACTION] == nil) then local makeLibDir = os.is("windows") and "mingw" or "gmake" - - if (#self.OrderedExtLibs > 0) then + + if (self.Config["BuildDependencies"]) then workspace("NazaraExtlibs") - platforms({"x32", "x64"}) + platforms(platformData) -- Configuration générale configurations({ @@ -23,12 +39,6 @@ function NazaraBuild:Execute() location(_ACTION) kind("StaticLib") - configuration("x32") - libdirs("../extlibs/lib/common/x86") - - configuration("x64") - libdirs("../extlibs/lib/common/x64") - configuration({"codeblocks or codelite or gmake", "x32"}) libdirs("../extlibs/lib/" .. makeLibDir .. "/x86") targetdir("../extlibs/lib/" .. makeLibDir .. "/x86") @@ -38,7 +48,7 @@ function NazaraBuild:Execute() targetdir("../extlibs/lib/" .. makeLibDir .. "/x64") configuration("vs*") - buildoptions("/MP") + buildoptions({"/MP", "/bigobj"}) -- Multiprocessus build and big .obj configuration({"vs*", "x32"}) libdirs("../extlibs/lib/msvc/x86") @@ -61,9 +71,9 @@ function NazaraBuild:Execute() configuration("Release*") flags("NoFramePointer") - optimize("Speed") - rtti("Off") - vectorextensions("SSE2") + optimize("Speed") + rtti("Off") + vectorextensions("SSE2") configuration({"Release*", "codeblocks or codelite or gmake or xcode3 or xcode4"}) buildoptions("-mfpmath=sse") -- Utilisation du SSE pour les calculs flottants @@ -75,12 +85,15 @@ function NazaraBuild:Execute() configuration("ReleaseStatic") targetsuffix("-s") + configuration({"not windows", "codeblocks or codelite or gmake or xcode3 or xcode4"}) + buildoptions("-fPIC") + configuration("codeblocks or codelite or gmake or xcode3 or xcode4") - buildoptions({"-fPIC", "-std=c++14"}) + buildoptions({"-std=c++14", "-U__STRICT_ANSI__"}) for k, libTable in ipairs(self.OrderedExtLibs) do project(libTable.Name) - + language(libTable.Language) location(_ACTION .. "/extlibs") @@ -92,17 +105,23 @@ function NazaraBuild:Execute() includedirs(libTable.Includes) links(libTable.Libraries) + configuration("x32") + libdirs(libTable.LibraryPaths.x86) + + configuration("x64") + libdirs(libTable.LibraryPaths.x64) + for k,v in pairs(libTable.ConfigurationLibraries) do configuration(k) links(v) end - + configuration({}) end end workspace("NazaraEngine") - platforms({"x32", "x64"}) + platforms(platformData) -- Configuration générale configurations({ @@ -120,10 +139,10 @@ function NazaraBuild:Execute() flags("Symbols") configuration("Release*") - flags("NoFramePointer") - optimize("Speed") - rtti("Off") - vectorextensions("SSE2") + flags("NoFramePointer") + optimize("Speed") + rtti("Off") + vectorextensions("SSE2") configuration({"Release*", "codeblocks or codelite or gmake or xcode3 or xcode4"}) buildoptions("-mfpmath=sse") -- Utilisation du SSE pour les calculs flottants @@ -138,11 +157,8 @@ function NazaraBuild:Execute() configuration({"linux or bsd or macosx", "gmake"}) buildoptions("-fvisibility=hidden") - configuration({"linux or bsd or macosx", "gmake"}) - buildoptions("-fvisibility=hidden") - configuration("vs*") - buildoptions("/MP") -- Multiprocessus build + buildoptions({"/MP", "/bigobj"}) -- Multiprocessus build and big .obj flags("NoMinimalRebuild") defines("_CRT_SECURE_NO_WARNINGS") defines("_SCL_SECURE_NO_WARNINGS") @@ -172,11 +188,11 @@ function NazaraBuild:Execute() libdirs("../extlibs/lib/common") configuration("x32") - libdirs("../extlibs/lib/common/x86") + libdirs(moduleTable.LibraryPaths.x86) configuration("x64") defines("NAZARA_PLATFORM_x64") - libdirs("../extlibs/lib/common/x64") + libdirs(moduleTable.LibraryPaths.x64) configuration({"codeblocks or codelite or gmake", "x32"}) libdirs("../extlibs/lib/" .. makeLibDir .. "/x86") @@ -188,6 +204,9 @@ function NazaraBuild:Execute() libdirs("../lib/" .. makeLibDir .. "/x64") targetdir("../lib/" .. makeLibDir .. "/x64") + -- Copy the module binaries to the example folder + self:MakeInstallCommands(moduleTable) + configuration({"vs*", "x32"}) libdirs("../extlibs/lib/msvc/x86") libdirs("../lib/msvc/x86") @@ -237,30 +256,34 @@ function NazaraBuild:Execute() configuration(k) links(v) end - + configuration({}) end - + -- Tools for k, toolTable in ipairs(self.OrderedTools) do local prefix = "Nazara" if (toolTable.Kind == "plugin") then prefix = "Plugin" end - + project(prefix .. toolTable.Name) location(_ACTION .. "/tools") - targetdir(toolTable.Directory) + targetdir(toolTable.TargetDirectory) if (toolTable.Kind == "plugin" or toolTable.Kind == "library") then kind("SharedLib") - elseif (toolTable.Kind == "consoleapp") then - debugdir(toolTable.Directory) - kind("ConsoleApp") - elseif (toolTable.Kind == "windowapp") then - debugdir(toolTable.Directory) - kind("WindowedApp") + + -- Copy the tool binaries to the example folder + self:MakeInstallCommands(toolTable) + elseif (toolTable.Kind == "application") then + debugdir(toolTable.TargetDirectory) + if (toolTable.EnableConsole) then + kind("ConsoleApp") + else + kind("WindowedApp") + end else assert(false, "Invalid tool Kind") end @@ -274,11 +297,11 @@ function NazaraBuild:Execute() libdirs("../extlibs/lib/common") configuration("x32") - libdirs("../extlibs/lib/common/x86") + libdirs(toolTable.LibraryPaths.x86) configuration("x64") defines("NAZARA_PLATFORM_x64") - libdirs("../extlibs/lib/common/x64") + libdirs(toolTable.LibraryPaths.x64) configuration({"codeblocks or codelite or gmake", "x32"}) libdirs("../extlibs/lib/" .. makeLibDir .. "/x86") @@ -286,7 +309,7 @@ function NazaraBuild:Execute() if (toolTable.Kind == "library") then targetdir("../lib/" .. makeLibDir .. "/x86") elseif (toolTable.Kind == "plugin") then - targetdir("../plugins/" .. toolTable.Name .. "/lib/" .. makeLibDir .. "/x32") + targetdir("../plugins/" .. toolTable.Name .. "/lib/" .. makeLibDir .. "/x86") end configuration({"codeblocks or codelite or gmake", "x64"}) @@ -298,14 +321,6 @@ function NazaraBuild:Execute() targetdir("../plugins/" .. toolTable.Name .. "/lib/" .. makeLibDir .. "/x64") end - -- Copy the module binaries to the example folder - if (toolTable.CopyTargetToExampleDir) then - if (os.is("windows")) then - configuration({}) - postbuildcommands({[[xcopy "%{path.translate(cfg.linktarget.relpath):sub(1, -5) .. ".dll"}" "..\..\..\examples\bin\" /E /Y]]}) - end - end - configuration({"vs*", "x32"}) libdirs("../extlibs/lib/msvc/x86") libdirs("../lib/msvc/x86") @@ -348,7 +363,7 @@ function NazaraBuild:Execute() configuration("*Dynamic") kind("SharedLib") - + configuration("DebugStatic") targetsuffix("-s-d") @@ -378,23 +393,34 @@ function NazaraBuild:Execute() end for k, exampleTable in ipairs(self.OrderedExamples) do + local destPath = "../examples/bin" + project("Demo" .. exampleTable.Name) location(_ACTION .. "/examples") - if (exampleTable.Console) then - kind("ConsoleApp") + if (exampleTable.Kind == "plugin" or exampleTable.Kind == "library") then + kind("SharedLib") + + self:MakeInstallCommands(toolTable) + elseif (exampleTable.Kind == "application") then + debugdir(exampleTable.TargetDirectory) + if (exampleTable.EnableConsole) then + kind("ConsoleApp") + else + kind("WindowedApp") + end else - kind("Window") + assert(false, "Invalid tool Kind") end - debugdir("../examples/bin") + debugdir(destPath) includedirs({ "../include", "../extlibs/include" }) libdirs("../lib") - targetdir("../examples/bin") + targetdir(destPath) files(exampleTable.Files) excludes(exampleTable.FilesExcluded) @@ -405,11 +431,11 @@ function NazaraBuild:Execute() links(exampleTable.Libraries) configuration("x32") - libdirs("../extlibs/lib/common/x86") + libdirs(exampleTable.LibraryPaths.x86) configuration("x64") defines("NAZARA_PLATFORM_x64") - libdirs("../extlibs/lib/common/x64") + libdirs(exampleTable.LibraryPaths.x64) configuration({"codeblocks or codelite or gmake", "x32"}) libdirs("../lib/" .. makeLibDir .. "/x86") @@ -433,34 +459,45 @@ function NazaraBuild:Execute() configuration(k) links(v) end - + configuration({}) end end end +function NazaraBuild:GetConfig() + return self.Config +end + +function NazaraBuild:GetDependency(infoTable, name) + local projectName = name:match("Nazara(%w+)") + if (projectName) then + -- tool or module + local moduleTable = self.Modules[projectName:lower()] + if (moduleTable) then + return moduleTable + else + local toolTable = self.Tools[projectName:lower()] + if (toolTable) then + return toolTable + end + end + else + return self.ExtLibs[name:lower()] + end +end + function NazaraBuild:Initialize() - -- Commençons par les options - newoption({ - trigger = "united", - description = "Builds all the modules as one united library" - }) - - newoption({ - trigger = "with-extlibs", - description = "Builds the extern libraries" - }) - - newoption({ - trigger = "with-examples", - description = "Builds the examples" - }) - self.Actions = {} self.Examples = {} + self.ExecutableDir = {} self.ExtLibs = {} + self.InstallDir = {} self.Modules = {} self.Tools = {} + + self.Config = {} + self:LoadConfig() -- Actions modules = os.matchfiles("scripts/actions/*.lua") @@ -482,26 +519,24 @@ function NazaraBuild:Initialize() ACTION = nil -- Extern libraries - if (_OPTIONS["with-extlibs"]) then - local extlibs = os.matchfiles("../extlibs/build/*.lua") - for k,v in pairs(extlibs) do - local f, err = loadfile(v) - if (f) then - LIBRARY = {} - self:SetupInfoTable(LIBRARY) + local extlibs = os.matchfiles("../extlibs/build/*.lua") + for k,v in pairs(extlibs) do + local f, err = loadfile(v) + if (f) then + LIBRARY = {} + self:SetupExtlibTable(LIBRARY) - f() + f() - local succeed, err = self:RegisterExternLibrary(LIBRARY) - if (not succeed) then - print("Unable to register extern library: " .. err) - end - else - print("Unable to load extern library file: " .. err) + local succeed, err = self:RegisterExternLibrary(LIBRARY) + if (not succeed) then + print("Unable to register extern library: " .. err) end + else + print("Unable to load extern library file: " .. err) end - LIBRARY = nil end + LIBRARY = nil -- Then the modules local modules = os.matchfiles("scripts/modules/*.lua") @@ -509,28 +544,19 @@ function NazaraBuild:Initialize() local moduleName = v:match(".*/(.*).lua") local moduleNameLower = moduleName:lower() - if (moduleNameLower ~= "core") then -- exclure le noyau n'aurait aucun sens - newoption({ - trigger = "exclude-" .. moduleNameLower, - description = "Exclude the " .. moduleName .. " module from the build system" - }) - end + local f, err = loadfile(v) + if (f) then + MODULE = {} + self:SetupModuleTable(MODULE) - if (not _OPTIONS["exclude-" .. moduleNameLower]) then - local f, err = loadfile(v) - if (f) then - MODULE = {} - self:SetupInfoTable(MODULE) + f() - f() - - local succeed, err = self:RegisterModule(MODULE) - if (not succeed) then - print("Unable to register module: " .. err) - end - else - print("Unable to load module file: " .. err) + local succeed, err = self:RegisterModule(MODULE) + if (not succeed) then + print("Unable to register module: " .. err) end + else + print("Unable to load module file: " .. err) end end MODULE = nil @@ -541,32 +567,25 @@ function NazaraBuild:Initialize() local toolName = v:match(".*/(.*).lua") local toolNameLower = toolName:lower() - newoption({ - trigger = "exclude-" .. toolNameLower, - description = "Exclude the " .. toolName .. " tool from the build system" - }) + local f, err = loadfile(v) + if (f) then + TOOL = {} + self:SetupToolTable(TOOL) - if (not _OPTIONS["exclude-" .. toolNameLower]) then - local f, err = loadfile(v) - if (f) then - TOOL = {} - self:SetupInfoTable(TOOL) + f() - f() - - local succeed, err = self:RegisterTool(TOOL) - if (not succeed) then - print("Unable to register tool: " .. err) - end - else - print("Unable to load tool file: " .. err) + local succeed, err = self:RegisterTool(TOOL) + if (not succeed) then + print("Unable to register tool: " .. err) end + else + print("Unable to load tool file: " .. err) end end TOOL = nil -- Examples - if (_OPTIONS["with-examples"]) then + if (self.Config["BuildExamples"]) then local examples = os.matchdirs("../examples/*") for k,v in pairs(examples) do local dirName = v:match(".*/(.*)") @@ -575,7 +594,7 @@ function NazaraBuild:Initialize() if (f) then EXAMPLE = {} EXAMPLE.Directory = dirName - self:SetupInfoTable(EXAMPLE) + self:SetupExampleTable(EXAMPLE) f() @@ -590,25 +609,261 @@ function NazaraBuild:Initialize() end EXAMPLE = nil end - + -- Once everything is registred, let's process all the tables self.OrderedExamples = {} self.OrderedExtLibs = {} self.OrderedModules = {} self.OrderedTools = {} - local tables = {self.Examples, self.ExtLibs, self.Modules, self.Tools} - local orderedTables = {self.OrderedExamples, self.OrderedExtLibs, self.OrderedModules, self.OrderedTools} + local tables = {self.ExtLibs, self.Modules, self.Tools, self.Examples} + local orderedTables = {self.OrderedExtLibs, self.OrderedModules, self.OrderedTools, self.OrderedExamples} for k,projects in ipairs(tables) do + -- Begin by resolving every project (because of dependencies in the same category) for projectId,projectTable in pairs(projects) do - self:Process(projectTable) - - table.insert(orderedTables[k], projectTable) + self:Resolve(projectTable) end - + + for projectId,projectTable in pairs(projects) do + if (self:Process(projectTable)) then + table.insert(orderedTables[k], projectTable) + else + print("Rejected " .. projectTable.Name .. " " .. string.lower(projectTable.Type) .. ": " .. projectTable.ExcludeReason) + end + end + table.sort(orderedTables[k], function (a, b) return a.Name < b.Name end) end end +function NazaraBuild:LoadConfig() + local f = io.open("config.lua", "r") + if (f) then + local content = f:read("*a") + f:close() + + local func, err = loadstring(content) + if (func) then + setfenv(func, self.Config) + + local status, err = pcall(func) + if (not status) then + print("Failed to load config.lua: " .. err) + end + else + print("Failed to parse config.lua: " .. err) + end + else + print("Failed to open config.lua") + end + + local configTable = self.Config + local AddBoolOption = function (option, name, description) + newoption({ + trigger = name, + description = description + }) + + local str = _OPTIONS[name] + if (str) then + if (#str == 0 or str == "1" or str == "yes" or str == "true") then + configTable[option] = true + elseif (str == "0" or str == "no" or str == "false") then + configTable[option] = false + else + error("Invalid entry for " .. name .. " option: \"" .. str .. "\"") + end + end + end + + AddBoolOption("BuildDependencies", "with-extlibs", "Builds the extern libraries") + AddBoolOption("BuildExamples", "with-examples", "Builds the examples") + AddBoolOption("ServerMode", "server", "Excludes client-only modules/tools/examples") + AddBoolOption("UniteModules", "united", "Builds all the modules as one united library") + + -- InstallDir + newoption({ + trigger = "install-path", + description = "Setup additionnals install directories (library binaries will be copied there)" + }) + + self.Config["InstallDir"] = self.Config["InstallDir"] or "" + if (_OPTIONS["install-path"] ~= nil) then + self.Config["InstallDir"] = self.Config["InstallDir"] .. ";" .. _OPTIONS["install-path"] + end + + local paths = string.explode(self.Config["InstallDir"], ";") + for k,v in pairs(paths) do + if (#v > 0) then + self:AddInstallPath(v) + end + end +end + +function NazaraBuild:MakeInstallCommands(infoTable) + if (PremakeVersion < 50) then + return + end + + if (os.is("windows")) then + configuration({}) + + for k,v in pairs(self.InstallDir) do + local destPath = path.translate(path.isabsolute(k) and k or "../../" .. k) + postbuildcommands({[[xcopy "%{path.translate(cfg.linktarget.relpath):sub(1, -5) .. ".dll"}" "]] .. destPath .. [[\" /E /Y]]}) + end + + for k,fileName in pairs(table.join(infoTable.Libraries, infoTable.DynLib)) do + local paths = {} + for k,v in pairs(infoTable.BinaryPaths.x86) do + table.insert(paths, {"x32", v .. "/" .. fileName .. ".dll"}) + table.insert(paths, {"x32", v .. "/lib" .. fileName .. ".dll"}) + end + + for k,v in pairs(infoTable.BinaryPaths.x64) do + table.insert(paths, {"x64", v .. "/" .. fileName .. ".dll"}) + table.insert(paths, {"x64", v .. "/lib" .. fileName .. ".dll"}) + end + + for k,v in pairs(paths) do + local config = v[1] + local srcPath = v[2] + if (os.isfile(srcPath)) then + if (infoTable.Kind == "plugin") then + srcPath = "../../" .. srcPath + end + + configuration(config) + + for k,v in pairs(self.ExecutableDir) do + local srcPath = path.isabsolute(srcPath) and path.translate(srcPath) or [[%{path.translate(cfg.linktarget.relpath:sub(1, -#cfg.linktarget.name - 1) .. "../../]] .. srcPath .. [[")}]] + local destPath = path.translate(path.isabsolute(k) and k or "../../" .. k) + postbuildcommands({[[xcopy "]] .. srcPath .. [[" "]] .. destPath .. [[\" /E /Y]]}) + end + end + end + end + end +end + +local PosixOSes = { + ["bsd"] = true, + ["linux"] = true, + ["macosx"] = true, + ["solaris"] = true +} + +function NazaraBuild:Process(infoTable) + local libraries = {} + for k, library in pairs(infoTable.Libraries) do + local libraryTable = self:GetDependency(infoTable, library) + if (libraryTable) then + if (libraryTable.Excluded) then + infoTable.Excluded = true + infoTable.ExcludeReason = "depends on excluded " .. library .. " " .. libraryTable.Type:lower() + return false + end + + if (libraryTable.Type == "Module") then + if (_OPTIONS["united"]) then + library = "NazaraEngine" + else + library = "Nazara" .. libraryTable.Name + end + + if (not self.Config["UniteModules"] or infoTable.Type ~= "Module") then + table.insert(infoTable.ConfigurationLibraries.DebugStatic, library .. "-s-d") + table.insert(infoTable.ConfigurationLibraries.ReleaseStatic, library .. "-s") + table.insert(infoTable.ConfigurationLibraries.DebugDynamic, library .. "-d") + table.insert(infoTable.ConfigurationLibraries.ReleaseDynamic, library) + end + elseif (libraryTable.Type == "ExternLib") then + library = libraryTable.Name + + table.insert(infoTable.ConfigurationLibraries.DebugStatic, library .. "-s-d") + table.insert(infoTable.ConfigurationLibraries.ReleaseStatic, library .. "-s") + table.insert(infoTable.ConfigurationLibraries.DebugDynamic, library .. "-s-d") + table.insert(infoTable.ConfigurationLibraries.ReleaseDynamic, library .. "-s") + elseif (libraryTable.Type == "Tool") then + library = "Nazara" .. libraryTable.Name + + -- Import tools includes + for k,v in ipairs(libraryTable.Includes) do + table.insert(infoTable.Includes, v) + end + + -- And libraries + for k, v in pairs(libraryTable.Libraries) do + table.insert(infoTable.Libraries, v) + end + + for config, libs in pairs(libraryTable.ConfigurationLibraries) do + for k,v in pairs(libs) do + table.insert(infoTable.ConfigurationLibraries[config], v) + end + end + + table.insert(infoTable.ConfigurationLibraries.DebugStatic, library .. "-s-d") + table.insert(infoTable.ConfigurationLibraries.ReleaseStatic, library .. "-s") + table.insert(infoTable.ConfigurationLibraries.DebugDynamic, library .. "-d") + table.insert(infoTable.ConfigurationLibraries.ReleaseDynamic, library) + else + infoTable.Excluded = true + infoTable.ExcludeReason = "dependency " .. library .. " has invalid type \"" .. libraryTable.Type .. "\"" + return false + end + else + table.insert(libraries, library) + end + end + infoTable.Libraries = libraries + + for k,v in pairs(infoTable) do + local target = k:match("Os(%w+)") + if (target) then + local targetTable = infoTable[target] + if (targetTable) then + local excludeTargetTable = infoTable[target .. "Excluded"] + for platform, defineTable in pairs(v) do + platform = string.lower(platform) + if (platform == "posix") then + local osname = os.get() + if (PosixOSes[osname]) then + platform = osname + end + end + + if (os.is(platform)) then + for k,v in ipairs(defineTable) do + table.insert(targetTable, v) + end + elseif (excludeTargetTable) then + for k,v in ipairs(defineTable) do + table.insert(excludeTargetTable, v) + end + end + end + + infoTable[k] = nil + end + end + end + + if (infoTable.Kind == "application") then + self:AddExecutablePath(infoTable.TargetDirectory) + end + + if (infoTable.Validate) then + local ret, err = infoTable:Validate() + if (not ret) then + infoTable.Excluded = true + infoTable.ExcludeReason = "validation failed: " .. err + return false + end + end + + return true +end + function NazaraBuild:RegisterAction(actionTable) if (actionTable.Name == nil or type(actionTable.Name) ~= "string" or string.len(actionTable.Name) == 0) then return false, "Invalid action name" @@ -664,13 +919,13 @@ function NazaraBuild:RegisterExample(exampleTable) if (#exampleTable.Files == 0) then return false, "This example has no files" end - + local files = {} for k, file in ipairs(exampleTable.Files) do table.insert(files, "../examples/" .. exampleTable.Directory .. "/" .. file) end exampleTable.Files = files - + exampleTable.Type = "Example" self.Examples[lowerCaseName] = exampleTable return true @@ -715,8 +970,8 @@ function NazaraBuild:RegisterModule(moduleTable) table.insert(moduleTable.Files, "../src/Nazara/" .. moduleTable.Name .. "/**.hpp") table.insert(moduleTable.Files, "../src/Nazara/" .. moduleTable.Name .. "/**.inl") table.insert(moduleTable.Files, "../src/Nazara/" .. moduleTable.Name .. "/**.cpp") - - if (_OPTIONS["united"] and lowerCaseName ~= "core") then + + if (self.Config["UniteModules"] and lowerCaseName ~= "core") then table.insert(moduleTable.FilesExcluded, "../src/Nazara/" .. moduleTable.Name .. "/Debug/NewOverload.cpp") end @@ -735,7 +990,7 @@ function NazaraBuild:RegisterTool(toolTable) return false, "This tool name is already in use" end - if (toolTable.Directory == nil or type(toolTable.Directory) ~= "string" or string.len(toolTable.Directory) == 0) then + if (toolTable.TargetDirectory == nil or type(toolTable.TargetDirectory) ~= "string" or string.len(toolTable.TargetDirectory) == 0) then return false, "Invalid tool directory" end @@ -744,7 +999,7 @@ function NazaraBuild:RegisterTool(toolTable) end local lowerCaseKind = toolTable.Kind:lower() - if (lowerCaseKind == "library" or lowerCaseKind == "plugin" or lowerCaseKind == "consoleapp" or lowerCaseKind == "windowapp") then + if (lowerCaseKind == "library" or lowerCaseKind == "plugin" or lowerCaseKind == "application") then toolTable.Kind = lowerCaseKind else return false, "Invalid tool type" @@ -755,105 +1010,78 @@ function NazaraBuild:RegisterTool(toolTable) return true end -local PosixOSes = { - ["bsd"] = true, - ["linux"] = true, - ["macosx"] = true, - ["solaris"] = true -} +function NazaraBuild:Resolve(infoTable) + if (infoTable.ClientOnly and self.Config["ServerMode"]) then + infoTable.Excluded = true + infoTable.ExcludeReason = "excluded by command-line options (client-only)" + end -function NazaraBuild:Process(infoTable) - local libraries = {} - for k, library in pairs(infoTable.Libraries) do - local moduleName = library:match("Nazara(%w+)") - local moduleTable = moduleName and self.Modules[moduleName:lower()] - local toolTable = moduleName and self.Tools[moduleName:lower()] - - if (moduleTable) then - if (_OPTIONS["united"]) then - library = "NazaraEngine" - else - library = "Nazara" .. moduleTable.Name - end + if (infoTable.Excludable) then + local optionName = "excludes-" .. string.lower(infoTable.Type .. "-" .. infoTable.Name) + newoption({ + trigger = optionName, + description = "Excludes the " .. infoTable.Name .. " " .. string.lower(infoTable.Type) .. " and projects relying on it" + }) - if (not _OPTIONS["united"] or infoTable.Type ~= "Module") then - table.insert(infoTable.ConfigurationLibraries.DebugStatic, library .. "-s-d") - table.insert(infoTable.ConfigurationLibraries.ReleaseStatic, library .. "-s") - table.insert(infoTable.ConfigurationLibraries.DebugDynamic, library .. "-d") - table.insert(infoTable.ConfigurationLibraries.ReleaseDynamic, library) - end - else - local extLibTable = self.ExtLibs[library:lower()] - if (extLibTable) then - library = extLibTable.Name - - table.insert(infoTable.ConfigurationLibraries.DebugStatic, library .. "-s-d") - table.insert(infoTable.ConfigurationLibraries.ReleaseStatic, library .. "-s") - table.insert(infoTable.ConfigurationLibraries.DebugDynamic, library .. "-s-d") - table.insert(infoTable.ConfigurationLibraries.ReleaseDynamic, library .. "-s") - else - if (toolTable and toolTable.Kind == "library") then - library = "Nazara" .. toolTable.Name - - -- Import tools includes - for k,v in ipairs(toolTable.Includes) do - table.insert(infoTable.Includes, v) - end - - table.insert(infoTable.ConfigurationLibraries.DebugStatic, library .. "-s-d") - table.insert(infoTable.ConfigurationLibraries.ReleaseStatic, library .. "-s") - table.insert(infoTable.ConfigurationLibraries.DebugDynamic, library .. "-d") - table.insert(infoTable.ConfigurationLibraries.ReleaseDynamic, library) - else - table.insert(libraries, library) - end - end + if (_OPTIONS[optionName]) then + infoTable.Excluded = true + infoTable.ExcludeReason = "excluded by command-line options" end end - infoTable.Libraries = libraries - for k,v in pairs(infoTable) do - local target = k:match("Os(%w+)") - if (target) then - local targetTable = infoTable[target] - if (targetTable) then - local excludeTargetTable = infoTable[target .. "Excluded"] - for platform, defineTable in pairs(v) do - platform = string.lower(platform) - if (platform == "posix") then - local osname = os.get() - if (PosixOSes[osname]) then - platform = osname - end - end - - if (os.is(platform)) then - for k,v in ipairs(defineTable) do - table.insert(targetTable, v) - end - elseif (excludeTargetTable) then - for k,v in ipairs(defineTable) do - table.insert(excludeTargetTable, v) - end - end - end - - infoTable[k] = nil - end - end + if (type(infoTable.Libraries) == "function") then + infoTable.Libraries = infoTable.Libraries() end end function NazaraBuild:SetupInfoTable(infoTable) + infoTable.BinaryPaths = {} + infoTable.BinaryPaths.x86 = {} + infoTable.BinaryPaths.x64 = {} infoTable.ConfigurationLibraries = {} infoTable.ConfigurationLibraries.DebugStatic = {} infoTable.ConfigurationLibraries.ReleaseStatic = {} infoTable.ConfigurationLibraries.DebugDynamic = {} infoTable.ConfigurationLibraries.ReleaseDynamic = {} - - local infos = {"Defines", "Files", "FilesExcluded", "Flags", "Includes", "Libraries"} + infoTable.Excludable = true + infoTable.LibraryPaths = {} + infoTable.LibraryPaths.x86 = {} + infoTable.LibraryPaths.x64 = {} + + local infos = {"Defines", "DynLib", "Files", "FilesExcluded", "Flags", "Includes", "Libraries"} for k,v in ipairs(infos) do infoTable[v] = {} infoTable["Os" .. v] = {} end -end \ No newline at end of file +end + +function NazaraBuild:SetupExampleTable(infoTable) + self:SetupInfoTable(infoTable) + + infoTable.Kind = "application" + infoTable.TargetDirectory = "../examples/bin" +end + +function NazaraBuild:SetupExtlibTable(infoTable) + self:SetupInfoTable(infoTable) + + infoTable.Kind = "library" + + table.insert(infoTable.BinaryPaths.x86, "../extlibs/lib/common/x86") + table.insert(infoTable.BinaryPaths.x64, "../extlibs/lib/common/x64") + table.insert(infoTable.LibraryPaths.x86, "../extlibs/lib/common/x86") + table.insert(infoTable.LibraryPaths.x64, "../extlibs/lib/common/x64") +end + +function NazaraBuild:SetupModuleTable(infoTable) + self:SetupInfoTable(infoTable) + + infoTable.Kind = "library" + + table.insert(infoTable.BinaryPaths.x86, "../extlibs/lib/common/x86") + table.insert(infoTable.BinaryPaths.x64, "../extlibs/lib/common/x64") + table.insert(infoTable.LibraryPaths.x86, "../extlibs/lib/common/x86") + table.insert(infoTable.LibraryPaths.x64, "../extlibs/lib/common/x64") +end + +NazaraBuild.SetupToolTable = NazaraBuild.SetupInfoTable \ No newline at end of file diff --git a/build/scripts/features/index_template.html b/build/scripts/features/index_template.html index 43bf5aea9..852811a4c 100644 --- a/build/scripts/features/index_template.html +++ b/build/scripts/features/index_template.html @@ -1,71 +1,71 @@ - - - - - - Avancement de Nazara - - -
- Nazara Engine - -
- - Retrouvez le moteur sur GitHub !
- Dépôt GitHub

- Venez vous renseigner sur les topics dédiés à Nazara présents sur plusieurs sites web :
- OpenClassrooms, Progdupeupl ou ZesteDeSavoir -

- ... ou pourquoi ne pas venir faire un tour sur le forum dédié au moteur ? - -
- -

Fonctionnalités de Nazara

- -
Dernière mise à jour : - %DATE% -
- -

Important:

-

Afin de faciliter la mise à jour, la page que vous voyez ici a été générée automatiquement par un script Lua, ce qui m'oblige néanmoins à encoder les fonctionnalités de chaque module dans un premier temps. - C'est un travail assez long (pour vous donner une idée, les données du noyau représentent un fichier de 200 lignes), et il n'est pas encore complet, c'est pourquoi des modules manquent sur cette page.
- Gardez donc à l'esprit que le moteur possède plus de fonctionnalités que ce qui est décrit actuellement sur cette page.

- -

Oh et bien sûr je ne suis pas concepteur de site web, c'est pourquoi cette page est moche (j'ai essayé de minimiser les dégâts).
- Si vous sentez en vous l'irrésistible envie d'améliorer cette page, sachez que votre aide serait grandement appréciée (vous pouvez me contacter via le lien de votre choix plus haut).

- -

Le pourcentage indiqué est calculé automatiquement en fonction des fonctionnalités, cela signifie qu'une fonctionnalité présente sera comptée à 100% à partir du moment où son implémentation de base est considérée fonctionnelle, cela n'est donc pas une assurance qu'aucun bug n'existe concernant cette fonctionnalité (cependant cela signifie que la fonctionnalité est utilisable).
- Et bien entendu, un module ou une fonctionnalité ayant atteint les 100% peut toujours évoluer par la suite.

- -
- - - - - - - - - - - %MODULELIST% - -
Sommaire
ModuleAvancement
- - %MODULEDESCRIPTION% -
- - - - - - - - - - %MODULELIST% - -
Sommaire
ModulePourcentage
-
- + + + + + + Avancement de Nazara + + +
+ Nazara Engine + +
+ + Retrouvez le moteur sur GitHub !
+ Dépôt GitHub

+ Venez vous renseigner sur les topics dédiés à Nazara présents sur plusieurs sites web :
+ OpenClassrooms, Progdupeupl ou ZesteDeSavoir +

+ ... ou pourquoi ne pas venir faire un tour sur le forum dédié au moteur ? + +
+ +

Fonctionnalités de Nazara

+ +
Dernière mise à jour : + %DATE% +
+ +

Important:

+

Afin de faciliter la mise à jour, la page que vous voyez ici a été générée automatiquement par un script Lua, ce qui m'oblige néanmoins à encoder les fonctionnalités de chaque module dans un premier temps. + C'est un travail assez long (pour vous donner une idée, les données du noyau représentent un fichier de 200 lignes), et il n'est pas encore complet, c'est pourquoi des modules manquent sur cette page.
+ Gardez donc à l'esprit que le moteur possède plus de fonctionnalités que ce qui est décrit actuellement sur cette page.

+ +

Oh et bien sûr je ne suis pas concepteur de site web, c'est pourquoi cette page est moche (j'ai essayé de minimiser les dégâts).
+ Si vous sentez en vous l'irrésistible envie d'améliorer cette page, sachez que votre aide serait grandement appréciée (vous pouvez me contacter via le lien de votre choix plus haut).

+ +

Le pourcentage indiqué est calculé automatiquement en fonction des fonctionnalités, cela signifie qu'une fonctionnalité présente sera comptée à 100% à partir du moment où son implémentation de base est considérée fonctionnelle, cela n'est donc pas une assurance qu'aucun bug n'existe concernant cette fonctionnalité (cependant cela signifie que la fonctionnalité est utilisable).
+ Et bien entendu, un module ou une fonctionnalité ayant atteint les 100% peut toujours évoluer par la suite.

+ +
+ + + + + + + + + + + %MODULELIST% + +
Sommaire
ModuleAvancement
+ + %MODULEDESCRIPTION% +
+ + + + + + + + + + %MODULELIST% + +
Sommaire
ModulePourcentage
+
+ \ No newline at end of file diff --git a/build/scripts/features/style.css b/build/scripts/features/style.css index ff83f706c..189b2a090 100644 --- a/build/scripts/features/style.css +++ b/build/scripts/features/style.css @@ -1,121 +1,121 @@ -/* Je ne suis pas développeur HTML/CSS, je dois y toucher une fois l'an, désolé pour les quelques atrocités que vous pourrez trouver ici */ - -body -{ - font-family: sans-serif; - text-align: center; - margin: 0; - background-color: #f1f1f1; -} - -#englob { - display: block; - margin-left: auto; - margin-right: auto; - background-color: white; - width: 50%; - min-width: 765px; - padding: 0 20px; -} - -hr { - height: 0; - border: 0; - border-top: 1px solid #eee; -} - -a -{ - color: #007ACC; -} - -a:hover -{ - color: lightblue; -} - -h1 -{ - display: inline; -} - -h2 -{ - display: inline; - text-decoration: underline; -} - -h4 -{ - text-decoration: underline; -} - -p { - text-align: justify; -} - -ol -{ - list-style-type: none; -} - -table -{ - border-collapse: collapse; - text-align: center; - display: inline-block; - border: white groove; - border-radius: 10px; - box-shadow: 0px 0px 10px lightblue; -} - -th -{ - text-shadow: 2px 2px 4px black; -} - -tr -{ - border: 1px solid white; -} - -tbody tr:hover -{ - text-shadow: 0px 0px 4px white; -} - -.description -{ - margin-left: 20px; -} - -.lastupdate -{ - font-size: x-large; - font-weight: bold; - color: #f1c40f; -} - -.modulename -{ - font-size: x-large; - font-weight: bold; - text-shadow: 2px 2px 10px #007ACC; -} - -.note -{ - margin-left: 20px; - color: #007ACC; -} - -.notedesc -{ - color: rgb(200, 200, 255); -} - -.portability -{ - margin-left: 20px; - color: red; +/* Je ne suis pas développeur HTML/CSS, je dois y toucher une fois l'an, désolé pour les quelques atrocités que vous pourrez trouver ici */ + +body +{ + font-family: sans-serif; + text-align: center; + margin: 0; + background-color: #f1f1f1; +} + +#englob { + display: block; + margin-left: auto; + margin-right: auto; + background-color: white; + width: 50%; + min-width: 765px; + padding: 0 20px; +} + +hr { + height: 0; + border: 0; + border-top: 1px solid #eee; +} + +a +{ + color: #007ACC; +} + +a:hover +{ + color: lightblue; +} + +h1 +{ + display: inline; +} + +h2 +{ + display: inline; + text-decoration: underline; +} + +h4 +{ + text-decoration: underline; +} + +p { + text-align: justify; +} + +ol +{ + list-style-type: none; +} + +table +{ + border-collapse: collapse; + text-align: center; + display: inline-block; + border: white groove; + border-radius: 10px; + box-shadow: 0px 0px 10px lightblue; +} + +th +{ + text-shadow: 2px 2px 4px black; +} + +tr +{ + border: 1px solid white; +} + +tbody tr:hover +{ + text-shadow: 0px 0px 4px white; +} + +.description +{ + margin-left: 20px; +} + +.lastupdate +{ + font-size: x-large; + font-weight: bold; + color: #f1c40f; +} + +.modulename +{ + font-size: x-large; + font-weight: bold; + text-shadow: 2px 2px 10px #007ACC; +} + +.note +{ + margin-left: 20px; + color: #007ACC; +} + +.notedesc +{ + color: rgb(200, 200, 255); +} + +.portability +{ + margin-left: 20px; + color: red; } \ No newline at end of file diff --git a/build/scripts/modules/audio.lua b/build/scripts/modules/audio.lua index f03e493a9..509972dc3 100644 --- a/build/scripts/modules/audio.lua +++ b/build/scripts/modules/audio.lua @@ -1,14 +1,27 @@ MODULE.Name = "Audio" +MODULE.ClientOnly = true + MODULE.Defines = { "NAZARA_AUDIO_OPENAL" } MODULE.Libraries = { - "NazaraCore", + "NazaraCore" +} + +MODULE.OsLibraries.Windows = { "sndfile-1" } +MODULE.OsLibraries.Posix = { + "sndfile" +} + +MODULE.OsDynLib.Windows = { + "soft_oal" +} + MODULE.OsFiles.Windows = { "../src/Nazara/Audio/Win32/**.hpp", "../src/Nazara/Audio/Win32/**.cpp" diff --git a/build/scripts/modules/core.lua b/build/scripts/modules/core.lua index 7156ef2f6..b33c0b61f 100644 --- a/build/scripts/modules/core.lua +++ b/build/scripts/modules/core.lua @@ -1,6 +1,7 @@ MODULE.Name = "Core" +MODULE.Excludable = false -- Excluding the core makes no sense as everything relies on it -MODULE.Files = { -- Les autres fichiers seront ajoutés automatiquement +MODULE.Files = { -- Other files will be automatically added "../include/Nazara/Prerequesites.hpp", "../include/Nazara/Math/**.hpp", "../include/Nazara/Math/**.inl", diff --git a/build/scripts/modules/physics.lua b/build/scripts/modules/physics.lua index fbf6f39c8..f6d485a67 100644 --- a/build/scripts/modules/physics.lua +++ b/build/scripts/modules/physics.lua @@ -2,5 +2,5 @@ MODULE.Name = "Physics" MODULE.Libraries = { "NazaraCore", - "newton" + "Newton" } diff --git a/build/scripts/modules/renderer.lua b/build/scripts/modules/renderer.lua index 7e3edadbe..bbc472b75 100644 --- a/build/scripts/modules/renderer.lua +++ b/build/scripts/modules/renderer.lua @@ -1,5 +1,7 @@ MODULE.Name = "Renderer" +MODULE.ClientOnly = true + MODULE.Defines = { "NAZARA_RENDERER_OPENGL" } diff --git a/build/scripts/modules/utility.lua b/build/scripts/modules/utility.lua index 1fafbd1a4..c1bc186c0 100644 --- a/build/scripts/modules/utility.lua +++ b/build/scripts/modules/utility.lua @@ -1,7 +1,6 @@ MODULE.Name = "Utility" MODULE.Libraries = { - "freetype-s", "NazaraCore", "stb_image" } @@ -17,10 +16,12 @@ MODULE.OsFiles.Posix = { } MODULE.OsLibraries.Windows = { + "freetype-s", "gdi32" } MODULE.OsLibraries.Posix = { + "freetype", "X11", "xcb", "xcb-cursor", diff --git a/build/scripts/modules/vulkan.lua b/build/scripts/modules/vulkan.lua new file mode 100644 index 000000000..62a00a745 --- /dev/null +++ b/build/scripts/modules/vulkan.lua @@ -0,0 +1,31 @@ +MODULE.Name = "Vulkan" + +MODULE.ClientOnly = true + +MODULE.Defines = { + "VK_NO_PROTOTYPES" +} + +MODULE.Libraries = { + "NazaraCore", + "NazaraUtility" +} + +MODULE.OsDefines.Linux = { +-- "VK_USE_PLATFORM_MIR_KHR", + "VK_USE_PLATFORM_XCB_KHR" +-- "VK_USE_PLATFORM_XLIB_KHR", +-- "VK_USE_PLATFORM_WAYLAND_KHR" +} + +MODULE.OsDefines.BSD = MODULE.OsDefines.Linux +MODULE.OsDefines.Solaris = MODULE.OsDefines.Linux + +MODULE.OsDefines.Windows = { + "VK_USE_PLATFORM_WIN32_KHR" +} + +MODULE.OsFiles.Windows = { + "../src/Nazara/Vulkan/Win32/**.hpp", + "../src/Nazara/Vulkan/Win32/**.cpp" +} diff --git a/build/scripts/tools/assimp.lua b/build/scripts/tools/assimp.lua index 3a1ca093b..c810d6c0a 100644 --- a/build/scripts/tools/assimp.lua +++ b/build/scripts/tools/assimp.lua @@ -1,11 +1,11 @@ TOOL.Name = "Assimp" -TOOL.Directory = "../SDK/lib" +TOOL.Directory = "../plugins/Assimp" TOOL.Kind = "Plugin" - -TOOL.CopyTargetToExampleDir = true +TOOL.TargetDirectory = "../SDK/lib" TOOL.Includes = { + "../extlibs/include", "../include", "../plugins/Assimp" } diff --git a/build/scripts/tools/ndk.lua b/build/scripts/tools/ndk.lua index 78ff544de..8a40ea170 100644 --- a/build/scripts/tools/ndk.lua +++ b/build/scripts/tools/ndk.lua @@ -1,7 +1,8 @@ TOOL.Name = "SDK" -TOOL.Directory = "../SDK/lib" +TOOL.Directory = "../SDK" TOOL.Kind = "Library" +TOOL.TargetDirectory = "../SDK/lib" TOOL.Defines = { "NDK_BUILD" @@ -20,14 +21,11 @@ TOOL.Files = { "../SDK/src/NDK/**.cpp" } -TOOL.Libraries = { - "NazaraCore", - "NazaraAudio", - "NazaraLua", - "NazaraNetwork", - "NazaraNoise", - "NazaraPhysics", - "NazaraUtility", - "NazaraRenderer", - "NazaraGraphics" -} +TOOL.Libraries = function() + local libraries = {} + for k,v in pairs(NazaraBuild.Modules) do + table.insert(libraries, "Nazara" .. v.Name) + end + + return libraries +end \ No newline at end of file diff --git a/build/scripts/tools/ndk_server.lua b/build/scripts/tools/ndk_server.lua index c9c443e57..e536f1036 100644 --- a/build/scripts/tools/ndk_server.lua +++ b/build/scripts/tools/ndk_server.lua @@ -1,7 +1,8 @@ TOOL.Name = "SDKServer" -TOOL.Directory = "../SDK/lib" +TOOL.Directory = "../SDK" TOOL.Kind = "Library" +TOOL.TargetDirectory = "../SDK/lib" TOOL.Defines = { "NDK_BUILD", @@ -21,7 +22,7 @@ TOOL.Files = { "../SDK/src/NDK/**.cpp" } --- Exlude client-only files +-- Excludes client-only files TOOL.FilesExcluded = { "../SDK/**/CameraComponent.*", "../SDK/**/Console.*", @@ -35,10 +36,11 @@ TOOL.FilesExcluded = { "../SDK/**/LuaBinding_Renderer.*" } + TOOL.Libraries = { "NazaraCore", "NazaraLua", - "NazaraNetwork", + "NazaraNetwork", "NazaraNoise", "NazaraPhysics", "NazaraUtility" diff --git a/build/scripts/tools/unittests.lua b/build/scripts/tools/unittests.lua index cc6051297..0d3dcc7d2 100644 --- a/build/scripts/tools/unittests.lua +++ b/build/scripts/tools/unittests.lua @@ -1,7 +1,9 @@ TOOL.Name = "UnitTests" TOOL.Directory = "../tests" -TOOL.Kind = "ConsoleApp" +TOOL.EnableConsole = true +TOOL.Kind = "Application" +TOOL.TargetDirectory = TOOL.Directory TOOL.Defines = { } @@ -19,9 +21,10 @@ TOOL.Libraries = { "NazaraCore", "NazaraAudio", "NazaraLua", + "NazaraGraphics", + "NazaraRenderer", + "NazaraNetwork", "NazaraNoise", "NazaraPhysics", - "NazaraUtility", - "NazaraRenderer", - "NazaraGraphics" + "NazaraUtility" } diff --git a/examples/DopplerEffect/build.lua b/examples/DopplerEffect/build.lua index 8298ef526..302011b81 100644 --- a/examples/DopplerEffect/build.lua +++ b/examples/DopplerEffect/build.lua @@ -1,6 +1,6 @@ EXAMPLE.Name = "DopplerEffect" -EXAMPLE.Console = true +EXAMPLE.EnableConsole = true EXAMPLE.Files = { "main.cpp" diff --git a/examples/FirstScene/build.lua b/examples/FirstScene/build.lua index bdcdf8bca..612965b5a 100644 --- a/examples/FirstScene/build.lua +++ b/examples/FirstScene/build.lua @@ -1,15 +1,11 @@ EXAMPLE.Name = "FirstScene" -EXAMPLE.Console = true +EXAMPLE.EnableConsole = true EXAMPLE.Files = { "main.cpp" } EXAMPLE.Libraries = { - "NazaraCore", - "NazaraGraphics", - "NazaraRenderer", - "NazaraUtility", "NazaraSDK" } diff --git a/examples/FirstScene/main.cpp b/examples/FirstScene/main.cpp index 24fef574f..07e26cd1c 100644 --- a/examples/FirstScene/main.cpp +++ b/examples/FirstScene/main.cpp @@ -1,25 +1,27 @@ /* -** FirstScene - Première scène graphique -** Prérequis: Aucun -** Utilisation du module utilitaire et graphique -** Présente: -** - Création et gestion d'une fenêtre (Traitement des évènements clavier/souris) -** - Gestion du clavier (Récupération de l'état d'une touche) -** - Des outils pour afficher une scène basique via le chargement d'un modèle (et son affichage) -** - Éclairage directionnel -** - Gestion d'une caméra free-fly (Avec déplacement fluide) -** - Gestion basique d'une horloge +* FirstScene - Première scène graphique +* Prérequis: Aucun +* Utilisation du module utilitaire et graphique +** Présente : + * -Création et gestion d'une fenêtre (Traitement des évènements clavier/souris) + * -Gestion du clavier(Récupération de l'état d'une touche) + * -Des outils pour afficher une scène basique via le chargement d'un modèle (et son affichage) + * -Éclairage directionnel + * -Gestion d'une caméra free-fly (Avec déplacement fluide) + * -Gestion basique d'une horloge + * -Console */ #include // Horloges +#include // Module de scripting #include // Module graphique #include // Module de rendu #include // Module utilitaire -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include @@ -29,26 +31,15 @@ Nz::Vector3f DampedString(const Nz::Vector3f& currentPos, const Nz::Vector3f& ta int main() { - // Pour commencer, nous initialisons le SDK de Nazara, celui-ci va préparer le terrain en initialisant le moteur, - // les composants, systèmes, etc. - // NzInitializer est une classe RAII appelant Initialize dans son constructeur et Uninitialize dans son destructeur. - // Autrement dit, une fois ceci fait nous n'avons plus à nous soucier de la libération du moteur. - Nz::Initializer nazara; - if (!nazara) - { - // Une erreur s'est produite dans l'initialisation d'un des modules - std::cout << "Failed to initialize Nazara, see NazaraLog.log for further informations" << std::endl; - std::getchar(); // On laise le temps de voir l'erreur - - return EXIT_FAILURE; - } + // Ndk::Application est une classe s'occupant de l'initialisation du moteur ainsi que de la gestion de beaucoup de choses + Ndk::Application application; // Nazara étant initialisé, nous pouvons créer le monde pour contenir notre scène. // Dans un ECS, le monde représente bien ce que son nom indique, c'est l'ensemble de ce qui existe au niveau de l'application. // Il contient les systèmes et les entités, ces dernières contiennent les composants. // Il est possible d'utiliser plusieurs mondes au sein d'une même application, par exemple pour gérer un mélange de 2D et de 3D, // mais nous verrons cela dans un prochain exemple. - Ndk::World world; + Ndk::WorldHandle world = application.AddWorld().CreateHandle(); // Nous pouvons maintenant ajouter des systèmes, mais dans cet exemple nous nous contenterons de ceux de base. @@ -73,7 +64,7 @@ int main() Nz::SkyboxBackgroundRef skybox = Nz::SkyboxBackground::New(std::move(texture)); // Accédons maintenant au système de rendu faisant partie du monde - Ndk::RenderSystem& renderSystem = world.GetSystem(); // Une assertion valide la précondition "le système doit faire partie du monde" + Ndk::RenderSystem& renderSystem = world->GetSystem(); // Une assertion valide la précondition "le système doit faire partie du monde" // Nous assignons ensuite notre skybox comme "fond par défaut" du système // La notion "par défaut" existe parce qu'une caméra pourrait utiliser son propre fond lors du rendu, @@ -101,7 +92,7 @@ int main() // Le format OBJ ne précise aucune échelle pour ses données, contrairement à Nazara (une unité = un mètre en 3D). // Comme le vaisseau est très grand (Des centaines de mètres de long), nous allons le rendre plus petit pour les besoins de la démo. // Ce paramètre sert à indiquer la mise à l'échelle désirée lors du chargement du modèle. - params.mesh.scale.Set(0.01f); // Un centième de la taille originelle + params.mesh.matrix.MakeScale(Nz::Vector3f(0.01f)); // Un centième de la taille originelle // Les UVs de ce fichier sont retournées (repère OpenGL, origine coin bas-gauche) par rapport à ce que le moteur attend (haut-gauche) // Nous devons donc indiquer au moteur de les retourner lors du chargement @@ -146,7 +137,7 @@ int main() // Bien, nous avons un modèle valide, mais celui-ci ne consiste qu'en des informations de rendu, de matériaux et de textures. // Commençons donc par créer une entité vide, cela se fait en demandant au monde de générer une nouvelle entité. - Ndk::EntityHandle spaceship = world.CreateEntity(); + Ndk::EntityHandle spaceship = world->CreateEntity(); // Note: Nous ne récupérons pas l'entité directement mais un "handle" vers elle, ce dernier est un pointeur intelligent non-propriétaire. // Pour des raisons techniques, le pointeur de l'entité peut venir à changer, ou l'entité être simplement détruite pour n'importe quelle raison. @@ -156,9 +147,10 @@ int main() // Nous devons donc lui rajouter les composants que nous voulons. // Un NodeComponent donne à notre entité une position, rotation, échelle, et nous permet de l'attacher à d'autres entités (ce que nous ne ferons pas ici). - // Étant donné que par défaut, un NodeComponent se place en (0,0,0) sans rotation et avec une échelle de 1,1,1 et que cela nous convient, + // Étant donné que par défaut, un NodeComponent se place en (0,0,0) sans rotation et avec une échelle de 1,1,1 et que cela nous convient, // nous n'avons pas besoin d'agir sur le composant créé. spaceship->AddComponent(); + //spaceship->AddComponent().linearVelocity.Set(-1.f, 0.f, 0.f); // Bien, notre entité nouvellement créé dispose maintenant d'une position dans la scène, mais est toujours invisible // Nous lui ajoutons donc un GraphicsComponent @@ -178,7 +170,7 @@ int main() // Nous créons donc une seconde entité // Note: La création d'entité est une opération légère au sein du moteur, mais plus vous aurez d'entités et plus le processeur devra travailler. - Ndk::EntityHandle camera = world.CreateEntity(); + Ndk::EntityHandle camera = world->CreateEntity(); // Notre caméra a elle aussi besoin d'être positionnée dans la scène Ndk::NodeComponent& cameraNode = camera->AddComponent(); @@ -188,6 +180,9 @@ int main() // Et dispose d'un composant pour chaque point de vue de la scène, le CameraComponent Ndk::CameraComponent& cameraComp = camera->AddComponent(); + // Ajoutons un composant écouteur, si nous venions à avoir du son + camera->AddComponent(); + // Et on n'oublie pas de définir les plans délimitant le champs de vision // (Seul ce qui se trouvera entre les deux plans sera rendu) @@ -208,7 +203,7 @@ int main() // Nous allons créer une lumière directionnelle pour représenter la nébuleuse de notre skybox // Encore une fois, nous créons notre entité - Ndk::EntityHandle nebulaLight = world.CreateEntity(); + Ndk::EntityHandle nebulaLight = world->CreateEntity(); // Lui ajoutons une position dans la scène Ndk::NodeComponent& nebulaLightNode = nebulaLight->AddComponent(); @@ -246,7 +241,7 @@ int main() Nz::RenderTargetParameters parameters; parameters.antialiasingLevel = 4; - Nz::RenderWindow window(mode, windowTitle, style, parameters); + Nz::RenderWindow& window = application.AddWindow(mode, windowTitle, style, parameters); if (!window.IsValid()) { std::cout << "Failed to create render window" << std::endl; @@ -272,8 +267,70 @@ int main() bool smoothMovement = true; Nz::Vector3f targetPos = cameraNode.GetPosition(); - // Début de la boucle de rendu du programme - while (window.IsOpen()) + // Pour ajouter une console à notre application, nous avons besoin d'un monde 2D pour gérer ces rendus + Ndk::WorldHandle world2D = application.AddWorld().CreateHandle(); + world2D->GetSystem().SetDefaultBackground(nullptr); + world2D->GetSystem().SetGlobalUp(Nz::Vector3f::Down()); + + // Nous ajoutons une caméra comme précédement + Ndk::EntityHandle viewEntity = world2D->CreateEntity(); + viewEntity->AddComponent(); + + // À la différence que celui-ci effectuera une projection orthogonale + Ndk::CameraComponent& viewer = viewEntity->AddComponent(); + viewer.SetTarget(&window); + viewer.SetProjectionType(Nz::ProjectionType_Orthogonal); + + // Nous créons un environnement Lua pour gérer nos scripts + Nz::LuaInstance lua; + + // Faisons en sorte d'enregistrer les classes du moteur dans cet environnement + Ndk::LuaAPI::RegisterClasses(lua); + + // Ensuite nous créons la console en elle-même + Ndk::Console console(*world2D, Nz::Vector2f(window.GetWidth(), window.GetHeight() / 4), lua); + + // Nous redirigeons les logs vers cette console + Nz::Log::OnLogWriteType::ConnectionGuard logGuard = Nz::Log::OnLogWrite.Connect([&console] (const Nz::String& str) + { + console.AddLine(str); + }); + + // Nous réécrivons la fonction "print" du Lua pour la rediriger vers la console + lua.PushFunction([&console] (Nz::LuaInstance& instance) + { + Nz::StringStream stream; + + unsigned int argCount = instance.GetStackTop(); + instance.GetGlobal("tostring"); + for (unsigned int i = 1; i <= argCount; ++i) + { + instance.PushValue(-1); // ToString + instance.PushValue(i); // Arg + instance.Call(1, 1); + + std::size_t length; + const char* str = instance.CheckString(-1, &length); + if (i > 1) + stream << '\t'; + + stream << Nz::String(str, length); + instance.Pop(1); + } + + console.AddLine(stream); + return 0; + }); + lua.SetGlobal("print"); + + // Définissons quelques variables de base + lua.PushGlobal("Application", Ndk::Application::Instance()); + lua.PushGlobal("Console", console.CreateHandle()); + lua.PushGlobal("Spaceship", spaceship->CreateHandle()); + lua.PushGlobal("World", world->CreateHandle()); + + // Début de la boucle de rendu du programme (s'occupant par exemple de mettre à jour le monde) + while (application.Run()) { // Ensuite nous allons traiter les évènements (Étape indispensable pour la fenêtre) Nz::WindowEvent event; @@ -283,6 +340,9 @@ int main() { case Nz::WindowEventType_MouseMoved: // La souris a bougé { + if (console.IsVisible()) + break; + // Gestion de la caméra free-fly (Rotation) float sensitivity = 0.3f; // Sensibilité de la souris @@ -297,15 +357,18 @@ int main() // Pour éviter que le curseur ne sorte de l'écran, nous le renvoyons au centre de la fenêtre // Cette fonction est codée de sorte à ne pas provoquer d'évènement MouseMoved - Nz::Mouse::SetPosition(window.GetWidth()/2, window.GetHeight()/2, window); + Nz::Mouse::SetPosition(window.GetWidth() / 2, window.GetHeight() / 2, window); break; } case Nz::WindowEventType_Quit: // L'utilisateur a cliqué sur la croix, ou l'OS veut terminer notre programme - window.Close(); // On demande la fermeture de la fenêtre (Qui aura lieu au prochain tour de boucle) + application.Quit(); break; case Nz::WindowEventType_KeyPressed: // Une touche a été pressée ! + if (console.IsVisible()) + console.SendEvent(event); + if (event.key.code == Nz::Keyboard::Key::Escape) window.Close(); else if (event.key.code == Nz::Keyboard::F1) @@ -318,6 +381,19 @@ int main() else smoothMovement = true; } + else if (event.key.code == Nz::Keyboard::F9) + console.Show(!console.IsVisible()); + break; + + case Nz::WindowEventType_TextEntered: + { + if (console.IsVisible()) + console.SendCharacter(event.text.character); + break; + } + + case Nz::WindowEventType_Resized: + console.SetSize({float(event.size.width), event.size.height / 4.f}); break; default: @@ -337,52 +413,48 @@ int main() { // Le temps écoulé en seconde depuis la dernière fois que ce bloc a été exécuté float elapsedTime = updateAccumulator / 1000000.f; - std::cout << elapsedTime << std::endl; // Vitesse de déplacement de la caméra float cameraSpeed = 3.f * elapsedTime; // Trois mètres par seconde - // Si la touche espace est enfoncée, notre vitesse de déplacement est multipliée par deux - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Space)) - cameraSpeed *= 2.f; + if (!console.IsVisible()) + { + // Si la touche espace est enfoncée, notre vitesse de déplacement est multipliée par deux + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Space)) + cameraSpeed *= 2.f; - // Pour que nos déplacement soient liés à la rotation de la caméra, nous allons utiliser - // les directions locales de la caméra + // Pour que nos déplacement soient liés à la rotation de la caméra, nous allons utiliser + // les directions locales de la caméra - // Si la flèche du haut ou la touche Z (vive ZQSD) est pressée, on avance - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Up) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Z)) - targetPos += cameraNode.GetForward() * cameraSpeed; + // Si la flèche du haut ou la touche Z (vive ZQSD) est pressée, on avance + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Up) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Z)) + targetPos += cameraNode.GetForward() * cameraSpeed; - // Si la flèche du bas ou la touche S est pressée, on recule - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Down) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::S)) - targetPos += cameraNode.GetBackward() * cameraSpeed; + // Si la flèche du bas ou la touche S est pressée, on recule + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Down) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::S)) + targetPos += cameraNode.GetBackward() * cameraSpeed; - // Etc... - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Left) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Q)) - targetPos += cameraNode.GetLeft() * cameraSpeed; + // Etc... + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Left) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Q)) + targetPos += cameraNode.GetLeft() * cameraSpeed; - // Etc... - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Right) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::D)) - targetPos += cameraNode.GetRight() * cameraSpeed; + // Etc... + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Right) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::D)) + targetPos += cameraNode.GetRight() * cameraSpeed; - // Majuscule pour monter, notez l'utilisation d'une direction globale (Non-affectée par la rotation) - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::RShift)) - targetPos += Nz::Vector3f::Up() * cameraSpeed; + // Majuscule pour monter, notez l'utilisation d'une direction globale (Non-affectée par la rotation) + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::RShift)) + targetPos += Nz::Vector3f::Up() * cameraSpeed; - // Contrôle (Gauche ou droite) pour descendre dans l'espace global, etc... - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::LControl) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::RControl)) - targetPos += Nz::Vector3f::Down() * cameraSpeed; + // Contrôle (Gauche ou droite) pour descendre dans l'espace global, etc... + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::LControl) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::RControl)) + targetPos += Nz::Vector3f::Down() * cameraSpeed; + } cameraNode.SetPosition((smoothMovement) ? DampedString(cameraNode.GetPosition(), targetPos, elapsedTime) : targetPos, Nz::CoordSys_Global); - updateAccumulator = 0; } - // Et maintenant pour rendre la scène, il nous suffit de mettre à jour le monde en lui envoyant le temps depuis la dernière mise à jour - // Note: La plupart des systèmes, à l'exception de celui de rendu, ont une fréquence de mise à jour fixe (modifiable) - // Il n'est donc pas nécessaire de limiter vous-même les mises à jour du monde - world.Update(elapsedUS / 1000000.f); - // Après avoir dessiné sur la fenêtre, il faut s'assurer qu'elle affiche cela // Cet appel ne fait rien d'autre qu'échanger les buffers de rendu (Double Buffering) window.Display(); @@ -410,7 +482,7 @@ int main() } } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } Nz::Vector3f DampedString(const Nz::Vector3f& currentPos, const Nz::Vector3f& targetPos, float frametime, float springStrength) @@ -430,7 +502,7 @@ Nz::Vector3f DampedString(const Nz::Vector3f& currentPos, const Nz::Vector3f& ta if (Nz::NumberEquals(displacementLength, 0.f)) return currentPos; - float invDisplacementLength = 1.f/displacementLength; + float invDisplacementLength = 1.f / displacementLength; const float dampConstant = 0.000065f; // Something v.small to offset 1/ displacement length diff --git a/examples/HardwareInfo/build.lua b/examples/HardwareInfo/build.lua index e1b487bd6..afcc7b544 100644 --- a/examples/HardwareInfo/build.lua +++ b/examples/HardwareInfo/build.lua @@ -1,6 +1,6 @@ EXAMPLE.Name = "HardwareInfo" -EXAMPLE.Console = true +EXAMPLE.EnableConsole = true EXAMPLE.Defines = { "NAZARA_RENDERER_OPENGL" diff --git a/examples/MeshInfos/build.lua b/examples/MeshInfos/build.lua index 9372ef5c1..4e26f318b 100644 --- a/examples/MeshInfos/build.lua +++ b/examples/MeshInfos/build.lua @@ -1,6 +1,6 @@ EXAMPLE.Name = "MeshInfos" -EXAMPLE.Console = true +EXAMPLE.EnableConsole = true EXAMPLE.Files = { "main.cpp" diff --git a/examples/MeshInfos/main.cpp b/examples/MeshInfos/main.cpp index 73864abaa..e95a2a4c2 100644 --- a/examples/MeshInfos/main.cpp +++ b/examples/MeshInfos/main.cpp @@ -173,12 +173,7 @@ int main() Nz::String data; if (!matData.GetStringParameter(Nz::MaterialData::FilePath, &data)) - { - if (matData.HasParameter(Nz::MaterialData::CustomDefined)) - data = ""; - else - data = ""; - } + data = ""; std::cout << "\t" << (i+1) << ": " << data << std::endl; } diff --git a/examples/Tut00/build.lua b/examples/Tut00/build.lua new file mode 100644 index 000000000..ed5ea5995 --- /dev/null +++ b/examples/Tut00/build.lua @@ -0,0 +1,20 @@ +EXAMPLE.Name = "Tut00_EmptyProject" + +EXAMPLE.EnableConsole = true + +EXAMPLE.Files = { + "main.cpp" +} + +EXAMPLE.Libraries = { + "NazaraAudio", + "NazaraCore", + "NazaraGraphics", + "NazaraLua", + "NazaraNetwork", + "NazaraNoise", + "NazaraPhysics", + "NazaraRenderer", + "NazaraUtility", + "NazaraSDK" +} diff --git a/examples/Tut00/main.cpp b/examples/Tut00/main.cpp new file mode 100644 index 000000000..472db3148 --- /dev/null +++ b/examples/Tut00/main.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + // This "example" has only one purpose: Giving an empty project for you to test whatever you want + // If you wish to have multiple test projects, you only have to copy/paste this directory and change the name in the build.lua + Ndk::Application app; + + // Do what you want here + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/Tut01/build.lua b/examples/Tut01/build.lua index 03b4457ab..c195cbc1b 100644 --- a/examples/Tut01/build.lua +++ b/examples/Tut01/build.lua @@ -1,15 +1,11 @@ EXAMPLE.Name = "Tut01_HelloWorld" -EXAMPLE.Console = true +EXAMPLE.EnableConsole = true EXAMPLE.Files = { "main.cpp" } EXAMPLE.Libraries = { - "NazaraCore", - "NazaraGraphics", - "NazaraRenderer", - "NazaraUtility", "NazaraSDK" } diff --git a/include/Nazara/Audio/Algorithm.inl b/include/Nazara/Audio/Algorithm.inl index ec89cdca9..1075a8b57 100644 --- a/include/Nazara/Audio/Algorithm.inl +++ b/include/Nazara/Audio/Algorithm.inl @@ -7,11 +7,21 @@ namespace Nz { + /*! + * \ingroup audio + * \brief Mixes channels in mono + * + * \param input Input buffer with multiples channels + * \param output Output butter for mono + * \param channelCount Number of channels + * \param frameCount Number of frames + * + * \remark The input buffer may be the same as the output one + */ template void MixToMono(T* input, T* output, unsigned int channelCount, unsigned int frameCount) { - ///DOC: Le buffer d'entrée peut être le même que le buffer de sortie - // Pour éviter l'overflow, on utilise comme accumulateur un type assez grand, (u)int 64 bits pour les entiers, double pour les flottants + // To avoid overflow, we use, as an accumulator, a type which is large enough: (u)int 64 bits for integers, double for floatings typedef typename std::conditional::value, UInt64, Int64>::type BiggestInt; typedef typename std::conditional::value, BiggestInt, double>::type Biggest; @@ -19,7 +29,7 @@ namespace Nz { Biggest acc = Biggest(0); for (unsigned int j = 0; j < channelCount; ++j) - acc += input[i*channelCount + j]; + acc += input[i * channelCount + j]; output[i] = static_cast(acc / channelCount); } diff --git a/include/Nazara/Audio/Config.hpp b/include/Nazara/Audio/Config.hpp index e11bda468..8e2068069 100644 --- a/include/Nazara/Audio/Config.hpp +++ b/include/Nazara/Audio/Config.hpp @@ -27,18 +27,23 @@ #ifndef NAZARA_CONFIG_AUDIO_HPP #define NAZARA_CONFIG_AUDIO_HPP -/// Modifier la configuration d'un module nécessite une recompilation quasi-intégrale de celui-ci et de ceux en héritant +/*! +* \defgroup audio (NazaraAudio) Audio module +* Audio/System module including classes to handle music, sound, etc... +*/ -// Utilise un manager de mémoire pour gérer les allocations dynamiques (détecte les leaks au prix d'allocations/libérations dynamiques plus lentes) +/// Each modification of a parameter needs a recompilation of the module + +// Use the MemoryManager to manage dynamic allocations (can detect memory leak but allocations/frees are slower) #define NAZARA_AUDIO_MANAGE_MEMORY 0 -// Active les tests de sécurité supplémentaires (Teste notamment les arguments des fonctions, conseillé pour le développement) +// Activate the security tests based on the code (Advised for development) #define NAZARA_AUDIO_SAFE 1 -// Le nombre de buffers utilisés lors du streaming d'objets audio (Au moins deux) +// The number of buffers used for audio streaming (At least two) #define NAZARA_AUDIO_STREAMED_BUFFER_COUNT 2 -/// Vérification des valeurs et types de certaines constantes +/// Checking the values and types of certain constants #include #if !defined(NAZARA_STATIC) diff --git a/include/Nazara/Audio/ConfigCheck.hpp b/include/Nazara/Audio/ConfigCheck.hpp index 9ed35c1a4..70d0c6fe5 100644 --- a/include/Nazara/Audio/ConfigCheck.hpp +++ b/include/Nazara/Audio/ConfigCheck.hpp @@ -7,12 +7,12 @@ #ifndef NAZARA_CONFIG_CHECK_AUDIO_HPP #define NAZARA_CONFIG_CHECK_AUDIO_HPP -/// Ce fichier sert à vérifier la valeur des constantes du fichier Config.hpp +/// This file is used to check the constant values defined in Config.hpp #include #define NazaraCheckTypeAndVal(name, type, op, val, err) static_assert(std::is_ ##type ::value && name op val, #type err) -// On force la valeur de MANAGE_MEMORY en mode debug +// We force the value of MANAGE_MEMORY in debug #if defined(NAZARA_DEBUG) && !NAZARA_AUDIO_MANAGE_MEMORY #undef NAZARA_AUDIO_MANAGE_MEMORY #define NAZARA_AUDIO_MANAGE_MEMORY 0 diff --git a/include/Nazara/Audio/DebugOff.hpp b/include/Nazara/Audio/DebugOff.hpp index 254d7701d..e5149d5b5 100644 --- a/include/Nazara/Audio/DebugOff.hpp +++ b/include/Nazara/Audio/DebugOff.hpp @@ -2,7 +2,7 @@ // This file is part of the "Nazara Engine - Audio module" // For conditions of distribution and use, see copyright notice in Config.hpp -// On suppose que Debug.hpp a déjà été inclus, tout comme Config.hpp +// We assume that Debug.hpp has already been included, same thing for Config.hpp #if NAZARA_AUDIO_MANAGE_MEMORY #undef delete #undef new diff --git a/include/Nazara/Audio/Enums.hpp b/include/Nazara/Audio/Enums.hpp index b8d3e3ec6..20866e5e9 100644 --- a/include/Nazara/Audio/Enums.hpp +++ b/include/Nazara/Audio/Enums.hpp @@ -13,7 +13,7 @@ namespace Nz { AudioFormat_Unknown = -1, - // La valeur entière est le nombre de canaux possédés par ce format + // The integer value is the number of channels used by the format AudioFormat_Mono = 1, AudioFormat_Stereo = 2, AudioFormat_Quad = 4, diff --git a/include/Nazara/Audio/OpenAL.hpp b/include/Nazara/Audio/OpenAL.hpp index eab95e6d0..aa80f406e 100644 --- a/include/Nazara/Audio/OpenAL.hpp +++ b/include/Nazara/Audio/OpenAL.hpp @@ -15,18 +15,18 @@ #include #include -// Inclusion des headers OpenAL +// Inclusion of OpenAL headers -// Étant donné que les headers OpenAL ne nous permettent pas de n'avoir que les signatures sans les pointeurs de fonctions -// Et que je ne souhaite pas les modifier, je suis contraint de les placer dans un espace de nom différent pour ensuite -// remettre dans l'espace global les choses intéressantes (les typedef notamment) +// OpenAL headers does not allow us to only get the signatures without the pointers to the functions +// And I do no want to modify them, I'm obliged to put them in a different namespace +// to put only interesting things back in the global namespace (specially typedef) namespace OpenALDetail { #include #include } -// Si quelqu'un a une meilleure idée ... +// If someone has a better idea ... using OpenALDetail::ALboolean; using OpenALDetail::ALbyte; using OpenALDetail::ALchar; diff --git a/include/Nazara/Audio/SoundBuffer.inl b/include/Nazara/Audio/SoundBuffer.inl index 9c5d1198b..a4c42be69 100644 --- a/include/Nazara/Audio/SoundBuffer.inl +++ b/include/Nazara/Audio/SoundBuffer.inl @@ -7,6 +7,13 @@ namespace Nz { + /*! + * \brief Creates a new sound buffer from the arguments + * \return A reference to the newly created sound buffer + * + * \param args Arguments for the sound buffer + */ + template SoundBufferRef SoundBuffer::New(Args&&... args) { diff --git a/include/Nazara/Audio/SoundEmitter.hpp b/include/Nazara/Audio/SoundEmitter.hpp index d56cd1bca..9a68d88cc 100644 --- a/include/Nazara/Audio/SoundEmitter.hpp +++ b/include/Nazara/Audio/SoundEmitter.hpp @@ -12,7 +12,7 @@ #include #include -///TODO: Faire hériter SoundEmitter de Node +///TODO: Inherit SoundEmitter from Node namespace Nz { diff --git a/include/Nazara/Core/Algorithm.hpp b/include/Nazara/Core/Algorithm.hpp index f031854c5..b8a3206af 100644 --- a/include/Nazara/Core/Algorithm.hpp +++ b/include/Nazara/Core/Algorithm.hpp @@ -26,6 +26,7 @@ namespace Nz template constexpr std::size_t CountOf(T(&name)[N]) noexcept; template std::size_t CountOf(const T& c); template void HashCombine(std::size_t& seed, const T& v); + template T ReverseBits(T integer); template struct PointedType diff --git a/include/Nazara/Core/Algorithm.inl b/include/Nazara/Core/Algorithm.inl index 821a9069f..c77a5c3bb 100644 --- a/include/Nazara/Core/Algorithm.inl +++ b/include/Nazara/Core/Algorithm.inl @@ -28,6 +28,8 @@ namespace Nz { return (object .* std::forward(fn))(std::get(std::forward(t))...); } + + NAZARA_CORE_API extern const UInt8 BitReverseTable256[256]; } /*! @@ -76,7 +78,7 @@ namespace Nz * \param v Object to hash * * \remark a HashAppend specialization for type T is required - * + * * \see ComputeHash */ template @@ -122,6 +124,7 @@ namespace Nz template constexpr std::size_t CountOf(T(&name)[N]) noexcept { + NazaraUnused(name); return N; } @@ -164,6 +167,23 @@ namespace Nz seed = static_cast(b * kMul); } + /*! + * \ingroup core + * \brief Reverse the bit order of the integer + * \return integer with reversed bits + * + * \param integer Integer whose bits are to be reversed + */ + template + T ReverseBits(T integer) + { + T reversed = 0; + for (std::size_t i = 0; i < sizeof(T); ++i) + reversed |= T(Detail::BitReverseTable256[(integer >> i * 8) & 0xFF]) << (sizeof(T) * 8 - (i + 1) * 8); + + return reversed; + } + template struct PointedType {typedef T type;}; template struct PointedType {typedef T type;}; template struct PointedType {typedef T type;}; diff --git a/include/Nazara/Core/Bitset.hpp b/include/Nazara/Core/Bitset.hpp index 3b440278c..e4e2d8042 100644 --- a/include/Nazara/Core/Bitset.hpp +++ b/include/Nazara/Core/Bitset.hpp @@ -26,9 +26,9 @@ namespace Nz class Bit; Bitset(); - explicit Bitset(unsigned int bitCount, bool val); + explicit Bitset(std::size_t bitCount, bool val); explicit Bitset(const char* bits); - Bitset(const char* bits, unsigned int bitCount); + Bitset(const char* bits, std::size_t bitCount); Bitset(const Bitset& bitset) = default; explicit Bitset(const String& bits); template Bitset(T value); @@ -36,16 +36,16 @@ namespace Nz ~Bitset() noexcept = default; void Clear() noexcept; - unsigned int Count() const; + std::size_t Count() const; void Flip(); - unsigned int FindFirst() const; - unsigned int FindNext(unsigned int bit) const; + std::size_t FindFirst() const; + std::size_t FindNext(std::size_t bit) const; - Block GetBlock(unsigned int i) const; - unsigned int GetBlockCount() const; - unsigned int GetCapacity() const; - unsigned int GetSize() const; + Block GetBlock(std::size_t i) const; + std::size_t GetBlockCount() const; + std::size_t GetCapacity() const; + std::size_t GetSize() const; void PerformsAND(const Bitset& a, const Bitset& b); void PerformsNOT(const Bitset& a); @@ -54,19 +54,19 @@ namespace Nz bool Intersects(const Bitset& bitset) const; - void Reserve(unsigned int bitCount); - void Resize(unsigned int bitCount, bool defaultVal = false); + void Reserve(std::size_t bitCount); + void Resize(std::size_t bitCount, bool defaultVal = false); void Reset(); - void Reset(unsigned int bit); + void Reset(std::size_t bit); void Set(bool val = true); - void Set(unsigned int bit, bool val = true); - void SetBlock(unsigned int i, Block block); + void Set(std::size_t bit, bool val = true); + void SetBlock(std::size_t i, Block block); void Swap(Bitset& bitset); - bool Test(unsigned int bit) const; + bool Test(std::size_t bit) const; bool TestAll() const; bool TestAny() const; bool TestNone() const; @@ -74,9 +74,9 @@ namespace Nz template T To() const; String ToString() const; - void UnboundedReset(unsigned int bit); - void UnboundedSet(unsigned int bit, bool val = true); - bool UnboundedTest(unsigned int bit) const; + void UnboundedReset(std::size_t bit); + void UnboundedSet(std::size_t bit, bool val = true); + bool UnboundedTest(std::size_t bit) const; Bit operator[](int index); bool operator[](int index) const; @@ -93,20 +93,20 @@ namespace Nz Bitset& operator^=(const Bitset& bitset); static constexpr Block fullBitMask = std::numeric_limits::max(); - static constexpr unsigned int bitsPerBlock = std::numeric_limits::digits; - static constexpr unsigned int npos = std::numeric_limits::max(); + static constexpr std::size_t bitsPerBlock = std::numeric_limits::digits; + static constexpr std::size_t npos = std::numeric_limits::max(); private: - unsigned int FindFirstFrom(unsigned int blockIndex) const; + std::size_t FindFirstFrom(std::size_t blockIndex) const; Block GetLastBlockMask() const; void ResetExtraBits(); - static unsigned int ComputeBlockCount(unsigned int bitCount); - static unsigned int GetBitIndex(unsigned int bit); - static unsigned int GetBlockIndex(unsigned int bit); + static std::size_t ComputeBlockCount(std::size_t bitCount); + static std::size_t GetBitIndex(std::size_t bit); + static std::size_t GetBlockIndex(std::size_t bit); std::vector m_blocks; - unsigned int m_bitCount; + std::size_t m_bitCount; }; template diff --git a/include/Nazara/Core/Bitset.inl b/include/Nazara/Core/Bitset.inl index 568d4a815..21457d7c7 100644 --- a/include/Nazara/Core/Bitset.inl +++ b/include/Nazara/Core/Bitset.inl @@ -42,7 +42,7 @@ namespace Nz */ template - Bitset::Bitset(unsigned int bitCount, bool val) : + Bitset::Bitset(std::size_t bitCount, bool val) : Bitset() { Resize(bitCount, val); @@ -72,11 +72,11 @@ namespace Nz */ template - Bitset::Bitset(const char* bits, unsigned int bitCount) : + Bitset::Bitset(const char* bits, std::size_t bitCount) : m_blocks(ComputeBlockCount(bitCount), 0U), m_bitCount(bitCount) { - for (unsigned int i = 0; i < bitCount; ++i) + for (std::size_t i = 0; i < bitCount; ++i) { switch (*bits++) { @@ -126,7 +126,7 @@ namespace Nz else { // Note: I was kinda tired when I wrote this, there's probably a much easier method than checking bits to write bits - for (unsigned int bitPos = 0; bitPos < std::numeric_limits::digits; bitPos++) + for (std::size_t bitPos = 0; bitPos < std::numeric_limits::digits; bitPos++) { if (value & (T(1U) << bitPos)) UnboundedSet(bitPos, true); @@ -153,13 +153,13 @@ namespace Nz */ template - unsigned int Bitset::Count() const + std::size_t Bitset::Count() const { if (m_blocks.empty()) return 0; - unsigned int count = 0; - for (unsigned int i = 0; i < m_blocks.size(); ++i) + std::size_t count = 0; + for (std::size_t i = 0; i < m_blocks.size(); ++i) count += CountBits(m_blocks[i]); return count; @@ -184,7 +184,7 @@ namespace Nz */ template - unsigned int Bitset::FindFirst() const + std::size_t Bitset::FindFirst() const { return FindFirstFrom(0); } @@ -199,7 +199,7 @@ namespace Nz */ template - unsigned int Bitset::FindNext(unsigned int bit) const + std::size_t Bitset::FindNext(std::size_t bit) const { NazaraAssert(bit < m_bitCount, "Bit index out of range"); @@ -207,8 +207,8 @@ namespace Nz return npos; // The block of the bit and its index - unsigned int blockIndex = GetBlockIndex(bit); - unsigned int bitIndex = GetBitIndex(bit); + std::size_t blockIndex = GetBlockIndex(bit); + std::size_t bitIndex = GetBitIndex(bit); // We get the block Block block = m_blocks[blockIndex]; @@ -233,7 +233,7 @@ namespace Nz */ template - Block Bitset::GetBlock(unsigned int i) const + Block Bitset::GetBlock(std::size_t i) const { NazaraAssert(i < m_blocks.size(), "Block index out of range"); @@ -246,7 +246,7 @@ namespace Nz */ template - unsigned int Bitset::GetBlockCount() const + std::size_t Bitset::GetBlockCount() const { return m_blocks.size(); } @@ -257,7 +257,7 @@ namespace Nz */ template - unsigned int Bitset::GetCapacity() const + std::size_t Bitset::GetCapacity() const { return m_blocks.capacity()*bitsPerBlock; } @@ -268,7 +268,7 @@ namespace Nz */ template - unsigned int Bitset::GetSize() const + std::size_t Bitset::GetSize() const { return m_bitCount; } @@ -285,7 +285,7 @@ namespace Nz template void Bitset::PerformsAND(const Bitset& a, const Bitset& b) { - std::pair minmax = std::minmax(a.GetBlockCount(), b.GetBlockCount()); + std::pair minmax = std::minmax(a.GetBlockCount(), b.GetBlockCount()); // We reinitialise our blocks with zero m_blocks.clear(); @@ -293,7 +293,7 @@ namespace Nz m_bitCount = std::max(a.GetSize(), b.GetSize()); // In case of the "AND", we can stop with the smallest size (because x & 0 = 0) - for (unsigned int i = 0; i < minmax.first; ++i) + for (std::size_t i = 0; i < minmax.first; ++i) m_blocks[i] = a.GetBlock(i) & b.GetBlock(i); ResetExtraBits(); @@ -311,7 +311,7 @@ namespace Nz m_blocks.resize(a.GetBlockCount()); m_bitCount = a.GetSize(); - for (unsigned int i = 0; i < m_blocks.size(); ++i) + for (std::size_t i = 0; i < m_blocks.size(); ++i) m_blocks[i] = ~a.GetBlock(i); ResetExtraBits(); @@ -329,18 +329,18 @@ namespace Nz template void Bitset::PerformsOR(const Bitset& a, const Bitset& b) { - const Bitset& greater = (a.GetBlockCount() > b.GetBlockCount()) ? a : b; - const Bitset& lesser = (a.GetBlockCount() > b.GetBlockCount()) ? b : a; + const Bitset& greater = (a.GetSize() > b.GetSize()) ? a : b; + const Bitset& lesser = (a.GetSize() > b.GetSize()) ? b : a; - unsigned int maxBlockCount = greater.GetBlockCount(); - unsigned int minBlockCount = lesser.GetBlockCount(); + std::size_t maxBlockCount = greater.GetBlockCount(); + std::size_t minBlockCount = lesser.GetBlockCount(); m_blocks.resize(maxBlockCount); m_bitCount = greater.GetSize(); - for (unsigned int i = 0; i < minBlockCount; ++i) + for (std::size_t i = 0; i < minBlockCount; ++i) m_blocks[i] = a.GetBlock(i) | b.GetBlock(i); - for (unsigned int i = minBlockCount; i < maxBlockCount; ++i) + for (std::size_t i = minBlockCount; i < maxBlockCount; ++i) m_blocks[i] = greater.GetBlock(i); // (x | 0 = x) ResetExtraBits(); @@ -358,18 +358,18 @@ namespace Nz template void Bitset::PerformsXOR(const Bitset& a, const Bitset& b) { - const Bitset& greater = (a.GetBlockCount() > b.GetBlockCount()) ? a : b; - const Bitset& lesser = (a.GetBlockCount() > b.GetBlockCount()) ? b : a; + const Bitset& greater = (a.GetSize() > b.GetSize()) ? a : b; + const Bitset& lesser = (a.GetSize() > b.GetSize()) ? b : a; - unsigned int maxBlockCount = greater.GetBlockCount(); - unsigned int minBlockCount = lesser.GetBlockCount(); + std::size_t maxBlockCount = greater.GetBlockCount(); + std::size_t minBlockCount = lesser.GetBlockCount(); m_blocks.resize(maxBlockCount); m_bitCount = greater.GetSize(); - for (unsigned int i = 0; i < minBlockCount; ++i) + for (std::size_t i = 0; i < minBlockCount; ++i) m_blocks[i] = a.GetBlock(i) ^ b.GetBlock(i); - for (unsigned int i = minBlockCount; i < maxBlockCount; ++i) + for (std::size_t i = minBlockCount; i < maxBlockCount; ++i) m_blocks[i] = greater.GetBlock(i); // (x ^ 0 = x) ResetExtraBits(); @@ -385,8 +385,8 @@ namespace Nz bool Bitset::Intersects(const Bitset& bitset) const { // We only test the blocks in common - unsigned int sharedBlocks = std::min(GetBlockCount(), bitset.GetBlockCount()); - for (unsigned int i = 0; i < sharedBlocks; ++i) + std::size_t sharedBlocks = std::min(GetBlockCount(), bitset.GetBlockCount()); + for (std::size_t i = 0; i < sharedBlocks; ++i) { Block a = GetBlock(i); Block b = bitset.GetBlock(i); @@ -404,7 +404,7 @@ namespace Nz */ template - void Bitset::Reserve(unsigned int bitCount) + void Bitset::Reserve(std::size_t bitCount) { m_blocks.reserve(ComputeBlockCount(bitCount)); } @@ -417,13 +417,13 @@ namespace Nz */ template - void Bitset::Resize(unsigned int bitCount, bool defaultVal) + void Bitset::Resize(std::size_t bitCount, bool defaultVal) { // We begin with changing the size of container, with the correct value of initialisation - unsigned int lastBlockIndex = m_blocks.size() - 1; + std::size_t lastBlockIndex = m_blocks.size() - 1; m_blocks.resize(ComputeBlockCount(bitCount), (defaultVal) ? fullBitMask : 0U); - unsigned int remainingBits = GetBitIndex(m_bitCount); + std::size_t remainingBits = GetBitIndex(m_bitCount); if (bitCount > m_bitCount && remainingBits > 0 && defaultVal) // Initialisation of unused bits in the last block before the size change m_blocks[lastBlockIndex] |= fullBitMask << remainingBits; @@ -451,7 +451,7 @@ namespace Nz */ template - void Bitset::Reset(unsigned int bit) + void Bitset::Reset(std::size_t bit) { Set(bit, false); } @@ -482,7 +482,7 @@ namespace Nz */ template - void Bitset::Set(unsigned int bit, bool val) + void Bitset::Set(std::size_t bit, bool val) { NazaraAssert(bit < m_bitCount, "Bit index out of range"); @@ -503,7 +503,7 @@ namespace Nz * \remark Produce a NazaraAssert if i is greather than number of blocks in bitset */ template - void Bitset::SetBlock(unsigned int i, Block block) + void Bitset::SetBlock(std::size_t i, Block block) { NazaraAssert(i < m_blocks.size(), "Block index out of range"); @@ -537,7 +537,7 @@ namespace Nz */ template - bool Bitset::Test(unsigned int bit) const + bool Bitset::Test(std::size_t bit) const { NazaraAssert(bit < m_bitCount, "Bit index out of range"); @@ -555,7 +555,7 @@ namespace Nz // Special case for the last block Block lastBlockMask = GetLastBlockMask(); - for (unsigned int i = 0; i < m_blocks.size(); ++i) + for (std::size_t i = 0; i < m_blocks.size(); ++i) { Block mask = (i == m_blocks.size() - 1) ? lastBlockMask : fullBitMask; if (m_blocks[i] == mask) // The extra bits are set to zero, thus we can't test without proceeding with a mask @@ -576,7 +576,7 @@ namespace Nz if (m_blocks.empty()) return false; - for (unsigned int i = 0; i < m_blocks.size(); ++i) + for (std::size_t i = 0; i < m_blocks.size(); ++i) { if (m_blocks[i]) return true; @@ -612,7 +612,7 @@ namespace Nz NazaraAssert(m_bitCount <= std::numeric_limits::digits, "Bit count cannot be greater than T bit count"); T value = 0; - for (unsigned int i = 0; i < m_blocks.size(); ++i) + for (std::size_t i = 0; i < m_blocks.size(); ++i) value |= static_cast(m_blocks[i]) << i*bitsPerBlock; return value; @@ -628,7 +628,7 @@ namespace Nz { String str(m_bitCount, '0'); - for (unsigned int i = 0; i < m_bitCount; ++i) + for (std::size_t i = 0; i < m_bitCount; ++i) { if (Test(i)) str[m_bitCount - i - 1] = '1'; // Inversion de l'indice @@ -648,7 +648,7 @@ namespace Nz */ template - void Bitset::UnboundedReset(unsigned int bit) + void Bitset::UnboundedReset(std::size_t bit) { UnboundedSet(bit, false); } @@ -665,7 +665,7 @@ namespace Nz */ template - void Bitset::UnboundedSet(unsigned int bit, bool val) + void Bitset::UnboundedSet(std::size_t bit, bool val) { if (bit < m_bitCount) Set(bit, val); @@ -687,7 +687,7 @@ namespace Nz */ template - bool Bitset::UnboundedTest(unsigned int bit) const + bool Bitset::UnboundedTest(std::size_t bit) const { if (bit < m_bitCount) return Test(bit); @@ -816,13 +816,13 @@ namespace Nz */ template - unsigned int Bitset::FindFirstFrom(unsigned int blockIndex) const + std::size_t Bitset::FindFirstFrom(std::size_t blockIndex) const { if (blockIndex >= m_blocks.size()) return npos; // We are looking for the first non-null block - unsigned int i = blockIndex; + std::size_t i = blockIndex; for (; i < m_blocks.size(); ++i) { if (m_blocks[i]) @@ -868,7 +868,7 @@ namespace Nz */ template - unsigned int Bitset::ComputeBlockCount(unsigned int bitCount) + std::size_t Bitset::ComputeBlockCount(std::size_t bitCount) { return GetBlockIndex(bitCount) + ((GetBitIndex(bitCount) != 0U) ? 1U : 0U); } @@ -879,7 +879,7 @@ namespace Nz */ template - unsigned int Bitset::GetBitIndex(unsigned int bit) + std::size_t Bitset::GetBitIndex(std::size_t bit) { return bit & (bitsPerBlock - 1U); // bit % bitsPerBlock } @@ -890,7 +890,7 @@ namespace Nz */ template - unsigned int Bitset::GetBlockIndex(unsigned int bit) + std::size_t Bitset::GetBlockIndex(std::size_t bit) { return bit / bitsPerBlock; } @@ -1106,18 +1106,18 @@ namespace Nz const Bitset& greater = (lhs.GetBlockCount() > rhs.GetBlockCount()) ? lhs : rhs; const Bitset& lesser = (lhs.GetBlockCount() > rhs.GetBlockCount()) ? rhs : lhs; - unsigned int maxBlockCount = greater.GetBlockCount(); - unsigned int minBlockCount = lesser.GetBlockCount(); + std::size_t maxBlockCount = greater.GetBlockCount(); + std::size_t minBlockCount = lesser.GetBlockCount(); // We test the blocks in common to check the equality of bits - for (unsigned int i = 0; i < minBlockCount; ++i) + for (std::size_t i = 0; i < minBlockCount; ++i) { if (lhs.GetBlock(i) != rhs.GetBlock(i)) return false; } // Now we check for the blocks that only the biggest bitset owns, and to be equal, they must be set to '0' - for (unsigned int i = minBlockCount; i < maxBlockCount; ++i) + for (std::size_t i = minBlockCount; i < maxBlockCount; ++i) if (greater.GetBlock(i)) return false; @@ -1152,20 +1152,20 @@ namespace Nz const Bitset& greater = (lhs.GetBlockCount() > rhs.GetBlockCount()) ? lhs : rhs; const Bitset& lesser = (lhs.GetBlockCount() > rhs.GetBlockCount()) ? rhs : lhs; - unsigned int maxBlockCount = greater.GetBlockCount(); - unsigned int minBlockCount = lesser.GetBlockCount(); + std::size_t maxBlockCount = greater.GetBlockCount(); + std::size_t minBlockCount = lesser.GetBlockCount(); // If the greatest bitset has a single bit active in a block outside the lesser bitset range, then it is greater - for (unsigned int i = maxBlockCount; i > minBlockCount; ++i) + for (std::size_t i = maxBlockCount; i > minBlockCount; ++i) { if (greater.GetBlock(i)) return lhs.GetBlockCount() < rhs.GetBlockCount(); } // Compare the common blocks - for (unsigned int i = 0; i < minBlockCount; ++i) + for (std::size_t i = 0; i < minBlockCount; ++i) { - unsigned int index = (minBlockCount - i - 1); // Compare from the most significant block to the less significant block + std::size_t index = (minBlockCount - i - 1); // Compare from the most significant block to the less significant block if (lhs.GetBlock(index) < rhs.GetBlock(index)) return true; } diff --git a/include/Nazara/Core/ByteArray.inl b/include/Nazara/Core/ByteArray.inl index 1ff9bf185..068bee4b6 100644 --- a/include/Nazara/Core/ByteArray.inl +++ b/include/Nazara/Core/ByteArray.inl @@ -23,10 +23,10 @@ namespace Nz /*! * \brief Constructs a ByteArray object with a raw memory and a size * - * \param ptr Pointer to raw memory + * \param buffer Pointer to raw memory * \param n Size that can be accessed * - * \remark If preallocated space of ptr is less than the size, the behaviour is undefined + * \remark If preallocated space of buffer is less than the size, the behaviour is undefined */ inline ByteArray::ByteArray(const void* buffer, size_type n) : @@ -62,10 +62,10 @@ namespace Nz /*! * \brief Appends the content of raw memory * - * \param ptr Constant pointer to raw memory + * \param buffer Constant pointer to raw memory * \param n Size that can be read * - * \remark If preallocated space of ptr is less than the size, the behaviour is undefined + * \remark If preallocated space of buffer is less than the size, the behaviour is undefined * * \see Insert */ @@ -298,11 +298,11 @@ namespace Nz } /*! - * \brief Inserts n times the same value at the iterator position + * \brief Inserts n times the same byte at the iterator position * * \param pos Iterator to the position * \param n Number of repetitions - * \param value Value to repeat + * \param byte Value to repeat */ inline ByteArray::iterator ByteArray::Insert(const_iterator pos, size_type n, value_type byte) @@ -359,10 +359,10 @@ namespace Nz /*! * \brief Prepends the content of raw memory * - * \param ptr Constant pointer to raw memory + * \param buffer Constant pointer to raw memory * \param n Size that can be read * - * \remark If preallocated space of ptr is less than the size, the behaviour is undefined + * \remark If preallocated space of buffer is less than the size, the behaviour is undefined * * \see Insert */ @@ -682,8 +682,7 @@ namespace Nz * \brief Checks whether the first byte array is equal to the second byte array * \return true if it is the case * - * \param first ByteArray to compare in left hand side - * \param second ByteArray to compare in right hand side + * \param rhs ByteArray to compare with */ inline bool ByteArray::operator==(const ByteArray& rhs) const @@ -695,8 +694,7 @@ namespace Nz * \brief Checks whether the first byte array is equal to the second byte array * \return false if it is the case * - * \param first ByteArray to compare in left hand side - * \param second ByteArray to compare in right hand side + * \param rhs ByteArray to compare with */ inline bool ByteArray::operator!=(const ByteArray& rhs) const @@ -708,8 +706,7 @@ namespace Nz * \brief Checks whether the first byte array is less than the second byte array * \return true if it is the case * - * \param first ByteArray to compare in left hand side - * \param second ByteArray to compare in right hand side + * \param rhs ByteArray to compare with */ inline bool ByteArray::operator<(const ByteArray& rhs) const @@ -721,8 +718,7 @@ namespace Nz * \brief Checks whether the first byte array is less or equal than the second byte array * \return true if it is the case * - * \param first ByteArray to compare in left hand side - * \param second ByteArray to compare in right hand side + * \param rhs ByteArray to compare with */ inline bool ByteArray::operator<=(const ByteArray& rhs) const @@ -731,11 +727,10 @@ namespace Nz } /*! - * \brief Checks whether the first byte array is greather than the second byte array + * \brief Checks whether the first byte array is greater than the second byte array * \return true if it is the case * - * \param first ByteArray to compare in left hand side - * \param second ByteArray to compare in right hand side + * \param rhs ByteArray to compare with */ inline bool ByteArray::operator>(const ByteArray& rhs) const @@ -744,11 +739,10 @@ namespace Nz } /*! - * \brief Checks whether the first byte array is greather or equal than the second byte array + * \brief Checks whether the first byte array is greater or equal than the second byte array * \return true if it is the case * - * \param first ByteArray to compare in left hand side - * \param second ByteArray to compare in right hand side + * \param rhs ByteArray to compare with */ inline bool ByteArray::operator>=(const ByteArray& rhs) const diff --git a/include/Nazara/Core/Color.hpp b/include/Nazara/Core/Color.hpp index 91a8cc43d..5fef4f854 100644 --- a/include/Nazara/Core/Color.hpp +++ b/include/Nazara/Core/Color.hpp @@ -25,6 +25,8 @@ namespace Nz inline Color(const Color& color) = default; inline ~Color() = default; + inline bool IsOpaque() const; + inline String ToString() const; inline Color operator+(const Color& angles) const; diff --git a/include/Nazara/Core/Color.inl b/include/Nazara/Core/Color.inl index 4ac3a2246..5d71393df 100644 --- a/include/Nazara/Core/Color.inl +++ b/include/Nazara/Core/Color.inl @@ -72,6 +72,15 @@ namespace Nz { } + /*! + * \brief Return true is the color has no degree of transparency + * \return true if the color has an alpha value of 255 + */ + inline bool Color::IsOpaque() const + { + return a == 255; + } + /*! * \brief Converts this to string * \return String representation of the object "Color(r, g, b[, a])" diff --git a/include/Nazara/Core/ConfigCheck.hpp b/include/Nazara/Core/ConfigCheck.hpp index 8329f4ff0..aa1ac5cfa 100644 --- a/include/Nazara/Core/ConfigCheck.hpp +++ b/include/Nazara/Core/ConfigCheck.hpp @@ -12,7 +12,7 @@ #include #define NazaraCheckTypeAndVal(name, type, op, val, err) static_assert(std::is_ ##type ::value && name op val, #type err) -// We fore the value of MANAGE_MEMORY in debug +// We force the value of MANAGE_MEMORY in debug #if defined(NAZARA_DEBUG) && !NAZARA_CORE_MANAGE_MEMORY #undef NAZARA_CORE_MANAGE_MEMORY #define NAZARA_CORE_MANAGE_MEMORY 0 diff --git a/include/Nazara/Core/Endianness.inl b/include/Nazara/Core/Endianness.inl index ee0d094ca..67df87c45 100644 --- a/include/Nazara/Core/Endianness.inl +++ b/include/Nazara/Core/Endianness.inl @@ -33,8 +33,8 @@ namespace Nz inline void SwapBytes(void* buffer, std::size_t size) { UInt8* bytes = static_cast(buffer); - unsigned int i = 0; - unsigned int j = size - 1; + std::size_t i = 0; + std::size_t j = size - 1; while (i < j) std::swap(bytes[i++], bytes[j--]); diff --git a/include/Nazara/Core/HandledObject.inl b/include/Nazara/Core/HandledObject.inl index 738a5f7a5..6b4d64c45 100644 --- a/include/Nazara/Core/HandledObject.inl +++ b/include/Nazara/Core/HandledObject.inl @@ -10,12 +10,29 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::HandledObject + * \brief Core class that represents a handled object + */ + + /*! + * \brief Constructs a HandledObject object by assignation + * + * \param object HandledObject to assign into this + */ template HandledObject::HandledObject(const HandledObject& object) { + NazaraUnused(object); // Don't copy anything, we're a copy of the object, we have no handle right now } + /*! + * \brief Constructs a HandledObject object by move semantic + * + * \param object HandledObject to move into this + */ template HandledObject::HandledObject(HandledObject&& object) : m_handles(std::move(object.m_handles)) @@ -24,18 +41,33 @@ namespace Nz handle->OnObjectMoved(static_cast(this)); } + /*! + * \brief Destructs the object and calls UnregisterAllHandles + * + * \see UnregisterAllHandles + */ template HandledObject::~HandledObject() { UnregisterAllHandles(); } + /*! + * \brief Creates a ObjectHandle for this + * \return ObjectHandle to this + */ template ObjectHandle HandledObject::CreateHandle() { return ObjectHandle(static_cast(this)); } + /*! + * \brief Sets the reference of the HandledObject with the handle from another + * \return A reference to this + * + * \param object The other HandledObject + */ template HandledObject& HandledObject::operator=(const HandledObject& object) { @@ -43,6 +75,12 @@ namespace Nz return *this; } + /*! + * \brief Moves the HandledObject into this + * \return A reference to this + * + * \param object HandledObject to move in this + */ template HandledObject& HandledObject::operator=(HandledObject&& object) { @@ -53,13 +91,22 @@ namespace Nz return *this; } + /*! + * \brief Registers a handle + * + * \param handle Handle to register + * + * \remark One handle can only be registered once, errors can occur if it's more than once + */ template void HandledObject::RegisterHandle(ObjectHandle* handle) { - ///DOC: Un handle ne doit être enregistré qu'une fois, des erreurs se produisent s'il l'est plus d'une fois m_handles.push_back(handle); } + /*! + * \brief Unregisters all handles + */ template void HandledObject::UnregisterAllHandles() { @@ -70,10 +117,17 @@ namespace Nz m_handles.clear(); } + /*! + * \brief Unregisters a handle + * + * \param handle Handle to unregister + * + * \remark One handle can only be unregistered once, crash can occur if it's more than once + * \remark Produces a NazaraAssert if handle not registered + */ template void HandledObject::UnregisterHandle(ObjectHandle* handle) noexcept { - ///DOC: Un handle ne doit être libéré qu'une fois, et doit faire partie de la liste, sous peine de crash auto it = std::find(m_handles.begin(), m_handles.end(), handle); NazaraAssert(it != m_handles.end(), "Handle not registered"); @@ -82,6 +136,14 @@ namespace Nz m_handles.pop_back(); } + /*! + * \brief Updates one handle with another + * + * \param oldHandle Old handle to replace + * \param newHandle New handle to take place + * + * \remark Produces a NazaraAssert if handle not registered + */ template void HandledObject::UpdateHandle(ObjectHandle* oldHandle, ObjectHandle* newHandle) noexcept { diff --git a/include/Nazara/Core/ObjectHandle.inl b/include/Nazara/Core/ObjectHandle.inl index cfcd6bc0d..d5badc964 100644 --- a/include/Nazara/Core/ObjectHandle.inl +++ b/include/Nazara/Core/ObjectHandle.inl @@ -9,12 +9,26 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::ObjectHandle + * \brief Core class that represents a object handle + */ + + /*! + * \brief Constructs a ObjectHandle object by default + */ template ObjectHandle::ObjectHandle() : m_object(nullptr) { } + /*! + * \brief Constructs a ObjectHandle object with a pointer to an object + * + * \param object Pointer to handle like an object (can be nullptr) + */ template ObjectHandle::ObjectHandle(T* object) : ObjectHandle() @@ -22,59 +36,97 @@ namespace Nz Reset(object); } + /*! + * \brief Constructs a ObjectHandle object by assignation + * + * \param handle ObjectHandle to assign into this + */ template - ObjectHandle::ObjectHandle(const ObjectHandle& handle) : + ObjectHandle::ObjectHandle(const ObjectHandle& handle) : ObjectHandle() { Reset(handle); } + /*! + * \brief Constructs a ObjectHandle object by move semantic + * + * \param handle ObjectHandle to move into this + */ template - ObjectHandle::ObjectHandle(ObjectHandle&& handle) noexcept : + ObjectHandle::ObjectHandle(ObjectHandle&& handle) noexcept : ObjectHandle() { Reset(std::move(handle)); } + /*! + * \brief Destructs the object and calls reset with nullptr + * + * \see Reset + */ template ObjectHandle::~ObjectHandle() { Reset(nullptr); } + /*! + * \brief Gets the underlying object + * \return Underlying object + */ template T* ObjectHandle::GetObject() const { return m_object; } + /*! + * \brief Checks whether the object is valid + * \return true if object is not nullptr + */ template bool ObjectHandle::IsValid() const { return m_object != nullptr; } + /*! + * \brief Resets the content of the ObjectHandle with another object + * + * \param object Object to handle + */ template void ObjectHandle::Reset(T* object) { - // Si nous avions déjà une entité, nous devons l'informer que nous ne pointons plus sur elle + // If we already have an entity, we must alert it that we are not pointing to it anymore if (m_object) m_object->UnregisterHandle(this); m_object = object; if (m_object) - // On informe la nouvelle entité que nous pointons sur elle + // We alert the new entity that we are pointing to it m_object->RegisterHandle(this); } + /*! + * \brief Resets the content of this with another object + * + * \param handle New object to handle + */ template - void ObjectHandle::Reset(const ObjectHandle& handle) + void ObjectHandle::Reset(const ObjectHandle& handle) { Reset(handle.GetObject()); } + /*! + * \brief Resets the content of this with another object by move semantic + * + * \param handle New object to handle to move into this + */ template - void ObjectHandle::Reset(ObjectHandle&& handle) noexcept + void ObjectHandle::Reset(ObjectHandle&& handle) noexcept { if (m_object) m_object->UnregisterHandle(this); @@ -87,12 +139,18 @@ namespace Nz } } + /*! + * \brief Swaps the content of the two ObjectHandle + * \return A reference to this + * + * \param handle ObjectHandle to swap + */ template - ObjectHandle& ObjectHandle::Swap(ObjectHandle& handle) + ObjectHandle& ObjectHandle::Swap(ObjectHandle& handle) { - // Comme nous inversons les handles, nous devons prévenir les entités - // La version par défaut de swap (à base de move) aurait fonctionné, - // mais en enregistrant les handles une fois de plus que nécessaire (à cause de la copie temporaire). + // As we swap the two handles, we must alert the entities + // The default version with swap (move) would be working, + // but will register handles one more time (due to temporary copy). if (m_object) { m_object->UnregisterHandle(this); @@ -105,11 +163,15 @@ namespace Nz handle.m_object->RegisterHandle(this); } - // On effectue l'échange + // We do the swap std::swap(m_object, handle.m_object); return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object "ObjectHandle(object representation) or Null" + */ template Nz::String ObjectHandle::ToString() const { @@ -125,24 +187,44 @@ namespace Nz return ss; } + /*! + * \brief Converts the ObjectHandle to bool + * \return true if reference is not nullptr + * + * \see IsValid + */ template ObjectHandle::operator bool() const { return IsValid(); } + /*! + * \brief Dereferences the ObjectHandle + * \return Underlying pointer + */ template ObjectHandle::operator T*() const { return m_object; } + /*! + * \brief Dereferences the ObjectHandle + * \return Underlying pointer + */ template T* ObjectHandle::operator->() const { return m_object; } + /*! + * \brief Assigns the entity into this + * \return A reference to this + * + * \param entity Pointer to handle like an object (can be nullptr) + */ template ObjectHandle& ObjectHandle::operator=(T* entity) { @@ -151,22 +233,37 @@ namespace Nz return *this; } + /*! + * \brief Sets the handle of the ObjectHandle with the handle from another + * \return A reference to this + * + * \param handle The other ObjectHandle + */ template - ObjectHandle& ObjectHandle::operator=(const ObjectHandle& handle) + ObjectHandle& ObjectHandle::operator=(const ObjectHandle& handle) { Reset(handle); return *this; } + /*! + * \brief Moves the ObjectHandle into this + * \return A reference to this + * + * \param handle ObjectHandle to move in this + */ template - ObjectHandle& ObjectHandle::operator=(ObjectHandle&& handle) noexcept + ObjectHandle& ObjectHandle::operator=(ObjectHandle&& handle) noexcept { Reset(std::move(handle)); return *this; } + /*! + * \brief Action to do on object destruction + */ template void ObjectHandle::OnObjectDestroyed() { @@ -174,6 +271,9 @@ namespace Nz m_object = nullptr; } + /*! + * \brief Action to do on object move + */ template void ObjectHandle::OnObjectMoved(T* newObject) { @@ -181,114 +281,248 @@ namespace Nz m_object = newObject; } + /*! + * \brief Output operator + * \return The stream + * + * \param out The stream + * \param handle The ObjectHandle to output + */ template std::ostream& operator<<(std::ostream& out, const ObjectHandle& handle) { - return handle.ToString(); + out << handle.ToString(); + return out; } + /*! + * \brief Checks whether the first object handle is equal to the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator==(const ObjectHandle& lhs, const ObjectHandle& rhs) { return lhs.GetObject() == rhs.GetObject(); } + /*! + * \brief Checks whether the object is equal to the second object handle + * \return true if it is the case + * + * \param lhs Object to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator==(const T& lhs, const ObjectHandle& rhs) { return &lhs == rhs.GetObject(); } + /*! + * \brief Checks whether the object handle is equal to the second object + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs Object to compare in right hand side + */ template bool operator==(const ObjectHandle& lhs, const T& rhs) { return lhs.GetObject() == &rhs; } + /*! + * \brief Checks whether the first object handle is equal to the second object handle + * \return false if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator!=(const ObjectHandle& lhs, const ObjectHandle& rhs) { return !(lhs == rhs); } + /*! + * \brief Checks whether the object is equal to the second object handle + * \return false if it is the case + * + * \param lhs Object to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator!=(const T& lhs, const ObjectHandle& rhs) { return !(lhs == rhs); } + /*! + * \brief Checks whether the object handle is equal to the second object + * \return false if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs Object to compare in right hand side + */ template bool operator!=(const ObjectHandle& lhs, const T& rhs) { return !(lhs == rhs); } + /*! + * \brief Checks whether the first object handle is less than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator<(const ObjectHandle& lhs, const ObjectHandle& rhs) { return lhs.m_object < rhs.m_object; } + /*! + * \brief Checks whether the first object handle is less than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator<(const T& lhs, const ObjectHandle& rhs) { return &lhs < rhs.m_object; } + /*! + * \brief Checks whether the first object handle is less than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator<(const ObjectHandle& lhs, const T& rhs) { return lhs.m_object < &rhs; } + /*! + * \brief Checks whether the first object handle is less or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator<=(const ObjectHandle& lhs, const ObjectHandle& rhs) { return !(lhs > rhs); } + /*! + * \brief Checks whether the first object handle is less or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator<=(const T& lhs, const ObjectHandle& rhs) { return !(lhs > rhs); } + /*! + * \brief Checks whether the first object handle is less or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator<=(const ObjectHandle& lhs, const T& rhs) { return !(lhs > rhs); } + /*! + * \brief Checks whether the first object handle is greather than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator>(const ObjectHandle& lhs, const ObjectHandle& rhs) { return rhs < lhs; } + /*! + * \brief Checks whether the first object handle is greather than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator>(const T& lhs, const ObjectHandle& rhs) { return rhs < lhs; } + /*! + * \brief Checks whether the first object handle is greather than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator>(const ObjectHandle& lhs, const T& rhs) { return rhs < lhs; } + /*! + * \brief Checks whether the first object handle is greather or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator>=(const ObjectHandle& lhs, const ObjectHandle& rhs) { return !(lhs < rhs); } + /*! + * \brief Checks whether the first object handle is greather or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator>=(const T& lhs, const ObjectHandle& rhs) { return !(lhs < rhs); } + /*! + * \brief Checks whether the first object handle is greather or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectHandle to compare in left hand side + * \param rhs ObjectHandle to compare in right hand side + */ template bool operator>=(const ObjectHandle& lhs, const T& rhs) { @@ -301,6 +535,12 @@ namespace Nz namespace std { + /*! + * \brief Swaps two ObjectHandle, specialisation of std + * + * \param lhs First object handle + * \param rhs Second object handle + */ template void swap(Nz::ObjectHandle& lhs, Nz::ObjectHandle& rhs) { diff --git a/include/Nazara/Core/ObjectRef.hpp b/include/Nazara/Core/ObjectRef.hpp index 8debb7d91..d1cd13ec0 100644 --- a/include/Nazara/Core/ObjectRef.hpp +++ b/include/Nazara/Core/ObjectRef.hpp @@ -17,8 +17,6 @@ namespace Nz template class ObjectRef { - static_assert(std::is_base_of::value, "ObjectRef shall only be used with RefCounted-derived type"); - public: ObjectRef(); ObjectRef(T* object); @@ -46,8 +44,33 @@ namespace Nz T* m_object; }; - template struct PointedType> {typedef T type;}; - template struct PointedType const> {typedef T type;}; + template bool operator==(const ObjectRef& lhs, const ObjectRef& rhs); + template bool operator==(const T& lhs, const ObjectRef& rhs); + template bool operator==(const ObjectRef& lhs, const T& rhs); + + template bool operator!=(const ObjectRef& lhs, const ObjectRef& rhs); + template bool operator!=(const T& lhs, const ObjectRef& rhs); + template bool operator!=(const ObjectRef& lhs, const T& rhs); + + template bool operator<(const ObjectRef& lhs, const ObjectRef& rhs); + template bool operator<(const T& lhs, const ObjectRef& rhs); + template bool operator<(const ObjectRef& lhs, const T& rhs); + + template bool operator<=(const ObjectRef, const ObjectRef& rhs); + template bool operator<=(const T& lhs, const ObjectRef& rhs); + template bool operator<=(const ObjectRef& lhs, const T& rhs); + + template bool operator>(const ObjectRef& lhs, const ObjectRef& rhs); + template bool operator>(const T& lhs, const ObjectRef& rhs); + template bool operator>(const ObjectRef& lhs, const T& rhs); + + template bool operator>=(const ObjectRef& lhs, const ObjectRef& rhs); + template bool operator>=(const T& lhs, const ObjectRef& rhs); + template bool operator>=(const ObjectRef& lhs, const T& rhs); + + + template struct PointedType> { typedef T type; }; + template struct PointedType const> { typedef T type; }; } #include diff --git a/include/Nazara/Core/ObjectRef.inl b/include/Nazara/Core/ObjectRef.inl index ad74b4f2e..85baff8bf 100644 --- a/include/Nazara/Core/ObjectRef.inl +++ b/include/Nazara/Core/ObjectRef.inl @@ -184,7 +184,7 @@ namespace Nz { return m_object; } - + /*! * \brief Assigns the object into this * \return A reference to this @@ -245,6 +245,241 @@ namespace Nz return *this; } + + + /*! + * \brief Checks whether the first object handle is equal to the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator==(const ObjectRef& lhs, const ObjectRef& rhs) + { + return lhs.Get() == rhs.Get(); + } + + /*! + * \brief Checks whether the object is equal to the second object handle + * \return true if it is the case + * + * \param lhs Object to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator==(const T& lhs, const ObjectRef& rhs) + { + return &lhs == rhs.Get(); + } + + /*! + * \brief Checks whether the object handle is equal to the second object + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs Object to compare in right hand side + */ + template + bool operator==(const ObjectRef& lhs, const T& rhs) + { + return lhs.Get() == &rhs; + } + + /*! + * \brief Checks whether the first object handle is equal to the second object handle + * \return false if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator!=(const ObjectRef& lhs, const ObjectRef& rhs) + { + return !(lhs == rhs); + } + + /*! + * \brief Checks whether the object is equal to the second object handle + * \return false if it is the case + * + * \param lhs Object to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator!=(const T& lhs, const ObjectRef& rhs) + { + return !(lhs == rhs); + } + + /*! + * \brief Checks whether the object handle is equal to the second object + * \return false if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs Object to compare in right hand side + */ + template + bool operator!=(const ObjectRef& lhs, const T& rhs) + { + return !(lhs == rhs); + } + + /*! + * \brief Checks whether the first object handle is less than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator<(const ObjectRef& lhs, const ObjectRef& rhs) + { + return lhs.m_object < rhs.m_object; + } + + /*! + * \brief Checks whether the first object handle is less than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator<(const T& lhs, const ObjectRef& rhs) + { + return &lhs < rhs.m_object; + } + + /*! + * \brief Checks whether the first object handle is less than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator<(const ObjectRef& lhs, const T& rhs) + { + return lhs.m_object < &rhs; + } + + /*! + * \brief Checks whether the first object handle is less or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator<=(const ObjectRef& lhs, const ObjectRef& rhs) + { + return !(lhs > rhs); + } + + /*! + * \brief Checks whether the first object handle is less or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator<=(const T& lhs, const ObjectRef& rhs) + { + return !(lhs > rhs); + } + + /*! + * \brief Checks whether the first object handle is less or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator<=(const ObjectRef& lhs, const T& rhs) + { + return !(lhs > rhs); + } + + /*! + * \brief Checks whether the first object handle is greather than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator>(const ObjectRef& lhs, const ObjectRef& rhs) + { + return rhs < lhs; + } + + /*! + * \brief Checks whether the first object handle is greather than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator>(const T& lhs, const ObjectRef& rhs) + { + return rhs < lhs; + } + + /*! + * \brief Checks whether the first object handle is greather than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator>(const ObjectRef& lhs, const T& rhs) + { + return rhs < lhs; + } + + /*! + * \brief Checks whether the first object handle is greather or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator>=(const ObjectRef& lhs, const ObjectRef& rhs) + { + return !(lhs < rhs); + } + + /*! + * \brief Checks whether the first object handle is greather or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator>=(const T& lhs, const ObjectRef& rhs) + { + return !(lhs < rhs); + } + + /*! + * \brief Checks whether the first object handle is greather or equal than the second object handle + * \return true if it is the case + * + * \param lhs ObjectRef to compare in left hand side + * \param rhs ObjectRef to compare in right hand side + */ + template + bool operator>=(const ObjectRef& lhs, const T& rhs) + { + return !(lhs < rhs); + } } namespace std diff --git a/include/Nazara/Core/ParameterList.hpp b/include/Nazara/Core/ParameterList.hpp index 119d05cca..751746aea 100644 --- a/include/Nazara/Core/ParameterList.hpp +++ b/include/Nazara/Core/ParameterList.hpp @@ -50,6 +50,8 @@ namespace Nz void SetParameter(const String& name, void* value); void SetParameter(const String& name, void* value, Destructor destructor); + String ToString() const; + ParameterList& operator=(const ParameterList& list); ParameterList& operator=(ParameterList&&) = default; @@ -73,7 +75,7 @@ namespace Nz ParameterType type; union Value { - // On définit un constructeur/destructeur vide, permettant de mettre des classes dans l'union + // We define an empty constructor/destructor, to be able to put classes in the union Value() {} Value(const Value&) {} // Placeholder ~Value() {} @@ -98,4 +100,6 @@ namespace Nz }; } +std::ostream& operator<<(std::ostream& out, const Nz::ParameterList& parameterList); + #endif // NAZARA_PARAMETERLIST_HPP diff --git a/include/Nazara/Core/ResourceLoader.hpp b/include/Nazara/Core/ResourceLoader.hpp index b0640dcbc..8af3e3cd3 100644 --- a/include/Nazara/Core/ResourceLoader.hpp +++ b/include/Nazara/Core/ResourceLoader.hpp @@ -39,7 +39,7 @@ namespace Nz static bool IsExtensionSupported(const String& extension); static bool LoadFromFile(Type* resource, const String& filePath, const Parameters& parameters = Parameters()); - static bool LoadFromMemory(Type* resource, const void* data, unsigned int size, const Parameters& parameters = Parameters()); + static bool LoadFromMemory(Type* resource, const void* data, std::size_t size, const Parameters& parameters = Parameters()); static bool LoadFromStream(Type* resource, Stream& stream, const Parameters& parameters = Parameters()); static void RegisterLoader(ExtensionGetter extensionGetter, StreamChecker checkFunc, StreamLoader streamLoader, FileLoader fileLoader = nullptr, MemoryLoader memoryLoader = nullptr); diff --git a/include/Nazara/Core/ResourceLoader.inl b/include/Nazara/Core/ResourceLoader.inl index af0a8a7f0..2ac72c6df 100644 --- a/include/Nazara/Core/ResourceLoader.inl +++ b/include/Nazara/Core/ResourceLoader.inl @@ -66,7 +66,7 @@ namespace Nz return false; } - File file(path); // Ouvert seulement en cas de besoin + File file(path); // Open only if needed bool found = false; for (Loader& loader : Type::s_loaders) @@ -160,7 +160,7 @@ namespace Nz * \remark Produces a NazaraError if all loaders failed or no loader was found */ template - bool ResourceLoader::LoadFromMemory(Type* resource, const void* data, unsigned int size, const Parameters& parameters) + bool ResourceLoader::LoadFromMemory(Type* resource, const void* data, std::size_t size, const Parameters& parameters) { NazaraAssert(resource, "Invalid resource"); NazaraAssert(data, "Invalid data pointer"); diff --git a/include/Nazara/Core/ResourceSaver.inl b/include/Nazara/Core/ResourceSaver.inl index 11d3f2a44..a22c29d60 100644 --- a/include/Nazara/Core/ResourceSaver.inl +++ b/include/Nazara/Core/ResourceSaver.inl @@ -48,7 +48,7 @@ namespace Nz * \remark The previous file content will be discarded, to prevent this behavior you should use SaveToStream * \remark The file extension will be used as format for the saver ("image.png" => "png", to write a specified format to a user-specified extension you should use SaveToStream * - * \seealso SaveToStream + * \see SaveToStream */ template bool ResourceSaver::SaveToFile(const Type& resource, const String& filePath, const Parameters& parameters) @@ -84,7 +84,7 @@ namespace Nz } else { - if (!file.Open(OpenMode_WriteOnly)) + if (!file.Open(OpenMode_WriteOnly | OpenMode_Truncate)) { NazaraError("Failed to save to file: unable to open \"" + filePath + "\" in write mode"); return false; @@ -114,7 +114,7 @@ namespace Nz * \param format Data format to save the resource to * \param parameters Parameters for the saving * - * \seealso SaveToFile + * \see SaveToFile */ template bool ResourceSaver::SaveToStream(const Type& resource, Stream& stream, const String& format, const Parameters& parameters) diff --git a/include/Nazara/Core/String.inl b/include/Nazara/Core/String.inl index aa4343371..a5e099a3c 100644 --- a/include/Nazara/Core/String.inl +++ b/include/Nazara/Core/String.inl @@ -45,7 +45,7 @@ namespace Nz */ inline String::SharedString::SharedString(std::size_t strSize) : - capacity(strSize), + capacity(strSize), size(strSize), string(new char[strSize + 1]) { @@ -84,16 +84,15 @@ namespace Nz namespace std { - /*! - * \brief Specialisation of std to hash - * \return Result of the hash - * - * \param str String to hash - */ - template<> struct hash { + /*! + * \brief Specialisation of std to hash + * \return Result of the hash + * + * \param str String to hash + */ size_t operator()(const Nz::String& str) const { // Algorithme DJB2 diff --git a/include/Nazara/Core/StringStream.hpp b/include/Nazara/Core/StringStream.hpp index da68e8b27..f727ed35c 100644 --- a/include/Nazara/Core/StringStream.hpp +++ b/include/Nazara/Core/StringStream.hpp @@ -22,6 +22,10 @@ namespace Nz StringStream(const StringStream&) = default; StringStream(StringStream&&) noexcept = default; + void Clear(); + + std::size_t GetBufferSize() const; + String ToString() const; StringStream& operator=(const StringStream&) = default; diff --git a/include/Nazara/Graphics.hpp b/include/Nazara/Graphics.hpp index 465aeb999..0195c123c 100644 --- a/include/Nazara/Graphics.hpp +++ b/include/Nazara/Graphics.hpp @@ -1,4 +1,4 @@ -// This file was automatically generated on 17 Nov 2015 at 13:20:45 +// This file was automatically generated on 20 Jul 2016 at 13:49:17 /* Nazara Engine - Graphics module @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include #include @@ -61,11 +63,12 @@ #include #include #include +#include #include #include #include -#include #include +#include #include #include #include diff --git a/include/Nazara/Graphics/AbstractRenderQueue.hpp b/include/Nazara/Graphics/AbstractRenderQueue.hpp index 6c069e1d9..a245787ab 100644 --- a/include/Nazara/Graphics/AbstractRenderQueue.hpp +++ b/include/Nazara/Graphics/AbstractRenderQueue.hpp @@ -38,7 +38,6 @@ namespace Nz // Je ne suis vraiment pas fan du nombre de surcharges pour AddBillboards, // mais je n'ai pas d'autre solution tout aussi performante pour le moment... - virtual void AddBillboard(int renderOrder, const Material* material, const Vector3f& position, const Vector2f& size, const Vector2f& sinCos = Vector2f(0.f, 1.f), const Color& color = Color::White) = 0; virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) = 0; virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) = 0; virtual void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) = 0; @@ -62,7 +61,9 @@ namespace Nz struct DirectionalLight { Color color; + Matrix4f transformMatrix; Vector3f direction; + Texture* shadowMap; float ambientFactor; float diffuseFactor; }; @@ -71,6 +72,7 @@ namespace Nz { Color color; Vector3f position; + Texture* shadowMap; float ambientFactor; float attenuation; float diffuseFactor; @@ -81,8 +83,10 @@ namespace Nz struct SpotLight { Color color; + Matrix4f transformMatrix; Vector3f direction; Vector3f position; + Texture* shadowMap; float ambientFactor; float attenuation; float diffuseFactor; diff --git a/include/Nazara/Graphics/Billboard.inl b/include/Nazara/Graphics/Billboard.inl index c9760d0d1..45c2ef918 100644 --- a/include/Nazara/Graphics/Billboard.inl +++ b/include/Nazara/Graphics/Billboard.inl @@ -7,6 +7,10 @@ namespace Nz { + /*! + * \brief Constructs a Billboard object by default + */ + inline Billboard::Billboard() { SetColor(Color::White); @@ -15,6 +19,12 @@ namespace Nz SetSize(64.f, 64.f); } + /*! + * \brief Constructs a Billboard object with a reference to a material + * + * \param material Reference to a material + */ + inline Billboard::Billboard(MaterialRef material) { SetColor(Color::White); @@ -23,6 +33,12 @@ namespace Nz SetSize(64.f, 64.f); } + /*! + * \brief Constructs a Billboard object with a pointer to a texture + * + * \param texture Pointer to a texture + */ + inline Billboard::Billboard(Texture* texture) { SetColor(Color::White); @@ -31,6 +47,12 @@ namespace Nz SetTexture(texture, true); } + /*! + * \brief Constructs a Billboard object by assignation + * + * \param billboard Billboard to copy into this + */ + inline Billboard::Billboard(const Billboard& billboard) : InstancedRenderable(billboard), m_color(billboard.m_color), @@ -41,40 +63,76 @@ namespace Nz { } + /*! + * \brief Gets the color of the billboard + * \return Current color + */ + inline const Color& Billboard::GetColor() const { return m_color; } + /*! + * \brief Gets the material of the billboard + * \return Current material + */ + inline const MaterialRef& Billboard::GetMaterial() const { return m_material; } + /*! + * \brief Gets the rotation of the billboard + * \return Current rotation + */ + inline float Billboard::GetRotation() const { return m_rotation; } + /*! + * \brief Gets the size of the billboard + * \return Current size + */ + inline const Vector2f& Billboard::GetSize() const { return m_size; } + /*! + * \brief Sets the color of the billboard + * + * \param color Color for the billboard + */ + inline void Billboard::SetColor(const Color& color) { m_color = color; } + /*! + * \brief Sets the default material of the billboard (just default material) + */ + inline void Billboard::SetDefaultMaterial() { MaterialRef material = Material::New(); - material->Enable(RendererParameter_FaceCulling, true); - material->EnableLighting(false); + material->EnableFaceCulling(true); SetMaterial(std::move(material)); } + /*! + * \brief Sets the material of the billboard + * + * \param material Material for the billboard + * \param resizeBillboard Should billboard be resized to the material size (diffuse map) + */ + inline void Billboard::SetMaterial(MaterialRef material, bool resizeBillboard) { m_material = std::move(material); @@ -86,25 +144,51 @@ namespace Nz } } + /*! + * \brief Sets the rotation of the billboard + * + * \param rotation Rotation for the billboard + */ + inline void Billboard::SetRotation(float rotation) { m_rotation = rotation; m_sinCos.Set(std::sin(m_rotation), std::cos(m_rotation)); } + /*! + * \brief Sets the size of the billboard + * + * \param size Size for the billboard + */ + inline void Billboard::SetSize(const Vector2f& size) { m_size = size; - // On invalide la bounding box + // We invalidate the bounding volume InvalidateBoundingVolume(); } + /*! + * \brief Sets the size of the billboard + * + * \param sizeX Size in X for the billboard + * \param sizeY Size in Y for the billboard + */ + inline void Billboard::SetSize(float sizeX, float sizeY) { SetSize(Vector2f(sizeX, sizeY)); } + /*! + * \brief Sets the texture of the billboard + * + * \param texture Texture for the billboard + * \param resizeBillboard Should billboard be resized to the texture size + */ + inline void Billboard::SetTexture(TextureRef texture, bool resizeBillboard) { if (!m_material) @@ -118,6 +202,13 @@ namespace Nz m_material->SetDiffuseMap(std::move(texture)); } + /*! + * \brief Sets the current billboard with the content of the other one + * \return A reference to this + * + * \param billboard The other Billboard + */ + inline Billboard& Billboard::operator=(const Billboard& billboard) { InstancedRenderable::operator=(billboard); @@ -131,6 +222,13 @@ namespace Nz return *this; } + /*! + * \brief Creates a new billboard from the arguments + * \return A reference to the newly created billboard + * + * \param args Arguments for the billboard + */ + template BillboardRef Billboard::New(Args&&... args) { diff --git a/include/Nazara/Graphics/ColorBackground.inl b/include/Nazara/Graphics/ColorBackground.inl index 21b67d92c..67b1f2558 100644 --- a/include/Nazara/Graphics/ColorBackground.inl +++ b/include/Nazara/Graphics/ColorBackground.inl @@ -7,6 +7,13 @@ namespace Nz { + /*! + * \brief Creates a new color background from the arguments + * \return A reference to the newly created color background + * + * \param args Arguments for the color background + */ + template ColorBackgroundRef ColorBackground::New(Args&&... args) { diff --git a/include/Nazara/Graphics/Config.hpp b/include/Nazara/Graphics/Config.hpp index 77f1669c4..e47c8d3a5 100644 --- a/include/Nazara/Graphics/Config.hpp +++ b/include/Nazara/Graphics/Config.hpp @@ -27,23 +27,28 @@ #ifndef NAZARA_CONFIG_GRAPHICS_HPP #define NAZARA_CONFIG_GRAPHICS_HPP -/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci +/*! +* \defgroup graphics (NazaraGraphics) Graphics module +* Graphics/System module including classes to handle graphical elements... +*/ -// À partir de combien d'instances d'un même mesh/matériau l'instancing doit-il être utilisé ? +/// Each modification of a paramater of the module needs a recompilation of the unit + +// How much instances are need of a same mesh/material to enable instancing ? #define NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT 10 -// Utilise un manager de mémoire pour gérer les allocations dynamiques (détecte les leaks au prix d'allocations/libérations dynamiques plus lentes) +// Use the MemoryManager to manage dynamic allocations (can detect memory leak but allocations/frees are slower) #define NAZARA_GRAPHICS_MANAGE_MEMORY 0 -// Active les tests de sécurité basés sur le code (Conseillé pour le développement) +// Activate the security tests based on the code (Advised for development) #define NAZARA_GRAPHICS_SAFE 1 -/// Chaque modification d'un paramètre ci-dessous implique une modification (souvent mineure) du code +/// Each modification of a parameter following implies a modification (often minor) of the code -// Le nombre maximum de lumières qu'un shader standard supportera +// The maximum number of lights in a standard shader #define NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS 3 -/// Vérification des valeurs et types de certaines constantes +/// Checking the values and types of certain constants #include #if defined(NAZARA_STATIC) diff --git a/include/Nazara/Graphics/ConfigCheck.hpp b/include/Nazara/Graphics/ConfigCheck.hpp index e93c8e0ec..c2bbda886 100644 --- a/include/Nazara/Graphics/ConfigCheck.hpp +++ b/include/Nazara/Graphics/ConfigCheck.hpp @@ -7,12 +7,12 @@ #ifndef NAZARA_CONFIG_CHECK_GRAPHICS_HPP #define NAZARA_CONFIG_CHECK_GRAPHICS_HPP -/// Ce fichier sert à vérifier la valeur des constantes du fichier Config.hpp +/// This file is used to check the constant values defined in Config.hpp #include #define NazaraCheckTypeAndVal(name, type, op, val, err) static_assert(std::is_ ##type ::value && name op val, #type err) -// On force la valeur de MANAGE_MEMORY en mode debug +// We fore the value of MANAGE_MEMORY in debug #if defined(NAZARA_DEBUG) && !NAZARA_GRAPHICS_MANAGE_MEMORY #undef NAZARA_GRAPHICS_MANAGE_MEMORY #define NAZARA_GRAPHICS_MANAGE_MEMORY 0 diff --git a/include/Nazara/Graphics/DebugOff.hpp b/include/Nazara/Graphics/DebugOff.hpp index 38a452aab..c90640fc0 100644 --- a/include/Nazara/Graphics/DebugOff.hpp +++ b/include/Nazara/Graphics/DebugOff.hpp @@ -2,7 +2,7 @@ // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp -// On suppose que Debug.hpp a déjà été inclus, tout comme Config.hpp +// We suppose that Debug.hpp is already included, same goes for Config.hpp #if NAZARA_GRAPHICS_MANAGE_MEMORY #undef delete #undef new diff --git a/include/Nazara/Graphics/DeferredBloomPass.hpp b/include/Nazara/Graphics/DeferredBloomPass.hpp index 603998d84..5c45a2277 100644 --- a/include/Nazara/Graphics/DeferredBloomPass.hpp +++ b/include/Nazara/Graphics/DeferredBloomPass.hpp @@ -29,7 +29,7 @@ namespace Nz float GetBrightThreshold() const; Texture* GetTexture(unsigned int i) const; - bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const; + bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const; bool Resize(const Vector2ui& dimensions); void SetBlurPassCount(unsigned int passCount); diff --git a/include/Nazara/Graphics/DeferredDOFPass.hpp b/include/Nazara/Graphics/DeferredDOFPass.hpp index 4da8824c7..c3d6e4c0f 100644 --- a/include/Nazara/Graphics/DeferredDOFPass.hpp +++ b/include/Nazara/Graphics/DeferredDOFPass.hpp @@ -23,7 +23,7 @@ namespace Nz DeferredDOFPass(); virtual ~DeferredDOFPass(); - bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const; + bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const; bool Resize(const Vector2ui& dimensions); protected: diff --git a/include/Nazara/Graphics/DeferredFXAAPass.hpp b/include/Nazara/Graphics/DeferredFXAAPass.hpp index 34fc46867..1c74ffb1c 100644 --- a/include/Nazara/Graphics/DeferredFXAAPass.hpp +++ b/include/Nazara/Graphics/DeferredFXAAPass.hpp @@ -21,7 +21,7 @@ namespace Nz DeferredFXAAPass(); virtual ~DeferredFXAAPass(); - bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const; + bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const; protected: RenderStates m_states; diff --git a/include/Nazara/Graphics/DeferredFinalPass.hpp b/include/Nazara/Graphics/DeferredFinalPass.hpp index c800d44d1..f6b633925 100644 --- a/include/Nazara/Graphics/DeferredFinalPass.hpp +++ b/include/Nazara/Graphics/DeferredFinalPass.hpp @@ -21,7 +21,7 @@ namespace Nz DeferredFinalPass(); virtual ~DeferredFinalPass(); - bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const; + bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const; protected: RenderStates m_states; diff --git a/include/Nazara/Graphics/DeferredFogPass.hpp b/include/Nazara/Graphics/DeferredFogPass.hpp index 37466305b..babccb0bf 100644 --- a/include/Nazara/Graphics/DeferredFogPass.hpp +++ b/include/Nazara/Graphics/DeferredFogPass.hpp @@ -21,7 +21,7 @@ namespace Nz DeferredFogPass(); virtual ~DeferredFogPass(); - bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const; + bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const; protected: RenderStates m_states; diff --git a/include/Nazara/Graphics/DeferredForwardPass.hpp b/include/Nazara/Graphics/DeferredForwardPass.hpp index 0b1d8aa0a..cd5341d85 100644 --- a/include/Nazara/Graphics/DeferredForwardPass.hpp +++ b/include/Nazara/Graphics/DeferredForwardPass.hpp @@ -21,7 +21,7 @@ namespace Nz virtual ~DeferredForwardPass(); void Initialize(DeferredRenderTechnique* technique); - bool Process(const SceneData& sceneData, unsigned int workTexture, unsigned sceneTexture) const; + bool Process(const SceneData& sceneData, unsigned int workTexture, unsigned int sceneTexture) const; protected: const ForwardRenderTechnique* m_forwardTechnique; diff --git a/include/Nazara/Graphics/DeferredGeometryPass.hpp b/include/Nazara/Graphics/DeferredGeometryPass.hpp index 652060b3d..15437f0d8 100644 --- a/include/Nazara/Graphics/DeferredGeometryPass.hpp +++ b/include/Nazara/Graphics/DeferredGeometryPass.hpp @@ -21,7 +21,7 @@ namespace Nz DeferredGeometryPass(); virtual ~DeferredGeometryPass(); - bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const; + bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const; bool Resize(const Vector2ui& dimensions); protected: diff --git a/include/Nazara/Graphics/DeferredPhongLightingPass.hpp b/include/Nazara/Graphics/DeferredPhongLightingPass.hpp index 40b480217..68eb96264 100644 --- a/include/Nazara/Graphics/DeferredPhongLightingPass.hpp +++ b/include/Nazara/Graphics/DeferredPhongLightingPass.hpp @@ -28,7 +28,7 @@ namespace Nz bool IsLightMeshesDrawingEnabled() const; - bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const; + bool Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const; protected: LightUniforms m_directionalLightUniforms; diff --git a/include/Nazara/Graphics/DeferredRenderPass.hpp b/include/Nazara/Graphics/DeferredRenderPass.hpp index d9ad69919..34a4020a6 100644 --- a/include/Nazara/Graphics/DeferredRenderPass.hpp +++ b/include/Nazara/Graphics/DeferredRenderPass.hpp @@ -38,7 +38,7 @@ namespace Nz bool IsEnabled() const; - virtual bool Process(const SceneData& sceneData, unsigned int workTexture, unsigned sceneTexture) const = 0; + virtual bool Process(const SceneData& sceneData, unsigned int workTexture, unsigned int sceneTexture) const = 0; virtual bool Resize(const Vector2ui& GBufferSize); DeferredRenderPass& operator=(const DeferredRenderPass&) = delete; diff --git a/include/Nazara/Graphics/DeferredRenderQueue.hpp b/include/Nazara/Graphics/DeferredRenderQueue.hpp index fecd507be..922174df6 100644 --- a/include/Nazara/Graphics/DeferredRenderQueue.hpp +++ b/include/Nazara/Graphics/DeferredRenderQueue.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -21,15 +21,12 @@ namespace Nz { - class ForwardRenderQueue; - class NAZARA_GRAPHICS_API DeferredRenderQueue : public AbstractRenderQueue { public: DeferredRenderQueue(ForwardRenderQueue* forwardQueue); ~DeferredRenderQueue() = default; - void AddBillboard(int renderOrder, const Material* material, const Vector3f& position, const Vector2f& size, const Vector2f& sinCos = Vector2f(0.f, 1.f), const Color& color = Color::White) override; void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; @@ -44,11 +41,6 @@ namespace Nz void Clear(bool fully = false) override; - struct MeshDataComparator - { - bool operator()(const MeshData& data1, const MeshData& data2) const; - }; - struct MeshInstanceEntry { NazaraSlot(IndexBuffer, OnIndexBufferRelease, indexBufferReleaseSlot); @@ -57,12 +49,7 @@ namespace Nz std::vector instances; }; - typedef std::map MeshInstanceContainer; - - struct BatchedModelMaterialComparator - { - bool operator()(const Material* mat1, const Material* mat2) const; - }; + typedef std::map MeshInstanceContainer; struct BatchedModelEntry { @@ -70,14 +57,21 @@ namespace Nz MeshInstanceContainer meshMap; bool enabled = false; - bool instancingEnabled = false; }; - typedef std::map ModelBatches; + typedef std::map MeshMaterialBatches; + + struct BatchedMaterialEntry + { + std::size_t maxInstanceCount = 0; + MeshMaterialBatches materialMap; + }; + + typedef std::map MeshPipelineBatches; struct Layer { - ModelBatches opaqueModels; + MeshPipelineBatches opaqueModels; unsigned int clearCount = 0; }; diff --git a/include/Nazara/Graphics/DeferredRenderTechnique.hpp b/include/Nazara/Graphics/DeferredRenderTechnique.hpp index 99d1fc813..e9e357184 100644 --- a/include/Nazara/Graphics/DeferredRenderTechnique.hpp +++ b/include/Nazara/Graphics/DeferredRenderTechnique.hpp @@ -67,7 +67,7 @@ namespace Nz }; std::map>, RenderPassComparator> m_passes; - ForwardRenderTechnique m_forwardTechnique; // Doit être initialisé avant la RenderQueue + ForwardRenderTechnique m_forwardTechnique; // Must be initialized before the RenderQueue DeferredRenderQueue m_renderQueue; mutable RenderBufferRef m_depthStencilBuffer; mutable RenderTexture m_GBufferRTT; diff --git a/include/Nazara/Graphics/DepthRenderQueue.hpp b/include/Nazara/Graphics/DepthRenderQueue.hpp new file mode 100644 index 000000000..81ec921c0 --- /dev/null +++ b/include/Nazara/Graphics/DepthRenderQueue.hpp @@ -0,0 +1,52 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_DEPTHRENDERQUEUE_HPP +#define NAZARA_DEPTHRENDERQUEUE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_GRAPHICS_API DepthRenderQueue : public ForwardRenderQueue + { + public: + DepthRenderQueue(); + ~DepthRenderQueue() = default; + + void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; + void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; + void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) override; + void AddDirectionalLight(const DirectionalLight& light) override; + void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; + void AddPointLight(const PointLight& light) override; + void AddSpotLight(const SpotLight& light) override; + void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) override; + + private: + inline bool IsMaterialSuitable(const Material* material) const; + + MaterialRef m_baseMaterial; + }; +} + +#include + +#endif // NAZARA_DEPTHRENDERQUEUE_HPP diff --git a/include/Nazara/Graphics/DepthRenderQueue.inl b/include/Nazara/Graphics/DepthRenderQueue.inl new file mode 100644 index 000000000..8fbce164b --- /dev/null +++ b/include/Nazara/Graphics/DepthRenderQueue.inl @@ -0,0 +1,25 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include + +namespace Nz +{ + + /*! + * \brief Checks whether the material is suitable to fit in the render queue + * \return true If it is the case + * + * \param material Material to verify + */ + + bool DepthRenderQueue::IsMaterialSuitable(const Material* material) const + { + NazaraAssert(material, "Invalid material"); + + return material->HasDepthMaterial() || (material->IsDepthBufferEnabled() && material->IsDepthWriteEnabled() && material->IsShadowCastingEnabled()); + } +} + +#include diff --git a/include/Nazara/Graphics/DepthRenderTechnique.hpp b/include/Nazara/Graphics/DepthRenderTechnique.hpp new file mode 100644 index 000000000..9d4ba601f --- /dev/null +++ b/include/Nazara/Graphics/DepthRenderTechnique.hpp @@ -0,0 +1,80 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_DEPTHRENDERTECHNIQUE_HPP +#define NAZARA_DEPTHRENDERTECHNIQUE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_GRAPHICS_API DepthRenderTechnique : public AbstractRenderTechnique + { + public: + DepthRenderTechnique(); + ~DepthRenderTechnique() = default; + + void Clear(const SceneData& sceneData) const override; + bool Draw(const SceneData& sceneData) const override; + + AbstractRenderQueue* GetRenderQueue() override; + RenderTechniqueType GetType() const override; + + static bool Initialize(); + static void Uninitialize(); + + private: + struct ShaderUniforms; + + void DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; + void DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; + void DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; + const ShaderUniforms* GetShaderUniforms(const Shader* shader) const; + void OnShaderInvalidated(const Shader* shader) const; + + struct LightIndex + { + LightType type; + float score; + unsigned int index; + }; + + struct ShaderUniforms + { + NazaraSlot(Shader, OnShaderUniformInvalidated, shaderUniformInvalidatedSlot); + NazaraSlot(Shader, OnShaderRelease, shaderReleaseSlot); + + // Autre uniformes + int eyePosition; + int sceneAmbient; + int textureOverlay; + }; + + mutable std::unordered_map m_shaderUniforms; + Buffer m_vertexBuffer; + mutable DepthRenderQueue m_renderQueue; + Texture m_whiteTexture; + VertexBuffer m_billboardPointBuffer; + VertexBuffer m_spriteBuffer; + + static IndexBuffer s_quadIndexBuffer; + static VertexBuffer s_quadVertexBuffer; + static VertexDeclaration s_billboardInstanceDeclaration; + static VertexDeclaration s_billboardVertexDeclaration; + }; +} + + +#include + +#endif // NAZARA_DEPTHRENDERTECHNIQUE_HPP diff --git a/include/Nazara/Graphics/DepthRenderTechnique.inl b/include/Nazara/Graphics/DepthRenderTechnique.inl new file mode 100644 index 000000000..ac6b052d0 --- /dev/null +++ b/include/Nazara/Graphics/DepthRenderTechnique.inl @@ -0,0 +1,3 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp diff --git a/include/Nazara/Graphics/Enums.hpp b/include/Nazara/Graphics/Enums.hpp index 6cbbe0f03..d378058f7 100644 --- a/include/Nazara/Graphics/Enums.hpp +++ b/include/Nazara/Graphics/Enums.hpp @@ -108,6 +108,7 @@ namespace Nz RenderTechniqueType_AdvancedForward, // AdvancedForwardRenderTechnique RenderTechniqueType_BasicForward, // BasicForwardRenderTechnique RenderTechniqueType_DeferredShading, // DeferredRenderTechnique + RenderTechniqueType_Depth, // DepthRenderTechnique RenderTechniqueType_LightPrePass, // LightPrePassRenderTechnique RenderTechniqueType_User, @@ -127,7 +128,7 @@ namespace Nz SceneNodeType_Max = SceneNodeType_User }; - // Ces paramètres sont indépendants du matériau: ils peuvent être demandés à tout moment + // These parameters are independant of the material: they can not be asked for the moment enum ShaderFlags { ShaderFlags_None = 0, @@ -138,7 +139,7 @@ namespace Nz ShaderFlags_TextureOverlay = 0x08, ShaderFlags_VertexColor = 0x10, - ShaderFlags_Max = ShaderFlags_VertexColor*2-1 + ShaderFlags_Max = ShaderFlags_VertexColor * 2 - 1 }; } diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index 9a17f5d2f..99be2743e 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -31,7 +31,6 @@ namespace Nz ForwardRenderQueue() = default; ~ForwardRenderQueue() = default; - void AddBillboard(int renderOrder, const Material* material, const Vector3f& position, const Vector2f& size, const Vector2f& sinCos = Vector2f(0.f, 1.f), const Color& color = Color::White) override; void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr = nullptr, SparsePtr colorPtr = nullptr) override; void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) override; void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr = nullptr) override; @@ -48,6 +47,16 @@ namespace Nz void Sort(const AbstractViewer* viewer); + struct MaterialComparator + { + bool operator()(const Material* mat1, const Material* mat2) const; + }; + + struct MaterialPipelineComparator + { + bool operator()(const MaterialPipeline* pipeline1, const MaterialPipeline* pipeline2) const; + }; + /// Billboards struct BillboardData { @@ -57,11 +66,6 @@ namespace Nz Vector2f sinCos; }; - struct BatchedBillboardComparator - { - bool operator()(const Material* mat1, const Material* mat2) const; - }; - struct BatchedBillboardEntry { NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot); @@ -69,7 +73,15 @@ namespace Nz std::vector billboards; }; - typedef std::map BatchedBillboardContainer; + typedef std::map BatchedBillboardContainer; + + struct BatchedBillboardPipelineEntry + { + BatchedBillboardContainer materialMap; + bool enabled = false; + }; + + typedef std::map BillboardPipelineBatches; /// Sprites struct SpriteChain_XYZ_Color_UV @@ -85,22 +97,25 @@ namespace Nz std::vector spriteChains; }; - struct BatchedSpriteMaterialComparator - { - bool operator()(const Material* mat1, const Material* mat2); - }; - - typedef std::map BasicSpriteOverlayContainer; + typedef std::map SpriteOverlayBatches; struct BatchedBasicSpriteEntry { NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot); - BasicSpriteOverlayContainer overlayMap; + SpriteOverlayBatches overlayMap; bool enabled = false; }; - typedef std::map BasicSpriteBatches; + typedef std::map SpriteMaterialBatches; + + struct BatchedSpritePipelineEntry + { + SpriteMaterialBatches materialMap; + bool enabled = false; + }; + + typedef std::map SpritePipelineBatches; /// Meshes struct MeshDataComparator @@ -119,21 +134,23 @@ namespace Nz typedef std::map MeshInstanceContainer; - struct BatchedModelMaterialComparator - { - bool operator()(const Material* mat1, const Material* mat2) const; - }; - struct BatchedModelEntry { NazaraSlot(Material, OnMaterialRelease, materialReleaseSlot); MeshInstanceContainer meshMap; bool enabled = false; - bool instancingEnabled = false; }; - typedef std::map ModelBatches; + typedef std::map MeshMaterialBatches; + + struct BatchedMaterialEntry + { + std::size_t maxInstanceCount = 0; + MeshMaterialBatches materialMap; + }; + + typedef std::map MeshPipelineBatches; struct TransparentModelData { @@ -147,9 +164,9 @@ namespace Nz struct Layer { - BatchedBillboardContainer billboards; - BasicSpriteBatches basicSprites; - ModelBatches opaqueModels; + BillboardPipelineBatches billboards; + SpritePipelineBatches basicSprites; + MeshPipelineBatches opaqueModels; TransparentModelContainer transparentModels; std::vector transparentModelData; std::vector otherDrawables; @@ -159,6 +176,7 @@ namespace Nz std::map layers; private: + BillboardData* GetBillboardData(int renderOrder, const Material* material, unsigned int count); Layer& GetLayer(int i); ///TODO: Inline void OnIndexBufferInvalidation(const IndexBuffer* indexBuffer); diff --git a/include/Nazara/Graphics/ForwardRenderTechnique.hpp b/include/Nazara/Graphics/ForwardRenderTechnique.hpp index 6881ac2d7..5d82c4bf9 100644 --- a/include/Nazara/Graphics/ForwardRenderTechnique.hpp +++ b/include/Nazara/Graphics/ForwardRenderTechnique.hpp @@ -31,12 +31,12 @@ namespace Nz AbstractRenderQueue* GetRenderQueue() override; RenderTechniqueType GetType() const override; - void SetMaxLightPassPerObject(unsigned int passCount); + void SetMaxLightPassPerObject(unsigned int maxLightPassPerObject); static bool Initialize(); static void Uninitialize(); - private: + protected: struct ShaderUniforms; void ChooseLights(const Spheref& object, bool includeDirectionalLights = true) const; @@ -46,7 +46,7 @@ namespace Nz void DrawTransparentModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; const ShaderUniforms* GetShaderUniforms(const Shader* shader) const; void OnShaderInvalidated(const Shader* shader) const; - void SendLightUniforms(const Shader* shader, const LightUniforms& uniforms, unsigned int index, unsigned int uniformOffset) const; + void SendLightUniforms(const Shader* shader, const LightUniforms& uniforms, unsigned int index, unsigned int uniformOffset, UInt8 availableTextureUnit) const; static float ComputeDirectionalLightScore(const Spheref& object, const AbstractRenderQueue::DirectionalLight& light); static float ComputePointLightScore(const Spheref& object, const AbstractRenderQueue::PointLight& light); @@ -70,11 +70,11 @@ namespace Nz LightUniforms lightUniforms; bool hasLightUniforms; - /// Moins coûteux en mémoire que de stocker un LightUniforms par index de lumière, - /// à voir si ça fonctionne chez tout le monde - int lightOffset; // "Distance" entre Lights[0].type et Lights[1].type + /// Less costly in memory than storing a LightUniforms by index of light, + /// this may not work everywhere + int lightOffset; // "Distance" between Lights[0].type and Lights[1].type - // Autre uniformes + // Other uniforms int eyePosition; int sceneAmbient; int textureOverlay; @@ -84,11 +84,13 @@ namespace Nz mutable std::vector m_lights; Buffer m_vertexBuffer; mutable ForwardRenderQueue m_renderQueue; + Texture m_whiteTexture; VertexBuffer m_billboardPointBuffer; VertexBuffer m_spriteBuffer; unsigned int m_maxLightPassPerObject; static IndexBuffer s_quadIndexBuffer; + static TextureSampler s_shadowSampler; static VertexBuffer s_quadVertexBuffer; static VertexDeclaration s_billboardInstanceDeclaration; static VertexDeclaration s_billboardVertexDeclaration; diff --git a/include/Nazara/Graphics/ForwardRenderTechnique.inl b/include/Nazara/Graphics/ForwardRenderTechnique.inl index 69ae7614f..6490513f8 100644 --- a/include/Nazara/Graphics/ForwardRenderTechnique.inl +++ b/include/Nazara/Graphics/ForwardRenderTechnique.inl @@ -2,10 +2,26 @@ // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include + namespace Nz { - inline void ForwardRenderTechnique::SendLightUniforms(const Shader* shader, const LightUniforms& uniforms, unsigned int index, unsigned int uniformOffset) const + /*! + * \brief Sens the uniforms for light + * + * \param shader Shader to send uniforms to + * \param uniforms Uniforms to send + * \param index Index of the light + * \param uniformOffset Offset for the uniform + * \param availableTextureUnit Unit texture available + */ + + inline void ForwardRenderTechnique::SendLightUniforms(const Shader* shader, const LightUniforms& uniforms, unsigned int index, unsigned int uniformOffset, UInt8 availableTextureUnit) const { + // If anyone got a better idea.. + int dummyCubemap = Renderer::GetMaxTextureUnits() - 1; + int dummyTexture = Renderer::GetMaxTextureUnits() - 2; + if (index < m_lights.size()) { const LightIndex& lightIndex = m_lights[index]; @@ -21,6 +37,20 @@ namespace Nz shader->SendColor(uniforms.locations.color + uniformOffset, light.color); shader->SendVector(uniforms.locations.factors + uniformOffset, Vector2f(light.ambientFactor, light.diffuseFactor)); shader->SendVector(uniforms.locations.parameters1 + uniformOffset, Vector4f(light.direction)); + + shader->SendBoolean(uniforms.locations.shadowMapping + uniformOffset, light.shadowMap != nullptr); + if (light.shadowMap) + { + Renderer::SetTexture(availableTextureUnit, light.shadowMap); + Renderer::SetTextureSampler(availableTextureUnit, s_shadowSampler); + + shader->SendMatrix(uniforms.locations.lightViewProjMatrix + index, light.transformMatrix); + shader->SendInteger(uniforms.locations.directionalSpotLightShadowMap + index, availableTextureUnit); + } + else + shader->SendInteger(uniforms.locations.directionalSpotLightShadowMap + index, dummyTexture); + + shader->SendInteger(uniforms.locations.pointLightShadowMap + index, dummyCubemap); break; } @@ -32,6 +62,19 @@ namespace Nz shader->SendVector(uniforms.locations.factors + uniformOffset, Vector2f(light.ambientFactor, light.diffuseFactor)); shader->SendVector(uniforms.locations.parameters1 + uniformOffset, Vector4f(light.position, light.attenuation)); shader->SendVector(uniforms.locations.parameters2 + uniformOffset, Vector4f(0.f, 0.f, 0.f, light.invRadius)); + + shader->SendBoolean(uniforms.locations.shadowMapping + uniformOffset, light.shadowMap != nullptr); + if (light.shadowMap) + { + Renderer::SetTexture(availableTextureUnit, light.shadowMap); + Renderer::SetTextureSampler(availableTextureUnit, s_shadowSampler); + + shader->SendInteger(uniforms.locations.pointLightShadowMap + index, availableTextureUnit); + } + else + shader->SendInteger(uniforms.locations.pointLightShadowMap + index, dummyCubemap); + + shader->SendInteger(uniforms.locations.directionalSpotLightShadowMap + index, dummyTexture); break; } @@ -44,14 +87,41 @@ namespace Nz shader->SendVector(uniforms.locations.parameters1 + uniformOffset, Vector4f(light.position, light.attenuation)); shader->SendVector(uniforms.locations.parameters2 + uniformOffset, Vector4f(light.direction, light.invRadius)); shader->SendVector(uniforms.locations.parameters3 + uniformOffset, Vector2f(light.innerAngleCosine, light.outerAngleCosine)); + + shader->SendBoolean(uniforms.locations.shadowMapping + uniformOffset, light.shadowMap != nullptr); + if (light.shadowMap) + { + Renderer::SetTexture(availableTextureUnit, light.shadowMap); + Renderer::SetTextureSampler(availableTextureUnit, s_shadowSampler); + + shader->SendMatrix(uniforms.locations.lightViewProjMatrix + index, light.transformMatrix); + shader->SendInteger(uniforms.locations.directionalSpotLightShadowMap + index, availableTextureUnit); + } + else + shader->SendInteger(uniforms.locations.directionalSpotLightShadowMap + index, dummyTexture); + + shader->SendInteger(uniforms.locations.pointLightShadowMap + index, dummyCubemap); + break; } } } else + { shader->SendInteger(uniforms.locations.type + uniformOffset, -1); //< Disable the light in the shader + shader->SendInteger(uniforms.locations.directionalSpotLightShadowMap + index, dummyTexture); + shader->SendInteger(uniforms.locations.pointLightShadowMap + index, dummyCubemap); + } } + /*! + * \brief Computes the score for directional light + * \return 0.f + * + * \param object Sphere symbolising the object + * \param light Light to compute + */ + inline float ForwardRenderTechnique::ComputeDirectionalLightScore(const Spheref& object, const AbstractRenderQueue::DirectionalLight& light) { NazaraUnused(object); @@ -61,33 +131,73 @@ namespace Nz return 0.f; } + /*! + * \brief Computes the score for point light + * \return Distance to the light + * + * \param object Sphere symbolising the object + * \param light Light to compute + */ + inline float ForwardRenderTechnique::ComputePointLightScore(const Spheref& object, const AbstractRenderQueue::PointLight& light) { ///TODO: Compute a score depending on the light luminosity return object.SquaredDistance(light.position); } + /*! + * \brief Computes the score for spot light + * \return Distance to the light + * + * \param object Sphere symbolising the object + * \param light Light to compute + */ + inline float ForwardRenderTechnique::ComputeSpotLightScore(const Spheref& object, const AbstractRenderQueue::SpotLight& light) { ///TODO: Compute a score depending on the light luminosity and spot direction return object.SquaredDistance(light.position); } + /*! + * \brief Checks whether the directional light is suitable for the computations + * \return true if light is enoughly close + * + * \param object Sphere symbolising the object + * \param light Light to compute + */ + inline bool ForwardRenderTechnique::IsDirectionalLightSuitable(const Spheref& object, const AbstractRenderQueue::DirectionalLight& light) { NazaraUnused(object); NazaraUnused(light); - // Directional light are always suitables + // Directional light are always suitable return true; } + /*! + * \brief Checks whether the point light is suitable for the computations + * \return true if light is enoughly close + * + * \param object Sphere symbolising the object + * \param light Light to compute + */ + inline bool ForwardRenderTechnique::IsPointLightSuitable(const Spheref& object, const AbstractRenderQueue::PointLight& light) { // If the object is too far away from this point light, there is not way it could light it return object.SquaredDistance(light.position) <= light.radius * light.radius; } + /*! + * \brief Checks whether the spot light is suitable for the computations + * \return true if light is enoughly close + * + * \param object Sphere symbolising the object + * \param light Light to compute + */ + inline bool ForwardRenderTechnique::IsSpotLightSuitable(const Spheref& object, const AbstractRenderQueue::SpotLight& light) { ///TODO: Exclude spot lights based on their direction and outer angle? diff --git a/include/Nazara/Graphics/InstancedRenderable.hpp b/include/Nazara/Graphics/InstancedRenderable.hpp index 561cc4c97..d854a41d6 100644 --- a/include/Nazara/Graphics/InstancedRenderable.hpp +++ b/include/Nazara/Graphics/InstancedRenderable.hpp @@ -54,15 +54,28 @@ namespace Nz struct InstanceData { - InstanceData(Matrix4f& referenceMatrix) : - transformMatrix(referenceMatrix), + InstanceData(const Matrix4f& referenceMatrix) : + transformMatrix(&referenceMatrix), flags(0) { } + InstanceData(InstanceData&& instanceData) noexcept = default; + + InstanceData& operator=(InstanceData&& instanceData) noexcept + { + data = std::move(instanceData.data); + flags = instanceData.flags; + renderOrder = instanceData.renderOrder; + transformMatrix = instanceData.transformMatrix; + volume = instanceData.volume; + + return *this; + } + std::vector data; BoundingVolumef volume; - Matrix4f& transformMatrix; + const Matrix4f* transformMatrix; UInt32 flags; int renderOrder; }; diff --git a/include/Nazara/Graphics/InstancedRenderable.inl b/include/Nazara/Graphics/InstancedRenderable.inl index 0cc97f101..7f2537fbf 100644 --- a/include/Nazara/Graphics/InstancedRenderable.inl +++ b/include/Nazara/Graphics/InstancedRenderable.inl @@ -4,6 +4,12 @@ namespace Nz { + /*! + * \brief Constructs a InstancedRenderable object by assignation + * + * \param renderable InstancedRenderable to copy into this + */ + inline InstancedRenderable::InstancedRenderable(const InstancedRenderable& renderable) : RefCounted(), m_boundingVolume(renderable.m_boundingVolume), @@ -11,22 +17,43 @@ namespace Nz { } + /*! + * \brief Ensures that the bounding volume is up to date + */ + inline void InstancedRenderable::EnsureBoundingVolumeUpdated() const { if (!m_boundingVolumeUpdated) UpdateBoundingVolume(); } + /*! + * \brief Invalidates the bounding volume + */ + inline void InstancedRenderable::InvalidateBoundingVolume() { m_boundingVolumeUpdated = false; } + /*! + * \brief Invalidates the instance data based on flags + * + * \param flags Flags to invalidate + */ + inline void InstancedRenderable::InvalidateInstanceData(UInt32 flags) { OnInstancedRenderableInvalidateData(this, flags); } + /*! + * \brief Sets the current instanced renderable with the content of the other one + * \return A reference to this + * + * \param renderable The other InstancedRenderable + */ + inline InstancedRenderable& InstancedRenderable::operator=(const InstancedRenderable& renderable) { m_boundingVolume = renderable.m_boundingVolume; @@ -35,6 +62,10 @@ namespace Nz return *this; } + /*! + * \brief Updates the bounding volume + */ + inline void InstancedRenderable::UpdateBoundingVolume() const { MakeBoundingVolume(); diff --git a/include/Nazara/Graphics/Light.hpp b/include/Nazara/Graphics/Light.hpp index efa468ba6..071b748c3 100644 --- a/include/Nazara/Graphics/Light.hpp +++ b/include/Nazara/Graphics/Light.hpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include namespace Nz { @@ -21,7 +23,8 @@ namespace Nz { public: Light(LightType type = LightType_Point); - Light(const Light& light) = default; + inline Light(const Light& light); + Light(Light&& light) = default; ~Light() = default; void AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const override; @@ -31,37 +34,56 @@ namespace Nz bool Cull(const Frustumf& frustum, const Matrix4f& transformMatrix) const override; - float GetAmbientFactor() const; - float GetAttenuation() const; - Color GetColor() const; - float GetDiffuseFactor() const; - float GetInnerAngle() const; - float GetInnerAngleCosine() const; - float GetInvRadius() const; - LightType GetLightType() const; - float GetOuterAngle() const; - float GetOuterAngleCosine() const; - float GetOuterAngleTangent() const; - float GetRadius() const; + inline void EnableShadowCasting(bool castShadows); - void SetAmbientFactor(float factor); - void SetAttenuation(float attenuation); - void SetColor(const Color& color); - void SetDiffuseFactor(float factor); - void SetInnerAngle(float innerAngle); - void SetLightType(LightType type); - void SetOuterAngle(float outerAngle); - void SetRadius(float radius); + inline void EnsureShadowMapUpdate() const; + + inline float GetAmbientFactor() const; + inline float GetAttenuation() const; + inline Color GetColor() const; + inline float GetDiffuseFactor() const; + inline float GetInnerAngle() const; + inline float GetInnerAngleCosine() const; + inline float GetInvRadius() const; + inline LightType GetLightType() const; + inline float GetOuterAngle() const; + inline float GetOuterAngleCosine() const; + inline float GetOuterAngleTangent() const; + inline float GetRadius() const; + inline TextureRef GetShadowMap() const; + inline PixelFormatType GetShadowMapFormat() const; + inline const Vector2ui& GetShadowMapSize() const; + + inline bool IsShadowCastingEnabled() const; + + inline void SetAmbientFactor(float factor); + inline void SetAttenuation(float attenuation); + inline void SetColor(const Color& color); + inline void SetDiffuseFactor(float factor); + inline void SetInnerAngle(float innerAngle); + inline void SetLightType(LightType type); + inline void SetOuterAngle(float outerAngle); + inline void SetRadius(float radius); + inline void SetShadowMapFormat(PixelFormatType shadowFormat); + inline void SetShadowMapSize(const Vector2ui& size); void UpdateBoundingVolume(const Matrix4f& transformMatrix) override; - Light& operator=(const Light& light) = default; + Light& operator=(const Light& light); + Light& operator=(Light&& light) = default; private: void MakeBoundingVolume() const override; + inline void InvalidateShadowMap(); + void UpdateShadowMap() const; - LightType m_type; Color m_color; + LightType m_type; + PixelFormatType m_shadowMapFormat; + Vector2ui m_shadowMapSize; + mutable TextureRef m_shadowMap; + bool m_shadowCastingEnabled; + mutable bool m_shadowMapUpdated; float m_ambientFactor; float m_attenuation; float m_diffuseFactor; @@ -80,10 +102,14 @@ namespace Nz { int type; int color; + int directionalSpotLightShadowMap; int factors; + int lightViewProjMatrix; int parameters1; int parameters2; int parameters3; + int pointLightShadowMap; + int shadowMapping; }; bool ubo; diff --git a/include/Nazara/Graphics/Light.inl b/include/Nazara/Graphics/Light.inl index ad64e93dc..2852834b7 100644 --- a/include/Nazara/Graphics/Light.inl +++ b/include/Nazara/Graphics/Light.inl @@ -7,97 +7,293 @@ namespace Nz { + /*! + * \brief Constructs a Light object by default + */ + + inline Light::Light(const Light& light) : + Renderable(light), + m_color(light.m_color), + m_type(light.m_type), + m_shadowMapFormat(light.m_shadowMapFormat), + m_shadowMapSize(light.m_shadowMapSize), + m_shadowCastingEnabled(light.m_shadowCastingEnabled), + m_shadowMapUpdated(false), + m_ambientFactor(light.m_ambientFactor), + m_attenuation(light.m_attenuation), + m_diffuseFactor(light.m_diffuseFactor), + m_innerAngle(light.m_innerAngle), + m_innerAngleCosine(light.m_innerAngleCosine), + m_invRadius(light.m_invRadius), + m_outerAngle(light.m_outerAngle), + m_outerAngleCosine(light.m_outerAngleCosine), + m_outerAngleTangent(light.m_outerAngleTangent), + m_radius(light.m_radius) + { + } + + /*! + * \brief Enables shadow casting + * + * \param castShadows Should shadows be cast + */ + + inline void Light::EnableShadowCasting(bool castShadows) + { + if (m_shadowCastingEnabled != castShadows) + { + m_shadowCastingEnabled = castShadows; + m_shadowMapUpdated = false; + } + } + + /*! + * \brief Ensures that the shadow map is up to date + */ + + inline void Light::EnsureShadowMapUpdate() const + { + if (!m_shadowMapUpdated) + UpdateShadowMap(); + } + + /*! + * \brief Gets the ambient factor + * \return Current ambient factor + */ + inline float Light::GetAmbientFactor() const { return m_ambientFactor; } + /*! + * \brief Gets the light attenuation (in 1 / R^2) + * \return Attenuation + */ + inline float Light::GetAttenuation() const { return m_attenuation; } + /*! + * \brief Gets the color of the light + * \return Light color + */ + inline Color Light::GetColor() const { return m_color; } + /*! + * \brief Gets the diffuse factor + * \return Current diffuse factor + */ + inline float Light::GetDiffuseFactor() const { return m_diffuseFactor; } + /*! + * \brief Gets the inner angle in spot light + * \return Inner angle + */ + inline float Light::GetInnerAngle() const { return m_innerAngle; } + /*! + * \brief Gets the cosine inner angle in spot light + * \return Cosine inner angle + */ + inline float Light::GetInnerAngleCosine() const { return m_innerAngleCosine; } + /*! + * \brief Gets the inverse of the radius + * \return Inverse of the radius + */ + inline float Light::GetInvRadius() const { return m_invRadius; } + /*! + * \brief Gets the type of the light + * \return Light type + */ + inline LightType Light::GetLightType() const { return m_type; } + /*! + * \brief Gets the outer angle in spot light + * \return Outer angle + */ + inline float Light::GetOuterAngle() const { return m_outerAngle; } + /*! + * \brief Gets the cosine outer angle in spot light + * \return Cosine outer angle + */ + inline float Light::GetOuterAngleCosine() const { return m_outerAngleCosine; } + /*! + * \brief Gets the tangent outer angle in spot light + * \return Tangent outer angle + */ + inline float Light::GetOuterAngleTangent() const { return m_outerAngleTangent; } + /*! + * \brief Gets the radius of the light + * \return Light radius + */ + inline float Light::GetRadius() const { return m_radius; } + /*! + * \brief Gets the shadow map + * \return Reference to the shadow map texture + */ + + inline TextureRef Light::GetShadowMap() const + { + EnsureShadowMapUpdate(); + + return m_shadowMap; + } + + /*! + * \brief Gets the format of the shadow map + * \return Shadow map format + */ + + inline PixelFormatType Light::GetShadowMapFormat() const + { + return m_shadowMapFormat; + } + + /*! + * \brief Gets the size of the shadow map + * \return Shadow map size + */ + + inline const Vector2ui& Light::GetShadowMapSize() const + { + return m_shadowMapSize; + } + + /*! + * \brief Checks whether the shadow casting is enabled + * \return true If it is the case + */ + + inline bool Light::IsShadowCastingEnabled() const + { + return m_shadowCastingEnabled; + } + + /*! + * \brief Sets the ambient factor + * + * \param factor Ambient factor + */ + inline void Light::SetAmbientFactor(float factor) { m_ambientFactor = factor; } + /*! + * \brief Sets the light attenuation (in 1 / R^2) + * + * \param attenuation Light attenuation + */ + inline void Light::SetAttenuation(float attenuation) { m_attenuation = attenuation; } + /*! + * \brief Sets the color of the light + * + * \param color Light color + */ + inline void Light::SetColor(const Color& color) { m_color = color; } + /*! + * \brief Sets the diffuse factor + * + * \param factor Diffuse factor + */ + inline void Light::SetDiffuseFactor(float factor) { m_diffuseFactor = factor; } + /*! + * \brief Sets the inner angle in spot light + * \return innerAngle Inner angle + */ + inline void Light::SetInnerAngle(float innerAngle) { m_innerAngle = innerAngle; m_innerAngleCosine = std::cos(DegreeToRadian(m_innerAngle)); } + /*! + * \brief Sets the type of light + * + * \param type Light type + */ + inline void Light::SetLightType(LightType type) { m_type = type; + + InvalidateShadowMap(); } + /*! + * \brief Sets the outer angle in spot light + * \return outerAngle Outer angle + * + * \remark Invalidates the bounding volume + */ + inline void Light::SetOuterAngle(float outerAngle) { m_outerAngle = outerAngle; @@ -107,6 +303,13 @@ namespace Nz InvalidateBoundingVolume(); } + /*! + * \brief Sets the radius of the light + * \return radius Light radius + * + * \remark Invalidates the bounding volume + */ + inline void Light::SetRadius(float radius) { m_radius = radius; @@ -115,6 +318,84 @@ namespace Nz InvalidateBoundingVolume(); } + + /*! + * \brief Sets the shadow map format + * + * \param shadowFormat Shadow map format + * + * \remark Invalidates the shadow map + * \remark Produces a NazaraAssert if format is not a depth type + */ + + inline void Light::SetShadowMapFormat(PixelFormatType shadowFormat) + { + NazaraAssert(PixelFormat::GetContent(shadowFormat) == PixelFormatContent_DepthStencil, "Shadow format type is not a depth format"); + + m_shadowMapFormat = shadowFormat; + + InvalidateShadowMap(); + } + + /*! + * \brief Sets the size of the shadow map + * + * \param size Shadow map size + * + * \remark Invalidates the shadow map + * \remark Produces a NazaraAssert if size is zero + */ + + inline void Light::SetShadowMapSize(const Vector2ui& size) + { + NazaraAssert(size.x > 0 && size.y > 0, "Shadow map size must have a positive size"); + + m_shadowMapSize = size; + + InvalidateShadowMap(); + } + + /*! + * \brief Sets the current light with the content of the other one + * \return A reference to this + * + * \param light The other Light + * + * \remark Invalidates the shadow map + */ + + inline Light& Light::operator=(const Light& light) + { + Renderable::operator=(light); + + m_ambientFactor = light.m_ambientFactor; + m_attenuation = light.m_attenuation; + m_color = light.m_color; + m_diffuseFactor = light.m_diffuseFactor; + m_innerAngle = light.m_innerAngle; + m_innerAngleCosine = light.m_innerAngleCosine; + m_invRadius = light.m_invRadius; + m_outerAngle = light.m_outerAngle; + m_outerAngleCosine = light.m_outerAngleCosine; + m_outerAngleTangent = light.m_outerAngleTangent; + m_radius = light.m_radius; + m_shadowCastingEnabled = light.m_shadowCastingEnabled; + m_shadowMapFormat = light.m_shadowMapFormat; + m_shadowMapSize = light.m_shadowMapSize; + m_type = light.m_type; + + InvalidateShadowMap(); + return *this; + } + + /*! + * \brief Invalidates the shadow map + */ + + inline void Light::InvalidateShadowMap() + { + m_shadowMapUpdated = false; + } } #include diff --git a/include/Nazara/Graphics/Material.hpp b/include/Nazara/Graphics/Material.hpp index 397253022..f7d15d6fe 100644 --- a/include/Nazara/Graphics/Material.hpp +++ b/include/Nazara/Graphics/Material.hpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -57,94 +57,123 @@ namespace Nz friend class Graphics; public: - Material(); - Material(const Material& material); - ~Material(); + inline Material(); + inline Material(const MaterialPipeline* pipeline); + inline Material(const MaterialPipelineInfo& pipelineInfo); + inline Material(const String& pipelineName); + inline Material(const Material& material); + inline ~Material(); - const Shader* Apply(UInt32 shaderFlags = 0, UInt8 textureUnit = 0, UInt8* lastUsedUnit = nullptr) const; + void Apply(const MaterialPipeline::Instance& instance, UInt8 textureUnit = 0, UInt8* lastUsedUnit = nullptr) const; void BuildFromParameters(const ParameterList& matData, const MaterialParams& matParams = MaterialParams()); - void Enable(RendererParameter renderParameter, bool enable); - void EnableAlphaTest(bool alphaTest); - void EnableDepthSorting(bool depthSorting); - void EnableLighting(bool lighting); - void EnableTransform(bool transform); + inline void Configure(const MaterialPipeline* pipeline); + inline void Configure(const MaterialPipelineInfo& pipelineInfo); + inline bool Configure(const String& pipelineName); - Texture* GetAlphaMap() const; - float GetAlphaThreshold() const; - Color GetAmbientColor() const; - RendererComparison GetDepthFunc() const; - Color GetDiffuseColor() const; - Texture* GetDiffuseMap() const; - TextureSampler& GetDiffuseSampler(); - const TextureSampler& GetDiffuseSampler() const; - BlendFunc GetDstBlend() const; - Texture* GetEmissiveMap() const; - FaceSide GetFaceCulling() const; - FaceFilling GetFaceFilling() const; - Texture* GetHeightMap() const; - Texture* GetNormalMap() const; - const RenderStates& GetRenderStates() const; - const UberShader* GetShader() const; - const UberShaderInstance* GetShaderInstance(UInt32 flags = ShaderFlags_None) const; - float GetShininess() const; - Color GetSpecularColor() const; - Texture* GetSpecularMap() const; - TextureSampler& GetSpecularSampler(); - const TextureSampler& GetSpecularSampler() const; - BlendFunc GetSrcBlend() const; + inline void EnableAlphaTest(bool alphaTest); + inline void EnableBlending(bool blending); + inline void EnableColorWrite(bool colorWrite); + inline void EnableDepthBuffer(bool depthBuffer); + inline void EnableDepthSorting(bool depthSorting); + inline void EnableDepthWrite(bool depthWrite); + inline void EnableFaceCulling(bool faceCulling); + inline void EnableScissorTest(bool scissorTest); + inline void EnableShadowCasting(bool castShadows); + inline void EnableShadowReceive(bool receiveShadows); + inline void EnableStencilTest(bool stencilTest); - bool HasAlphaMap() const; - bool HasDiffuseMap() const; - bool HasEmissiveMap() const; - bool HasHeightMap() const; - bool HasNormalMap() const; - bool HasSpecularMap() const; + inline void EnsurePipelineUpdate() const; - bool IsAlphaTestEnabled() const; - bool IsDepthSortingEnabled() const; - bool IsEnabled(RendererParameter renderParameter) const; - bool IsLightingEnabled() const; - bool IsTransformEnabled() const; + inline const TextureRef& GetAlphaMap() const; + inline float GetAlphaThreshold() const; + inline Color GetAmbientColor() const; + inline RendererComparison GetDepthFunc() const; + inline const MaterialRef& GetDepthMaterial() const; + inline Color GetDiffuseColor() const; + inline const TextureRef& GetDiffuseMap() const; + inline TextureSampler& GetDiffuseSampler(); + inline const TextureSampler& GetDiffuseSampler() const; + inline BlendFunc GetDstBlend() const; + inline const TextureRef& GetEmissiveMap() const; + inline FaceSide GetFaceCulling() const; + inline FaceFilling GetFaceFilling() const; + inline const TextureRef& GetHeightMap() const; + inline float GetLineWidth() const; + inline const TextureRef& GetNormalMap() const; + inline const MaterialPipeline* GetPipeline() const; + inline const MaterialPipelineInfo& GetPipelineInfo() const; + inline float GetPointSize() const; + inline const UberShader* GetShader() const; + inline float GetShininess() const; + inline Color GetSpecularColor() const; + inline const TextureRef& GetSpecularMap() const; + inline TextureSampler& GetSpecularSampler(); + inline const TextureSampler& GetSpecularSampler() const; + inline BlendFunc GetSrcBlend() const; - bool LoadFromFile(const String& filePath, const MaterialParams& params = MaterialParams()); - bool LoadFromMemory(const void* data, std::size_t size, const MaterialParams& params = MaterialParams()); - bool LoadFromStream(Stream& stream, const MaterialParams& params = MaterialParams()); + inline bool HasAlphaMap() const; + inline bool HasDepthMaterial() const; + inline bool HasDiffuseMap() const; + inline bool HasEmissiveMap() const; + inline bool HasHeightMap() const; + inline bool HasNormalMap() const; + inline bool HasSpecularMap() const; + + inline bool IsAlphaTestEnabled() const; + inline bool IsBlendingEnabled() const; + inline bool IsColorWriteEnabled() const; + inline bool IsDepthBufferEnabled() const; + inline bool IsDepthSortingEnabled() const; + inline bool IsDepthWriteEnabled() const; + inline bool IsFaceCullingEnabled() const; + inline bool IsScissorTestEnabled() const; + inline bool IsStencilTestEnabled() const; + inline bool IsShadowCastingEnabled() const; + inline bool IsShadowReceiveEnabled() const; + + inline bool LoadFromFile(const String& filePath, const MaterialParams& params = MaterialParams()); + inline bool LoadFromMemory(const void* data, std::size_t size, const MaterialParams& params = MaterialParams()); + inline bool LoadFromStream(Stream& stream, const MaterialParams& params = MaterialParams()); void Reset(); - bool SetAlphaMap(const String& textureName); - void SetAlphaMap(TextureRef alphaMap); - void SetAlphaThreshold(float alphaThreshold); - void SetAmbientColor(const Color& ambient); - void SetDepthFunc(RendererComparison depthFunc); - void SetDiffuseColor(const Color& diffuse); - bool SetDiffuseMap(const String& textureName); - void SetDiffuseMap(TextureRef diffuseMap); - void SetDiffuseSampler(const TextureSampler& sampler); - void SetDstBlend(BlendFunc func); - bool SetEmissiveMap(const String& textureName); - void SetEmissiveMap(TextureRef textureName); - void SetFaceCulling(FaceSide faceSide); - void SetFaceFilling(FaceFilling filling); - bool SetHeightMap(const String& textureName); - void SetHeightMap(TextureRef textureName); - bool SetNormalMap(const String& textureName); - void SetNormalMap(TextureRef textureName); - void SetRenderStates(const RenderStates& states); - void SetShader(UberShaderConstRef uberShader); - bool SetShader(const String& uberShaderName); - void SetShininess(float shininess); - void SetSpecularColor(const Color& specular); - bool SetSpecularMap(const String& textureName); - void SetSpecularMap(TextureRef specularMap); - void SetSpecularSampler(const TextureSampler& sampler); - void SetSrcBlend(BlendFunc func); + void SaveToParameters(ParameterList* matData); - Material& operator=(const Material& material); + inline bool SetAlphaMap(const String& textureName); + inline void SetAlphaMap(TextureRef alphaMap); + inline void SetAlphaThreshold(float alphaThreshold); + inline void SetAmbientColor(const Color& ambient); + inline void SetDepthFunc(RendererComparison depthFunc); + inline void SetDepthMaterial(MaterialRef depthMaterial); + inline void SetDiffuseColor(const Color& diffuse); + inline bool SetDiffuseMap(const String& textureName); + inline void SetDiffuseMap(TextureRef diffuseMap); + inline void SetDiffuseSampler(const TextureSampler& sampler); + inline void SetDstBlend(BlendFunc func); + inline bool SetEmissiveMap(const String& textureName); + inline void SetEmissiveMap(TextureRef textureName); + inline void SetFaceCulling(FaceSide faceSide); + inline void SetFaceFilling(FaceFilling filling); + inline bool SetHeightMap(const String& textureName); + inline void SetHeightMap(TextureRef textureName); + inline void SetLineWidth(float lineWidth); + inline bool SetNormalMap(const String& textureName); + inline void SetNormalMap(TextureRef textureName); + inline void SetPointSize(float pointSize); + inline void SetShader(UberShaderConstRef uberShader); + inline bool SetShader(const String& uberShaderName); + inline void SetShininess(float shininess); + inline void SetSpecularColor(const Color& specular); + inline bool SetSpecularMap(const String& textureName); + inline void SetSpecularMap(TextureRef specularMap); + inline void SetSpecularSampler(const TextureSampler& sampler); + inline void SetSrcBlend(BlendFunc func); - static MaterialRef GetDefault(); + inline Material& operator=(const Material& material); + + inline static MaterialRef GetDefault(); template static MaterialRef New(Args&&... args); // Signals: @@ -152,16 +181,9 @@ namespace Nz NazaraSignal(OnMaterialReset, const Material* /*material*/); private: - struct ShaderInstance - { - const Shader* shader; - UberShaderInstance* uberInstance = nullptr; - int uniforms[MaterialUniform_Max+1]; - }; - void Copy(const Material& material); - void GenerateShader(UInt32 flags) const; - void InvalidateShaders(); + inline void InvalidatePipeline(); + inline void UpdatePipeline() const; static bool Initialize(); static void Uninitialize(); @@ -169,7 +191,9 @@ namespace Nz Color m_ambientColor; Color m_diffuseColor; Color m_specularColor; - RenderStates m_states; + MaterialRef m_depthMaterial; //< Materialception + mutable const MaterialPipeline* m_pipeline; + MaterialPipelineInfo m_pipelineInfo; TextureSampler m_diffuseSampler; TextureSampler m_specularSampler; TextureRef m_alphaMap; @@ -178,12 +202,8 @@ namespace Nz TextureRef m_heightMap; TextureRef m_normalMap; TextureRef m_specularMap; - UberShaderConstRef m_uberShader; - mutable ShaderInstance m_shaders[ShaderFlags_Max+1]; - bool m_alphaTestEnabled; - bool m_depthSortingEnabled; - bool m_lightingEnabled; - bool m_transformEnabled; + mutable bool m_pipelineUpdated; + bool m_shadowCastingEnabled; float m_alphaThreshold; float m_shininess; diff --git a/include/Nazara/Graphics/Material.inl b/include/Nazara/Graphics/Material.inl index 177c8130b..cec965e6e 100644 --- a/include/Nazara/Graphics/Material.inl +++ b/include/Nazara/Graphics/Material.inl @@ -2,11 +2,1377 @@ // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include namespace Nz { + /*! + * \brief Constructs a Material object with default states + * + * \see Reset + */ + inline Material::Material() + { + Reset(); + } + + /*! + * \brief Constructs a Material object using a MaterialPipeline + * + * Calls Configure with the pipeline parameter + * + * \see Configure + */ + inline Material::Material(const MaterialPipeline* pipeline) + { + ErrorFlags errFlags(ErrorFlag_ThrowException, true); + + Reset(); + Configure(pipeline); + } + + /*! + * \brief Constructs a Material object using a MaterialPipelineInfo + * + * Calls Configure with the pipelineInfo parameter + * + * \see Configure + */ + inline Material::Material(const MaterialPipelineInfo& pipelineInfo) + { + ErrorFlags errFlags(ErrorFlag_ThrowException, true); + + Reset(); + Configure(pipelineInfo); + } + + /*! + * \brief Constructs a Material object using a MaterialPipeline name + * + * Calls Configure with the pipelineName parameter + * + * \remark In case of error (ie. named pipeline is not registered), throw an exception + * + * \see Configure + */ + inline Material::Material(const String& pipelineName) + { + ErrorFlags errFlags(ErrorFlag_ThrowException, true); + + Reset(); + Configure(pipelineName); + } + + /*! + * \brief Constructs a Material object by assignation + * + * \param material Material to copy into this + */ + inline Material::Material(const Material& material) : + RefCounted(), + Resource(material) + { + Copy(material); + } + + /*! + * \brief Destructs the object and calls OnMaterialRelease + * + * \see OnMaterialRelease + */ + inline Material::~Material() + { + OnMaterialRelease(this); + } + + /*! + * \brief Reset material pipeline state + * + * Sets the material pipeline + * + * \remark pipeline must be valid + * + * \see Configure + */ + inline void Material::Configure(const MaterialPipeline* pipeline) + { + NazaraAssert(pipeline, "Invalid material pipeline"); + + m_pipeline = pipeline; + m_pipelineInfo = m_pipeline->GetInfo(); + m_pipelineUpdated = true; + } + + /*! + * \brief Reset material pipeline state + * + * Sets the material pipeline using pipeline info + * + * \remark pipeline must be valid + * + * \see Configure + */ + inline void Material::Configure(const MaterialPipelineInfo& pipelineInfo) + { + m_pipelineInfo = pipelineInfo; + + InvalidatePipeline(); + } + + /*! + * \brief Reset material pipeline state + * + * Sets the material pipeline using a name to lookup in the MaterialPipelineLibrary + * + * \return True if the material pipeline was found in the library + * + * \see Configure + */ + inline bool Material::Configure(const String& pipelineName) + { + MaterialPipelineRef pipeline = MaterialPipelineLibrary::Query(pipelineName); + if (!pipeline) + { + NazaraError("Failed to get pipeline \"" + pipelineName + "\""); + return false; + } + + Configure(std::move(pipeline)); + return true; + } + + /*! + * \brief Enable/Disable alpha test for this material + * + * When enabled, all objects using this material will be rendered using alpha testing, + * rejecting pixels if their alpha component is under a defined threshold. + * This allows some kind of transparency with a much cheaper cost as it doesn't prevent any optimization (as deferred rendering or batching). + * + * \param alphaTest Defines if this material will use alpha testing + * + * \remark Invalidates the pipeline + * + * \see IsAlphaTestEnabled + * \see SetAlphaThreshold + */ + inline void Material::EnableAlphaTest(bool alphaTest) + { + m_pipelineInfo.alphaTest = alphaTest; + + InvalidatePipeline(); + } + + /*! + * \brief Enable/Disable blending for this material + * + * When enabled, all objects using this material will be rendered using blending, obeying the dstBlend and srcBlend parameters + * This is useful with translucent objects, but will reduces performance as it prevents some optimizations (as deferred rendering) + * + * \param blending Defines if this material will use blending + * + * \remark Invalidates the pipeline + * + * \see IsBlendingEnabled + * \see SetDstBlend + * \see SetSrcBlend + */ + inline void Material::EnableBlending(bool blending) + { + m_pipelineInfo.blending = blending; + + InvalidatePipeline(); + } + + /*! + * \brief Enable/Disable color writing for this material + * + * \param colorWrite Defines if this material will use color writing + * + * \remark Invalidates the pipeline + * + * \see IsColorWritingEnabled + */ + inline void Material::EnableColorWrite(bool colorWrite) + { + m_pipelineInfo.colorWrite = colorWrite; + + InvalidatePipeline(); + } + + /*! + * \brief Enable/Disable depth buffer for this material + * + * When enabled, all objects using this material will be rendered using a depth buffer, if the RenderTarget has one. + * This will enable Depth Test, preventing further fragments to render on top of closer ones. + * + * This parameter is required for depth writing. + * + * In order to enable depth writing without enabling depth test, set the depth comparison function to RendererComparison_Never + * + * \param depthBuffer Defines if this material will use depth buffer + * + * \remark Invalidates the pipeline + * + * \see EnableDepthWrite + * \see IsDepthBufferEnabled + * \see SetDepthFunc + */ + inline void Material::EnableDepthBuffer(bool depthBuffer) + { + m_pipelineInfo.depthBuffer = depthBuffer; + + InvalidatePipeline(); + } + + /*! + * \brief Enable/Disable depth sorting for this material + * + * When enabled, all objects using this material will be rendered far from near + * This is useful with translucent objects, but will reduces performance as it breaks batching + * + * \param depthSorting Defines if this material will use depth sorting + * + * \remark Depth sorting may not be perfect (may be object-sorting instead of triangle-sorting) + * \remark Invalidates the pipeline + * + * \see IsDepthSortingEnabled + */ + inline void Material::EnableDepthSorting(bool depthSorting) + { + m_pipelineInfo.depthSorting = depthSorting; + + InvalidatePipeline(); + } + + /*! + * \brief Enable/Disable depth writing for this material + * + * When enabled, and if depth buffer is enabled and present, all fragments generated with this material will write + * to the depth buffer if they pass depth test. + * + * This is usually disabled with translucent objects, as depth test is wanted to prevent them from rendering on top of opaque objects but + * not depth writing (which could make other translucent fragments to fail depth test) + * + * \param depthBuffer Defines if this material will use depth write + * + * \remark Invalidates the pipeline + * + * \see EnableDepthBuffer + * \see IsDepthWriteEnabled + */ + inline void Material::EnableDepthWrite(bool depthWrite) + { + m_pipelineInfo.depthWrite = depthWrite; + + InvalidatePipeline(); + } + + /*! + * \brief Enable/Disable face culling for this material + * + * When enabled, the material prevents front and/or back faces from rendering. + * This is commonly used as an optimization to prevent processing of hidden faces by the rendering device. + * + * Use SetFaceCulling to control which side will be eliminated. + * + * \param faceCulling Defines if this material will use face culling + * + * \remark Invalidates the pipeline + * + * \see IsFaceCullingEnabled + * \see SetFaceCulling + */ + inline void Material::EnableFaceCulling(bool faceCulling) + { + m_pipelineInfo.faceCulling = faceCulling; + + InvalidatePipeline(); + } + + /*! + * \brief Enable/Disable scissor test for this material + * + * When enabled, the material prevents fragments out of the scissor box to be rendered. + * This can be useful with GUI, where widgets must not be rendered outside of their parent rendering area. + * + * \param scissorTest Defines if this material will use scissor test + * + * \remark Invalidates the pipeline + * + * \see IsScissorTestEnabled + */ + inline void Material::EnableScissorTest(bool scissorTest) + { + m_pipelineInfo.scissorTest = scissorTest; + + InvalidatePipeline(); + } + + /*! + * \brief Enable/Disable shadow casting for this material + * + * When enabled, all objects using this material will be allowed to cast shadows upon any objects using a material with shadow receiving enabled. + * The depth material replaces this one when rendering shadows. + * + * \param castShadows Defines if this material will be allowed to cast shadows + * + * \remark Does not invalidate the pipeline + * + * \see EnableShadowReceive + * \see IsShadowCastingEnabled + * \see SetDepthMaterial + */ + inline void Material::EnableShadowCasting(bool castShadows) + { + // Has no influence on pipeline + m_shadowCastingEnabled = castShadows; + } + + /*! + * \brief Enable/Disable shadow receiving for this material + * + * When enabled, all objects using this material will be allowed to be casted shadows upon themselves + * Disabling this can be helpful to prevent some rendering artifacts (especially with translucent objects) + * + * \param receiveShadows Defines if this material will be able to receive shadows + * + * \remark Invalidates the pipeline + * + * \see IsShadowReceiveEnabled + */ + inline void Material::EnableShadowReceive(bool receiveShadows) + { + m_pipelineInfo.shadowReceive = receiveShadows; + + InvalidatePipeline(); + } + + /*! + * \brief Enable/Disable stencil test for this material + * + * When enabled, all fragments must pass the stencil test to be rendered. + * + * \param scissorTest Defines if this material will use stencil test + * + * \remark Invalidates the pipeline + * + * \see IsStencilTestEnabled + */ + inline void Material::EnableStencilTest(bool stencilTest) + { + m_pipelineInfo.stencilTest = stencilTest; + + InvalidatePipeline(); + } + + /*! + * \brief Ensures the pipeline gets updated + * + * When the pipeline gets invalidated, it's not updated until required (per example by calling GetPipeline). + * Using this function forces the pipeline update, making GetPipeline thread-safe as long as the pipeline does not get invalidated. + * + * \see GetPipeline + */ + inline void Material::EnsurePipelineUpdate() const + { + if (!m_pipelineUpdated) + UpdatePipeline(); + } + + /*! + * \brief Gets the alpha map + * + * \return Constant reference to the current texture + * + * \see SetAlphaMap + */ + inline const TextureRef& Material::GetAlphaMap() const + { + return m_alphaMap; + } + + /*! + * \brief Gets the alpha test threshold + * + * \return The threshold value for the alpha test + * + * \see EnableAlphaTest + * \see SetAlphaThreshold + */ + inline float Material::GetAlphaThreshold() const + { + return m_alphaThreshold; + } + + /*! + * \brief Gets the ambient color + * + * \return Ambient color + * + * \see SetAmbientColor + */ + inline Color Material::GetAmbientColor() const + { + return m_ambientColor; + } + + /*! + * \brief Gets the function to compare depth + * + * \return Function comparing the depth of two materials + * + * \see EnableDepthTest + * \see SetAmbientColor + */ + inline RendererComparison Material::GetDepthFunc() const + { + return m_pipelineInfo.depthFunc; + } + + /*! + * \brief Gets the depth material + * + * \return Constant reference to the depth material + * + * \see EnableShadowCasting + */ + inline const MaterialRef& Material::GetDepthMaterial() const + { + return m_depthMaterial; + } + + /*! + * \brief Gets the diffuse color + * + * \return Diffuse color + * + * \see SetDiffuseColor + */ + inline Color Material::GetDiffuseColor() const + { + return m_diffuseColor; + } + + /*! + * \brief Gets the diffuse sampler + * + * \return Reference to the current texture sampler for the diffuse + * + * \see SetDiffuseSampler + */ + inline TextureSampler& Material::GetDiffuseSampler() + { + return m_diffuseSampler; + } + + /*! + * \brief Gets the diffuse sampler + * + * \return Constant reference to the current texture sampler for the diffuse + * + * \see SetDiffuseSampler + */ + inline const TextureSampler& Material::GetDiffuseSampler() const + { + return m_diffuseSampler; + } + + /*! + * \brief Gets the diffuse map + * + * \return Constant reference to the texture + * + * \see SetDiffuseMap + */ + const TextureRef& Material::GetDiffuseMap() const + { + return m_diffuseMap; + } + + /*! + * \brief Gets the dst in blend + * + * \return Function for dst blending + * + * \see SetDstBlend + */ + inline BlendFunc Material::GetDstBlend() const + { + return m_pipelineInfo.dstBlend; + } + + /*! + * \brief Gets the emissive map + * + * \return Constant reference to the texture + * + * \see SetEmissiveMap + */ + inline const TextureRef& Material::GetEmissiveMap() const + { + return m_emissiveMap; + } + + /*! + * \brief Gets the face culling + * + * \return Current face culling side + * + * \see SetFaceCulling + */ + inline FaceSide Material::GetFaceCulling() const + { + return m_pipelineInfo.cullingSide; + } + + /*! + * \brief Gets the face filling + * \return Current face filling + */ + inline FaceFilling Material::GetFaceFilling() const + { + return m_pipelineInfo.faceFilling; + } + + /*! + * \brief Gets the height map + * \return Constant reference to the texture + */ + inline const TextureRef& Material::GetHeightMap() const + { + return m_heightMap; + } + + /*! + * \brief Gets the line width of this material + * \return Line width + */ + inline float Material::GetLineWidth() const + { + return m_pipelineInfo.lineWidth; + } + + /*! + * \brief Gets the normal map + * \return Constant reference to the texture + */ + + inline const TextureRef& Material::GetNormalMap() const + { + return m_normalMap; + } + + /*! + * \brief Gets the render states + * \return Constant reference to the render states + */ + inline const MaterialPipeline* Material::GetPipeline() const + { + EnsurePipelineUpdate(); + + return m_pipeline; + } + + /*! + * \brief Gets the pipeline informations + * \return Constant reference to the pipeline info + */ + inline const MaterialPipelineInfo& Material::GetPipelineInfo() const + { + return m_pipelineInfo; + } + + /*! + * \brief Gets the point size of this material + * \return Point size + */ + inline float Material::GetPointSize() const + { + return m_pipelineInfo.pointSize; + } + + /*! + * \brief Gets the über-shader used by this material + * \return Constant pointer to the über-shader used + */ + inline const UberShader* Material::GetShader() const + { + return m_pipelineInfo.uberShader; + } + + /*! + * \brief Gets the shininess + * \return Current shininess + */ + inline float Material::GetShininess() const + { + return m_shininess; + } + + /*! + * \brief Gets the specular color + * \return Specular color + */ + inline Color Material::GetSpecularColor() const + { + return m_specularColor; + } + + /*! + * \brief Gets the specular map + * \return Constant reference to the texture + */ + inline const TextureRef& Material::GetSpecularMap() const + { + return m_specularMap; + } + + /*! + * \brief Gets the specular sampler + * \return Reference to the current texture sampler for the specular + */ + inline TextureSampler& Material::GetSpecularSampler() + { + return m_specularSampler; + } + + /*! + * \brief Gets the specular sampler + * \return Constant reference to the current texture sampler for the specular + */ + inline const TextureSampler& Material::GetSpecularSampler() const + { + return m_specularSampler; + } + + /*! + * \brief Gets the src in blend + * \return Function for src blending + */ + inline BlendFunc Material::GetSrcBlend() const + { + return m_pipelineInfo.srcBlend; + } + + /*! + * \brief Checks whether this material has an alpha map + * \return true If it is the case + */ + inline bool Material::HasAlphaMap() const + { + return m_alphaMap.IsValid(); + } + + /*! + * \brief Checks whether this material has a depth material + * \return true If it is the case + */ + inline bool Material::HasDepthMaterial() const + { + return m_depthMaterial.IsValid(); + } + + /*! + * \brief Checks whether this material has a diffuse map + * \return true If it is the case + */ + inline bool Material::HasDiffuseMap() const + { + return m_diffuseMap.IsValid(); + } + + /*! + * \brief Checks whether this material has a emissive map + * \return true If it is the case + */ + inline bool Material::HasEmissiveMap() const + { + return m_emissiveMap.IsValid(); + } + + /*! + * \brief Checks whether this material has a height map + * \return true If it is the case + */ + inline bool Material::HasHeightMap() const + { + return m_heightMap.IsValid(); + } + + /*! + * \brief Checks whether this material has a normal map + * \return true If it is the case + */ + inline bool Material::HasNormalMap() const + { + return m_normalMap.IsValid(); + } + + /*! + * \brief Checks whether this material has a specular map + * \return true If it is the case + */ + inline bool Material::HasSpecularMap() const + { + return m_specularMap.IsValid(); + } + + /*! + * \brief Checks whether this material has alpha test enabled + * \return true If it is the case + */ + inline bool Material::IsAlphaTestEnabled() const + { + return m_pipelineInfo.alphaTest; + } + + /*! + * \brief Checks whether this material has blending enabled + * \return true If it is the case + */ + inline bool Material::IsBlendingEnabled() const + { + return m_pipelineInfo.blending; + } + + /*! + * \brief Checks whether this material has color write enabled + * \return true If it is the case + */ + inline bool Material::IsColorWriteEnabled() const + { + return m_pipelineInfo.colorWrite; + } + + /*! + * \brief Checks whether this material has depth buffer enabled + * \return true If it is the case + */ + inline bool Material::IsDepthBufferEnabled() const + { + return m_pipelineInfo.depthBuffer; + } + + /*! + * \brief Checks whether this material has depth sorting enabled + * \return true If it is the case + */ + inline bool Material::IsDepthSortingEnabled() const + { + return m_pipelineInfo.depthSorting; + } + + /*! + * \brief Checks whether this material has depth writing enabled + * \return true If it is the case + */ + inline bool Material::IsDepthWriteEnabled() const + { + return m_pipelineInfo.depthWrite; + } + + /*! + * \brief Checks whether this material has face culling enabled + * \return true If it is the case + */ + inline bool Material::IsFaceCullingEnabled() const + { + return m_pipelineInfo.faceCulling; + } + + /*! + * \brief Checks whether this material has scissor test enabled + * \return true If it is the case + */ + inline bool Material::IsScissorTestEnabled() const + { + return m_pipelineInfo.scissorTest; + } + + /*! + * \brief Checks whether this material has stencil test enabled + * \return true If it is the case + */ + inline bool Material::IsStencilTestEnabled() const + { + return m_pipelineInfo.stencilTest; + } + + /*! + * \brief Checks whether this material cast shadow + * \return true If it is the case + */ + inline bool Material::IsShadowCastingEnabled() const + { + return m_shadowCastingEnabled; + } + + /*! + * \brief Checks whether this material receive shadow + * \return true If it is the case + */ + inline bool Material::IsShadowReceiveEnabled() const + { + return m_pipelineInfo.shadowReceive; + } + + /*! + * \brief Loads the material from file + * \return true if loading is successful + * + * \param filePath Path to the file + * \param params Parameters for the material + */ + inline bool Material::LoadFromFile(const String& filePath, const MaterialParams& params) + { + return MaterialLoader::LoadFromFile(this, filePath, params); + } + + /*! + * \brief Loads the material from memory + * \return true if loading is successful + * + * \param data Raw memory + * \param size Size of the memory + * \param params Parameters for the material + */ + inline bool Material::LoadFromMemory(const void* data, std::size_t size, const MaterialParams& params) + { + return MaterialLoader::LoadFromMemory(this, data, size, params); + } + + /*! + * \brief Loads the material from stream + * \return true if loading is successful + * + * \param stream Stream to the material + * \param params Parameters for the material + */ + inline bool Material::LoadFromStream(Stream& stream, const MaterialParams& params) + { + return MaterialLoader::LoadFromStream(this, stream, params); + } + + /*! + * \brief Sets the alpha map by name + * \return true If successful + * + * \param textureName Named texture + */ + inline bool Material::SetAlphaMap(const String& textureName) + { + TextureRef texture = TextureLibrary::Query(textureName); + if (!texture) + { + texture = TextureManager::Get(textureName); + if (!texture) + { + NazaraError("Failed to get alpha map \"" + textureName + "\""); + return false; + } + } + + SetAlphaMap(std::move(texture)); + return true; + } + + /*! + * \brief Sets the alpha map with a reference to a texture + * \return true If successful + * + * \param alphaMap Texture + * + * \remark Invalidates the pipeline + */ + inline void Material::SetAlphaMap(TextureRef alphaMap) + { + m_alphaMap = std::move(alphaMap); + m_pipelineInfo.hasAlphaMap = m_alphaMap.IsValid(); + + InvalidatePipeline(); + } + + /*! + * \brief Sets the alpha threshold + * + * \param alphaThreshold Threshold for the alpha + */ + inline void Material::SetAlphaThreshold(float alphaThreshold) + { + m_alphaThreshold = alphaThreshold; + } + + /*! + * \brief Sets the color for ambient + * + * \param ambient Color for ambient + */ + inline void Material::SetAmbientColor(const Color& ambient) + { + m_ambientColor = ambient; + } + + /*! + * \brief Sets the depth functor + * + * \param depthFunc + * + * \remark Invalidates the pipeline + */ + inline void Material::SetDepthFunc(RendererComparison depthFunc) + { + m_pipelineInfo.depthFunc = depthFunc; + + InvalidatePipeline(); + } + + /*! + * \brief Sets the depth material + * \return true If successful + * + * \param depthMaterial Material for depth + */ + inline void Material::SetDepthMaterial(MaterialRef depthMaterial) + { + m_depthMaterial = std::move(depthMaterial); + } + + /*! + * \brief Sets the color for diffuse + * + * \param diffuse Color for diffuse + */ + inline void Material::SetDiffuseColor(const Color& diffuse) + { + m_diffuseColor = diffuse; + } + + /*! + * \brief Sets the diffuse map by name + * \return true If successful + * + * \param textureName Named texture + * + * \remark Invalidates the pipeline + */ + inline bool Material::SetDiffuseMap(const String& textureName) + { + TextureRef texture = TextureLibrary::Query(textureName); + if (!texture) + { + texture = TextureManager::Get(textureName); + if (!texture) + { + NazaraError("Failed to get diffuse map \"" + textureName + "\""); + return false; + } + } + + SetDiffuseMap(std::move(texture)); + return true; + } + + /*! + * \brief Sets the diffuse map with a reference to a texture + * \return true If successful + * + * \param diffuseMap Texture + * + * \remark Invalidates the pipeline + */ + inline void Material::SetDiffuseMap(TextureRef diffuseMap) + { + m_diffuseMap = std::move(diffuseMap); + m_pipelineInfo.hasDiffuseMap = m_diffuseMap.IsValid(); + + InvalidatePipeline(); + } + + /*! + * \brief Sets the diffuse sampler + * + * \param sampler Diffuse sample + */ + + inline void Material::SetDiffuseSampler(const TextureSampler& sampler) + { + m_diffuseSampler = sampler; + } + + /*! + * \brief Sets the dst in blend + * + * \param func Function for dst blending + * + * \remark Invalidates the pipeline + */ + inline void Material::SetDstBlend(BlendFunc func) + { + m_pipelineInfo.dstBlend = func; + + InvalidatePipeline(); + } + + /*! + * \brief Sets the emissive map by name + * \return true If successful + * + * \param textureName Named texture + * + * \see GetEmissiveMap + */ + inline bool Material::SetEmissiveMap(const String& textureName) + { + TextureRef texture = TextureLibrary::Query(textureName); + if (!texture) + { + texture = TextureManager::Get(textureName); + if (!texture) + { + NazaraError("Failed to get emissive map \"" + textureName + "\""); + return false; + } + } + + SetEmissiveMap(std::move(texture)); + return true; + } + + /*! + * \brief Sets the emissive map with a reference to a texture + * \return true If successful + * + * \param emissiveMap Texture + * + * \remark Invalidates the pipeline + */ + inline void Material::SetEmissiveMap(TextureRef emissiveMap) + { + m_emissiveMap = std::move(emissiveMap); + m_pipelineInfo.hasEmissiveMap = m_emissiveMap.IsValid(); + + InvalidatePipeline(); + } + + /*! + * \brief Sets the face culling + * + * \param faceSide Face to cull + * + * \remark Invalidates the pipeline + */ + inline void Material::SetFaceCulling(FaceSide faceSide) + { + m_pipelineInfo.cullingSide = faceSide; + + InvalidatePipeline(); + } + + /*! + * \brief Sets the face filling + * + * \param filling Face to fill + * + * \remark Invalidates the pipeline + */ + inline void Material::SetFaceFilling(FaceFilling filling) + { + m_pipelineInfo.faceFilling = filling; + + InvalidatePipeline(); + } + + /*! + * \brief Sets the height map by path or name + * \return true If successful + * + * \param textureName Named texture + * + * \see GetHeightMap + */ + inline bool Material::SetHeightMap(const String& textureName) + { + TextureRef texture = TextureLibrary::Query(textureName); + if (!texture) + { + texture = TextureManager::Get(textureName); + if (!texture) + { + NazaraError("Failed to get height map \"" + textureName + "\""); + return false; + } + } + + SetHeightMap(std::move(texture)); + return true; + } + + /*! + * \brief Sets the height map with a reference to a texture + * + * \param heightMap Texture + * + * \remark Invalidates the pipeline + * + * \see GetHeightMap + */ + inline void Material::SetHeightMap(TextureRef heightMap) + { + m_heightMap = std::move(heightMap); + m_pipelineInfo.hasHeightMap = m_heightMap.IsValid(); + + InvalidatePipeline(); + } + + /*! + * \brief Sets the line width for this material + * + * This parameter is used when rendering lines, to define the width (in pixels) the line will take on the framebuffer + * + * \param lineWidth Width of the line + * + * \remark Invalidates the pipeline + * + * \see GetLineWidth + */ + inline void Material::SetLineWidth(float lineWidth) + { + m_pipelineInfo.lineWidth = lineWidth; + + InvalidatePipeline(); + } + + /*! + * \brief Sets the normal map by path or name + * \return true If successful + * + * \param textureName Named texture + * + * \remark Invalidates the pipeline + * + * \see GetNormalMap + */ + inline bool Material::SetNormalMap(const String& textureName) + { + TextureRef texture = TextureLibrary::Query(textureName); + if (!texture) + { + texture = TextureManager::Get(textureName); + if (!texture) + { + NazaraError("Failed to get normal map \"" + textureName + "\""); + return false; + } + } + + SetNormalMap(std::move(texture)); + return true; + } + + /*! + * \brief Sets the normal map with a reference to a texture + * \return true If successful + * + * \param normalMap Texture + * + * \remark Invalidates the pipeline + * + * \see GetNormalMap + */ + inline void Material::SetNormalMap(TextureRef normalMap) + { + m_normalMap = std::move(normalMap); + m_pipelineInfo.hasNormalMap = m_normalMap.IsValid(); + + InvalidatePipeline(); + } + + /*! + * \brief Sets the point size for this material + * + * This parameter is used when rendering points, to define the size (in pixels) the point will take on the framebuffer + * + * \param pointSize Size of the point + * + * \remark Invalidates the pipeline + * + * \see GetPointSize + */ + inline void Material::SetPointSize(float pointSize) + { + m_pipelineInfo.pointSize = pointSize; + + InvalidatePipeline(); + } + + /*! + * \brief Sets the shader with a constant reference to a ubershader + * + * \param uberShader Uber shader to apply + * + * \remark Invalidates the pipeline + * + * \see GetShader + */ + inline void Material::SetShader(UberShaderConstRef uberShader) + { + m_pipelineInfo.uberShader = std::move(uberShader); + + InvalidatePipeline(); + } + + /*! + * \brief Sets the shader by name + * \return true If successful + * + * \param uberShaderName Named shader + */ + inline bool Material::SetShader(const String& uberShaderName) + { + UberShaderConstRef uberShader = UberShaderLibrary::Get(uberShaderName); + if (!uberShader) + return false; + + SetShader(std::move(uberShader)); + return true; + } + + /*! + * \brief Sets the shininess of the material + * + * \param shininess Value of the shininess + */ + inline void Material::SetShininess(float shininess) + { + m_shininess = shininess; + } + + /*! + * \brief Sets the color for specular + * + * \param specular Color + */ + inline void Material::SetSpecularColor(const Color& specular) + { + m_specularColor = specular; + } + + /*! + * \brief Sets the specular map by name + * \return true If successful + * + * \param textureName Named texture + * + * \remark Invalidates the pipeline + */ + inline bool Material::SetSpecularMap(const String& textureName) + { + TextureRef texture = TextureLibrary::Query(textureName); + if (!texture) + { + texture = TextureManager::Get(textureName); + if (!texture) + { + NazaraError("Failed to get specular map \"" + textureName + "\""); + return false; + } + } + + SetSpecularMap(std::move(texture)); + return true; + } + + /*! + * \brief Sets the specular map with a reference to a texture + * \return true If successful + * + * \param specularMap Texture + * + * \remark Invalidates the pipeline + * + * \see GetSpecularMap + */ + inline void Material::SetSpecularMap(TextureRef specularMap) + { + m_specularMap = std::move(specularMap); + m_pipelineInfo.hasSpecularMap = m_specularMap.IsValid(); + + InvalidatePipeline(); + } + + /*! + * \brief Sets the specular sampler + * + * \param sampler Specular sample + * + * \see GetSpecularSampler + */ + inline void Material::SetSpecularSampler(const TextureSampler& sampler) + { + m_specularSampler = sampler; + } + + /*! + * \brief Sets the src in blend + * + * \param func Function for src blending + * + * \remark Invalidates the pipeline + * + * \see GetSrcBlend + */ + inline void Material::SetSrcBlend(BlendFunc func) + { + m_pipelineInfo.srcBlend = func; + + InvalidatePipeline(); + } + + /*! + * \brief Sets the current material with the content of the other one + * \return A reference to this + * + * \param material The other Material + */ + inline Material& Material::operator=(const Material& material) + { + Resource::operator=(material); + + Copy(material); + return *this; + } + + /*! + * \brief Gets the default material + * + * \return Reference to the default material + * + * \remark This material should NOT be modified as it would affect all objects using it + */ + inline MaterialRef Material::GetDefault() + { + return s_defaultMaterial; + } + + inline void Material::InvalidatePipeline() + { + m_pipelineUpdated = false; + } + + inline void Material::UpdatePipeline() const + { + m_pipeline = MaterialPipeline::GetPipeline(m_pipelineInfo); + m_pipelineUpdated = true; + } + + /*! + * \brief Creates a new material from the arguments + * \return A reference to the newly created material + * + * \param args Arguments for the material + */ template MaterialRef Material::New(Args&&... args) { @@ -18,3 +1384,4 @@ namespace Nz } #include +#include "Material.hpp" diff --git a/include/Nazara/Graphics/MaterialPipeline.hpp b/include/Nazara/Graphics/MaterialPipeline.hpp new file mode 100644 index 000000000..cf64d5e40 --- /dev/null +++ b/include/Nazara/Graphics/MaterialPipeline.hpp @@ -0,0 +1,95 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_MATERIALPIPELINE_HPP +#define NAZARA_MATERIALPIPELINE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + struct MaterialPipelineInfo : RenderStates + { + bool alphaTest = false; + bool depthSorting = false; + bool hasAlphaMap = false; + bool hasDiffuseMap = false; + bool hasEmissiveMap = false; + bool hasHeightMap = false; + bool hasNormalMap = false; + bool hasSpecularMap = false; + bool shadowReceive = true; + + UberShaderConstRef uberShader; + }; + + inline bool operator==(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs); + inline bool operator!=(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs); + + class MaterialPipeline; + + using MaterialPipelineConstRef = ObjectRef; + using MaterialPipelineLibrary = ObjectLibrary; + using MaterialPipelineRef = ObjectRef; + + class NAZARA_GRAPHICS_API MaterialPipeline : public RefCounted + { + friend class Graphics; + friend MaterialPipelineLibrary; + + public: + struct Instance; + + MaterialPipeline(const MaterialPipeline&) = delete; + MaterialPipeline(MaterialPipeline&&) = delete; + ~MaterialPipeline() = default; + + inline const Instance& Apply(UInt32 flags = ShaderFlags_None) const; + + MaterialPipeline& operator=(const MaterialPipeline&) = delete; + MaterialPipeline& operator=(MaterialPipeline&&) = delete; + + inline const MaterialPipelineInfo& GetInfo() const; + inline const Instance& GetInstance(UInt32 flags = ShaderFlags_None) const; + + static MaterialPipelineRef GetPipeline(const MaterialPipelineInfo& pipelineInfo); + + struct Instance + { + RenderPipeline renderPipeline; + UberShaderInstance* uberInstance = nullptr; + std::array uniforms; + }; + + private: + inline MaterialPipeline(const MaterialPipelineInfo& pipelineInfo); + + void GenerateRenderPipeline(UInt32 flags) const; + + static bool Initialize(); + template static MaterialPipelineRef New(Args&&... args); + static void Uninitialize(); + + MaterialPipelineInfo m_pipelineInfo; + mutable std::array m_instances; + + using PipelineCache = std::unordered_map; + static PipelineCache s_pipelineCache; + + static MaterialPipelineLibrary::LibraryMap s_library; + }; +} + +#include + +#endif // NAZARA_MATERIALPIPELINE_HPP diff --git a/include/Nazara/Graphics/MaterialPipeline.inl b/include/Nazara/Graphics/MaterialPipeline.inl new file mode 100644 index 000000000..ebd94a9ee --- /dev/null +++ b/include/Nazara/Graphics/MaterialPipeline.inl @@ -0,0 +1,143 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + inline MaterialPipeline::MaterialPipeline(const MaterialPipelineInfo& pipelineInfo) : + m_pipelineInfo(pipelineInfo) + { + } + + /*! + * \brief Enable pipeline states for rendering + * + * \param flags Shader flags + */ + inline const MaterialPipeline::Instance& MaterialPipeline::Apply(UInt32 flags) const + { + const Instance& instance = GetInstance(flags); + instance.uberInstance->Activate(); + + Renderer::SetRenderStates(m_pipelineInfo); + + return instance; + } + + /*! + * \brief Retrieve a MaterialPipelineInfo object describing this pipeline + * + * \return Pipeline informations + */ + const MaterialPipelineInfo& MaterialPipeline::GetInfo() const + { + return m_pipelineInfo; + } + + /*! + * \brief Retrieve (and generate if required) a pipeline instance using shader flags without applying it + * + * \param flags Shader flags + * + * \return Pipeline instance + */ + inline const MaterialPipeline::Instance& MaterialPipeline::GetInstance(UInt32 flags) const + { + const Instance& instance = m_instances[flags]; + if (!instance.uberInstance) + GenerateRenderPipeline(flags); + + return instance; + } + + bool operator==(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs) + { + if (!operator==(static_cast(lhs), static_cast(rhs))) + return false; + + #define NazaraPipelineMember(field) if (lhs.##field != rhs.##field) return false + #define NazaraPipelineBoolMember NazaraPipelineMember + + NazaraPipelineBoolMember(alphaTest); + NazaraPipelineBoolMember(depthSorting); + NazaraPipelineBoolMember(hasAlphaMap); + NazaraPipelineBoolMember(hasDiffuseMap); + NazaraPipelineBoolMember(hasEmissiveMap); + NazaraPipelineBoolMember(hasHeightMap); + NazaraPipelineBoolMember(hasNormalMap); + NazaraPipelineBoolMember(hasSpecularMap); + NazaraPipelineBoolMember(shadowReceive); + + NazaraPipelineMember(uberShader); + + #undef NazaraPipelineMember + #undef NazaraPipelineBoolMember + + return true; + } + + bool operator!=(const MaterialPipelineInfo& lhs, const MaterialPipelineInfo& rhs) + { + return !operator==(lhs, rhs); + } + + /*! + * \brief Creates a new MaterialPipeline from the arguments + * \return A reference to the newly created material pipeline + * + * \param args Arguments for the material pipeline + */ + template + MaterialPipelineRef MaterialPipeline::New(Args&&... args) + { + std::unique_ptr object(new MaterialPipeline(std::forward(args)...)); + return object.release(); + } +} + +namespace std +{ + template<> + struct hash + { + size_t operator()(const Nz::MaterialPipelineInfo& pipelineInfo) const + { + hash parentHash; + + std::size_t seed = parentHash(pipelineInfo); + + Nz::UInt16 parameterHash = 0; + Nz::UInt16 parameterIndex = 0; + + #define NazaraPipelineMember(member) Nz::HashCombine(seed, pipelineInfo.##member) + #define NazaraPipelineBoolMember(member) parameterHash |= ((pipelineInfo.##member) ? 1U : 0U) << (parameterIndex++) + + NazaraPipelineBoolMember(alphaTest); + NazaraPipelineBoolMember(depthSorting); + NazaraPipelineBoolMember(hasAlphaMap); + NazaraPipelineBoolMember(hasDiffuseMap); + NazaraPipelineBoolMember(hasEmissiveMap); + NazaraPipelineBoolMember(hasHeightMap); + NazaraPipelineBoolMember(hasNormalMap); + NazaraPipelineBoolMember(hasSpecularMap); + NazaraPipelineBoolMember(shadowReceive); + + NazaraPipelineMember(uberShader); + + #undef NazaraPipelineMember + #undef NazaraPipelineBoolMember + + Nz::HashCombine(seed, parameterHash); + + return seed; + } + }; +} + +#include diff --git a/include/Nazara/Graphics/Model.hpp b/include/Nazara/Graphics/Model.hpp index b4564d1b8..b1755c7d8 100644 --- a/include/Nazara/Graphics/Model.hpp +++ b/include/Nazara/Graphics/Model.hpp @@ -45,6 +45,7 @@ namespace Nz virtual ~Model(); void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; + inline void AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix, unsigned int renderOrder = 0); Material* GetMaterial(const String& subMeshName) const; Material* GetMaterial(unsigned int matIndex) const; @@ -68,8 +69,6 @@ namespace Nz bool SetMaterial(unsigned int skinIndex, const String& subMeshName, Material* material); void SetMaterial(unsigned int skinIndex, unsigned int matIndex, Material* material); virtual void SetMesh(Mesh* mesh); - bool SetSequence(const String& sequenceName); - void SetSequence(unsigned int sequenceIndex); void SetSkin(unsigned int skin); void SetSkinCount(unsigned int skinCount); diff --git a/include/Nazara/Graphics/Model.inl b/include/Nazara/Graphics/Model.inl index ea30ebd72..243611fb5 100644 --- a/include/Nazara/Graphics/Model.inl +++ b/include/Nazara/Graphics/Model.inl @@ -7,6 +7,29 @@ namespace Nz { + /*! + * \brief Adds this model to a render queue, using user-specified transform matrix and render order + * + * This can be useful when drawing particles + * + * \param renderQueue Queue to be added + * \param transformMatrix Transform matrix to be used for rendering the model + * \param renderOrder Specify the renderqueue layer to be used + */ + inline void Model::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix, unsigned int renderOrder) + { + InstanceData instanceData(transformMatrix); + instanceData.renderOrder = renderOrder; + return AddToRenderQueue(renderQueue, instanceData); + } + + /*! + * \brief Creates a new Model from the arguments + * \return A reference to the newly created model + * + * \param args Arguments for the model + */ + template ModelRef Model::New(Args&&... args) { diff --git a/include/Nazara/Graphics/ParticleController.hpp b/include/Nazara/Graphics/ParticleController.hpp index 17e4ac9e1..05537ba55 100644 --- a/include/Nazara/Graphics/ParticleController.hpp +++ b/include/Nazara/Graphics/ParticleController.hpp @@ -18,7 +18,7 @@ namespace Nz { class ParticleController; class ParticleMapper; - class ParticleSystem; + class ParticleGroup; using ParticleControllerConstRef = ObjectRef; using ParticleControllerLibrary = ObjectLibrary; @@ -34,7 +34,7 @@ namespace Nz ParticleController(const ParticleController& controller); virtual ~ParticleController(); - virtual void Apply(ParticleSystem& system, ParticleMapper& mapper, unsigned int startId, unsigned int endId, float elapsedTime) = 0; + virtual void Apply(ParticleGroup& system, ParticleMapper& mapper, unsigned int startId, unsigned int endId, float elapsedTime) = 0; // Signals: NazaraSignal(OnParticleControllerRelease, const ParticleController* /*particleController*/); diff --git a/include/Nazara/Graphics/ParticleDeclaration.hpp b/include/Nazara/Graphics/ParticleDeclaration.hpp index 4fd8b6c33..278d25961 100644 --- a/include/Nazara/Graphics/ParticleDeclaration.hpp +++ b/include/Nazara/Graphics/ParticleDeclaration.hpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace Nz { @@ -46,6 +47,7 @@ namespace Nz static ParticleDeclaration* Get(ParticleLayout layout); static bool IsTypeSupported(ComponentType type); + template static ParticleDeclarationRef New(Args&&... args); // Signals: NazaraSignal(OnParticleDeclarationRelease, const ParticleDeclaration* /*particleDeclaration*/); @@ -62,18 +64,20 @@ namespace Nz /* ** -Lynix: - ** Il serait aussi possible de préciser le stride de façon indépendante, ce que je ne permets pas - ** pour décomplexifier l'interface en enlevant quelque chose que je juge inutile. - ** Si vous pensez que ça peut être utile, n'hésitez pas à me le faire savoir ! + ** It would be also possible to precise the stride by an independant way, what I don't allow + ** to decomplexify the interface of something I consider useless. + ** If you think that could be useful, don't hesitate to make me aware ! */ }; - Component m_components[ParticleComponent_Max+1]; + std::array m_components; unsigned int m_stride; - static ParticleDeclaration s_declarations[ParticleLayout_Max+1]; + static std::array s_declarations; static ParticleDeclarationLibrary::LibraryMap s_library; }; } +#include + #endif // NAZARA_PARTICLEDECLARATION_HPP diff --git a/include/Nazara/Graphics/ParticleDeclaration.inl b/include/Nazara/Graphics/ParticleDeclaration.inl new file mode 100644 index 000000000..27cd92a22 --- /dev/null +++ b/include/Nazara/Graphics/ParticleDeclaration.inl @@ -0,0 +1,18 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + template + ParticleDeclarationRef ParticleDeclaration::New(Args&&... args) + { + std::unique_ptr object(new ParticleDeclaration(std::forward(args)...)); + return object.release(); + } +} + +#include diff --git a/include/Nazara/Graphics/ParticleEmitter.hpp b/include/Nazara/Graphics/ParticleEmitter.hpp index d5e3c8ea8..8bd26a4bd 100644 --- a/include/Nazara/Graphics/ParticleEmitter.hpp +++ b/include/Nazara/Graphics/ParticleEmitter.hpp @@ -8,23 +8,23 @@ #define NAZARA_PARTICLEEMITTER_HPP #include +#include #include -#include namespace Nz { class ParticleMapper; - class ParticleSystem; + class ParticleGroup; - class NAZARA_GRAPHICS_API ParticleEmitter : public Node + class NAZARA_GRAPHICS_API ParticleEmitter { public: ParticleEmitter(); - ParticleEmitter(const ParticleEmitter& emitter) = default; - ParticleEmitter(ParticleEmitter&& emitter) = default; + ParticleEmitter(const ParticleEmitter& emitter); + ParticleEmitter(ParticleEmitter&& emitter); virtual ~ParticleEmitter(); - virtual void Emit(ParticleSystem& system, float elapsedTime) const; + virtual void Emit(ParticleGroup& system, float elapsedTime) const; void EnableLagCompensation(bool enable); @@ -37,7 +37,11 @@ namespace Nz void SetEmissionRate(float rate); ParticleEmitter& operator=(const ParticleEmitter& emitter) = default; - ParticleEmitter& operator=(ParticleEmitter&& emitter) = default; + ParticleEmitter& operator=(ParticleEmitter&& emitter); + + // Signals: + NazaraSignal(OnParticleEmitterMove, ParticleEmitter* /*oldParticleEmitter*/, ParticleEmitter* /*newParticleEmitter*/); + NazaraSignal(OnParticleEmitterRelease, const ParticleEmitter* /*particleEmitter*/); private: virtual void SetupParticles(ParticleMapper& mapper, unsigned int count) const = 0; diff --git a/include/Nazara/Graphics/ParticleGenerator.hpp b/include/Nazara/Graphics/ParticleGenerator.hpp index d9d2a789a..d072ae346 100644 --- a/include/Nazara/Graphics/ParticleGenerator.hpp +++ b/include/Nazara/Graphics/ParticleGenerator.hpp @@ -18,7 +18,7 @@ namespace Nz { class ParticleGenerator; class ParticleMapper; - class ParticleSystem; + class ParticleGroup; using ParticleGeneratorConstRef = ObjectRef; using ParticleGeneratorLibrary = ObjectLibrary; @@ -34,7 +34,7 @@ namespace Nz ParticleGenerator(const ParticleGenerator& generator); virtual ~ParticleGenerator(); - virtual void Generate(ParticleSystem& system, ParticleMapper& mapper, unsigned int startId, unsigned int endId) = 0; + virtual void Generate(ParticleGroup& system, ParticleMapper& mapper, unsigned int startId, unsigned int endId) = 0; // Signals: NazaraSignal(OnParticleGeneratorRelease, const ParticleGenerator* /*particleGenerator*/); diff --git a/include/Nazara/Graphics/ParticleSystem.hpp b/include/Nazara/Graphics/ParticleGroup.hpp similarity index 70% rename from include/Nazara/Graphics/ParticleSystem.hpp rename to include/Nazara/Graphics/ParticleGroup.hpp index 76b834f6e..9e48d5797 100644 --- a/include/Nazara/Graphics/ParticleSystem.hpp +++ b/include/Nazara/Graphics/ParticleGroup.hpp @@ -4,10 +4,11 @@ #pragma once -#ifndef NAZARA_PARTICLESYSTEM_HPP -#define NAZARA_PARTICLESYSTEM_HPP +#ifndef NAZARA_PARTICLEGROUP_HPP +#define NAZARA_PARTICLEGROUP_HPP #include +#include #include #include #include @@ -22,13 +23,13 @@ namespace Nz { - class NAZARA_GRAPHICS_API ParticleSystem : public Renderable + class NAZARA_GRAPHICS_API ParticleGroup : public Renderable { public: - ParticleSystem(unsigned int maxParticleCount, ParticleLayout layout); - ParticleSystem(unsigned int maxParticleCount, ParticleDeclarationConstRef declaration); - ParticleSystem(const ParticleSystem& emitter); - ~ParticleSystem(); + ParticleGroup(unsigned int maxParticleCount, ParticleLayout layout); + ParticleGroup(unsigned int maxParticleCount, ParticleDeclarationConstRef declaration); + ParticleGroup(const ParticleGroup& emitter); + ~ParticleGroup(); void AddController(ParticleControllerRef controller); void AddEmitter(ParticleEmitter* emitter); @@ -40,19 +41,14 @@ namespace Nz void* CreateParticle(); void* CreateParticles(unsigned int count); - void EnableFixedStep(bool fixedStep); - void* GenerateParticle(); void* GenerateParticles(unsigned int count); const ParticleDeclarationConstRef& GetDeclaration() const; - float GetFixedStepSize() const; unsigned int GetMaxParticleCount() const; unsigned int GetParticleCount() const; unsigned int GetParticleSize() const; - bool IsFixedStepEnabled() const; - void KillParticle(unsigned int index); void KillParticles(); @@ -60,33 +56,42 @@ namespace Nz void RemoveEmitter(ParticleEmitter* emitter); void RemoveGenerator(ParticleGenerator* generator); - void SetFixedStepSize(float stepSize); void SetRenderer(ParticleRenderer* renderer); void Update(float elapsedTime); void UpdateBoundingVolume(const Matrix4f& transformMatrix) override; - ParticleSystem& operator=(const ParticleSystem& emitter); + ParticleGroup& operator=(const ParticleGroup& emitter); + + // Signals: + NazaraSignal(OnParticleGroupRelease, const ParticleGroup* /*particleGroup*/); private: void MakeBoundingVolume() const override; + void OnEmitterMove(ParticleEmitter* oldEmitter, ParticleEmitter* newEmitter); + void OnEmitterRelease(const ParticleEmitter* emitter); void ResizeBuffer(); + struct EmitterEntry + { + NazaraSlot(ParticleEmitter, OnParticleEmitterMove, moveSlot); + NazaraSlot(ParticleEmitter, OnParticleEmitterRelease, releaseSlot); + + ParticleEmitter* emitter; + }; + std::set> m_dyingParticles; mutable std::vector m_buffer; std::vector m_controllers; - std::vector m_emitters; + std::vector m_emitters; std::vector m_generators; ParticleDeclarationConstRef m_declaration; ParticleRendererRef m_renderer; - bool m_fixedStepEnabled; bool m_processing; - float m_stepAccumulator; - float m_stepSize; unsigned int m_maxParticleCount; unsigned int m_particleCount; unsigned int m_particleSize; }; } -#endif // NAZARA_PARTICLESYSTEM_HPP +#endif // NAZARA_PARTICLEGROUP_HPP diff --git a/include/Nazara/Graphics/ParticleMapper.inl b/include/Nazara/Graphics/ParticleMapper.inl index a28315312..63c9c49ba 100644 --- a/include/Nazara/Graphics/ParticleMapper.inl +++ b/include/Nazara/Graphics/ParticleMapper.inl @@ -7,10 +7,20 @@ namespace Nz { + /*! + * \brief Gets a pointer to iterate through same components + * \return SparsePtr pointing to same components + * + * \param component Component to get in the declaration + * + * \remark The same components are not continguous but separated by sizeof(ParticleSize) + * \remark Produces a NazaraError if component is disabled + */ + template SparsePtr ParticleMapper::GetComponentPtr(ParticleComponent component) { - // Ensuite le composant qui nous intéresse + // Then the component that are interesting bool enabled; ComponentType type; unsigned int offset; @@ -18,7 +28,7 @@ namespace Nz if (enabled) { - ///TODO: Vérifier le rapport entre le type de l'attribut et le type template ? + ///TODO: Check the ratio between the type of the attribute and the template type ? return SparsePtr(m_ptr + offset, m_declaration->GetStride()); } else @@ -28,10 +38,20 @@ namespace Nz } } + /*! + * \brief Gets a pointer to iterate through same components + * \return SparsePtr pointing to same components + * + * \param component Component to get in the declaration + * + * \remark The same components are not continguous but separated by sizeof(ParticleSize) + * \remark Produces a NazaraError if component is disabled + */ + template SparsePtr ParticleMapper::GetComponentPtr(ParticleComponent component) const { - // Ensuite le composant qui nous intéresse + // Then the component that are interesting bool enabled; ComponentType type; unsigned int offset; @@ -39,7 +59,7 @@ namespace Nz if (enabled) { - ///TODO: Vérifier le rapport entre le type de l'attribut et le type template ? + ///TODO: Check the ratio between the type of the attribute and the template type ? return SparsePtr(m_ptr + offset, m_declaration->GetStride()); } else diff --git a/include/Nazara/Graphics/ParticleRenderer.hpp b/include/Nazara/Graphics/ParticleRenderer.hpp index d79d448f2..2ddca18b3 100644 --- a/include/Nazara/Graphics/ParticleRenderer.hpp +++ b/include/Nazara/Graphics/ParticleRenderer.hpp @@ -19,7 +19,7 @@ namespace Nz class AbstractRenderQueue; class ParticleMapper; class ParticleRenderer; - class ParticleSystem; + class ParticleGroup; using ParticleRendererConstRef = ObjectRef; using ParticleRendererLibrary = ObjectLibrary; @@ -35,7 +35,7 @@ namespace Nz ParticleRenderer(const ParticleRenderer& renderer); virtual ~ParticleRenderer(); - virtual void Render(const ParticleSystem& system, const ParticleMapper& mapper, unsigned int startId, unsigned int endId, AbstractRenderQueue* renderQueue) = 0; + virtual void Render(const ParticleGroup& system, const ParticleMapper& mapper, unsigned int startId, unsigned int endId, AbstractRenderQueue* renderQueue) = 0; // Signals: NazaraSignal(OnParticleRendererRelease, const ParticleRenderer* /*particleRenderer*/); diff --git a/include/Nazara/Graphics/ParticleStruct.hpp b/include/Nazara/Graphics/ParticleStruct.hpp index b68836935..72ce961b9 100644 --- a/include/Nazara/Graphics/ParticleStruct.hpp +++ b/include/Nazara/Graphics/ParticleStruct.hpp @@ -35,8 +35,8 @@ namespace Nz struct ParticleStruct_Sprite { Color color; - Vector2f position; - Vector2f velocity; + Vector3f position; + Vector3f velocity; UInt32 life; float rotation; }; diff --git a/include/Nazara/Graphics/Renderable.inl b/include/Nazara/Graphics/Renderable.inl index ed445ec96..804567814 100644 --- a/include/Nazara/Graphics/Renderable.inl +++ b/include/Nazara/Graphics/Renderable.inl @@ -4,17 +4,29 @@ namespace Nz { + /*! + * \brief Ensures that the bounding volume is up to date + */ + inline void Renderable::EnsureBoundingVolumeUpdated() const { if (!m_boundingVolumeUpdated) UpdateBoundingVolume(); } + /*! + * \brief Invalidates the bounding volume + */ + inline void Renderable::InvalidateBoundingVolume() { m_boundingVolumeUpdated = false; } + /*! + * \brief Updates the bounding volume + */ + inline void Renderable::UpdateBoundingVolume() const { MakeBoundingVolume(); diff --git a/include/Nazara/Graphics/SkyboxBackground.inl b/include/Nazara/Graphics/SkyboxBackground.inl index 8edb694c9..6171f1e28 100644 --- a/include/Nazara/Graphics/SkyboxBackground.inl +++ b/include/Nazara/Graphics/SkyboxBackground.inl @@ -7,31 +7,62 @@ namespace Nz { + /*! + * \brief Gets the movement offset + * \return Offset of the movement + */ + inline const Vector3f& Nz::SkyboxBackground::GetMovementOffset() const { return m_movementOffset; } + /*! + * \brief Gets the movement scale + * \return Scale of the movement + */ + inline float SkyboxBackground::GetMovementScale() const { return m_movementScale; } + /*! + * \brief Gets the texture of the background + * \return Texture of the background + */ + inline const TextureRef& SkyboxBackground::GetTexture() const { return m_texture; } + /*! + * \brief Gets the texture sampler of the background + * \return A reference to the texture sampler of the background + */ + inline TextureSampler& SkyboxBackground::GetTextureSampler() { return m_sampler; } + /*! + * \brief Gets the texture sampler of the background + * \return A constant reference to the texture sampler of the background + */ + inline const TextureSampler& SkyboxBackground::GetTextureSampler() const { return m_sampler; } + /*! + * \brief Sets the movement offset + * + * \param offset Offset of the movement + */ + inline void SkyboxBackground::SetMovementOffset(const Vector3f& offset) { NazaraAssert(std::isfinite(offset.x) && std::isfinite(offset.y) && std::isfinite(offset.z), "Offset must be a finite vector"); @@ -39,6 +70,12 @@ namespace Nz m_movementOffset = offset; } + /*! + * \brief Sets the movement scale + * + * \param scale Scale of the movement + */ + inline void SkyboxBackground::SetMovementScale(float scale) { NazaraAssert(std::isfinite(scale), "Scale must be a finite value"); @@ -46,6 +83,12 @@ namespace Nz m_movementScale = scale; } + /*! + * \brief Sets the texture of the background + * + * \param cubemapTexture Texture of the background + */ + inline void SkyboxBackground::SetTexture(TextureRef cubemapTexture) { NazaraAssert(!cubemapTexture || cubemapTexture->IsValid(), "Invalid texture"); @@ -54,11 +97,24 @@ namespace Nz m_texture = std::move(cubemapTexture); } + /*! + * \brief Sets the texture sampler of the background + * + * \param sampler Texture sampler of the background + */ + void SkyboxBackground::SetTextureSampler(const TextureSampler& sampler) { m_sampler = sampler; } + /*! + * \brief Creates a new skybox background from the arguments + * \return A reference to the newly created skybox background + * + * \param args Arguments for the skybox background + */ + template SkyboxBackgroundRef SkyboxBackground::New(Args&&... args) { diff --git a/include/Nazara/Graphics/Sprite.inl b/include/Nazara/Graphics/Sprite.inl index 8969cddf0..f0e05c5a2 100644 --- a/include/Nazara/Graphics/Sprite.inl +++ b/include/Nazara/Graphics/Sprite.inl @@ -8,6 +8,10 @@ namespace Nz { + /*! + * \brief Constructs a Sprite object by default + */ + inline Sprite::Sprite() : m_color(Color::White), m_textureCoords(0.f, 0.f, 1.f, 1.f), @@ -16,6 +20,12 @@ namespace Nz SetDefaultMaterial(); } + /*! + * \brief Constructs a Sprite object with a reference to a material + * + * \param material Reference to a material + */ + inline Sprite::Sprite(MaterialRef material) : m_color(Color::White), m_textureCoords(0.f, 0.f, 1.f, 1.f), @@ -24,6 +34,12 @@ namespace Nz SetMaterial(std::move(material), true); } + /*! + * \brief Constructs a Sprite object with a pointer to a texture + * + * \param texture Pointer to a texture + */ + inline Sprite::Sprite(Texture* texture) : m_color(Color::White), m_textureCoords(0.f, 0.f, 1.f, 1.f), @@ -32,6 +48,12 @@ namespace Nz SetTexture(texture, true); } + /*! + * \brief Constructs a Sprite object by assignation + * + * \param sprite Sprite to copy into this + */ + inline Sprite::Sprite(const Sprite& sprite) : InstancedRenderable(sprite), m_color(sprite.m_color), @@ -41,26 +63,52 @@ namespace Nz { } + /*! + * \brief Gets the color of the sprite + * \return Current color + */ + inline const Color& Sprite::GetColor() const { return m_color; } + /*! + * \brief Gets the material of the sprite + * \return Current material + */ + inline const MaterialRef& Sprite::GetMaterial() const { return m_material; } + /*! + * \brief Gets the size of the sprite + * \return Current size + */ + inline const Vector2f& Sprite::GetSize() const { return m_size; } + /*! + * \brief Gets the texture coordinates of the sprite + * \return Current texture coordinates + */ + inline const Rectf& Sprite::GetTextureCoords() const { return m_textureCoords; } + /*! + * \brief Sets the color of the billboard + * + * \param color Color for the billboard + */ + inline void Sprite::SetColor(const Color& color) { m_color = color; @@ -68,15 +116,25 @@ namespace Nz InvalidateVertices(); } + /*! + * \brief Sets the default material of the sprite (just default material) + */ + inline void Sprite::SetDefaultMaterial() { MaterialRef material = Material::New(); - material->Enable(RendererParameter_FaceCulling, false); - material->EnableLighting(false); + material->EnableFaceCulling(false); SetMaterial(std::move(material)); } + /*! + * \brief Sets the material of the sprite + * + * \param material Material for the sprite + * \param resizeSprite Should sprite be resized to the material size (diffuse map) + */ + inline void Sprite::SetMaterial(MaterialRef material, bool resizeSprite) { m_material = std::move(material); @@ -88,6 +146,12 @@ namespace Nz } } + /*! + * \brief Sets the size of the sprite + * + * \param size Size for the sprite + */ + inline void Sprite::SetSize(const Vector2f& size) { m_size = size; @@ -97,11 +161,25 @@ namespace Nz InvalidateVertices(); } + /*! + * \brief Sets the size of the sprite + * + * \param sizeX Size in X for the sprite + * \param sizeY Size in Y for the sprite + */ + inline void Sprite::SetSize(float sizeX, float sizeY) { SetSize(Vector2f(sizeX, sizeY)); } + /*! + * \brief Sets the texture of the sprite + * + * \param texture Texture for the sprite + * \param resizeSprite Should sprite be resized to the texture size + */ + inline void Sprite::SetTexture(TextureRef texture, bool resizeSprite) { if (!m_material) @@ -115,12 +193,27 @@ namespace Nz m_material->SetDiffuseMap(std::move(texture)); } + /*! + * \brief Sets the texture coordinates of the sprite + * + * \param coords Texture coordinates + */ + inline void Sprite::SetTextureCoords(const Rectf& coords) { m_textureCoords = coords; InvalidateVertices(); } + /*! + * \brief Sets the texture rectangle of the sprite + * + * \param rect Rectangles symbolizing the size of the texture + * + * \remark Produces a NazaraAssert if material is invalid + * \remark Produces a NazaraAssert if material has no diffuse map + */ + inline void Sprite::SetTextureRect(const Rectui& rect) { NazaraAssert(m_material, "Sprite has no material"); @@ -128,12 +221,19 @@ namespace Nz Texture* diffuseMap = m_material->GetDiffuseMap(); - float invWidth = 1.f/diffuseMap->GetWidth(); - float invHeight = 1.f/diffuseMap->GetHeight(); + float invWidth = 1.f / diffuseMap->GetWidth(); + float invHeight = 1.f / diffuseMap->GetHeight(); - SetTextureCoords(Rectf(invWidth*rect.x, invHeight*rect.y, invWidth*rect.width, invHeight*rect.height)); + SetTextureCoords(Rectf(invWidth * rect.x, invHeight * rect.y, invWidth * rect.width, invHeight * rect.height)); } + /*! + * \brief Sets the current sprite with the content of the other one + * \return A reference to this + * + * \param sprite The other Sprite + */ + inline Sprite& Sprite::operator=(const Sprite& sprite) { InstancedRenderable::operator=(sprite); @@ -143,18 +243,29 @@ namespace Nz m_textureCoords = sprite.m_textureCoords; m_size = sprite.m_size; - // On ne copie pas les sommets finaux car il est très probable que nos paramètres soient modifiés et qu'ils doivent être régénérés de toute façon + // We do not copy final vertices because it's highly probable that our parameters are modified and they must be regenerated InvalidateBoundingVolume(); InvalidateVertices(); return *this; } + /*! + * \brief Invalidates the vertices + */ + inline void Sprite::InvalidateVertices() { InvalidateInstanceData(0); } + /*! + * \brief Creates a new sprite from the arguments + * \return A reference to the newly created sprite + * + * \param args Arguments for the sprite + */ + template SpriteRef Sprite::New(Args&&... args) { diff --git a/include/Nazara/Graphics/TextSprite.inl b/include/Nazara/Graphics/TextSprite.inl index 312658557..2ed880a38 100644 --- a/include/Nazara/Graphics/TextSprite.inl +++ b/include/Nazara/Graphics/TextSprite.inl @@ -7,6 +7,10 @@ namespace Nz { + /*! + * \brief Constructs a TextSprite object by default + */ + inline TextSprite::TextSprite() : m_color(Color::White), m_scale(1.f) @@ -14,12 +18,24 @@ namespace Nz SetDefaultMaterial(); } + /*! + * \brief Constructs a TextSprite object with a drawer + * + * \param drawer Drawer used to compose text on the sprite + */ + inline TextSprite::TextSprite(const AbstractTextDrawer& drawer) : TextSprite() { Update(drawer); } + /*! + * \brief Constructs a TextSprite object by assignation + * + * \param sprite TextSprite to copy into this + */ + inline TextSprite::TextSprite(const TextSprite& sprite) : InstancedRenderable(sprite), m_renderInfos(sprite.m_renderInfos), @@ -40,6 +56,10 @@ namespace Nz } } + /*! + * \brief Clears the data + */ + inline void TextSprite::Clear() { m_atlases.clear(); @@ -48,21 +68,42 @@ namespace Nz m_renderInfos.clear(); } + /*! + * \brief Gets the color of the text sprite + * \return Current color + */ + inline const Color& TextSprite::GetColor() const { return m_color; } + /*! + * \brief Gets the material of the text sprite + * \return Current material + */ + inline const MaterialRef& TextSprite::GetMaterial() const { return m_material; } + /*! + * \brief Gets the current scale of the text sprite + * \return Current scale + */ + inline float TextSprite::GetScale() const { return m_scale; } + /*! + * \brief Sets the color of the text sprite + * + * \param color Color for the text sprite + */ + inline void TextSprite::SetColor(const Color& color) { m_color = color; @@ -70,24 +111,40 @@ namespace Nz InvalidateVertices(); } + /*! + * \brief Sets the default material of the text sprite (just default material) + */ + + inline void TextSprite::SetDefaultMaterial() { MaterialRef material = Material::New(); - material->Enable(RendererParameter_Blend, true); - material->Enable(RendererParameter_DepthWrite, false); - material->Enable(RendererParameter_FaceCulling, false); - material->EnableLighting(false); + material->EnableBlending(true); + material->EnableDepthWrite(false); + material->EnableFaceCulling(false); material->SetDstBlend(BlendFunc_InvSrcAlpha); material->SetSrcBlend(BlendFunc_SrcAlpha); SetMaterial(material); } + /*! + * \brief Sets the material of the text sprite + * + * \param material Material for the text sprite + */ + inline void TextSprite::SetMaterial(MaterialRef material) { m_material = std::move(material); } + /*! + * \brief Sets the current scale of the text sprite + * + * \param scale Scale of the text sprite + */ + inline void TextSprite::SetScale(float scale) { m_scale = scale; @@ -95,10 +152,12 @@ namespace Nz InvalidateVertices(); } - inline void TextSprite::InvalidateVertices() - { - InvalidateInstanceData(0); - } + /*! + * \brief Sets the current text sprite with the content of the other one + * \return A reference to this + * + * \param text sprite The other TextSprite + */ inline TextSprite& TextSprite::operator=(const TextSprite& text) { @@ -130,6 +189,22 @@ namespace Nz return *this; } + /*! + * \brief Invalidates the vertices + */ + + inline void TextSprite::InvalidateVertices() + { + InvalidateInstanceData(0); + } + + /*! + * \brief Creates a new text sprite from the arguments + * \return A reference to the newly created text sprite + * + * \param args Arguments for the text sprite + */ + template TextSpriteRef TextSprite::New(Args&&... args) { diff --git a/include/Nazara/Graphics/TextureBackground.inl b/include/Nazara/Graphics/TextureBackground.inl index c40ad3d2d..31067f36c 100644 --- a/include/Nazara/Graphics/TextureBackground.inl +++ b/include/Nazara/Graphics/TextureBackground.inl @@ -7,11 +7,22 @@ namespace Nz { + /*! + * \brief Gets the texture of the background + * \return Texture of the background + */ + inline const TextureRef& TextureBackground::GetTexture() const { return m_texture; } + /*! + * \brief Sets the texture of the background + * + * \param texture Texture of the background + */ + inline void TextureBackground::SetTexture(TextureRef texture) { NazaraAssert(!texture || texture->IsValid(), "Invalid texture"); @@ -19,6 +30,13 @@ namespace Nz m_texture = std::move(texture); } + /*! + * \brief Creates a new texture background from the arguments + * \return A reference to the newly created texture background + * + * \param args Arguments for the texture background + */ + template TextureBackgroundRef TextureBackground::New(Args&&... args) { diff --git a/include/Nazara/Graphics/TileMap.hpp b/include/Nazara/Graphics/TileMap.hpp new file mode 100644 index 000000000..89a901b9a --- /dev/null +++ b/include/Nazara/Graphics/TileMap.hpp @@ -0,0 +1,97 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_TILEMAP_HPP +#define NAZARA_TILEMAP_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class TileMap; + + using TileMapConstRef = ObjectRef; + using TileMapLibrary = ObjectLibrary; + using TileMapRef = ObjectRef; + + class NAZARA_GRAPHICS_API TileMap : public InstancedRenderable + { + friend TileMapLibrary; + friend class Graphics; + + public: + struct Tile; + + inline TileMap(const Nz::Vector2ui& mapSize, const Nz::Vector2f& tileSize, std::size_t materialCount = 1); + TileMap(const TileMap& TileMap) = default; + TileMap(TileMap&&) = delete; + ~TileMap() = default; + + void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const override; + + inline void DisableTile(const Vector2ui& tilePos); + inline void DisableTiles(); + inline void DisableTiles(const Vector2ui* tilesPos, std::size_t tileCount); + + inline void EnableTile(const Vector2ui& tilePos, const Rectf& coords, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTile(const Vector2ui& tilePos, const Rectui& rect, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTiles(const Rectf& coords, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTiles(const Rectui& rect, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTiles(const Vector2ui* tilesPos, std::size_t tileCount, const Rectf& coords, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTiles(const Vector2ui* tilesPos, std::size_t tileCount, const Rectui& rect, const Color& color = Color::White, std::size_t materialIndex = 0U); + + inline const MaterialRef& GetMaterial(std::size_t index) const; + inline std::size_t GetMaterialCount() const; + inline const Vector2ui& GetMapSize() const; + inline Vector2f GetSize() const; + inline const Tile& GetTile(const Vector2ui& tilePos) const; + inline const Vector2f& GetTileSize() const; + + inline void SetMaterial(std::size_t index, MaterialRef material); + + inline TileMap& operator=(const TileMap& TileMap); + TileMap& operator=(TileMap&& TileMap) = delete; + + template static TileMapRef New(Args&&... args); + + struct Tile + { + std::size_t layerIndex = 0U; + Color color = Color::White; + Rectf textureCoords = Rectf::Zero(); + bool enabled = false; + }; + + private: + void MakeBoundingVolume() const override; + void UpdateData(InstanceData* instanceData) const override; + + static bool Initialize(); + static void Uninitialize(); + + struct Layer + { + MaterialRef material; + std::set tiles; + }; + + std::vector m_tiles; + std::vector m_layers; + Vector2ui m_mapSize; + Vector2f m_tileSize; + + static TileMapLibrary::LibraryMap s_library; + }; +} + +#include + +#endif // NAZARA_TILEMAP_HPP diff --git a/include/Nazara/Graphics/TileMap.inl b/include/Nazara/Graphics/TileMap.inl new file mode 100644 index 000000000..405683f40 --- /dev/null +++ b/include/Nazara/Graphics/TileMap.inl @@ -0,0 +1,441 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \brief Constructs a TileMap object, containing mapSize tileSize-sized tiles + * + * \param mapSize Number of tiles in each dimension, must be + * \param tileSize Size of each tile of the TileMap + * \param materialCount The maximum number of differents Material this TileMap will use + * + * \remark When constructed, a TileMap has no tile active and will not be rendered + * To use it, you have to enable some tiles. + * + * \remark The default material is used for every material requested + */ + inline TileMap::TileMap(const Nz::Vector2ui& mapSize, const Nz::Vector2f& tileSize, std::size_t materialCount) : + m_tiles(mapSize.x * mapSize.y), + m_layers(materialCount), + m_mapSize(mapSize), + m_tileSize(tileSize) + { + NazaraAssert(m_tiles.size() != 0U, "Invalid map size"); + NazaraAssert(m_tileSize.x != 0U && m_tileSize.y != 0U, "Invalid tile size"); + NazaraAssert(m_layers.size() != 0U, "Invalid material count"); + + for (Layer& layer : m_layers) + layer.material = Material::GetDefault(); + + InvalidateBoundingVolume(); + } + + /*! + * \brief Disable the tile at position tilePos, disabling rendering at this location + * + * \param tilePos Position of the tile to disable + * + * \see DisableTiles + */ + inline void TileMap::DisableTile(const Vector2ui& tilePos) + { + NazaraAssert(tilePos.x < m_mapSize.x && tilePos.y < m_mapSize.y, "Tile position is out of bounds"); + + std::size_t tileIndex = tilePos.y * m_mapSize.x + tilePos.x; + Tile& tile = m_tiles[tileIndex]; + tile.enabled = false; + + m_layers[tile.layerIndex].tiles.erase(tileIndex); + + InvalidateInstanceData(1U << tile.layerIndex); + } + + /*! + * \brief Disable all tiles + */ + inline void TileMap::DisableTiles() + { + for (Tile& tile : m_tiles) + tile.enabled = false; + + for (Layer& layer : m_layers) + layer.tiles.clear(); + + InvalidateInstanceData(0xFFFFFFFF); + } + + /*! + * \brief Disable tileCount tiles at positions contained at tilesPos location, disabling rendering at those locations + * + * This is equivalent to calling tileCount times DisableTile with the positions contained at tilesPos + * + * \param tilesPos Pointer to a valid array of at least tileCount positions + * \param tileCount Number of tiles to disable + * + * \remark if tileCount is zero, this is a no-op and the value of tilesPos is not used + * + * \see DisableTile + */ + inline void TileMap::DisableTiles(const Vector2ui* tilesPos, std::size_t tileCount) + { + NazaraAssert(tilesPos || tileCount == 0, "Invalid tile position array with a non-zero tileCount"); + + UInt32 invalidatedLayers = 0; + + for (std::size_t i = 0; i < tileCount; ++i) + { + NazaraAssert(tilesPos->x < m_mapSize.x && tilesPos->y < m_mapSize.y, "Tile position is out of bounds"); + + std::size_t tileIndex = tilesPos->y * m_mapSize.x + tilesPos->x; + Tile& tile = m_tiles[tileIndex]; + tile.enabled = false; + + m_layers[tile.layerIndex].tiles.erase(tileIndex); + + invalidatedLayers |= 1U << tile.layerIndex; + + tilesPos++; + } + + if (tileCount > 0) + InvalidateInstanceData(invalidatedLayers); + } + + /*! + * \brief Enable and sets the tile at position tilePos + * + * Setup the tile at position tilePos using color, normalized coordinates coords and materialIndex + * + * \param tilePos Position of the tile to enable + * \param coords Normalized coordinates ([0..1]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \see EnableTiles + */ + inline void TileMap::EnableTile(const Vector2ui& tilePos, const Rectf& coords, const Color& color, std::size_t materialIndex) + { + NazaraAssert(tilePos.x < m_mapSize.x && tilePos.y < m_mapSize.y, "Tile position is out of bounds"); + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + UInt32 invalidatedLayers = 1U << materialIndex; + + std::size_t tileIndex = tilePos.y * m_mapSize.x + tilePos.x; + Tile& tile = m_tiles[tilePos.y * m_mapSize.x + tilePos.x]; + + if (!tile.enabled) + m_layers[materialIndex].tiles.insert(tileIndex); + else if (materialIndex != tile.layerIndex) + { + m_layers[materialIndex].tiles.erase(tileIndex); + m_layers[tile.layerIndex].tiles.insert(tileIndex); + + invalidatedLayers |= 1U << tile.layerIndex; + } + + tile.enabled = true; + tile.color = color; + tile.textureCoords = coords; + tile.layerIndex = materialIndex; + + InvalidateInstanceData(invalidatedLayers); + } + + /*! + * \brief Enable and sets the tile at position tilePos + * + * Setup the tile at position tilePos using color, unnormalized coordinates rect and materialIndex + * + * \param tilePos Position of the tile to enable + * \param coords Unnormalized coordinates ([0..size]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \remark The material at [materialIndex] must have a valid diffuse map before using this function, + * as the size of the material diffuse map is used to compute normalized texture coordinates before returning. + * + * \see EnableTiles + */ + inline void TileMap::EnableTile(const Vector2ui& tilePos, const Rectui& rect, const Color& color, std::size_t materialIndex) + { + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + NazaraAssert(m_layers[materialIndex].material->HasDiffuseMap(), "Material has no diffuse map"); + + Texture* diffuseMap = m_layers[materialIndex].material->GetDiffuseMap(); + float invWidth = 1.f / diffuseMap->GetWidth(); + float invHeight = 1.f / diffuseMap->GetHeight(); + + Rectf unnormalizedCoords(invWidth * rect.x, invHeight * rect.y, invWidth * rect.width, invHeight * rect.height); + EnableTile(tilePos, unnormalizedCoords, color, materialIndex); + } + + /*! + * \brief Enable and sets all the tiles + * + * Setup all tiles using color, normalized coordinates coords and materialIndex + * + * \param coords Normalized coordinates ([0..1]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \remark The material at [materialIndex] must have a valid diffuse map before using this function, + * as the size of the material diffuse map is used to compute normalized texture coordinates before returning. + * + * \see EnableTile + */ + inline void TileMap::EnableTiles(const Rectf& coords, const Color& color, std::size_t materialIndex) + { + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + for (Layer& layer : m_layers) + layer.tiles.clear(); + + std::size_t tileIndex = 0; + for (Tile& tile : m_tiles) + { + tile.enabled = true; + tile.color = color; + tile.textureCoords = coords; + tile.layerIndex = materialIndex; + + m_layers[materialIndex].tiles.insert(tileIndex++); + } + + InvalidateInstanceData(0xFFFFFFFF); + } + + /*! + * \brief Enable and sets all the tiles + * + * Setup all tiles using color, unnormalized coordinates coords and materialIndex + * + * \param coords Unnormalized coordinates ([0..size]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \remark The material at [materialIndex] must have a valid diffuse map before using this function, + * as the size of the material diffuse map is used to compute normalized texture coordinates before returning. + * + * \see EnableTile + */ + inline void TileMap::EnableTiles(const Rectui& rect, const Color& color, std::size_t materialIndex) + { + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + Texture* diffuseMap = m_layers[materialIndex].material->GetDiffuseMap(); + float invWidth = 1.f / diffuseMap->GetWidth(); + float invHeight = 1.f / diffuseMap->GetHeight(); + + Rectf unnormalizedCoords(invWidth * rect.x, invHeight * rect.y, invWidth * rect.width, invHeight * rect.height); + EnableTiles(unnormalizedCoords, color, materialIndex); + } + + /*! + * \brief Enable and sets tileCount tiles at positions contained at tilesPos location, enabling rendering at those locations + * + * Setup all tiles using color, normalized coordinates coords and materialIndex + * + * \param tilesPos Pointer to a valid array of at least tileCount positions + * \param tileCount Number of tiles to enable + * \param coords Normalized coordinates ([0..1]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \see EnableTile + */ + inline void TileMap::EnableTiles(const Vector2ui* tilesPos, std::size_t tileCount, const Rectf& coords, const Color& color, std::size_t materialIndex) + { + NazaraAssert(tilesPos || tileCount == 0, "Invalid tile position array with a non-zero tileCount"); + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + UInt32 invalidatedLayers = 1U << materialIndex; + + for (std::size_t i = 0; i < tileCount; ++i) + { + NazaraAssert(tilesPos->x < m_mapSize.x && tilesPos->y < m_mapSize.y, "Tile position is out of bounds"); + + std::size_t tileIndex = tilesPos->y * m_mapSize.x + tilesPos->x; + Tile& tile = m_tiles[tileIndex]; + + if (!tile.enabled) + m_layers[materialIndex].tiles.insert(tileIndex); + else if (materialIndex != tile.layerIndex) + { + m_layers[materialIndex].tiles.erase(tileIndex); + m_layers[tile.layerIndex].tiles.insert(tileIndex); + + invalidatedLayers |= 1U << tile.layerIndex; + } + + tile.enabled = true; + tile.color = color; + tile.textureCoords = coords; + tile.layerIndex = materialIndex; + tilesPos++; + } + + if (tileCount > 0) + InvalidateInstanceData(invalidatedLayers); + } + + /*! + * \brief Enable and sets tileCount tiles at positions contained at tilesPos location, enabling rendering at those locations + * + * Setup all tiles using color, unnormalized coordinates coords and materialIndex + * + * \param tilesPos Pointer to a valid array of at least tileCount positions + * \param tileCount Number of tiles to enable + * \param coords Unnormalized coordinates ([0..size]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \remark The material at [materialIndex] must have a valid diffuse map before using this function, + * as the size of the material diffuse map is used to compute normalized texture coordinates before returning. + * + * \see EnableTile + */ + inline void TileMap::EnableTiles(const Vector2ui* tilesPos, std::size_t tileCount, const Rectui& rect, const Color& color, std::size_t materialIndex) + { + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + NazaraAssert(m_layers[materialIndex].material->HasDiffuseMap(), "Material has no diffuse map"); + + Texture* diffuseMap = m_layers[materialIndex].material->GetDiffuseMap(); + float invWidth = 1.f / diffuseMap->GetWidth(); + float invHeight = 1.f / diffuseMap->GetHeight(); + + Rectf unnormalizedCoords(invWidth * rect.x, invHeight * rect.y, invWidth * rect.width, invHeight * rect.height); + EnableTiles(tilesPos, tileCount, unnormalizedCoords, color, materialIndex); + } + + /*! + * \brief Gets the material at position index used by the TileMap + * + * \param index Index of the material to query + * + * \return Material at index + */ + inline const MaterialRef& TileMap::GetMaterial(std::size_t index) const + { + NazaraAssert(index < m_layers.size(), "Material out of bounds"); + + return m_layers[index].material; + } + + /*! + * \brief Gets the maximum material count this TileMap can use + * \return Material count + */ + inline std::size_t TileMap::GetMaterialCount() const + { + return m_layers.size(); + } + + /*! + * \brief Gets the tilemap size (i.e. number of tiles in each dimension) + * \return Number of tiles in each dimension + * + * \see GetSize + * \see GetTileSize + */ + inline const Vector2ui& TileMap::GetMapSize() const + { + return m_mapSize; + } + + /*! + * \brief Returns the size of the tilemap in units (which is equivalent to GetMapSize() * GetTileSize()) + * \return Maximum size in units occupied by this tilemap + * + * \see GetMapSize + * \see GetTileSize + */ + inline Vector2f TileMap::GetSize() const + { + return Vector2f(m_mapSize) * m_tileSize; + } + + /*! + * \brief Returns informations about a particular tile + * + * \param tilePos Position of the tile to get (enabled or not) + * + * \return Maximum size in units occupied by this tilemap + */ + inline const TileMap::Tile& TileMap::GetTile(const Vector2ui& tilePos) const + { + NazaraAssert(tilePos.x < m_mapSize.x && tilePos.y < m_mapSize.y, "Tile position is out of bounds"); + + return m_tiles[tilePos.y * m_mapSize.x + tilePos.x]; + } + + /*! + * \brief Gets the tile size (i.e. number of units occupied by a tile in each dimension) + * \return Tile size in each dimension + * + * \see GetMapSize + * \see GetSize + */ + inline const Vector2f& TileMap::GetTileSize() const + { + return m_tileSize; + } + + /*! + * \brief Sets a material of the TileMap + * + * \param index Index of the material to change + * \param material Material for the TileMap + */ + inline void TileMap::SetMaterial(std::size_t index, MaterialRef material) + { + NazaraAssert(index < m_layers.size(), "Material out of bounds"); + + m_layers[index].material = std::move(material); + } + + /*! + * \brief Sets the current TileMap with the content of the other one + * \return A reference to this + * + * \param TileMap The other TileMap + */ + inline TileMap& TileMap::operator=(const TileMap& TileMap) + { + InstancedRenderable::operator=(TileMap); + + m_layers = TileMap.m_layers; + m_mapSize = TileMap.m_mapSize; + m_tiles = TileMap.m_tiles; + m_tileSize = TileMap.m_tileSize; + + // We do not copy final vertices because it's highly probable that our parameters are modified and they must be regenerated + InvalidateBoundingVolume(); + InvalidateInstanceData(0xFFFFFFFF); + + return *this; + } + + /*! + * \brief Creates a new TileMap from the arguments + * \return A reference to the newly created TileMap + * + * \param args Arguments for the TileMap + */ + template + TileMapRef TileMap::New(Args&&... args) + { + std::unique_ptr object(new TileMap(std::forward(args)...)); + object->SetPersistent(false); + + return object.release(); + } +} + +#include diff --git a/include/Nazara/Lua/LuaClass.inl b/include/Nazara/Lua/LuaClass.inl index 710b50416..89487b653 100644 --- a/include/Nazara/Lua/LuaClass.inl +++ b/include/Nazara/Lua/LuaClass.inl @@ -21,6 +21,8 @@ namespace Nz { SetConstructor([] (Nz::LuaInstance& lua, T* instance) { + NazaraUnused(lua); + PlacementNew(instance); return true; }); @@ -132,9 +134,9 @@ namespace Nz lua.SetField(pair.first); // Method name } - m_info->instanceGetters[m_info->name] = [info = m_info] (LuaInstance& lua) + m_info->instanceGetters[m_info->name] = [info = m_info] (LuaInstance& instance) { - return static_cast(lua.CheckUserdata(1, info->name)); + return static_cast(instance.CheckUserdata(1, info->name)); }; } lua.Pop(); // On pop la metatable @@ -391,11 +393,11 @@ namespace Nz if (!lua.IsValid(-1)) { - for (const ParentFunc& getter : info->parentGetters) + for (const ParentFunc& parentGetter : info->parentGetters) { lua.Pop(); //< Pop the last nil value - getter(lua, instance); + parentGetter(lua, instance); if (lua.IsValid(-1)) return; } diff --git a/include/Nazara/Lua/LuaInstance.hpp b/include/Nazara/Lua/LuaInstance.hpp index b924f6a05..9affe6f87 100644 --- a/include/Nazara/Lua/LuaInstance.hpp +++ b/include/Nazara/Lua/LuaInstance.hpp @@ -84,7 +84,7 @@ namespace Nz bool Execute(const String& code); bool ExecuteFromFile(const String& filePath); - bool ExecuteFromMemory(const void* data, unsigned int size); + bool ExecuteFromMemory(const void* data, std::size_t size); bool ExecuteFromStream(Stream& stream); int GetAbsIndex(int index) const; @@ -142,10 +142,10 @@ namespace Nz void PushNumber(double value) const; void PushReference(int ref) const; void PushString(const char* str) const; - void PushString(const char* str, unsigned int size) const; + void PushString(const char* str, std::size_t size) const; void PushString(const String& str) const; void PushTable(unsigned int sequenceElementCount = 0, unsigned int arrayElementCount = 0) const; - void* PushUserdata(unsigned int size) const; + void* PushUserdata(std::size_t size) const; void PushValue(int index) const; void Remove(int index) const; @@ -158,7 +158,7 @@ namespace Nz void SetMetatable(const char* tname) const; void SetMetatable(const String& tname) const; void SetMetatable(int index) const; - void SetMemoryLimit(UInt32 memoryLimit); + void SetMemoryLimit(std::size_t memoryLimit); void SetTable(int index = -3) const; void SetTimeLimit(UInt32 timeLimit); @@ -185,8 +185,8 @@ namespace Nz static int ProxyFunc(lua_State* state); static void TimeLimiter(lua_State* state, lua_Debug* debug); - UInt32 m_memoryLimit; - UInt32 m_memoryUsage; + std::size_t m_memoryLimit; + std::size_t m_memoryUsage; UInt32 m_timeLimit; Clock m_clock; String m_lastError; diff --git a/include/Nazara/Math/Algorithm.hpp b/include/Nazara/Math/Algorithm.hpp index 74cc45593..45281bb50 100644 --- a/include/Nazara/Math/Algorithm.hpp +++ b/include/Nazara/Math/Algorithm.hpp @@ -1,3 +1,4 @@ + // Copyright (C) 2015 Jérôme Leclercq // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp @@ -28,6 +29,10 @@ #define M_SQRT3 1.7320508075688772935274463 #endif +#ifndef M_SQRT5 +#define M_SQRT5 2.23606797749979 +#endif + namespace Nz { template /*constexpr*/ T Approach(T value, T objective, T increment); diff --git a/include/Nazara/Math/Algorithm.inl b/include/Nazara/Math/Algorithm.inl index e7e205e45..2c865ad1b 100644 --- a/include/Nazara/Math/Algorithm.inl +++ b/include/Nazara/Math/Algorithm.inl @@ -90,7 +90,7 @@ namespace Nz // Appel de la fonction avec le nombre 32bits, si le résultat est non-nul nous avons la réponse unsigned int log2 = IntegralLog2Pot(val); - if (log2) + if (log2 || val == 1) return log2 + i*8; } @@ -105,7 +105,7 @@ namespace Nz * * \param value Initial value * \param objective Target value - * \parma increment One step value + * \param increment One step value */ template @@ -412,7 +412,7 @@ namespace Nz * \brief Gets the log in base 2 of integral number, only works for power of two ! * \return Log of the number * - * \param number To get log in base 2 + * \param pot To get log in base 2 * * \remark Only works for power of two * \remark If number is 0, 0 is returned @@ -431,7 +431,7 @@ namespace Nz * \return base^exponent for integral * * \param base Base of the exponentation - * \parma exponent Power for the base + * \param exponent Power for the base */ //TODO: Mark as constexpr when supported by all major compilers diff --git a/include/Nazara/Math/BoundingVolume.hpp b/include/Nazara/Math/BoundingVolume.hpp index 6f1f4b804..6c863b729 100644 --- a/include/Nazara/Math/BoundingVolume.hpp +++ b/include/Nazara/Math/BoundingVolume.hpp @@ -53,6 +53,7 @@ namespace Nz void Update(const Vector3& translation); BoundingVolume operator*(T scalar) const; + BoundingVolume& operator=(const BoundingVolume& other) = default; BoundingVolume& operator*=(T scalar); diff --git a/include/Nazara/Math/Box.hpp b/include/Nazara/Math/Box.hpp index a2a6f25d1..25e9a8069 100644 --- a/include/Nazara/Math/Box.hpp +++ b/include/Nazara/Math/Box.hpp @@ -79,6 +79,7 @@ namespace Nz Box operator*(T scalar) const; Box operator*(const Vector3& vec) const; + Box& operator=(const Box& other) = default; Box& operator*=(T scalar); Box& operator*=(const Vector3& vec); diff --git a/include/Nazara/Math/Box.inl b/include/Nazara/Math/Box.inl index a6200cbe4..71c98a70b 100644 --- a/include/Nazara/Math/Box.inl +++ b/include/Nazara/Math/Box.inl @@ -787,7 +787,7 @@ namespace Nz * \brief Multiplies the lengths with the scalar * \return A box where the position is the same and width, height and depth are the product of the old width, height and depth and the scalar * - * \param scale The scalar to multiply width, height and depth with + * \param scalar The scalar to multiply width, height and depth with */ template diff --git a/include/Nazara/Math/EulerAngles.hpp b/include/Nazara/Math/EulerAngles.hpp index 6ada39982..ab267e81f 100644 --- a/include/Nazara/Math/EulerAngles.hpp +++ b/include/Nazara/Math/EulerAngles.hpp @@ -47,6 +47,7 @@ namespace Nz EulerAngles operator-(const EulerAngles& angles) const; /*EulerAngles operator*(const EulerAngles& angles) const; EulerAngles operator/(const EulerAngles& angles) const;*/ + EulerAngles& operator=(const EulerAngles& other) = default; EulerAngles& operator+=(const EulerAngles& angles); EulerAngles& operator-=(const EulerAngles& angles); diff --git a/include/Nazara/Math/Frustum.hpp b/include/Nazara/Math/Frustum.hpp index 2e51c0bd6..96c78004c 100644 --- a/include/Nazara/Math/Frustum.hpp +++ b/include/Nazara/Math/Frustum.hpp @@ -50,6 +50,8 @@ namespace Nz IntersectionSide Intersect(const Sphere& sphere) const; IntersectionSide Intersect(const Vector3* points, unsigned int pointCount) const; + Frustum& operator=(const Frustum& other) = default; + Frustum& Set(const Frustum& frustum); template Frustum& Set(const Frustum& frustum); diff --git a/include/Nazara/Math/Frustum.inl b/include/Nazara/Math/Frustum.inl index b9f56c1e1..71f385cac 100644 --- a/include/Nazara/Math/Frustum.inl +++ b/include/Nazara/Math/Frustum.inl @@ -491,8 +491,8 @@ namespace Nz * * \remark If volume is infinite, IntersectionSide_Intersecting is returned * \remark If volume is null, IntersectionSide_Outside is returned - * \remark If enumeration of the volume is not defined in Extend, a NazaraError is thrown and false is returned - * \remark If enumeration of the intersection is not defined in IntersectionSide, a NazaraError is thrown and false is returned. This should not never happen for a user of the library + * \remark If enumeration of the volume is not defined in Extend, a NazaraError is thrown and IntersectionSide_Outside is returned + * \remark If enumeration of the intersection is not defined in IntersectionSide, a NazaraError is thrown and IntersectionSide_Outside is returned. This should not never happen for a user of the library */ template diff --git a/include/Nazara/Math/Matrix4.inl b/include/Nazara/Math/Matrix4.inl index b0decee2a..4cf2eaa6e 100644 --- a/include/Nazara/Math/Matrix4.inl +++ b/include/Nazara/Math/Matrix4.inl @@ -750,7 +750,7 @@ namespace Nz * \brief Inverts this matrix * \return A reference to this matrix inverted * - * \param bool Optional argument to know if matrix has been successfully inverted + * \param succeeded Optional argument to know if matrix has been successfully inverted * * \see InverseAffine */ @@ -769,7 +769,7 @@ namespace Nz * \brief Inverts this matrix * \return A reference to this matrix inverted * - * \param bool Optional argument to know if matrix has been successfully inverted + * \param succeeded Optional argument to know if matrix has been successfully inverted * * \see Inverse */ @@ -1487,7 +1487,7 @@ namespace Nz * \brief Compares the matrix to other one * \return true if the matrices are the same * - * \param matrix Other matrix to compare with + * \param mat Other matrix to compare with */ template @@ -1504,7 +1504,7 @@ namespace Nz * \brief Compares the matrix to other one * \return false if the matrices are the same * - * \param matrix Other matrix to compare with + * \param mat Other matrix to compare with */ template diff --git a/include/Nazara/Math/OrientedBox.hpp b/include/Nazara/Math/OrientedBox.hpp index 43d4b1aaf..2b806e56b 100644 --- a/include/Nazara/Math/OrientedBox.hpp +++ b/include/Nazara/Math/OrientedBox.hpp @@ -52,6 +52,7 @@ namespace Nz Vector3 operator()(unsigned int i) const; OrientedBox operator*(T scalar) const; + OrientedBox& operator=(const OrientedBox& other) = default; OrientedBox& operator*=(T scalar); diff --git a/include/Nazara/Math/OrientedBox.inl b/include/Nazara/Math/OrientedBox.inl index 9bcd94e1d..3320dce1a 100644 --- a/include/Nazara/Math/OrientedBox.inl +++ b/include/Nazara/Math/OrientedBox.inl @@ -340,7 +340,7 @@ namespace Nz * \brief Multiplies the lengths with the scalar * \return A OrientedBox where the position is the same and width, height and depth are the product of the old width, height and depth and the scalar * - * \param scale The scalar to multiply width, height and depth with + * \param scalar The scalar to multiply width, height and depth with */ template diff --git a/include/Nazara/Math/Plane.hpp b/include/Nazara/Math/Plane.hpp index f37fc90c7..0eb201123 100644 --- a/include/Nazara/Math/Plane.hpp +++ b/include/Nazara/Math/Plane.hpp @@ -45,6 +45,8 @@ namespace Nz String ToString() const; + Plane& operator=(const Plane& other) = default; + bool operator==(const Plane& plane) const; bool operator!=(const Plane& plane) const; diff --git a/include/Nazara/Math/Ray.hpp b/include/Nazara/Math/Ray.hpp index b391ad866..7e8c0790a 100644 --- a/include/Nazara/Math/Ray.hpp +++ b/include/Nazara/Math/Ray.hpp @@ -61,6 +61,7 @@ namespace Nz String ToString() const; Vector3 operator*(T lambda) const; + Ray& operator=(const Ray& other) = default; bool operator==(const Ray& ray) const; bool operator!=(const Ray& ray) const; diff --git a/include/Nazara/Math/Rect.hpp b/include/Nazara/Math/Rect.hpp index 393d8d116..843ff6126 100644 --- a/include/Nazara/Math/Rect.hpp +++ b/include/Nazara/Math/Rect.hpp @@ -71,6 +71,7 @@ namespace Nz Rect operator*(const Vector2& vec) const; Rect operator/(T scalar) const; Rect operator/(const Vector2& vec) const; + Rect& operator=(const Rect& other) = default; Rect& operator*=(T scalar); Rect& operator*=(const Vector2& vec); diff --git a/include/Nazara/Math/Rect.inl b/include/Nazara/Math/Rect.inl index f53dd0c0f..21d0e7e93 100644 --- a/include/Nazara/Math/Rect.inl +++ b/include/Nazara/Math/Rect.inl @@ -629,7 +629,7 @@ namespace Nz * \brief Multiplies the lengths with the scalar * \return A rectangle where the position is the same and width and height are the product of the old width and height and the scalar * - * \param scale The scalar to multiply width and height with + * \param scalar The scalar to multiply width and height with */ template @@ -655,13 +655,13 @@ namespace Nz * \brief Divides the lengths with the scalar * \return A rectangle where the position is the same and width and height are the quotient of the old width and height and the scalar * - * \param scale The scalar to divide width and height with + * \param scalar The scalar to divide width and height with */ template Rect Rect::operator/(T scalar) const { - return Rect(x, y, width/scalar, height/scalar); + return Rect(x, y, width / scalar, height / scalar); } /*! @@ -745,7 +745,7 @@ namespace Nz * \brief Compares the rectangle to other one * \return true if the rectangles are the same * - * \param rec Other rectangle to compare with + * \param rect Other rectangle to compare with */ template @@ -759,7 +759,7 @@ namespace Nz * \brief Compares the rectangle to other one * \return false if the rectangles are the same * - * \param rec Other rectangle to compare with + * \param rect Other rectangle to compare with */ template diff --git a/include/Nazara/Math/Sphere.hpp b/include/Nazara/Math/Sphere.hpp index 929c1ec71..8f7ac9e14 100644 --- a/include/Nazara/Math/Sphere.hpp +++ b/include/Nazara/Math/Sphere.hpp @@ -67,6 +67,7 @@ namespace Nz T operator[](unsigned int i) const; Sphere operator*(T scalar) const; + Sphere& operator=(const Sphere& other) = default; Sphere& operator*=(T scalar); diff --git a/include/Nazara/Math/Sphere.inl b/include/Nazara/Math/Sphere.inl index 4d30c7ca3..0a8d1adae 100644 --- a/include/Nazara/Math/Sphere.inl +++ b/include/Nazara/Math/Sphere.inl @@ -132,7 +132,7 @@ namespace Nz } /*! - * \brief Returns the distance from the center of the sphere to the point + * \brief Returns the distance from the sphere to the point (is negative when the point is inside the sphere) * \return Distance to the point * * \param X X position of the point @@ -145,12 +145,11 @@ namespace Nz template T Sphere::Distance(T X, T Y, T Z) const { - Vector3 distance(X-x, Y-y, Z-z); - return distance.GetLength(); + return Distance({X, Y, Z}); } /*! - * \brief Returns the distance from the center of the sphere to the point + * \brief Returns the distance from the sphere to the point (is negative when the point is inside the sphere) * \return Distance to the point * * \param point Position of the point @@ -161,7 +160,7 @@ namespace Nz template T Sphere::Distance(const Vector3& point) const { - return Distance(point.x, point.y, point.z); + return Vector3f::Distance(point, GetPosition()) - radius; } /*! @@ -305,7 +304,7 @@ namespace Nz template bool Sphere::Intersect(const Sphere& sphere) const { - return SquaredDistance(sphere.x, sphere.y, sphere.z) - radius * radius <= sphere.radius * sphere.radius; + return SquaredDistance(sphere.x, sphere.y, sphere.z) <= sphere.radius * sphere.radius; } /*! @@ -460,7 +459,7 @@ namespace Nz } /*! - * \brief Returns the squared distance from the center of the sphere to the point + * \brief Returns the squared distance from the sphere to the point (can be negative if the point is inside the sphere) * \return Squared distance to the point * * \param X X position of the point @@ -469,27 +468,24 @@ namespace Nz * * \see Distance */ - template T Sphere::SquaredDistance(T X, T Y, T Z) const { - Vector3 distance(X - x, Y - y, Z - z); - return distance.GetSquaredLength(); + return SquaredDistance({X, Y, Z}); } /*! - * \brief Returns the squared distance from the center of the sphere to the point + * \brief Returns the squared distance from the sphere to the point (can be negative if the point is inside the sphere) * \return Squared distance to the point * * \param point Position of the point * * \see Distance */ - template T Sphere::SquaredDistance(const Vector3& point) const { - return SquaredDistance(point.x, point.y, point.z); + return Vector3f::SquaredDistance(point, GetPosition() + (point - GetPosition()).Normalize() * radius); } /*! diff --git a/include/Nazara/Math/Vector2.hpp b/include/Nazara/Math/Vector2.hpp index b275d0544..e0ac1d68d 100644 --- a/include/Nazara/Math/Vector2.hpp +++ b/include/Nazara/Math/Vector2.hpp @@ -76,6 +76,7 @@ namespace Nz Vector2 operator*(T scale) const; Vector2 operator/(const Vector2& vec) const; Vector2 operator/(T scale) const; + Vector2& operator=(const Vector2& other) = default; Vector2& operator+=(const Vector2& vec); Vector2& operator-=(const Vector2& vec); diff --git a/include/Nazara/Math/Vector3.hpp b/include/Nazara/Math/Vector3.hpp index b9d16011c..4e6da14ec 100644 --- a/include/Nazara/Math/Vector3.hpp +++ b/include/Nazara/Math/Vector3.hpp @@ -87,6 +87,7 @@ namespace Nz Vector3 operator*(T scale) const; Vector3 operator/(const Vector3& vec) const; Vector3 operator/(T scale) const; + Vector3& operator=(const Vector3& vec) = default; Vector3& operator+=(const Vector3& vec); Vector3& operator-=(const Vector3& vec); @@ -105,12 +106,15 @@ namespace Nz static Vector3 Backward(); static Vector3 CrossProduct(const Vector3& vec1, const Vector3& vec2); static T DotProduct(const Vector3& vec1, const Vector3& vec2); + static T Distance(const Vector3& vec1, const Vector3& vec2); + static float Distancef(const Vector3& vec1, const Vector3& vec2); static Vector3 Down(); static Vector3 Forward(); static Vector3 Left(); static Vector3 Lerp(const Vector3& from, const Vector3& to, T interpolation); static Vector3 Normalize(const Vector3& vec); static Vector3 Right(); + static T SquaredDistance(const Vector3& vec1, const Vector3& vec2); static Vector3 Unit(); static Vector3 UnitX(); static Vector3 UnitY(); diff --git a/include/Nazara/Math/Vector3.inl b/include/Nazara/Math/Vector3.inl index fa48b7bef..c5a0155c7 100644 --- a/include/Nazara/Math/Vector3.inl +++ b/include/Nazara/Math/Vector3.inl @@ -1011,6 +1011,40 @@ namespace Nz return vector; } + /*! + * \brief Measure the distance between two points + * Shorthand for vec1.Distance(vec2) + * + * param vec1 the first point + * param vec2 the second point + * + * \return The distance between the two vectors + * + * \see SquaredDistance + */ + template + T Vector3::Distance(const Vector3& vec1, const Vector3& vec2) + { + return vec1.Distance(vec2); + } + + /*! + * \brief Measure the distance between two points as a float + * Shorthand for vec1.Distancef(vec2) + * + * param vec1 the first point + * param vec2 the second point + * + * \return The distance between the two vectors as a float + * + * \see SquaredDistancef + */ + template + float Vector3::Distancef(const Vector3& vec1, const Vector3& vec2) + { + return vec1.Distancef(vec2); + } + /*! * \brief Shorthand for the vector (0, -1, 0) * \return A vector with components (0, -1, 0) @@ -1110,6 +1144,21 @@ namespace Nz return vector; } + /*! + * \brief Calculates the squared distance between two vectors + * \return The metric distance between two vectors with the squared euclidean norm + * + * \param vec1 The first point to measure the distance with + * \param vec2 The second point to measure the distance with + * + * \see Distance + */ + template + T Vector3::SquaredDistance(const Vector3& vec1, const Vector3& vec2) + { + return vec1.SquaredDistance(vec2); + } + /*! * \brief Shorthand for the vector (1, 1, 1) * \return A vector with components (1, 1, 1) diff --git a/include/Nazara/Math/Vector4.hpp b/include/Nazara/Math/Vector4.hpp index d1e51433a..0f8e907fc 100644 --- a/include/Nazara/Math/Vector4.hpp +++ b/include/Nazara/Math/Vector4.hpp @@ -28,7 +28,7 @@ namespace Nz explicit Vector4(T scale); Vector4(const T vec[4]); Vector4(const Vector2& vec, T Z = 0.0, T W = 1.0); - Vector4(const Vector3& vec, T W = 0.0); + Vector4(const Vector3& vec, T W = 1.0); template explicit Vector4(const Vector4& vec); Vector4(const Vector4& vec) = default; ~Vector4() = default; @@ -74,6 +74,7 @@ namespace Nz Vector4 operator*(T scale) const; Vector4 operator/(const Vector4& vec) const; Vector4 operator/(T scale) const; + Vector4& operator=(const Vector4& other) = default; Vector4& operator+=(const Vector4& vec); Vector4& operator-=(const Vector4& vec); diff --git a/include/Nazara/Network/AbstractSocket.inl b/include/Nazara/Network/AbstractSocket.inl index 8bebabfdb..5b5174b98 100644 --- a/include/Nazara/Network/AbstractSocket.inl +++ b/include/Nazara/Network/AbstractSocket.inl @@ -6,31 +6,62 @@ namespace Nz { + /*! + * \brief Gets the last error + * \return Socket error + */ + inline SocketError AbstractSocket::GetLastError() const { return m_lastError; } + /*! + * \brief Gets the internal socket handle + * \return Socket handle + */ + inline SocketHandle AbstractSocket::GetNativeHandle() const { return m_handle; } + /*! + * \brief Gets the internal state + * \return Socket state + */ + inline SocketState AbstractSocket::GetState() const { return m_state; } + /*! + * \brief Gets the internal type + * \return Socket type + */ + inline SocketType AbstractSocket::GetType() const { return m_type; } + /*! + * \brief Checks whether the blocking is enabled + * \return true If successful + */ + inline bool AbstractSocket::IsBlockingEnabled() const { return m_isBlockingEnabled; } + /*! + * \brief Updates the state of the socket + * + * \param newState Next state for the socket + */ + inline void AbstractSocket::UpdateState(SocketState newState) { if (m_state != newState) diff --git a/include/Nazara/Network/Config.hpp b/include/Nazara/Network/Config.hpp index 727108061..2d73c26bb 100644 --- a/include/Nazara/Network/Config.hpp +++ b/include/Nazara/Network/Config.hpp @@ -27,17 +27,22 @@ #ifndef NAZARA_CONFIG_NETWORK_HPP #define NAZARA_CONFIG_NETWORK_HPP -/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci +/*! +* \defgroup network (NazaraNetwork) Network module +* Network/System module including classes to handle networking elements... +*/ -// Utilise le MemoryManager pour gérer les allocations dynamiques (détecte les leaks au prix d'allocations/libérations dynamiques plus lentes) +/// Each modification of a paramater of the module needs a recompilation of the unit + +// Use the MemoryManager to manage dynamic allocations (can detect memory leak but allocations/frees are slower) #define NAZARA_NETWORK_MANAGE_MEMORY 0 -// Active les tests de sécurité basés sur le code (Conseillé pour le développement) +// Activate the security tests based on the code (Advised for development) #define NAZARA_NETWORK_SAFE 1 -/// Chaque modification d'un paramètre ci-dessous implique une modification (souvent mineure) du code +/// Each modification of a parameter following implies a modification (often minor) of the code -/// Vérification des valeurs et types de certaines constantes +/// Checking the values and types of certain constants #include #if defined(NAZARA_STATIC) diff --git a/include/Nazara/Network/ConfigCheck.hpp b/include/Nazara/Network/ConfigCheck.hpp index 73513ebc9..2f1ba1dc8 100644 --- a/include/Nazara/Network/ConfigCheck.hpp +++ b/include/Nazara/Network/ConfigCheck.hpp @@ -7,11 +7,11 @@ #ifndef NAZARA_CONFIG_CHECK_NETWORK_HPP #define NAZARA_CONFIG_CHECK_NETWORK_HPP -/// Ce fichier sert à vérifier la valeur des constantes du fichier Config.hpp +/// This file is used to check the constant values defined in Config.hpp #include -// On force la valeur de MANAGE_MEMORY en mode debug +// We fore the value of MANAGE_MEMORY in debug #if defined(NAZARA_DEBUG) && !NAZARA_NETWORK_MANAGE_MEMORY #undef NAZARA_NETWORK_MANAGE_MEMORY #define NAZARA_NETWORK_MANAGE_MEMORY 0 diff --git a/include/Nazara/Network/DebugOff.hpp b/include/Nazara/Network/DebugOff.hpp index dd45ac45c..e9df99f1f 100644 --- a/include/Nazara/Network/DebugOff.hpp +++ b/include/Nazara/Network/DebugOff.hpp @@ -2,7 +2,7 @@ // This file is part of the "Nazara Engine - Network module" // For conditions of distribution and use, see copyright notice in Config.hpp -// On suppose que Debug.hpp a déjà été inclus, tout comme Config.hpp +// We suppose that Debug.hpp is already included, same goes for Config.hpp #if NAZARA_NETWORK_MANAGE_MEMORY #undef delete #undef new diff --git a/include/Nazara/Network/IpAddress.inl b/include/Nazara/Network/IpAddress.inl index c11c1879a..2fc9ac2bd 100644 --- a/include/Nazara/Network/IpAddress.inl +++ b/include/Nazara/Network/IpAddress.inl @@ -9,11 +9,22 @@ namespace Nz { + /*! + * \brief Constructs a IpAddress object by default + */ + inline IpAddress::IpAddress() : m_isValid(false) { } + /*! + * \brief Constructs a IpAddress object with an IP and a port + * + * \param ip IPv4 address + * \param port Port of the IP + */ + inline IpAddress::IpAddress(const IPv4& ip, UInt16 port) : m_ipv4(ip), m_protocol(NetProtocol_IPv4), @@ -22,6 +33,13 @@ namespace Nz { } + /*! + * \brief Constructs a IpAddress object with an IP and a port + * + * \param ip IPv6 address + * \param port Port of the IP + */ + inline IpAddress::IpAddress(const IPv6& ip, UInt16 port) : m_ipv6(ip), m_protocol(NetProtocol_IPv6), @@ -30,46 +48,100 @@ namespace Nz { } + /*! + * \brief Constructs a IpAddress object with an IP and a port + * + * \param ip IPv4 address (a.b.c.d) + * \param port Port of the IP + */ + inline IpAddress::IpAddress(const UInt8& a, const UInt8& b, const UInt8& c, const UInt8& d, UInt16 port) : IpAddress(IPv4{a, b, c, d}, port) { } + /*! + * \brief Constructs a IpAddress object with an IP and a port + * + * \param ip IPv6 address (a.b.c.d.e.f.g.h) + * \param port Port of the IP + */ + inline IpAddress::IpAddress(const UInt16& a, const UInt16& b, const UInt16& c, const UInt16& d, const UInt16& e, const UInt16& f, const UInt16& g, const UInt16& h, UInt16 port) : IpAddress(IPv6{a, b, c, d, e, f, g, h}, port) { } + /*! + * Constructs a IpAddress object with a C-string + * + * \param address Hostname or textual IP address + */ + inline IpAddress::IpAddress(const char* address) { BuildFromAddress(address); } + /*! + * Constructs a IpAddress object with a string + * + * \param address Hostname or textual IP address + */ + inline IpAddress::IpAddress(const String& address) { BuildFromAddress(address.GetConstBuffer()); } + /*! + * \brief Gets the port + * \return Port attached to the IP address + */ + inline UInt16 IpAddress::GetPort() const { return m_port; } + /*! + * \brief Gets the net protocol + * \return Protocol attached to the IP address + */ + inline NetProtocol IpAddress::GetProtocol() const { return m_protocol; } + /*! + * \brief Checks whether the IP address is valid + * \return true If successful + */ + inline bool IpAddress::IsValid() const { return m_isValid; } + /*! + * \brief Sets the port + * + * \param port Port attached to the IP address + */ + inline void IpAddress::SetPort(UInt16 port) { m_port = port; } + /*! + * \brief Converts IpAddress to IPv4 + * \return Corresponding IPv4 + * + * \remark Produces a NazaraAssert if net protocol is not IPv4 + */ + inline IpAddress::IPv4 IpAddress::ToIPv4() const { NazaraAssert(m_isValid && m_protocol == NetProtocol_IPv4, "Address is not a valid IPv4"); @@ -77,6 +149,13 @@ namespace Nz return m_ipv4; } + /*! + * \brief Converts IpAddress to IPv6 + * \return Corresponding IPv6 + * + * \remark Produces a NazaraAssert if net protocol is not IPv6 + */ + inline IpAddress::IPv6 IpAddress::ToIPv6() const { NazaraAssert(m_isValid && m_protocol == NetProtocol_IPv6, "IP is not a valid IPv6"); @@ -84,6 +163,13 @@ namespace Nz return m_ipv6; } + /*! + * \brief Converts IpAddress to UInt32 + * \return Corresponding UInt32 + * + * \remark Produces a NazaraAssert if net protocol is not IPv4 + */ + inline UInt32 IpAddress::ToUInt32() const { NazaraAssert(m_isValid && m_protocol == NetProtocol_IPv4, "Address is not a valid IPv4"); @@ -94,17 +180,40 @@ namespace Nz UInt32(m_ipv4[3]) << 0; } + /*! + * \brief Converts IpAddress to boolean + * \return true If IpAddress is valid + * + * \see IsValid + */ + inline IpAddress::operator bool() const { return IsValid(); } + /*! + * \brief Output operator + * \return The stream + * + * \param out The stream + * \param address The address to output + */ + inline std::ostream& operator<<(std::ostream& out, const IpAddress& address) { out << "IpAddress(" << address.ToString() << ')'; return out; } + /*! + * \brief Compares the IpAddress to other one + * \return true if the ip addresses are the same + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator==(const IpAddress& first, const IpAddress& second) { // We need to check the validity of each address before comparing them @@ -146,11 +255,27 @@ namespace Nz return true; } + /*! + * \brief Compares the IpAddress to other one + * \return false if the ip addresses are the same + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator!=(const IpAddress& first, const IpAddress& second) { return !operator==(first, second); } + /*! + * \brief Compares the IpAddress to other one + * \return true if this ip address is inferior to the other one + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator<(const IpAddress& first, const IpAddress& second) { // If the second address is invalid, there's no way we're lower than it @@ -196,16 +321,40 @@ namespace Nz return false; //< Same address } + /*! + * \brief Compares the IpAddress to other one + * \return true if this ip address is inferior or equal to the other one + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator<=(const IpAddress& first, const IpAddress& second) { return !operator<(second, first); } + /*! + * \brief Compares the IpAddress to other one + * \return true if this ip address is greather to the other one + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator>(const IpAddress& first, const IpAddress& second) { return second < first; } + /*! + * \brief Compares the IpAddress to other one + * \return true if this ip address is greather or equal to the other one + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator>=(const IpAddress& first, const IpAddress& second) { return !operator<(first, second); @@ -217,6 +366,13 @@ namespace std template<> struct hash { + /*! + * \brief Converts IpAddress to hash + * \return Hash of the IpAddress + * + * \param ip IpAddress to hash + */ + size_t operator()(const Nz::IpAddress& ip) const { if (!ip) diff --git a/include/Nazara/Network/NetPacket.inl b/include/Nazara/Network/NetPacket.inl index c9bbed681..e9765c9dc 100644 --- a/include/Nazara/Network/NetPacket.inl +++ b/include/Nazara/Network/NetPacket.inl @@ -9,21 +9,46 @@ namespace Nz { + /*! + * \brief Constructs a NetPacket object by default + */ + inline NetPacket::NetPacket() : m_netCode(NetCode_Invalid) { } + /*! + * \brief Constructs a NetPacket object with a packet number and a minimal capacity + * + * \param netCode Packet number + * \param minCapacity Minimal capacity of the packet + */ + inline NetPacket::NetPacket(UInt16 netCode, std::size_t minCapacity) { Reset(netCode, minCapacity); } + /*! + * \brief Constructs a NetPacket object with a packet number and raw memory + * + * \param netCode Packet number + * \param ptr Raw memory + * \param size Size of the memory + */ + inline NetPacket::NetPacket(UInt16 netCode, const void* ptr, std::size_t size) { Reset(netCode, ptr, size); } + /*! + * \brief Constructs a NetPacket object with another one by move semantic + * + * \param packet NetPacket to move into this + */ + inline NetPacket::NetPacket(NetPacket&& packet) : ByteStream(std::move(packet)), m_buffer(std::move(packet.m_buffer)), @@ -35,12 +60,23 @@ namespace Nz SetStream(&m_memoryStream); } + /*! + * \brief Destructs the object + */ + inline NetPacket::~NetPacket() { FlushBits(); //< Needs to be done here as the stream will be freed before ByteStream calls it FreeStream(); } + /*! + * \brief Gets the raw buffer + * \return Constant raw buffer + * + * \remark Produces a NazaraAssert if internal buffer is invalid + */ + inline const UInt8* NetPacket::GetConstData() const { NazaraAssert(m_buffer, "Invalid buffer"); @@ -48,6 +84,13 @@ namespace Nz return m_buffer->GetConstBuffer(); } + /*! + * \brief Gets the raw buffer + * \return Raw buffer + * + * \remark Produces a NazaraAssert if internal buffer is invalid + */ + inline UInt8* NetPacket::GetData() const { NazaraAssert(m_buffer, "Invalid buffer"); @@ -55,6 +98,11 @@ namespace Nz return m_buffer->GetBuffer(); } + /*! + * \brief Gets the size of the data + * \return Size of the data + */ + inline size_t NetPacket::GetDataSize() const { if (m_buffer) @@ -63,22 +111,46 @@ namespace Nz return 0; } + /*! + * \brief Gets the packet number + * \return Packet number + */ + inline UInt16 NetPacket::GetNetCode() const { return m_netCode; } + /*! + * \brief Resets the packet + */ + inline void NetPacket::Reset() { FreeStream(); } + /*! + * \brief Resets the packet with a packet number and a minimal capacity + * + * \param netCode Packet number + * \param minCapacity Minimal capacity of the packet + */ + inline void NetPacket::Reset(UInt16 netCode, std::size_t minCapacity) { InitStream(HeaderSize + minCapacity, HeaderSize, OpenMode_ReadWrite); m_netCode = netCode; } + /*! + * \brief Resets the packet with a packet number and raw memory + * + * \param netCode Packet number + * \param ptr Raw memory + * \param size Size of the memory + */ + inline void NetPacket::Reset(UInt16 netCode, const void* ptr, std::size_t size) { InitStream(HeaderSize + size, HeaderSize, OpenMode_ReadOnly); @@ -88,6 +160,14 @@ namespace Nz m_netCode = netCode; } + /*! + * \brief Resizes the packet + * + * \param newSize Size for the resizing operation + * + * \remark Produces a NazaraAssert if internal buffer is invalid + */ + inline void NetPacket::Resize(std::size_t newSize) { NazaraAssert(m_buffer, "Invalid buffer"); @@ -95,11 +175,24 @@ namespace Nz m_buffer->Resize(newSize); } + /*! + * \brief Sets the packet number + * + * \param netCode Packet number + */ + inline void NetPacket::SetNetCode(UInt16 netCode) { m_netCode = netCode; } + /*! + * \brief Moves the NetPacket into this + * \return A reference to this + * + * \param packet NetPacket to move in this + */ + inline NetPacket& Nz::NetPacket::operator=(NetPacket&& packet) { FreeStream(); diff --git a/include/Nazara/Network/RUdpConnection.inl b/include/Nazara/Network/RUdpConnection.inl index b2372285d..2960cbced 100644 --- a/include/Nazara/Network/RUdpConnection.inl +++ b/include/Nazara/Network/RUdpConnection.inl @@ -8,31 +8,66 @@ namespace Nz { + /*! + * \brief Closes the connection + */ + inline void RUdpConnection::Close() { m_socket.Close(); } + /*! + * \brief Disconnects the connection + * + * \see Close + */ + inline void RUdpConnection::Disconnect() { Close(); } + /*! + * \brief Gets the bound address + * \return IpAddress we are linked to + */ + inline IpAddress RUdpConnection::GetBoundAddress() const { return m_socket.GetBoundAddress(); } + /*! + * \brief Gets the port of the bound address + * \return Port we are linked to + */ + inline UInt16 RUdpConnection::GetBoundPort() const { return m_socket.GetBoundPort(); } + /*! + * \brief Gets the last error + * \return Socket error + */ + inline SocketError RUdpConnection::GetLastError() const { return m_lastError; } + /*! + * \brief Listens to a socket + * \return true If successfully bound + * + * \param protocol Net protocol to listen to + * \param port Port to listen to + * + * \remark Produces a NazaraAssert if protocol is unknown or any + */ + inline bool RUdpConnection::Listen(NetProtocol protocol, UInt16 port) { NazaraAssert(protocol != NetProtocol_Any, "Any protocol not supported for Listen"); //< TODO @@ -59,16 +94,36 @@ namespace Nz return Listen(any); } + /*! + * \brief Sets the protocol id + * + * \param protocolId Protocol ID like NNet + */ + inline void RUdpConnection::SetProtocolId(UInt32 protocolId) { m_protocol = protocolId; } + /*! + * \brief Sets the time before ack + * + * \param Time before acking to send many together (in ms) + */ + inline void RUdpConnection::SetTimeBeforeAck(UInt32 ms) { m_forceAckSendTime = ms * 1000; //< Store in microseconds for easier handling } - + + /*! + * \brief Computes the difference of sequence + * \return Delta between the two sequences + * + * \param sequence First sequence + * \param sequence2 Second sequence + */ + inline unsigned int RUdpConnection::ComputeSequenceDifference(SequenceIndex sequence, SequenceIndex sequence2) { unsigned int difference; @@ -77,9 +132,16 @@ namespace Nz else difference = sequence - sequence2; - return 0; + return difference; } + /*! + * \brief Checks whether the peer has pending packets + * \return true If it is the case + * + * \param peer Data relative to the peer + */ + inline bool RUdpConnection::HasPendingPackets(PeerData& peer) { for (unsigned int priority = PacketPriority_Highest; priority <= PacketPriority_Lowest; ++priority) @@ -87,13 +149,19 @@ namespace Nz std::vector& pendingPackets = peer.pendingPackets[priority]; if (!pendingPackets.empty()) return true; - - pendingPackets.clear(); } return false; } + /*! + * \brief Checks whether the ack is more recent + * \return true If it is the case + * + * \param ack First sequence + * \param ack2 Second sequence + */ + inline bool RUdpConnection::IsAckMoreRecent(SequenceIndex ack, SequenceIndex ack2) { constexpr SequenceIndex maxDifference = std::numeric_limits::max() / 2; @@ -106,6 +174,13 @@ namespace Nz return false; ///< Same ack } + /*! + * \brief Checks whether the connection is reliable + * \return true If it is the case + * + * \remark Produces a NazaraError if enumeration is invalid + */ + inline bool RUdpConnection::IsReliable(PacketReliability reliability) { switch (reliability) @@ -122,6 +197,14 @@ namespace Nz return false; } + /*! + * \brief Simulates the loss of packets on network + * + * \param packetLoss Ratio of packet loss according to bernoulli distribution + * + * \remark Produces a NazaraAssert if packetLoss is not in between 0.0 and 1.0 + */ + inline void RUdpConnection::SimulateNetwork(double packetLoss) { NazaraAssert(packetLoss >= 0.0 && packetLoss <= 1.0, "Packet loss must be in range [0..1]"); diff --git a/include/Nazara/Network/TcpClient.inl b/include/Nazara/Network/TcpClient.inl index da292b982..23f5412da 100644 --- a/include/Nazara/Network/TcpClient.inl +++ b/include/Nazara/Network/TcpClient.inl @@ -7,6 +7,10 @@ namespace Nz { + /*! + * \brief Constructs a TcpClient object by default + */ + inline TcpClient::TcpClient() : AbstractSocket(SocketType_TCP), Stream(StreamOption_Sequential), @@ -17,31 +21,62 @@ namespace Nz { } + /*! + * \brief Disconnects the connection + * + * \see Close + */ + inline void TcpClient::Disconnect() { Close(); } + /*! + * \brief Gets the interval between two keep alive pings + * \return Interval in milliseconds between two pings + */ + inline UInt64 TcpClient::GetKeepAliveInterval() const { return m_keepAliveInterval; } + /*! + * \brief Gets the time before expiration of connection + * \return Time in milliseconds before expiration + */ + inline UInt64 TcpClient::GetKeepAliveTime() const { return m_keepAliveTime; } + /*! + * \brief Gets the remote address + * \return Address of peer + */ + inline IpAddress TcpClient::GetRemoteAddress() const { return m_peerAddress; } + /*! + * \brief Checks whether low delay is enabled + * \return true If it is the case + */ + inline bool TcpClient::IsLowDelayEnabled() const { return m_isLowDelayEnabled; } + /*! + * \brief Checks whether the keep alive flag is enabled + * \return true If it is the case + */ + inline bool TcpClient::IsKeepAliveEnabled() const { return m_isKeepAliveEnabled; diff --git a/include/Nazara/Network/TcpServer.inl b/include/Nazara/Network/TcpServer.inl index 5350a58ee..1fcb4eb32 100644 --- a/include/Nazara/Network/TcpServer.inl +++ b/include/Nazara/Network/TcpServer.inl @@ -7,27 +7,58 @@ namespace Nz { + /*! + * \brief Constructs a TcpServer object by default + */ + inline TcpServer::TcpServer() : AbstractSocket(SocketType_TCP) { } + /*! + * \brief Constructs a TcpServer object with another one by move semantic + * + * \param tcpServer TcpServer to move into this + */ + inline TcpServer::TcpServer(TcpServer&& tcpServer) : AbstractSocket(std::move(tcpServer)), m_boundAddress(std::move(tcpServer.m_boundAddress)) { } + /*! + * \brief Gets the bound address + * \return IpAddress we are linked to + */ + inline IpAddress TcpServer::GetBoundAddress() const { return m_boundAddress; } + /*! + * \brief Gets the port of the bound address + * \return Port we are linked to + */ + inline UInt16 TcpServer::GetBoundPort() const { return m_boundAddress.GetPort(); } + /*! + * \brief Listens to a socket + * \return State of the socket + * + * \param protocol Net protocol to listen to + * \param port Port to listen to + * \param queueSize Size of the queue + * + * \remark Produces a NazaraAssert if protocol is unknown or any + */ + inline SocketState TcpServer::Listen(NetProtocol protocol, UInt16 port, unsigned int queueSize) { NazaraAssert(protocol != NetProtocol_Any, "Any protocol not supported for Listen"); //< TODO diff --git a/include/Nazara/Network/UdpSocket.hpp b/include/Nazara/Network/UdpSocket.hpp index 96c391436..2aac124af 100644 --- a/include/Nazara/Network/UdpSocket.hpp +++ b/include/Nazara/Network/UdpSocket.hpp @@ -49,7 +49,6 @@ namespace Nz void OnOpened() override; IpAddress m_boundAddress; - SocketState m_state; bool m_isBroadCastingEnabled; }; } diff --git a/include/Nazara/Network/UdpSocket.inl b/include/Nazara/Network/UdpSocket.inl index 9ad4d067b..4096d8c04 100644 --- a/include/Nazara/Network/UdpSocket.inl +++ b/include/Nazara/Network/UdpSocket.inl @@ -6,24 +6,46 @@ namespace Nz { + /*! + * \brief Constructs a UdpSocket object by default + */ + inline UdpSocket::UdpSocket() : AbstractSocket(SocketType_UDP) { } + /*! + * \brief Constructs a UdpSocket object with a net protocol + * + * \param protocol Net protocol to use + */ + inline UdpSocket::UdpSocket(NetProtocol protocol) : UdpSocket() { Create(protocol); } + /*! + * \brief Constructs a UdpSocket object with another one by move semantic + * + * \param udpSocket UdpSocket to move into this + */ + inline UdpSocket::UdpSocket(UdpSocket&& udpSocket) : AbstractSocket(std::move(udpSocket)), - m_boundAddress(std::move(udpSocket.m_boundAddress)), - m_state(udpSocket.m_state) + m_boundAddress(std::move(udpSocket.m_boundAddress)) { } + /*! + * \brief Binds a specific port + * \return State of the socket + * + * \param port Port to bind + */ + inline SocketState UdpSocket::Bind(UInt16 port) { IpAddress any; @@ -47,6 +69,13 @@ namespace Nz return Bind(any); } + /*! + * \brief Creates a UDP socket + * \return true If successful + * + * \param protocol Net protocol to use + */ + bool UdpSocket::Create(NetProtocol protocol) { NazaraAssert(protocol != NetProtocol_Unknown, "Invalid protocol"); @@ -54,21 +83,41 @@ namespace Nz return Open(protocol); } + /*! + * \brief Gets the bound address + * \return IpAddress we are linked to + */ + inline IpAddress UdpSocket::GetBoundAddress() const { return m_boundAddress; } + /*! + * \brief Gets the port of the bound address + * \return Port we are linked to + */ + inline UInt16 UdpSocket::GetBoundPort() const { return m_boundAddress.GetPort(); } + /*! + * \brief Gets the state of the socket + * \return State of the socket + */ + inline SocketState UdpSocket::GetState() const { return m_state; } + /*! + * \brief Checks whether the broadcasting is enabled + * \return true If it is the case + */ + inline bool UdpSocket::IsBroadcastingEnabled() const { return m_isBroadCastingEnabled; diff --git a/include/Nazara/Noise.hpp b/include/Nazara/Noise.hpp index 45b240378..e1d14460e 100644 --- a/include/Nazara/Noise.hpp +++ b/include/Nazara/Noise.hpp @@ -1,9 +1,9 @@ -// This file was automatically generated on 17 Nov 2015 at 13:20:45 +// This file was automatically generated on 12 Jul 2016 at 17:44:43 /* Nazara Engine - Noise module - Copyright (C) 2015 Rémi "Overdrivr" Bèges (remi.beges@laposte.net) + Copyright (C) 2016 Rémi "Overdrivr" Bèges (remi.beges@laposte.net) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -29,25 +29,16 @@ #ifndef NAZARA_GLOBAL_NOISE_HPP #define NAZARA_GLOBAL_NOISE_HPP -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #endif // NAZARA_GLOBAL_NOISE_HPP diff --git a/include/Nazara/Noise/Abstract2DNoise.hpp b/include/Nazara/Noise/Abstract2DNoise.hpp deleted file mode 100644 index 0eb5c23e5..000000000 --- a/include/Nazara/Noise/Abstract2DNoise.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_ABSTRACT2DNOISE_HPP -#define NAZARA_ABSTRACT2DNOISE_HPP - -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API Abstract2DNoise : public MappedNoiseBase - { - public: - virtual ~Abstract2DNoise(); - - float GetBasicValue(float x, float y); - float GetMappedValue(float x, float y); - virtual float GetValue(float x, float y, float resolution) = 0; - }; -} - -#endif // NAZARA_ABSTRACT2DNOISE_HPP diff --git a/include/Nazara/Noise/Abstract3DNoise.hpp b/include/Nazara/Noise/Abstract3DNoise.hpp deleted file mode 100644 index 19a0c765c..000000000 --- a/include/Nazara/Noise/Abstract3DNoise.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_ABSTRACT3DNOISE_HPP -#define NAZARA_ABSTRACT3DNOISE_HPP - -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API Abstract3DNoise : public MappedNoiseBase - { - public: - virtual ~Abstract3DNoise(); - - float GetBasicValue(float x, float y, float z); - float GetMappedValue(float x, float y, float z); - virtual float GetValue(float x, float y, float z, float resolution) = 0; - }; -} - -#endif // NAZARA_ABSTRACT3DNOISE_HPP diff --git a/include/Nazara/Noise/Abstract4DNoise.hpp b/include/Nazara/Noise/Abstract4DNoise.hpp deleted file mode 100644 index 631228020..000000000 --- a/include/Nazara/Noise/Abstract4DNoise.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_ABSTRACT4DNOISE_HPP -#define NAZARA_ABSTRACT4DNOISE_HPP - -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API Abstract4DNoise : public MappedNoiseBase - { - public: - virtual ~Abstract4DNoise(); - - float GetBasicValue(float x, float y, float z, float w); - float GetMappedValue(float x, float y, float z, float w); - virtual float GetValue(float x, float y, float z, float w, float resolution) = 0; - }; -} - -#endif // NAZARA_ABSTRACT4DNOISE_HPP diff --git a/include/Nazara/Noise/ComplexNoiseBase.hpp b/include/Nazara/Noise/ComplexNoiseBase.hpp deleted file mode 100644 index 6be8667a5..000000000 --- a/include/Nazara/Noise/ComplexNoiseBase.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef COMPLEXNOISEBASE_HPP -#define COMPLEXNOISEBASE_HPP - -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API ComplexNoiseBase - { - public: - ComplexNoiseBase(); - ~ComplexNoiseBase() = default; - - float GetHurstParameter() const; - float GetLacunarity() const; - float GetOctaveNumber() const; - void SetHurstParameter(float h); - void SetLacunarity(float lacunarity); - void SetOctavesNumber(float octaves); - void RecomputeExponentArray(); - - protected: - float m_lacunarity; - float m_hurst; - float m_octaves; - std::array m_exponent_array; - float m_sum; - - private: - bool m_parametersModified; - }; -} - -#endif // COMPLEXNOISEBASE_HPP diff --git a/include/Nazara/Noise/Config.hpp b/include/Nazara/Noise/Config.hpp index 5df2d6dc4..afe6d577e 100644 --- a/include/Nazara/Noise/Config.hpp +++ b/include/Nazara/Noise/Config.hpp @@ -1,7 +1,7 @@ /* Nazara Engine - Noise module - Copyright (C) 2015 Rémi "Overdrivr" Bèges (remi.beges@laposte.net) + Copyright (C) 2016 Rémi "Overdrivr" Bèges (remi.beges@laposte.net) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/include/Nazara/Noise/ConfigCheck.hpp b/include/Nazara/Noise/ConfigCheck.hpp index f7682bd28..9c6df7851 100644 --- a/include/Nazara/Noise/ConfigCheck.hpp +++ b/include/Nazara/Noise/ConfigCheck.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2015 Jérôme Leclercq +// Copyright (C) 2016 Rémi Bèges // This file is part of the "Nazara Engine - Noise module" // For conditions of distribution and use, see copyright notice in Config.hpp diff --git a/include/Nazara/Noise/Debug.hpp b/include/Nazara/Noise/Debug.hpp index 377ae7b1c..4696c79b9 100644 --- a/include/Nazara/Noise/Debug.hpp +++ b/include/Nazara/Noise/Debug.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2015 Rémi Bèges +// Copyright (C) 2016 Rémi Bèges // This file is part of the "Nazara Engine - Noise module" // For conditions of distribution and use, see copyright notice in Config.hpp diff --git a/include/Nazara/Noise/DebugOff.hpp b/include/Nazara/Noise/DebugOff.hpp index 75903b6cc..4b368b6ea 100644 --- a/include/Nazara/Noise/DebugOff.hpp +++ b/include/Nazara/Noise/DebugOff.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2015 Rémi Bèges +// Copyright (C) 2016 Rémi Bèges // This file is part of the "Nazara Engine - Noise module" // For conditions of distribution and use, see copyright notice in Config.hpp diff --git a/include/Nazara/Noise/Enums.hpp b/include/Nazara/Noise/Enums.hpp new file mode 100644 index 000000000..7962d8415 --- /dev/null +++ b/include/Nazara/Noise/Enums.hpp @@ -0,0 +1,18 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef NAZARA_ENUMS_NOISE_HPP +#define NAZARA_ENUMS_NOISE_HPP + +namespace Nz +{ + enum WorleyFunction + { + WorleyFunction_F1 = 0, + WorleyFunction_F2 = 1, + WorleyFunction_F3 = 2, + WorleyFunction_F4 = 3 + }; +} +#endif // NAZARA_ENUMS_NOISE_HPP diff --git a/include/Nazara/Noise/FBM.hpp b/include/Nazara/Noise/FBM.hpp new file mode 100644 index 000000000..584fb3a10 --- /dev/null +++ b/include/Nazara/Noise/FBM.hpp @@ -0,0 +1,32 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef NAZARA_FBM_HPP +#define NAZARA_FBM_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_NOISE_API FBM : public MixerBase + { + public: + FBM(const NoiseBase& source); + FBM(const FBM&) = delete; + ~FBM() = default; + + float Get(float x, float y, float scale) const override; + float Get(float x, float y, float z, float scale) const override; + float Get(float x, float y, float z, float w, float scale) const override; + + FBM& operator=(const FBM&) = delete; + + private: + const NoiseBase& m_source; + }; +} + +#endif // NAZARA_FBM_HPP diff --git a/include/Nazara/Noise/FBM2D.hpp b/include/Nazara/Noise/FBM2D.hpp deleted file mode 100644 index 9b456de0a..000000000 --- a/include/Nazara/Noise/FBM2D.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef FBM2D_HPP -#define FBM2D_HPP - -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API FBM2D : public Abstract2DNoise, public ComplexNoiseBase - { - public: - FBM2D(NoiseType source, unsigned int seed); - float GetValue(float x, float y, float resolution); - ~FBM2D(); - - private: - Abstract2DNoise* m_source; - float m_value; - float m_remainder; - NoiseType m_noiseType; - }; -} - -#endif // FBM2D_HPP diff --git a/include/Nazara/Noise/FBM3D.hpp b/include/Nazara/Noise/FBM3D.hpp deleted file mode 100644 index 722ad843d..000000000 --- a/include/Nazara/Noise/FBM3D.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef FBM3D_HPP -#define FBM3D_HPP - -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API FBM3D : public Abstract3DNoise, public ComplexNoiseBase - { - public: - FBM3D(NoiseType source, unsigned int seed); - float GetValue(float x, float y, float z, float resolution); - ~FBM3D(); - - private: - Abstract3DNoise* m_source; - float m_value; - float m_remainder; - NoiseType m_noiseType; - }; -} - -#endif // FBM3D_HPP - diff --git a/include/Nazara/Noise/FBM4D.hpp b/include/Nazara/Noise/FBM4D.hpp deleted file mode 100644 index e18c93111..000000000 --- a/include/Nazara/Noise/FBM4D.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef FBM4D_HPP -#define FBM4D_HPP - -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API FBM4D : public Abstract4DNoise, public ComplexNoiseBase - { - public: - FBM4D(NoiseType source, unsigned int seed); - float GetValue(float x, float y, float z, float w, float resolution); - ~FBM4D(); - - private: - Abstract4DNoise* m_source; - float m_value; - float m_remainder; - NoiseType m_noiseType; - }; -} - -#endif // FBM4D_HPP - diff --git a/include/Nazara/Noise/HybridMultiFractal.hpp b/include/Nazara/Noise/HybridMultiFractal.hpp new file mode 100644 index 000000000..c73dafc73 --- /dev/null +++ b/include/Nazara/Noise/HybridMultiFractal.hpp @@ -0,0 +1,36 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef NAZARA_HYBRIDMULTIFRACTAL_HPP +#define NAZARA_HYBRIDMULTIFRACTAL_HPP + +#include +#include + +namespace Nz +{ + class NAZARA_NOISE_API HybridMultiFractal : public MixerBase + { + public: + HybridMultiFractal(const NoiseBase & source); + HybridMultiFractal(const HybridMultiFractal&) = delete; + ~HybridMultiFractal() = default; + + float Get(float x, float y, float scale) const override; + float Get(float x, float y, float z, float scale) const override; + float Get(float x, float y, float z, float w, float scale) const override; + + HybridMultiFractal& operator=(const HybridMultiFractal&) = delete; + + private: + const NoiseBase& m_source; + float m_value; + float m_remainder; + float m_offset; + float m_weight; + float m_signal; + }; +} + +#endif // NAZARA_HYBRIDMULTIFRACTAL_HPP diff --git a/include/Nazara/Noise/HybridMultiFractal2D.hpp b/include/Nazara/Noise/HybridMultiFractal2D.hpp deleted file mode 100644 index 4b0ad9bc6..000000000 --- a/include/Nazara/Noise/HybridMultiFractal2D.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef HYBRIDMULTIFRACTAL2D_HPP -#define HYBRIDMULTIFRACTAL2D_HPP - -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API HybridMultiFractal2D : public Abstract2DNoise, public ComplexNoiseBase - { - public: - HybridMultiFractal2D(NoiseType source, unsigned int seed); - ~HybridMultiFractal2D(); - - float GetValue(float x, float y, float resolution); - - private: - Abstract2DNoise* m_source; - float m_value; - float m_remainder; - float m_offset; - float m_weight; - float m_signal; - NoiseType m_noiseType; - }; -} - -#endif // HYBRIDMULTIFRACTAL2D_HPP - diff --git a/include/Nazara/Noise/HybridMultiFractal3D.hpp b/include/Nazara/Noise/HybridMultiFractal3D.hpp deleted file mode 100644 index 34d554292..000000000 --- a/include/Nazara/Noise/HybridMultiFractal3D.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef HYBRIDMULTIFRACTAL3D_HPP -#define HYBRIDMULTIFRACTAL3D_HPP - -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API HybridMultiFractal3D : public Abstract3DNoise, public ComplexNoiseBase - { - public: - HybridMultiFractal3D(NoiseType source, unsigned int seed); - ~HybridMultiFractal3D(); - - float GetValue(float x, float y, float z, float resolution); - - private: - Abstract3DNoise* m_source; - float m_value; - float m_remainder; - float m_offset; - float m_weight; - float m_signal; - NoiseType m_noiseType; - }; -} - -#endif // HYBRIDMULTIFRACTAL3D_HPP - diff --git a/include/Nazara/Noise/HybridMultiFractal4D.hpp b/include/Nazara/Noise/HybridMultiFractal4D.hpp deleted file mode 100644 index 859ed8087..000000000 --- a/include/Nazara/Noise/HybridMultiFractal4D.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef HYBRIDMULTIFRACTAL4D_HPP -#define HYBRIDMULTIFRACTAL4D_HPP - -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API HybridMultiFractal4D : public Abstract4DNoise, public ComplexNoiseBase - { - public: - HybridMultiFractal4D(NoiseType source, unsigned int seed); - ~HybridMultiFractal4D(); - - float GetValue(float x, float y, float z, float w, float resolution); - - private: - Abstract4DNoise* m_source; - float m_value; - float m_remainder; - float m_offset; - float m_weight; - float m_signal; - NoiseType m_noiseType; - }; -} - -#endif // HYBRIDMULTIFRACTAL4D_HPP - diff --git a/include/Nazara/Noise/MappedNoiseBase.hpp b/include/Nazara/Noise/MappedNoiseBase.hpp deleted file mode 100644 index 7e5106a31..000000000 --- a/include/Nazara/Noise/MappedNoiseBase.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_MAPPEDNOISEBASE_HPP -#define NAZARA_MAPPEDNOISEBASE_HPP - -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API MappedNoiseBase : public NoiseBase - { - public: - MappedNoiseBase(); - ~MappedNoiseBase() = default; - - float GetGain() const; - float GetOffset() const; - float GetResolution() const; - void SetGain(float gain); - void SetOffset(float offset); - void SetResolution(float resolution); - - protected: - float m_gain; - float m_offset; - float m_resolution; - }; -} - -#endif // NAZARA_MAPPEDNOISEBASE_HPP diff --git a/include/Nazara/Noise/MixerBase.hpp b/include/Nazara/Noise/MixerBase.hpp new file mode 100644 index 000000000..8b7a99d56 --- /dev/null +++ b/include/Nazara/Noise/MixerBase.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef NAZARA_MIXERBASE_HPP +#define NAZARA_MIXERBASE_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_NOISE_API MixerBase + { + public: + MixerBase(); + ~MixerBase() = default; + + virtual float Get(float x, float y, float scale) const = 0; + virtual float Get(float x, float y, float z, float scale) const = 0; + virtual float Get(float x, float y, float z, float w, float scale) const = 0; + + float GetHurstParameter() const; + float GetLacunarity() const; + float GetOctaveNumber() const; + + void SetParameters(float hurst, float lacunarity, float octaves); + + protected: + float m_hurst; + float m_lacunarity; + float m_octaves; + std::vector m_exponent_array; + float m_sum; + + private: + void Recompute(); + }; +} + +#endif // NAZARA_MIXERBASE_HPP diff --git a/include/Nazara/Noise/Noise.hpp b/include/Nazara/Noise/Noise.hpp index a1e5b7d11..c1ec5b1da 100644 --- a/include/Nazara/Noise/Noise.hpp +++ b/include/Nazara/Noise/Noise.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2015 Rémi Bèges +// Copyright (C) 2016 Rémi Bèges // This file is part of the "Nazara Engine - Noise module" // For conditions of distribution and use, see copyright notice in Config.hpp diff --git a/include/Nazara/Noise/NoiseBase.hpp b/include/Nazara/Noise/NoiseBase.hpp index bd268ee8c..f7f9eba09 100644 --- a/include/Nazara/Noise/NoiseBase.hpp +++ b/include/Nazara/Noise/NoiseBase.hpp @@ -1,48 +1,47 @@ -// Copyright (C) 2015 Rémi Bèges +// Copyright (C) 2016 Rémi Bèges // This file is part of the "Nazara Engine - Noise module" // For conditions of distribution and use, see copyright notice in Config.hpp -#pragma once - -#ifndef NOISEBASE_HPP -#define NOISEBASE_HPP +#ifndef NAZARA_NOISEBASE_HPP +#define NAZARA_NOISEBASE_HPP #include +#include +#include +#include #include +#include +#include namespace Nz { - enum NoiseType - { - PERLIN, - SIMPLEX, - CELL - }; - class NAZARA_NOISE_API NoiseBase { public: NoiseBase(unsigned int seed = 0); ~NoiseBase() = default; - void SetNewSeed(unsigned int seed); + virtual float Get(float x, float y, float scale) const = 0; + virtual float Get(float x, float y, float z, float scale) const = 0; + virtual float Get(float x, float y, float z, float w, float scale) const = 0; + float GetScale(); - void ShufflePermutationTable(); + void SetScale(float scale); + void SetSeed(unsigned int seed); - unsigned int GetUniformRandomValue(); - - int fastfloor(float n); - int JenkinsHash(int a, int b, int c); + void Shuffle(); protected: - unsigned int perm[512]; + std::array m_permutations; + float m_scale; + + static std::array s_gradients2; + static std::array s_gradients3; + static std::array s_gradients4; private: - unsigned int Ua, Uc, Um; - unsigned int UcurrentSeed; - unsigned int Uprevious, Ulast; - + std::default_random_engine m_randomEngine; }; } -#endif // NOISEBASE_HPP +#endif // NAZARA_NOISEBASE_HPP diff --git a/include/Nazara/Noise/NoiseTools.hpp b/include/Nazara/Noise/NoiseTools.hpp new file mode 100644 index 000000000..6238caec3 --- /dev/null +++ b/include/Nazara/Noise/NoiseTools.hpp @@ -0,0 +1,14 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef NAZARA_NOISETOOLS_HPP +#define NAZARA_NOISETOOLS_HPP + +namespace Nz +{ + int fastfloor(float n); + int JenkinsHash(int a, int b, int c); +} + +#endif // NAZARA_NOISETOOLS_HPP diff --git a/include/Nazara/Noise/Perlin.hpp b/include/Nazara/Noise/Perlin.hpp new file mode 100644 index 000000000..781d03148 --- /dev/null +++ b/include/Nazara/Noise/Perlin.hpp @@ -0,0 +1,28 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef NAZARA_PERLIN_HPP +#define NAZARA_PERLIN_HPP + +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_NOISE_API Perlin : public NoiseBase + { + public: + Perlin() = default; + Perlin(unsigned int seed); + ~Perlin() = default; + + float Get(float x, float y, float scale) const override; + float Get(float x, float y, float z, float scale) const override; + float Get(float x, float y, float z, float w, float scale) const override; + }; +} + +#endif // NAZARA_PERLIN_HPP diff --git a/include/Nazara/Noise/Perlin2D.hpp b/include/Nazara/Noise/Perlin2D.hpp deleted file mode 100644 index 88a98155b..000000000 --- a/include/Nazara/Noise/Perlin2D.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef PERLIN2D_HPP -#define PERLIN2D_HPP - -#include -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API Perlin2D : public Abstract2DNoise - { - public: - Perlin2D(); - Perlin2D(unsigned int seed); - ~Perlin2D() = default; - - float GetValue(float x, float y, float resolution); - - private: - int x0, y0; - int gi0,gi1,gi2,gi3; - int ii, jj; - float gradient2[8][2]; - float s,t,u,v; - float Cx,Cy; - float Li1, Li2; - Vector2 temp; - }; -} - -#endif // PERLIN2D_HPP - diff --git a/include/Nazara/Noise/Perlin3D.hpp b/include/Nazara/Noise/Perlin3D.hpp deleted file mode 100644 index 47be7f29b..000000000 --- a/include/Nazara/Noise/Perlin3D.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef PERLIN3D_HPP -#define PERLIN3D_HPP - -#include -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API Perlin3D : public Abstract3DNoise - { - public: - Perlin3D(); - Perlin3D(unsigned int seed); - ~Perlin3D() = default; - - float GetValue(float x, float y, float z, float resolution); - - private: - int x0,y0,z0; - int gi0,gi1,gi2,gi3,gi4,gi5,gi6,gi7; - int ii,jj,kk; - float gradient3[16][3]; - float Li1,Li2,Li3,Li4,Li5,Li6; - float s[2],t[2],u[2],v[2]; - float Cx,Cy,Cz; - float nx,ny,nz; - float tmp; - Vector3 temp; - }; -} - -#endif // PERLIN3D_HPP diff --git a/include/Nazara/Noise/Perlin4D.hpp b/include/Nazara/Noise/Perlin4D.hpp deleted file mode 100644 index 66d7a1b7b..000000000 --- a/include/Nazara/Noise/Perlin4D.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef PERLIN4D_HPP -#define PERLIN4D_HPP - -#include -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API Perlin4D : public Abstract4DNoise - { - public: - Perlin4D(); - Perlin4D(unsigned int seed); - ~Perlin4D() = default; - - float GetValue(float x, float y, float z, float w, float resolution); - - private: - int x0,y0,z0,w0; - int gi0,gi1,gi2,gi3,gi4,gi5,gi6,gi7,gi8,gi9,gi10,gi11,gi12,gi13,gi14,gi15; - int ii,jj,kk,ll; - float gradient4[32][4]; - float Li1,Li2,Li3,Li4,Li5,Li6,Li7,Li8,Li9,Li10,Li11,Li12,Li13,Li14; - float s[4],t[4],u[4],v[4]; - float Cx,Cy,Cz,Cw; - float tmp; - Vector4 temp; - }; -} - -#endif // PERLIN4D_HPP diff --git a/include/Nazara/Noise/Simplex.hpp b/include/Nazara/Noise/Simplex.hpp new file mode 100644 index 000000000..ae018f7bf --- /dev/null +++ b/include/Nazara/Noise/Simplex.hpp @@ -0,0 +1,28 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef SIMPLEX_HPP +#define SIMPLE_HPP + +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_NOISE_API Simplex : public NoiseBase + { + public: + Simplex() = default; + Simplex(unsigned int seed); + ~Simplex() = default; + + float Get(float x, float y, float scale) const override; + float Get(float x, float y, float z, float scale) const override; + float Get(float x, float y, float z, float w, float scale) const override; + }; +} + +#endif // SIMPLEX_HPP diff --git a/include/Nazara/Noise/Simplex2D.hpp b/include/Nazara/Noise/Simplex2D.hpp deleted file mode 100644 index 69a73478d..000000000 --- a/include/Nazara/Noise/Simplex2D.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef SIMPLEX2D_HPP -#define SIMPLEX2D_HPP - -#include -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API Simplex2D : public Abstract2DNoise - { - public: - Simplex2D(); - Simplex2D(unsigned int seed); - virtual ~Simplex2D() = default; - - float GetValue(float x, float y, float resolution); - - private: - int ii,jj; - int gi0,gi1,gi2; - Vector2i skewedCubeOrigin,off1; - float n1,n2,n3; - float c1,c2,c3; - float gradient2[8][2]; - float UnskewCoeff2D; - float SkewCoeff2D; - float sum; - Vector2 unskewedCubeOrigin, unskewedDistToOrigin; - Vector2 d1,d2,d3; - }; -} - -#endif // SIMPLEX2D_HPP - diff --git a/include/Nazara/Noise/Simplex3D.hpp b/include/Nazara/Noise/Simplex3D.hpp deleted file mode 100644 index ecfc4c2a4..000000000 --- a/include/Nazara/Noise/Simplex3D.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef SIMPLEX3D_HPP -#define SIMPLEX3D_HPP - -#include -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API Simplex3D : public Abstract3DNoise - { - public: - Simplex3D(); - Simplex3D(unsigned int seed); - ~Simplex3D() = default; - - float GetValue(float x, float y, float z, float resolution); - - private: - int ii,jj,kk; - int gi0,gi1,gi2,gi3; - Vector3i skewedCubeOrigin,off1,off2; - float n1,n2,n3,n4; - float c1,c2,c3,c4; - float gradient3[12][3]; - float UnskewCoeff3D; - float SkewCoeff3D; - float sum; - Vector3 unskewedCubeOrigin, unskewedDistToOrigin; - Vector3 d1,d2,d3,d4; - }; -} - -#endif // SIMPLEX3D_HPP - diff --git a/include/Nazara/Noise/Simplex4D.hpp b/include/Nazara/Noise/Simplex4D.hpp deleted file mode 100644 index 545c5f290..000000000 --- a/include/Nazara/Noise/Simplex4D.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef SIMPLEX4D_HPP -#define SIMPLEX4D_HPP - -#include -#include -#include -#include - -namespace Nz -{ - class NAZARA_NOISE_API Simplex4D : public Abstract4DNoise - { - public: - Simplex4D(); - Simplex4D(unsigned int seed); - ~Simplex4D() = default; - - float GetValue(float x, float y, float z, float w, float resolution); - - private: - int ii,jj,kk,ll; - int gi0,gi1,gi2,gi3,gi4; - Vector4i skewedCubeOrigin,off1,off2,off3; - int lookupTable4D[64][4]; - int c; - float n1,n2,n3,n4,n5; - float c1,c2,c3,c4,c5,c6; - float gradient4[32][4]; - float UnskewCoeff4D; - float SkewCoeff4D; - float sum; - Vector4 unskewedCubeOrigin, unskewedDistToOrigin; - Vector4 d1,d2,d3,d4,d5; - }; -} - -#endif // SIMPLEX4D_H - diff --git a/include/Nazara/Noise/Worley.hpp b/include/Nazara/Noise/Worley.hpp new file mode 100644 index 000000000..6b9f21f73 --- /dev/null +++ b/include/Nazara/Noise/Worley.hpp @@ -0,0 +1,38 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef NAZARA_WORLEY_HPP +#define NAZARA_WORLEY_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_NOISE_API Worley : public NoiseBase + { + public: + Worley(); + Worley(unsigned int seed); + ~Worley() = default; + + float Get(float x, float y, float scale) const override; + float Get(float x, float y, float z, float scale) const override; + float Get(float x, float y, float z, float w, float scale) const override; + + void Set(WorleyFunction func); + + private: + void SquareTest(int xi, int yi, float x, float y, std::map & featurePoints) const; + + WorleyFunction m_function; + }; +} + +#endif // NAZARA_WORLEY_HPP diff --git a/include/Nazara/Physics/Geom.hpp b/include/Nazara/Physics/Geom.hpp index 34b6c58ba..23a02c72b 100644 --- a/include/Nazara/Physics/Geom.hpp +++ b/include/Nazara/Physics/Geom.hpp @@ -20,7 +20,7 @@ #include #include -struct NewtonCollision; +class NewtonCollision; namespace Nz { diff --git a/include/Nazara/Physics/PhysObject.hpp b/include/Nazara/Physics/PhysObject.hpp index 7df72b1d1..d4d9c61b7 100644 --- a/include/Nazara/Physics/PhysObject.hpp +++ b/include/Nazara/Physics/PhysObject.hpp @@ -15,7 +15,7 @@ #include #include -struct NewtonBody; +class NewtonBody; namespace Nz { diff --git a/include/Nazara/Physics/PhysWorld.hpp b/include/Nazara/Physics/PhysWorld.hpp index b6fd7eae3..0300048b5 100644 --- a/include/Nazara/Physics/PhysWorld.hpp +++ b/include/Nazara/Physics/PhysWorld.hpp @@ -12,7 +12,7 @@ #include #include -struct NewtonWorld; +class NewtonWorld; namespace Nz { diff --git a/include/Nazara/Renderer/OpenGL.hpp b/include/Nazara/Renderer/OpenGL.hpp index b7691cd17..d008b52f4 100644 --- a/include/Nazara/Renderer/OpenGL.hpp +++ b/include/Nazara/Renderer/OpenGL.hpp @@ -319,6 +319,7 @@ NAZARA_RENDERER_API extern PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv; NAZARA_RENDERER_API extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; NAZARA_RENDERER_API extern PFNGLUNMAPBUFFERPROC glUnmapBuffer; NAZARA_RENDERER_API extern PFNGLUSEPROGRAMPROC glUseProgram; +NAZARA_RENDERER_API extern PFNGLVALIDATEPROGRAMPROC glValidateProgram; NAZARA_RENDERER_API extern PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f; NAZARA_RENDERER_API extern PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor; NAZARA_RENDERER_API extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; diff --git a/include/Nazara/Renderer/RenderPipeline.hpp b/include/Nazara/Renderer/RenderPipeline.hpp new file mode 100644 index 000000000..aee5584b7 --- /dev/null +++ b/include/Nazara/Renderer/RenderPipeline.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RENDERPIPELINE_HPP +#define NAZARA_RENDERPIPELINE_HPP + +#include +#include +#include + +namespace Nz +{ + struct RenderPipelineInfo : RenderStates + { + ShaderConstRef shader; + }; + + class RenderPipeline + { + public: + inline RenderPipeline(); + inline ~RenderPipeline(); + + inline bool Create(const RenderPipelineInfo& pipelineInfo); + inline void Destroy(); + + inline const RenderPipelineInfo& GetInfo() const; + + inline bool IsValid() const; + + private: + RenderPipelineInfo m_pipelineInfo; + bool m_valid; + }; +} + +#include + +#endif // NAZARA_RENDERPIPELINE_HPP diff --git a/include/Nazara/Renderer/RenderPipeline.inl b/include/Nazara/Renderer/RenderPipeline.inl new file mode 100644 index 000000000..5982b0562 --- /dev/null +++ b/include/Nazara/Renderer/RenderPipeline.inl @@ -0,0 +1,48 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline RenderPipeline::RenderPipeline() : + m_valid(false) + { + } + + inline RenderPipeline::~RenderPipeline() + { + } + + inline bool RenderPipeline::Create(const RenderPipelineInfo& pipelineInfo) + { + NazaraAssert(pipelineInfo.shader, "Invalid shader"); + + m_pipelineInfo = pipelineInfo; + m_valid = true; + + return true; + } + + inline void RenderPipeline::Destroy() + { + m_valid = false; + } + + inline const RenderPipelineInfo& RenderPipeline::GetInfo() const + { + NazaraAssert(m_valid, "Invalid pipeline info"); + + return m_pipelineInfo; + } + + inline bool RenderPipeline::IsValid() const + { + return m_valid; + } +} + +#include diff --git a/include/Nazara/Renderer/RenderStates.hpp b/include/Nazara/Renderer/RenderStates.hpp index 4040bec55..fc4d10eeb 100644 --- a/include/Nazara/Renderer/RenderStates.hpp +++ b/include/Nazara/Renderer/RenderStates.hpp @@ -7,39 +7,74 @@ #ifndef NAZARA_RENDERSTATES_HPP #define NAZARA_RENDERSTATES_HPP +#include #include namespace Nz { struct RenderStates { - RenderStates(); - RenderStates(const RenderStates& states); - ~RenderStates() = default; + BlendFunc dstBlend = BlendFunc_Zero; + BlendFunc srcBlend = BlendFunc_One; + FaceFilling faceFilling = FaceFilling_Fill; + FaceSide cullingSide = FaceSide_Back; + RendererComparison depthFunc = RendererComparison_Less; - RenderStates& operator=(const RenderStates& states); - - struct Face + struct { - RendererComparison stencilCompare; - StencilOperation stencilFail; - StencilOperation stencilPass; - StencilOperation stencilZFail; - UInt32 stencilMask; - unsigned int stencilReference; - }; + RendererComparison back = RendererComparison_Always; + RendererComparison front = RendererComparison_Always; + } stencilCompare; - Face backFace; - Face frontFace; - BlendFunc dstBlend; - BlendFunc srcBlend; - FaceFilling faceFilling; - FaceSide faceCulling; - RendererComparison depthFunc; - bool parameters[RendererParameter_Max+1]; - float lineWidth; - float pointSize; + struct + { + UInt32 back = 0xFFFFFFFF; + UInt32 front = 0xFFFFFFFF; + } stencilCompareMask; + + struct + { + StencilOperation back = StencilOperation_Keep; + StencilOperation front = StencilOperation_Keep; + } stencilDepthFail; + + struct + { + StencilOperation back = StencilOperation_Keep; + StencilOperation front = StencilOperation_Keep; + } stencilFail; + + struct + { + StencilOperation back = StencilOperation_Keep; + StencilOperation front = StencilOperation_Keep; + } stencilPass; + + struct + { + UInt32 back = 0U; + UInt32 front = 0U; + } stencilReference; + + struct + { + UInt32 back = 0xFFFFFFFF; + UInt32 front = 0xFFFFFFFF; + } stencilWriteMask; + + bool blending = false; + bool colorWrite = true; + bool depthBuffer = false; + bool depthWrite = true; + bool faceCulling = false; + bool scissorTest = false; + bool stencilTest = false; + + float lineWidth = 1.f; + float pointSize = 1.f; }; + + inline bool operator==(const RenderStates& lhs, const RenderStates& rhs); } #include diff --git a/include/Nazara/Renderer/RenderStates.inl b/include/Nazara/Renderer/RenderStates.inl index 6840d3b5f..d9b6bfc31 100644 --- a/include/Nazara/Renderer/RenderStates.inl +++ b/include/Nazara/Renderer/RenderStates.inl @@ -2,52 +2,144 @@ // This file is part of the "Nazara Engine - Renderer module" // For conditions of distribution and use, see copyright notice in Config.hpp -#include +#include +#include +#include +#include #include namespace Nz { - inline RenderStates::RenderStates() : - dstBlend(BlendFunc_Zero), - srcBlend(BlendFunc_One), - faceFilling(FaceFilling_Fill), - faceCulling(FaceSide_Back), - depthFunc(RendererComparison_Less), - lineWidth(1.f), - pointSize(1.f) + bool operator==(const RenderStates& lhs, const RenderStates& rhs) { - parameters[RendererParameter_Blend] = false; - parameters[RendererParameter_ColorWrite] = true; - parameters[RendererParameter_DepthBuffer] = false; - parameters[RendererParameter_DepthWrite] = true; - parameters[RendererParameter_FaceCulling] = false; - parameters[RendererParameter_ScissorTest] = false; - parameters[RendererParameter_StencilTest] = false; + #define NazaraRenderStateMember(field) if (lhs.##field != rhs.##field) return false + #define NazaraRenderStateBoolMember NazaraRenderStateMember + #define NazaraRenderStateFloatMember(field, maxDiff) if (!NumberEquals(lhs.##field, rhs.##field, maxDiff)) return false - for (unsigned int i = 0; i < 2; ++i) + NazaraRenderStateBoolMember(blending); + NazaraRenderStateBoolMember(colorWrite); + NazaraRenderStateBoolMember(depthBuffer); + NazaraRenderStateBoolMember(faceCulling); + NazaraRenderStateBoolMember(scissorTest); + NazaraRenderStateBoolMember(stencilTest); + + if (lhs.depthBuffer) + NazaraRenderStateBoolMember(depthWrite); + + NazaraRenderStateMember(faceFilling); + + if (lhs.blending) //< Remember, at this time we know lhs.blending == rhs.blending { - Face& face = (i == 0) ? backFace : frontFace; - - face.stencilCompare = RendererComparison_Always; - face.stencilFail = StencilOperation_Keep; - face.stencilMask = 0xFFFFFFFF; - face.stencilPass = StencilOperation_Keep; - face.stencilReference = 0; - face.stencilZFail = StencilOperation_Keep; + NazaraRenderStateMember(dstBlend); + NazaraRenderStateMember(srcBlend); } - } - inline RenderStates::RenderStates(const RenderStates& states) - { - std::memcpy(this, &states, sizeof(RenderStates)); - } + if (lhs.depthBuffer) + NazaraRenderStateMember(depthFunc); - inline RenderStates& RenderStates::operator=(const RenderStates& states) - { - std::memcpy(this, &states, sizeof(RenderStates)); + if (lhs.faceCulling) + NazaraRenderStateMember(cullingSide); - return *this; + if (lhs.stencilTest) + { + NazaraRenderStateMember(stencilCompare.back); + NazaraRenderStateMember(stencilCompare.front); + NazaraRenderStateMember(stencilCompareMask.back); + NazaraRenderStateMember(stencilCompareMask.front); + NazaraRenderStateMember(stencilDepthFail.back); + NazaraRenderStateMember(stencilDepthFail.front); + NazaraRenderStateMember(stencilFail.back); + NazaraRenderStateMember(stencilFail.front); + NazaraRenderStateMember(stencilPass.back); + NazaraRenderStateMember(stencilPass.front); + NazaraRenderStateMember(stencilReference.back); + NazaraRenderStateMember(stencilReference.front); + NazaraRenderStateMember(stencilWriteMask.back); + NazaraRenderStateMember(stencilWriteMask.front); + } + + NazaraRenderStateFloatMember(lineWidth, 0.001f); + NazaraRenderStateFloatMember(pointSize, 0.001f); + + #undef NazaraRenderStateMember + #undef NazaraRenderStateBoolMember + #undef NazaraRenderStateFloatMember + + return true; } } +namespace std +{ + template<> + struct hash + { + size_t operator()(const Nz::RenderStates& pipelineInfo) const + { + std::size_t seed = 0; + + Nz::UInt8 parameterHash = 0; + Nz::UInt8 parameterIndex = 0; + + #define NazaraRenderStateMember(member) Nz::HashCombine(seed, pipelineInfo.##member) + #define NazaraRenderStateBoolMember(member) parameterHash |= ((pipelineInfo.##member) ? 1U : 0U) << (parameterIndex++) + #define NazaraRenderStateBoolMemberDep(dependency, member) parameterHash |= ((pipelineInfo.##dependency && pipelineInfo.##member) ? 1U : 0U) << (parameterIndex++) + #define NazaraRenderStateFloatMember(member, maxDiff) Nz::HashCombine(seed, std::floor(pipelineInfo.##member / maxDiff) * maxDiff) + + NazaraRenderStateBoolMember(blending); + NazaraRenderStateBoolMember(colorWrite); + NazaraRenderStateBoolMember(depthBuffer); + NazaraRenderStateBoolMember(faceCulling); + NazaraRenderStateBoolMember(scissorTest); + NazaraRenderStateBoolMember(stencilTest); + + NazaraRenderStateBoolMemberDep(depthBuffer, depthWrite); + + NazaraRenderStateMember(faceFilling); + + if (pipelineInfo.blending) //< Remember, at this time we know lhs.blending == rhs.blending + { + NazaraRenderStateMember(dstBlend); + NazaraRenderStateMember(srcBlend); + } + + if (pipelineInfo.depthBuffer) + NazaraRenderStateMember(depthFunc); + + if (pipelineInfo.faceCulling) + NazaraRenderStateMember(cullingSide); + + if (pipelineInfo.stencilTest) + { + NazaraRenderStateMember(stencilCompare.back); + NazaraRenderStateMember(stencilCompare.front); + NazaraRenderStateMember(stencilCompareMask.back); + NazaraRenderStateMember(stencilCompareMask.front); + NazaraRenderStateMember(stencilDepthFail.back); + NazaraRenderStateMember(stencilDepthFail.front); + NazaraRenderStateMember(stencilFail.back); + NazaraRenderStateMember(stencilFail.front); + NazaraRenderStateMember(stencilPass.back); + NazaraRenderStateMember(stencilPass.front); + NazaraRenderStateMember(stencilReference.back); + NazaraRenderStateMember(stencilReference.front); + NazaraRenderStateMember(stencilWriteMask.back); + NazaraRenderStateMember(stencilWriteMask.front); + } + + NazaraRenderStateFloatMember(lineWidth, 0.001f); + NazaraRenderStateFloatMember(pointSize, 0.001f); + + #undef NazaraRenderStateMember + #undef NazaraRenderStateBoolMember + #undef NazaraRenderStateBoolMemberDep + #undef NazaraRenderStateFloatMember + + Nz::HashCombine(seed, parameterHash); + + return seed; + } + }; +} + #include diff --git a/include/Nazara/Renderer/RenderWindow.hpp b/include/Nazara/Renderer/RenderWindow.hpp index d9889d803..1cda9d63d 100644 --- a/include/Nazara/Renderer/RenderWindow.hpp +++ b/include/Nazara/Renderer/RenderWindow.hpp @@ -65,12 +65,11 @@ namespace Nz protected: bool Activate() const override; void EnsureTargetUpdated() const override; - - private: bool OnWindowCreated() override; void OnWindowDestroy() override; void OnWindowResized() override; + private: mutable std::vector m_buffer; Clock m_clock; ContextParameters m_parameters; diff --git a/include/Nazara/Renderer/Shader.hpp b/include/Nazara/Renderer/Shader.hpp index 35789d09b..aa9dabba8 100644 --- a/include/Nazara/Renderer/Shader.hpp +++ b/include/Nazara/Renderer/Shader.hpp @@ -98,6 +98,8 @@ namespace Nz void SendVectorArray(int location, const Vector4f* vectors, unsigned int count) const; void SendVectorArray(int location, const Vector4i* vectors, unsigned int count) const; + bool Validate() const; + // Fonctions OpenGL unsigned int GetOpenGLID() const; diff --git a/include/Nazara/Renderer/Texture.hpp b/include/Nazara/Renderer/Texture.hpp index 73069aa37..2fd368b53 100644 --- a/include/Nazara/Renderer/Texture.hpp +++ b/include/Nazara/Renderer/Texture.hpp @@ -59,8 +59,8 @@ namespace Nz unsigned int GetHeight(UInt8 level = 0) const; UInt8 GetLevelCount() const; UInt8 GetMaxLevel() const; - unsigned int GetMemoryUsage() const; - unsigned int GetMemoryUsage(UInt8 level) const; + std::size_t GetMemoryUsage() const; + std::size_t GetMemoryUsage(UInt8 level) const; Vector3ui GetSize(UInt8 level = 0) const; ImageType GetType() const; unsigned int GetWidth(UInt8 level = 0) const; diff --git a/include/Nazara/Utility.hpp b/include/Nazara/Utility.hpp index 156ca6389..d7676277e 100644 --- a/include/Nazara/Utility.hpp +++ b/include/Nazara/Utility.hpp @@ -1,4 +1,4 @@ -// This file was automatically generated on 24 Jun 2015 at 13:55:50 +// This file was automatically generated on 12 Jul 2016 at 17:44:43 /* Nazara Engine - Utility module @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Utility/AbstractAtlas.hpp b/include/Nazara/Utility/AbstractAtlas.hpp index 4d135d465..16d09235f 100644 --- a/include/Nazara/Utility/AbstractAtlas.hpp +++ b/include/Nazara/Utility/AbstractAtlas.hpp @@ -29,7 +29,7 @@ namespace Nz virtual void Clear() = 0; virtual void Free(SparsePtr rects, SparsePtr layers, unsigned int count) = 0; virtual AbstractImage* GetLayer(unsigned int layerIndex) const = 0; - virtual unsigned int GetLayerCount() const = 0; + virtual std::size_t GetLayerCount() const = 0; virtual UInt32 GetStorage() const = 0; virtual bool Insert(const Image& image, Rectui* rect, bool* flipped, unsigned int* layerIndex) = 0; diff --git a/include/Nazara/Utility/AbstractImage.hpp b/include/Nazara/Utility/AbstractImage.hpp index 3a09f3a06..93821e105 100644 --- a/include/Nazara/Utility/AbstractImage.hpp +++ b/include/Nazara/Utility/AbstractImage.hpp @@ -28,8 +28,8 @@ namespace Nz virtual unsigned int GetHeight(UInt8 level = 0) const = 0; virtual UInt8 GetLevelCount() const = 0; virtual UInt8 GetMaxLevel() const = 0; - virtual unsigned int GetMemoryUsage() const = 0; - virtual unsigned int GetMemoryUsage(UInt8 level) const = 0; + virtual std::size_t GetMemoryUsage() const = 0; + virtual std::size_t GetMemoryUsage(UInt8 level) const = 0; virtual Vector3ui GetSize(UInt8 level = 0) const = 0; virtual ImageType GetType() const = 0; virtual unsigned int GetWidth(UInt8 level = 0) const = 0; diff --git a/include/Nazara/Utility/AbstractTextDrawer.hpp b/include/Nazara/Utility/AbstractTextDrawer.hpp index ffd704f6a..9f941b9c9 100644 --- a/include/Nazara/Utility/AbstractTextDrawer.hpp +++ b/include/Nazara/Utility/AbstractTextDrawer.hpp @@ -27,10 +27,10 @@ namespace Nz virtual ~AbstractTextDrawer(); virtual const Recti& GetBounds() const = 0; - virtual Font* GetFont(unsigned int index) const = 0; - virtual unsigned int GetFontCount() const = 0; - virtual const Glyph& GetGlyph(unsigned int index) const = 0; - virtual unsigned int GetGlyphCount() const = 0; + virtual Font* GetFont(std::size_t index) const = 0; + virtual std::size_t GetFontCount() const = 0; + virtual const Glyph& GetGlyph(std::size_t index) const = 0; + virtual std::size_t GetGlyphCount() const = 0; struct Glyph { diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index 143390906..3f32945c2 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -142,6 +142,17 @@ namespace Nz NodeType_Max = NodeType_Skeletal }; + enum PixelFormatContent + { + PixelFormatContent_Undefined = -1, + + PixelFormatContent_ColorRGBA, + PixelFormatContent_DepthStencil, + PixelFormatContent_Stencil, + + PixelFormatContent_Max = PixelFormatContent_Stencil + }; + enum PixelFormatType { PixelFormatType_Undefined = -1, @@ -204,27 +215,16 @@ namespace Nz enum PixelFormatSubType { - PixelFormatSubType_Double, // F64 - PixelFormatSubType_Float, // F32 - PixelFormatSubType_Half, // F16 - PixelFormatSubType_Int, // I32 - PixelFormatSubType_Unsigned, // U32 + PixelFormatSubType_Compressed, // Opaque + PixelFormatSubType_Double, // F64 + PixelFormatSubType_Float, // F32 + PixelFormatSubType_Half, // F16 + PixelFormatSubType_Int, // Signed integer + PixelFormatSubType_Unsigned, // Unsigned integer PixelFormatSubType_Max = PixelFormatSubType_Unsigned }; - enum PixelFormatTypeType - { - PixelFormatTypeType_Undefined = -1, - - PixelFormatTypeType_Color, - PixelFormatTypeType_Depth, - PixelFormatTypeType_DepthStencil, - PixelFormatTypeType_Stencil, - - PixelFormatTypeType_Max = PixelFormatTypeType_Stencil - }; - enum PixelFlipping { PixelFlipping_Horizontally, diff --git a/include/Nazara/Utility/Font.hpp b/include/Nazara/Utility/Font.hpp index 257e91245..9a226d91e 100644 --- a/include/Nazara/Utility/Font.hpp +++ b/include/Nazara/Utility/Font.hpp @@ -61,8 +61,8 @@ namespace Nz bool ExtractGlyph(unsigned int characterSize, char32_t character, UInt32 style, FontGlyph* glyph) const; const std::shared_ptr& GetAtlas() const; - unsigned int GetCachedGlyphCount(unsigned int characterSize, UInt32 style) const; - unsigned int GetCachedGlyphCount() const; + std::size_t GetCachedGlyphCount(unsigned int characterSize, UInt32 style) const; + std::size_t GetCachedGlyphCount() const; String GetFamilyName() const; int GetKerning(unsigned int characterSize, char32_t first, char32_t second) const; const Glyph& GetGlyph(unsigned int characterSize, UInt32 style, char32_t character) const; diff --git a/include/Nazara/Utility/Formats/MD5AnimParser.hpp b/include/Nazara/Utility/Formats/MD5AnimParser.hpp index cfba9fdc4..558608142 100644 --- a/include/Nazara/Utility/Formats/MD5AnimParser.hpp +++ b/include/Nazara/Utility/Formats/MD5AnimParser.hpp @@ -47,12 +47,12 @@ namespace Nz Ternary Check(); - unsigned int GetAnimatedComponentCount() const; + std::size_t GetAnimatedComponentCount() const; const Frame* GetFrames() const; - unsigned int GetFrameCount() const; - unsigned int GetFrameRate() const; + std::size_t GetFrameCount() const; + std::size_t GetFrameRate() const; const Joint* GetJoints() const; - unsigned int GetJointCount() const; + std::size_t GetJointCount() const; bool Parse(); diff --git a/include/Nazara/Utility/Formats/MD5MeshParser.hpp b/include/Nazara/Utility/Formats/MD5MeshParser.hpp index 5d114bdbb..cd2d19309 100644 --- a/include/Nazara/Utility/Formats/MD5MeshParser.hpp +++ b/include/Nazara/Utility/Formats/MD5MeshParser.hpp @@ -58,9 +58,9 @@ namespace Nz Ternary Check(); const Joint* GetJoints() const; - unsigned int GetJointCount() const; + std::size_t GetJointCount() const; const Mesh* GetMeshes() const; - unsigned int GetMeshCount() const; + std::size_t GetMeshCount() const; bool Parse(); diff --git a/include/Nazara/Utility/Formats/MTLParser.hpp b/include/Nazara/Utility/Formats/MTLParser.hpp index d58148ec7..9c5b49828 100644 --- a/include/Nazara/Utility/Formats/MTLParser.hpp +++ b/include/Nazara/Utility/Formats/MTLParser.hpp @@ -19,6 +19,22 @@ namespace Nz class NAZARA_UTILITY_API MTLParser { public: + struct Material; + + MTLParser() = default; + ~MTLParser() = default; + + inline Material* AddMaterial(const String& matName); + + inline void Clear(); + + inline const Material* GetMaterial(const String& materialName) const; + inline const std::unordered_map& GetMaterials() const; + + bool Parse(Stream& stream); + + bool Save(Stream& stream) const; + struct Material { Color ambient = Color::White; @@ -39,27 +55,26 @@ namespace Nz unsigned int illumModel = 0; }; - MTLParser(Stream& stream$); - ~MTLParser(); - - const Material* GetMaterial(const String& materialName) const; - const std::unordered_map& GetMaterials() const; - - bool Parse(); - private: bool Advance(bool required = true); - void Error(const String& message); - void Warning(const String& message); - void UnrecognizedLine(bool error = false); + template void Emit(const T& text) const; + inline void EmitLine() const; + template void EmitLine(const T& line) const; + inline void Error(const String& message); + inline void Flush() const; + inline void Warning(const String& message); + inline void UnrecognizedLine(bool error = false); std::unordered_map m_materials; - Stream& m_stream; + mutable Stream* m_currentStream; String m_currentLine; + mutable StringStream m_outputStream; bool m_keepLastLine; unsigned int m_lineCount; unsigned int m_streamFlags; }; } +#include + #endif // NAZARA_FORMATS_MTLPARSER_HPP diff --git a/include/Nazara/Utility/Formats/MTLParser.inl b/include/Nazara/Utility/Formats/MTLParser.inl new file mode 100644 index 000000000..0f8cd4f66 --- /dev/null +++ b/include/Nazara/Utility/Formats/MTLParser.inl @@ -0,0 +1,83 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline MTLParser::Material* MTLParser::AddMaterial(const String& matName) + { + return &m_materials[matName]; + } + + inline void MTLParser::Clear() + { + m_materials.clear(); + } + + inline const MTLParser::Material* MTLParser::GetMaterial(const String& materialName) const + { + auto it = m_materials.find(materialName); + if (it != m_materials.end()) + return &it->second; + else + return nullptr; + } + + inline const std::unordered_map& MTLParser::GetMaterials() const + { + return m_materials; + } + + template + void MTLParser::Emit(const T& text) const + { + m_outputStream << text; + if (m_outputStream.GetBufferSize() > 1024 * 1024) + Flush(); + } + + inline void MTLParser::EmitLine() const + { + Emit('\n'); + } + + template + void MTLParser::EmitLine(const T& line) const + { + Emit(line); + Emit('\n'); + } + + inline void MTLParser::Error(const String& message) + { + NazaraError(message + " at line #" + String::Number(m_lineCount)); + } + + inline void MTLParser::Flush() const + { + m_currentStream->Write(m_outputStream); + m_outputStream.Clear(); + } + + inline void MTLParser::Warning(const String& message) + { + NazaraWarning(message + " at line #" + String::Number(m_lineCount)); + } + + inline void MTLParser::UnrecognizedLine(bool error) + { + String message = "Unrecognized \"" + m_currentLine + '"'; + + if (error) + Error(message); + else + Warning(message); + } +} + +#include +#include "MTLParser.hpp" diff --git a/include/Nazara/Utility/Formats/OBJParser.hpp b/include/Nazara/Utility/Formats/OBJParser.hpp index 90e653f93..114f72635 100644 --- a/include/Nazara/Utility/Formats/OBJParser.hpp +++ b/include/Nazara/Utility/Formats/OBJParser.hpp @@ -20,60 +20,89 @@ namespace Nz class NAZARA_UTILITY_API OBJParser { public: - struct FaceVertex - { - int normal; - int position; - int texCoord; - }; + struct Face; + struct FaceVertex; + struct Mesh; + + OBJParser() = default; + ~OBJParser() = default; + + inline void Clear(); + + inline String* GetMaterials(); + inline const String* GetMaterials() const; + inline unsigned int GetMaterialCount() const; + inline Mesh* GetMeshes(); + inline const Mesh* GetMeshes() const; + inline unsigned int GetMeshCount() const; + inline const String& GetMtlLib() const; + inline Vector3f* GetNormals(); + inline const Vector3f* GetNormals() const; + inline unsigned int GetNormalCount() const; + inline Vector4f* GetPositions(); + inline const Vector4f* GetPositions() const; + inline unsigned int GetPositionCount() const; + inline Vector3f* GetTexCoords(); + inline const Vector3f* GetTexCoords() const; + inline unsigned int GetTexCoordCount() const; + + bool Parse(Stream& stream, std::size_t reservedVertexCount = 100); + + bool Save(Stream& stream) const; + + inline String* SetMaterialCount(std::size_t materialCount); + inline Mesh* SetMeshCount(std::size_t meshCount); + inline void SetMtlLib(const String& mtlLib); + inline Vector3f* SetNormalCount(std::size_t normalCount); + inline Vector4f* SetPositionCount(std::size_t positionCount); + inline Vector3f* SetTexCoordCount(std::size_t texCoordCount); struct Face { - std::vector vertices; + std::size_t firstVertex; + std::size_t vertexCount; + }; + + struct FaceVertex + { + std::size_t normal; + std::size_t position; + std::size_t texCoord; }; struct Mesh { std::vector faces; + std::vector vertices; String name; - unsigned int material; + std::size_t material; }; - OBJParser(Stream& stream$); - ~OBJParser(); - - const String* GetMaterials() const; - unsigned int GetMaterialCount() const; - const Mesh* GetMeshes() const; - unsigned int GetMeshCount() const; - const String& GetMtlLib() const; - const Vector3f* GetNormals() const; - unsigned int GetNormalCount() const; - const Vector4f* GetPositions() const; - unsigned int GetPositionCount() const; - const Vector3f* GetTexCoords() const; - unsigned int GetTexCoordCount() const; - - bool Parse(std::size_t reservedVertexCount = 100); - private: bool Advance(bool required = true); - void Error(const String& message); - void Warning(const String& message); - void UnrecognizedLine(bool error = false); + template void Emit(const T& text) const; + inline void EmitLine() const; + template void EmitLine(const T& line) const; + inline void Error(const String& message); + inline void Flush() const; + inline void Warning(const String& message); + inline bool UnrecognizedLine(bool error = false); std::vector m_meshes; std::vector m_materials; std::vector m_normals; std::vector m_positions; std::vector m_texCoords; - Stream& m_stream; + mutable Stream* m_currentStream; String m_currentLine; String m_mtlLib; + mutable StringStream m_outputStream; bool m_keepLastLine; unsigned int m_lineCount; - unsigned int m_streamFlags; + unsigned int m_errorCount; }; } +#include + #endif // NAZARA_FORMATS_OBJPARSER_HPP diff --git a/include/Nazara/Utility/Formats/OBJParser.inl b/include/Nazara/Utility/Formats/OBJParser.inl new file mode 100644 index 000000000..b04506fdf --- /dev/null +++ b/include/Nazara/Utility/Formats/OBJParser.inl @@ -0,0 +1,192 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline void OBJParser::Clear() + { + m_materials.clear(); + m_meshes.clear(); + m_positions.clear(); + m_normals.clear(); + m_texCoords.clear(); + } + + inline String* OBJParser::GetMaterials() + { + return m_materials.data(); + } + + inline const String* OBJParser::GetMaterials() const + { + return m_materials.data(); + } + + inline unsigned int OBJParser::GetMaterialCount() const + { + return m_materials.size(); + } + + inline OBJParser::Mesh* OBJParser::GetMeshes() + { + return m_meshes.data(); + } + + inline const OBJParser::Mesh* OBJParser::GetMeshes() const + { + return m_meshes.data(); + } + + inline unsigned int OBJParser::GetMeshCount() const + { + return m_meshes.size(); + } + + inline const String& OBJParser::GetMtlLib() const + { + return m_mtlLib; + } + + inline Vector3f* OBJParser::GetNormals() + { + return m_normals.data(); + } + + inline const Vector3f* OBJParser::GetNormals() const + { + return m_normals.data(); + } + + inline unsigned int OBJParser::GetNormalCount() const + { + return m_normals.size(); + } + + inline Vector4f* OBJParser::GetPositions() + { + return m_positions.data(); + } + + inline const Vector4f* OBJParser::GetPositions() const + { + return m_positions.data(); + } + + inline unsigned int OBJParser::GetPositionCount() const + { + return m_positions.size(); + } + + inline Vector3f* OBJParser::GetTexCoords() + { + return m_texCoords.data(); + } + + inline const Vector3f* OBJParser::GetTexCoords() const + { + return m_texCoords.data(); + } + + inline unsigned int OBJParser::GetTexCoordCount() const + { + return m_texCoords.size(); + } + + inline String* OBJParser::SetMaterialCount(std::size_t materialCount) + { + m_materials.resize(materialCount); + return m_materials.data(); + } + + inline OBJParser::Mesh* OBJParser::SetMeshCount(std::size_t meshCount) + { + m_meshes.resize(meshCount); + return m_meshes.data(); + } + + inline void OBJParser::SetMtlLib(const String& mtlLib) + { + m_mtlLib = mtlLib; + } + + inline Vector3f* OBJParser::SetNormalCount(std::size_t normalCount) + { + m_normals.resize(normalCount); + return m_normals.data(); + } + + inline Vector4f* OBJParser::SetPositionCount(std::size_t positionCount) + { + m_positions.resize(positionCount); + return m_positions.data(); + } + + inline Vector3f* OBJParser::SetTexCoordCount(std::size_t texCoordCount) + { + m_texCoords.resize(texCoordCount); + return m_texCoords.data(); + } + + template + void OBJParser::Emit(const T& text) const + { + m_outputStream << text; + if (m_outputStream.GetBufferSize() > 1024 * 1024) + Flush(); + } + + inline void OBJParser::EmitLine() const + { + Emit('\n'); + } + + template + void OBJParser::EmitLine(const T& line) const + { + Emit(line); + Emit('\n'); + } + + inline void OBJParser::Error(const String& message) + { + NazaraError(message + " at line #" + String::Number(m_lineCount)); + } + + inline void OBJParser::Flush() const + { + m_currentStream->Write(m_outputStream); + m_outputStream.Clear(); + } + + inline void OBJParser::Warning(const String& message) + { + NazaraWarning(message + " at line #" + String::Number(m_lineCount)); + } + + inline bool OBJParser::UnrecognizedLine(bool error) + { + String message = "Unrecognized \"" + m_currentLine + '"'; + + if (error) + Error(message); + else + Warning(message); + + m_errorCount++; + + if (m_errorCount > 10 && (m_errorCount * 100 / m_lineCount) > 50) + { + NazaraError("Aborting parsing because of error percentage"); + return false; //< Abort parsing if error percentage is too high + } + + return true; + } +} + +#include diff --git a/include/Nazara/Utility/GuillotineImageAtlas.hpp b/include/Nazara/Utility/GuillotineImageAtlas.hpp index f26ae13ea..318b92d6b 100644 --- a/include/Nazara/Utility/GuillotineImageAtlas.hpp +++ b/include/Nazara/Utility/GuillotineImageAtlas.hpp @@ -23,16 +23,17 @@ namespace Nz GuillotineImageAtlas(); virtual ~GuillotineImageAtlas(); - void Clear(); - void Free(SparsePtr rects, SparsePtr layers, unsigned int count); + void Clear() override; + + void Free(SparsePtr rects, SparsePtr layers, unsigned int count) override; GuillotineBinPack::FreeRectChoiceHeuristic GetRectChoiceHeuristic() const; GuillotineBinPack::GuillotineSplitHeuristic GetRectSplitHeuristic() const; - AbstractImage* GetLayer(unsigned int layerIndex) const; - unsigned int GetLayerCount() const; - UInt32 GetStorage() const; + AbstractImage* GetLayer(unsigned int layerIndex) const override; + std::size_t GetLayerCount() const override; + UInt32 GetStorage() const override; - bool Insert(const Image& image, Rectui* rect, bool* flipped, unsigned int* layerIndex); + bool Insert(const Image& image, Rectui* rect, bool* flipped, unsigned int* layerIndex) override; void SetRectChoiceHeuristic(GuillotineBinPack::FreeRectChoiceHeuristic heuristic); void SetRectSplitHeuristic(GuillotineBinPack::GuillotineSplitHeuristic heuristic); diff --git a/include/Nazara/Utility/Image.hpp b/include/Nazara/Utility/Image.hpp index 65c03d6bd..d936ee4d8 100644 --- a/include/Nazara/Utility/Image.hpp +++ b/include/Nazara/Utility/Image.hpp @@ -84,8 +84,8 @@ namespace Nz unsigned int GetHeight(UInt8 level = 0) const; UInt8 GetLevelCount() const; UInt8 GetMaxLevel() const; - unsigned int GetMemoryUsage() const; - unsigned int GetMemoryUsage(UInt8 level) const; + std::size_t GetMemoryUsage() const; + std::size_t GetMemoryUsage(UInt8 level) const; Color GetPixelColor(unsigned int x, unsigned int y = 0, unsigned int z = 0) const; UInt8* GetPixels(unsigned int x = 0, unsigned int y = 0, unsigned int z = 0, UInt8 level = 0); Vector3ui GetSize(UInt8 level = 0) const; diff --git a/include/Nazara/Utility/IndexMapper.hpp b/include/Nazara/Utility/IndexMapper.hpp index 9dd6a5c46..7f632f196 100644 --- a/include/Nazara/Utility/IndexMapper.hpp +++ b/include/Nazara/Utility/IndexMapper.hpp @@ -23,9 +23,10 @@ namespace Nz class NAZARA_UTILITY_API IndexMapper { public: - IndexMapper(IndexBuffer* indexBuffer, BufferAccess access = BufferAccess_ReadWrite); - IndexMapper(const IndexBuffer* indexBuffer, BufferAccess access = BufferAccess_ReadOnly); - IndexMapper(const SubMesh* subMesh); + IndexMapper(IndexBuffer* indexBuffer, BufferAccess access = BufferAccess_ReadWrite, std::size_t indexCount = 0); + IndexMapper(SubMesh* subMesh, BufferAccess access = BufferAccess_ReadWrite); + IndexMapper(const IndexBuffer* indexBuffer, BufferAccess access = BufferAccess_ReadOnly, std::size_t indexCount = 0); + IndexMapper(const SubMesh* subMesh, BufferAccess access = BufferAccess_ReadOnly); ~IndexMapper() = default; UInt32 Get(unsigned int i) const; diff --git a/include/Nazara/Utility/MaterialData.hpp b/include/Nazara/Utility/MaterialData.hpp index 0f69c9846..0c1e28cdd 100644 --- a/include/Nazara/Utility/MaterialData.hpp +++ b/include/Nazara/Utility/MaterialData.hpp @@ -22,7 +22,7 @@ namespace Nz static constexpr const char* BackFaceStencilReference = "MatBackFaceStencilReference"; static constexpr const char* BackFaceStencilZFail = "MatBackFaceStencilZFail"; static constexpr const char* Blending = "MatBlending"; - static constexpr const char* CustomDefined = "MatCustomDefined"; + static constexpr const char* CullingSide = "MatCullingSide"; static constexpr const char* ColorWrite = "MatColorWrite"; static constexpr const char* DepthBuffer = "MatDepthBuffer"; static constexpr const char* DepthFunc = "MatDepthfunc"; @@ -41,6 +41,7 @@ namespace Nz static constexpr const char* HeightTexturePath = "MatHeightTexturePath"; static constexpr const char* Lighting = "MatLighting"; static constexpr const char* LineWidth = "MatLineWidth"; + static constexpr const char* Name = "MatName"; static constexpr const char* NormalTexturePath = "MatNormalTexturePath"; static constexpr const char* PointSize = "MatPointSize"; static constexpr const char* ScissorTest = "MatScissorTest"; diff --git a/include/Nazara/Utility/Mesh.hpp b/include/Nazara/Utility/Mesh.hpp index ff11b5d4d..62a372384 100644 --- a/include/Nazara/Utility/Mesh.hpp +++ b/include/Nazara/Utility/Mesh.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -29,8 +30,8 @@ namespace Nz { MeshParams(); // Vérifie que le storage par défaut est supporté (software autrement) - // La mise à l'échelle éventuelle que subira le mesh - Vector3f scale = Vector3f::Unit(); + // La transformation appliquée à tous les sommets du mesh + Matrix4f matrix = Matrix4f::Identity(); // Si ceci sera le stockage utilisé par les buffers UInt32 storage = DataStorage_Hardware; @@ -61,6 +62,7 @@ namespace Nz using MeshLoader = ResourceLoader; using MeshManager = ResourceManager; using MeshRef = ObjectRef; + using MeshSaver = ResourceSaver; struct MeshImpl; @@ -69,6 +71,7 @@ namespace Nz friend MeshLibrary; friend MeshLoader; friend MeshManager; + friend MeshSaver; friend class Utility; public: @@ -124,6 +127,9 @@ namespace Nz void RemoveSubMesh(const String& identifier); void RemoveSubMesh(unsigned int index); + bool SaveToFile(const String& filePath, const MeshParams& params = MeshParams()); + bool SaveToStream(Stream& stream, const String& format, const MeshParams& params = MeshParams()); + void SetAnimation(const String& animationPath); void SetMaterialCount(unsigned int matCount); void SetMaterialData(unsigned int matIndex, ParameterList data); @@ -146,6 +152,7 @@ namespace Nz static MeshLoader::LoaderList s_loaders; static MeshManager::ManagerMap s_managerMap; static MeshManager::ManagerParams s_managerParameters; + static MeshSaver::SaverList s_savers; }; } diff --git a/include/Nazara/Utility/PixelFormat.hpp b/include/Nazara/Utility/PixelFormat.hpp index 6844162d5..92eecb7f4 100644 --- a/include/Nazara/Utility/PixelFormat.hpp +++ b/include/Nazara/Utility/PixelFormat.hpp @@ -23,33 +23,36 @@ namespace Nz { struct PixelFormatInfo { - PixelFormatInfo() : - bitsPerPixel(0) - { - } + inline PixelFormatInfo(); + inline PixelFormatInfo(PixelFormatContent formatContent, UInt8 bpp, PixelFormatSubType subType); + inline PixelFormatInfo(const String& formatName, PixelFormatContent formatContent, UInt8 bpp, PixelFormatSubType subType); + inline PixelFormatInfo(const String& formatName, PixelFormatContent formatContent, Bitset<> rMask, Bitset<> gMask, Bitset<> bMask, Bitset<> aMask, PixelFormatSubType subType); + inline PixelFormatInfo(const String& formatName, PixelFormatContent formatContent, PixelFormatSubType rType, Bitset<> rMask, PixelFormatSubType gType, Bitset<> gMask, PixelFormatSubType bType, Bitset<> bMask, PixelFormatSubType aType, Bitset<> aMask, UInt8 bpp = 0); - PixelFormatInfo(UInt8 bpp, PixelFormatSubType subType) : - bitsPerPixel(bpp), - redType(subType), - greenType(subType), - blueType(subType), - alphaType(subType) - { - } + inline void Clear(); - // Warning: Bit Endian + inline bool IsCompressed() const; + inline bool IsValid() const; + + inline void RecomputeBitsPerPixel(); + + inline bool Validate() const; + + // Warning: Masks bit order is reversed Bitset<> redMask; Bitset<> greenMask; Bitset<> blueMask; Bitset<> alphaMask; + PixelFormatContent content; PixelFormatSubType redType; PixelFormatSubType greenType; PixelFormatSubType blueType; PixelFormatSubType alphaType; + String name; UInt8 bitsPerPixel; }; - class PixelFormat + class NAZARA_UTILITY_API PixelFormat { friend class Utility; @@ -59,34 +62,35 @@ namespace Nz static inline std::size_t ComputeSize(PixelFormatType format, unsigned int width, unsigned int height, unsigned int depth); - static bool Convert(PixelFormatType srcFormat, PixelFormatType dstFormat, const void* src, void* dst); - static bool Convert(PixelFormatType srcFormat, PixelFormatType dstFormat, const void* start, const void* end, void* dst); + static inline bool Convert(PixelFormatType srcFormat, PixelFormatType dstFormat, const void* src, void* dst); + static inline bool Convert(PixelFormatType srcFormat, PixelFormatType dstFormat, const void* start, const void* end, void* dst); - static bool Flip(PixelFlipping flipping, PixelFormatType format, unsigned int width, unsigned int height, unsigned int depth, const void* src, void* dst); + static inline bool Flip(PixelFlipping flipping, PixelFormatType format, unsigned int width, unsigned int height, unsigned int depth, const void* src, void* dst); - static UInt8 GetBitsPerPixel(PixelFormatType format); - static UInt8 GetBytesPerPixel(PixelFormatType format); - static PixelFormatTypeType GetType(PixelFormatType format); + static inline UInt8 GetBitsPerPixel(PixelFormatType format); + static inline PixelFormatContent GetContent(PixelFormatType format); + static inline UInt8 GetBytesPerPixel(PixelFormatType format); + static inline const PixelFormatInfo& GetInfo(PixelFormatType format); + static inline const String& GetName(PixelFormatType format); - static bool HasAlpha(PixelFormatType format); + static inline bool HasAlpha(PixelFormatType format); static PixelFormatType IdentifyFormat(const PixelFormatInfo& info); - static bool IsCompressed(PixelFormatType format); - static bool IsConversionSupported(PixelFormatType srcFormat, PixelFormatType dstFormat); - static bool IsValid(PixelFormatType format); + static inline bool IsCompressed(PixelFormatType format); + static inline bool IsConversionSupported(PixelFormatType srcFormat, PixelFormatType dstFormat); + static inline bool IsValid(PixelFormatType format); - static void SetConvertFunction(PixelFormatType srcFormat, PixelFormatType dstFormat, ConvertFunction func); - static void SetFlipFunction(PixelFlipping flipping, PixelFormatType format, FlipFunction func); - - static String ToString(PixelFormatType format); + static inline void SetConvertFunction(PixelFormatType srcFormat, PixelFormatType dstFormat, ConvertFunction func); + static inline void SetFlipFunction(PixelFlipping flipping, PixelFormatType format, FlipFunction func); private: static bool Initialize(); static void Uninitialize(); - static NAZARA_UTILITY_API ConvertFunction s_convertFunctions[PixelFormatType_Max+1][PixelFormatType_Max+1]; - static NAZARA_UTILITY_API std::map s_flipFunctions[PixelFlipping_Max+1]; + static PixelFormatInfo s_pixelFormatInfos[PixelFormatType_Max + 1]; + static ConvertFunction s_convertFunctions[PixelFormatType_Max+1][PixelFormatType_Max+1]; + static std::map s_flipFunctions[PixelFlipping_Max+1]; }; } diff --git a/include/Nazara/Utility/PixelFormat.inl b/include/Nazara/Utility/PixelFormat.inl index 2d09273b9..bf827c4c0 100644 --- a/include/Nazara/Utility/PixelFormat.inl +++ b/include/Nazara/Utility/PixelFormat.inl @@ -2,13 +2,151 @@ // This file is part of the "Nazara Engine - Utility module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include +#include #include #include namespace Nz { + inline PixelFormatInfo::PixelFormatInfo() : + content(PixelFormatContent_Undefined), + bitsPerPixel(0) + { + } + + inline PixelFormatInfo::PixelFormatInfo(PixelFormatContent formatContent, UInt8 bpp, PixelFormatSubType subType) : + content(formatContent), + redType(subType), + greenType(subType), + blueType(subType), + alphaType(subType), + bitsPerPixel(bpp) + { + } + + inline PixelFormatInfo::PixelFormatInfo(const String& formatName, PixelFormatContent formatContent, UInt8 bpp, PixelFormatSubType subType) : + content(formatContent), + redType(subType), + greenType(subType), + blueType(subType), + alphaType(subType), + name(formatName), + bitsPerPixel(bpp) + { + } + + inline PixelFormatInfo::PixelFormatInfo(const String& formatName, PixelFormatContent formatContent, Bitset<> rMask, Bitset<> gMask, Bitset<> bMask, Bitset<> aMask, PixelFormatSubType subType) : + redMask(rMask), + greenMask(gMask), + blueMask(bMask), + alphaMask(aMask), + content(formatContent), + redType(subType), + greenType(subType), + blueType(subType), + alphaType(subType), + name(formatName) + { + RecomputeBitsPerPixel(); + } + + inline PixelFormatInfo::PixelFormatInfo(const String& formatName, PixelFormatContent formatContent, PixelFormatSubType rType, Bitset<> rMask, PixelFormatSubType gType, Bitset<> gMask, PixelFormatSubType bType, Bitset<> bMask, PixelFormatSubType aType, Bitset<> aMask, UInt8 bpp) : + redMask(rMask), + greenMask(gMask), + blueMask(bMask), + alphaMask(aMask), + content(formatContent), + redType(rType), + greenType(gType), + blueType(bType), + alphaType(aType), + name(formatName) + { + if (bpp == 0) + RecomputeBitsPerPixel(); + } + + inline void PixelFormatInfo::Clear() + { + bitsPerPixel = 0; + alphaMask.Clear(); + blueMask.Clear(); + greenMask.Clear(); + redMask.Clear(); + name.Clear(); + } + + inline bool PixelFormatInfo::IsCompressed() const + { + return redType == PixelFormatSubType_Compressed || + greenType == PixelFormatSubType_Compressed || + blueType == PixelFormatSubType_Compressed || + alphaType == PixelFormatSubType_Compressed; + } + + inline bool PixelFormatInfo::IsValid() const + { + return bitsPerPixel != 0; + } + + inline void PixelFormatInfo::RecomputeBitsPerPixel() + { + Bitset<> counter; + counter |= redMask; + counter |= greenMask; + counter |= blueMask; + counter |= alphaMask; + + bitsPerPixel = static_cast(counter.Count()); + } + + inline bool PixelFormatInfo::Validate() const + { + if (!IsValid()) + return false; + + if (content <= PixelFormatContent_Undefined || content > PixelFormatContent_Max) + return false; + + std::array*, 4> masks = {&redMask, &greenMask, &blueMask, &alphaMask}; + std::array types = {redType, greenType, blueType, alphaType}; + + for (unsigned int i = 0; i < 4; ++i) + { + UInt8 usedBits = static_cast(masks[i]->Count()); + if (usedBits == 0) + continue; + + if (usedBits > bitsPerPixel) + return false; + + switch (types[i]) + { + case PixelFormatSubType_Half: + if (usedBits != 16) + return false; + + break; + + case PixelFormatSubType_Float: + if (usedBits != 32) + return false; + + break; + + default: + break; + } + } + + return true; + } + + + inline std::size_t PixelFormat::ComputeSize(PixelFormatType format, unsigned int width, unsigned int height, unsigned int depth) { if (IsCompressed(format)) @@ -18,7 +156,7 @@ namespace Nz case PixelFormatType_DXT1: case PixelFormatType_DXT3: case PixelFormatType_DXT5: - return (((width + 3) / 4) * ((height + 3) / 4) * (format == PixelFormatType_DXT1) ? 8 : 16) * depth; + return (((width + 3) / 4) * ((height + 3) / 4) * ((format == PixelFormatType_DXT1) ? 8 : 16)) * depth; default: NazaraError("Unsupported format"); @@ -54,13 +192,13 @@ namespace Nz ConvertFunction func = s_convertFunctions[srcFormat][dstFormat]; if (!func) { - NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " is not supported"); + NazaraError("Pixel format conversion from " + GetName(srcFormat) + " to " + GetName(dstFormat) + " is not supported"); return false; } if (!func(reinterpret_cast(src), reinterpret_cast(src) + GetBytesPerPixel(srcFormat), reinterpret_cast(dst))) { - NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " failed"); + NazaraError("Pixel format conversion from " + GetName(srcFormat) + " to " + GetName(dstFormat) + " failed"); return false; } @@ -78,13 +216,13 @@ namespace Nz ConvertFunction func = s_convertFunctions[srcFormat][dstFormat]; if (!func) { - NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " is not supported"); + NazaraError("Pixel format conversion from " + GetName(srcFormat) + " to " + GetName(dstFormat) + " is not supported"); return false; } if (!func(reinterpret_cast(start), reinterpret_cast(end), reinterpret_cast(dst))) { - NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " failed"); + NazaraError("Pixel format conversion from " + GetName(srcFormat) + " to " + GetName(dstFormat) + " failed"); return false; } @@ -189,126 +327,7 @@ namespace Nz inline UInt8 PixelFormat::GetBitsPerPixel(PixelFormatType format) { - switch (format) - { - case PixelFormatType_A8: - return 8; - - case PixelFormatType_BGR8: - return 24; - - case PixelFormatType_BGRA8: - return 32; - - case PixelFormatType_DXT1: - return 8; - - case PixelFormatType_DXT3: - return 16; - - case PixelFormatType_DXT5: - return 16; - - case PixelFormatType_L8: - return 8; - - case PixelFormatType_LA8: - return 16; - - case PixelFormatType_R8: - case PixelFormatType_R8I: - case PixelFormatType_R8UI: - return 8; - - case PixelFormatType_R16: - case PixelFormatType_R16F: - case PixelFormatType_R16I: - case PixelFormatType_R16UI: - return 16; - - case PixelFormatType_R32F: - case PixelFormatType_R32I: - case PixelFormatType_R32UI: - return 32; - - case PixelFormatType_RG8: - case PixelFormatType_RG8I: - case PixelFormatType_RG8UI: - return 16; - - case PixelFormatType_RG16: - case PixelFormatType_RG16F: - case PixelFormatType_RG16I: - case PixelFormatType_RG16UI: - return 32; - - case PixelFormatType_RG32F: - case PixelFormatType_RG32I: - case PixelFormatType_RG32UI: - return 64; - - case PixelFormatType_RGB16F: - case PixelFormatType_RGB16I: - case PixelFormatType_RGB16UI: - return 48; - - case PixelFormatType_RGB32F: - case PixelFormatType_RGB32I: - case PixelFormatType_RGB32UI: - return 96; - - case PixelFormatType_RGBA16F: - case PixelFormatType_RGBA16I: - case PixelFormatType_RGBA16UI: - return 64; - - case PixelFormatType_RGBA32F: - case PixelFormatType_RGBA32I: - case PixelFormatType_RGBA32UI: - return 128; - - case PixelFormatType_RGBA4: - return 16; - - case PixelFormatType_RGB5A1: - return 16; - - case PixelFormatType_RGB8: - return 24; - - case PixelFormatType_RGBA8: - return 32; - - case PixelFormatType_Depth16: - return 16; - - case PixelFormatType_Depth24: - return 24; - - case PixelFormatType_Depth24Stencil8: - return 32; - - case PixelFormatType_Depth32: - return 32; - - case PixelFormatType_Stencil1: - return 1; - - case PixelFormatType_Stencil4: - return 2; - - case PixelFormatType_Stencil8: - return 8; - - case PixelFormatType_Stencil16: - return 16; - - case PixelFormatType_Undefined: - break; - } - - NazaraError("Invalid pixel format"); - return 0; + return s_pixelFormatInfos[format].bitsPerPixel; } inline UInt8 PixelFormat::GetBytesPerPixel(PixelFormatType format) @@ -316,212 +335,29 @@ namespace Nz return GetBitsPerPixel(format)/8; } - inline PixelFormatTypeType PixelFormat::GetType(PixelFormatType format) + inline PixelFormatContent PixelFormat::GetContent(PixelFormatType format) { - switch (format) - { - case PixelFormatType_A8: - case PixelFormatType_BGR8: - case PixelFormatType_BGRA8: - case PixelFormatType_DXT1: - case PixelFormatType_DXT3: - case PixelFormatType_DXT5: - case PixelFormatType_L8: - case PixelFormatType_LA8: - case PixelFormatType_R8: - case PixelFormatType_R8I: - case PixelFormatType_R8UI: - case PixelFormatType_R16: - case PixelFormatType_R16F: - case PixelFormatType_R16I: - case PixelFormatType_R16UI: - case PixelFormatType_R32F: - case PixelFormatType_R32I: - case PixelFormatType_R32UI: - case PixelFormatType_RG8: - case PixelFormatType_RG8I: - case PixelFormatType_RG8UI: - case PixelFormatType_RG16: - case PixelFormatType_RG16F: - case PixelFormatType_RG16I: - case PixelFormatType_RG16UI: - case PixelFormatType_RG32F: - case PixelFormatType_RG32I: - case PixelFormatType_RG32UI: - case PixelFormatType_RGB5A1: - case PixelFormatType_RGB8: - case PixelFormatType_RGB16F: - case PixelFormatType_RGB16I: - case PixelFormatType_RGB16UI: - case PixelFormatType_RGB32F: - case PixelFormatType_RGB32I: - case PixelFormatType_RGB32UI: - case PixelFormatType_RGBA4: - case PixelFormatType_RGBA8: - case PixelFormatType_RGBA16F: - case PixelFormatType_RGBA16I: - case PixelFormatType_RGBA16UI: - case PixelFormatType_RGBA32F: - case PixelFormatType_RGBA32I: - case PixelFormatType_RGBA32UI: - return PixelFormatTypeType_Color; + return s_pixelFormatInfos[format].content; + } - case PixelFormatType_Depth16: - case PixelFormatType_Depth24: - case PixelFormatType_Depth32: - return PixelFormatTypeType_Depth; + inline const PixelFormatInfo& PixelFormat::GetInfo(PixelFormatType format) + { + return s_pixelFormatInfos[format]; + } - case PixelFormatType_Depth24Stencil8: - return PixelFormatTypeType_DepthStencil; - - case PixelFormatType_Stencil1: - case PixelFormatType_Stencil4: - case PixelFormatType_Stencil8: - case PixelFormatType_Stencil16: - return PixelFormatTypeType_Stencil; - - case PixelFormatType_Undefined: - break; - } - - NazaraError("Invalid pixel format"); - return PixelFormatTypeType_Undefined; + inline const String& PixelFormat::GetName(PixelFormatType format) + { + return s_pixelFormatInfos[format].name; } inline bool PixelFormat::HasAlpha(PixelFormatType format) { - switch (format) - { - case PixelFormatType_A8: - case PixelFormatType_BGRA8: - case PixelFormatType_DXT3: - case PixelFormatType_DXT5: - case PixelFormatType_LA8: - case PixelFormatType_RGB5A1: - case PixelFormatType_RGBA16F: - case PixelFormatType_RGBA16I: - case PixelFormatType_RGBA16UI: - case PixelFormatType_RGBA32F: - case PixelFormatType_RGBA32I: - case PixelFormatType_RGBA32UI: - case PixelFormatType_RGBA4: - case PixelFormatType_RGBA8: - return true; - - case PixelFormatType_BGR8: - case PixelFormatType_DXT1: - case PixelFormatType_L8: - case PixelFormatType_R8: - case PixelFormatType_R8I: - case PixelFormatType_R8UI: - case PixelFormatType_R16: - case PixelFormatType_R16F: - case PixelFormatType_R16I: - case PixelFormatType_R16UI: - case PixelFormatType_R32F: - case PixelFormatType_R32I: - case PixelFormatType_R32UI: - case PixelFormatType_RG8: - case PixelFormatType_RG8I: - case PixelFormatType_RG8UI: - case PixelFormatType_RG16: - case PixelFormatType_RG16F: - case PixelFormatType_RG16I: - case PixelFormatType_RG16UI: - case PixelFormatType_RG32F: - case PixelFormatType_RG32I: - case PixelFormatType_RG32UI: - case PixelFormatType_RGB8: - case PixelFormatType_RGB16F: - case PixelFormatType_RGB16I: - case PixelFormatType_RGB16UI: - case PixelFormatType_RGB32F: - case PixelFormatType_RGB32I: - case PixelFormatType_RGB32UI: - case PixelFormatType_Depth16: - case PixelFormatType_Depth24: - case PixelFormatType_Depth24Stencil8: - case PixelFormatType_Depth32: - case PixelFormatType_Stencil1: - case PixelFormatType_Stencil4: - case PixelFormatType_Stencil8: - case PixelFormatType_Stencil16: - return false; - - case PixelFormatType_Undefined: - break; - } - - NazaraError("Invalid pixel format"); - return false; + return s_pixelFormatInfos[format].alphaMask.TestAny(); } inline bool PixelFormat::IsCompressed(PixelFormatType format) { - switch (format) - { - case PixelFormatType_DXT1: - case PixelFormatType_DXT3: - case PixelFormatType_DXT5: - return true; - - case PixelFormatType_A8: - case PixelFormatType_BGR8: - case PixelFormatType_BGRA8: - case PixelFormatType_L8: - case PixelFormatType_LA8: - case PixelFormatType_R8: - case PixelFormatType_R8I: - case PixelFormatType_R8UI: - case PixelFormatType_R16: - case PixelFormatType_R16F: - case PixelFormatType_R16I: - case PixelFormatType_R16UI: - case PixelFormatType_R32F: - case PixelFormatType_R32I: - case PixelFormatType_R32UI: - case PixelFormatType_RG8: - case PixelFormatType_RG8I: - case PixelFormatType_RG8UI: - case PixelFormatType_RG16: - case PixelFormatType_RG16F: - case PixelFormatType_RG16I: - case PixelFormatType_RG16UI: - case PixelFormatType_RG32F: - case PixelFormatType_RG32I: - case PixelFormatType_RG32UI: - case PixelFormatType_RGB5A1: - case PixelFormatType_RGB8: - case PixelFormatType_RGB16F: - case PixelFormatType_RGB16I: - case PixelFormatType_RGB16UI: - case PixelFormatType_RGB32F: - case PixelFormatType_RGB32I: - case PixelFormatType_RGB32UI: - case PixelFormatType_RGBA4: - case PixelFormatType_RGBA8: - case PixelFormatType_RGBA16F: - case PixelFormatType_RGBA16I: - case PixelFormatType_RGBA16UI: - case PixelFormatType_RGBA32F: - case PixelFormatType_RGBA32I: - case PixelFormatType_RGBA32UI: - case PixelFormatType_Depth16: - case PixelFormatType_Depth24: - case PixelFormatType_Depth24Stencil8: - case PixelFormatType_Depth32: - case PixelFormatType_Stencil1: - case PixelFormatType_Stencil4: - case PixelFormatType_Stencil8: - case PixelFormatType_Stencil16: - return false; - - case PixelFormatType_Undefined: - break; - } - - NazaraError("Invalid pixel format"); - return false; + return s_pixelFormatInfos[format].IsCompressed(); } inline bool PixelFormat::IsConversionSupported(PixelFormatType srcFormat, PixelFormatType dstFormat) @@ -546,174 +382,6 @@ namespace Nz { s_flipFunctions[flipping][format] = func; } - - inline String PixelFormat::ToString(PixelFormatType format) - { - switch (format) - { - case PixelFormatType_A8: - return "A8"; - - case PixelFormatType_BGR8: - return "BGR8"; - - case PixelFormatType_BGRA8: - return "BGRA8"; - - case PixelFormatType_DXT1: - return "DXT1"; - - case PixelFormatType_DXT3: - return "DXT3"; - - case PixelFormatType_DXT5: - return "DXT5"; - - case PixelFormatType_L8: - return "L8"; - - case PixelFormatType_LA8: - return "LA8"; - - case PixelFormatType_R8: - return "R8"; - - case PixelFormatType_R8I: - return "R8I"; - - case PixelFormatType_R8UI: - return "R8UI"; - - case PixelFormatType_R16: - return "R16"; - - case PixelFormatType_R16F: - return "R16F"; - - case PixelFormatType_R16I: - return "R16I"; - - case PixelFormatType_R16UI: - return "R16UI"; - - case PixelFormatType_R32F: - return "R32F"; - - case PixelFormatType_R32I: - return "R32I"; - - case PixelFormatType_R32UI: - return "R32UI"; - - case PixelFormatType_RG8: - return "RG8"; - - case PixelFormatType_RG8I: - return "RG8I"; - - case PixelFormatType_RG8UI: - return "RG8UI"; - - case PixelFormatType_RG16: - return "RG16"; - - case PixelFormatType_RG16F: - return "RG16F"; - - case PixelFormatType_RG16I: - return "RG16I"; - - case PixelFormatType_RG16UI: - return "RG16UI"; - - case PixelFormatType_RG32F: - return "RG32F"; - - case PixelFormatType_RG32I: - return "RG32I"; - - case PixelFormatType_RG32UI: - return "RG32UI"; - - case PixelFormatType_RGB5A1: - return "RGB5A1"; - - case PixelFormatType_RGB8: - return "RGB8"; - - case PixelFormatType_RGB16F: - return "RGB16F"; - - case PixelFormatType_RGB16I: - return "RGB16I"; - - case PixelFormatType_RGB16UI: - return "RGB16UI"; - - case PixelFormatType_RGB32F: - return "RGB32F"; - - case PixelFormatType_RGB32I: - return "RGB32I"; - - case PixelFormatType_RGB32UI: - return "RGB32UI"; - - case PixelFormatType_RGBA4: - return "RGBA4"; - - case PixelFormatType_RGBA8: - return "RGBA8"; - - case PixelFormatType_RGBA16F: - return "RGBA16F"; - - case PixelFormatType_RGBA16I: - return "RGBA16I"; - - case PixelFormatType_RGBA16UI: - return "RGBA16UI"; - - case PixelFormatType_RGBA32F: - return "RGBA32F"; - - case PixelFormatType_RGBA32I: - return "RGBA32I"; - - case PixelFormatType_RGBA32UI: - return "RGBA32UI"; - - case PixelFormatType_Depth16: - return "Depth16"; - - case PixelFormatType_Depth24: - return "Depth24"; - - case PixelFormatType_Depth24Stencil8: - return "Depth24Stencil8"; - - case PixelFormatType_Depth32: - return "Depth32"; - - case PixelFormatType_Stencil1: - return "Stencil1"; - - case PixelFormatType_Stencil4: - return "Stencil4"; - - case PixelFormatType_Stencil8: - return "Stencil8"; - - case PixelFormatType_Stencil16: - return "Stencil16"; - - case PixelFormatType_Undefined: - return "Undefined"; - } - - NazaraError("Invalid pixel format"); - return "Invalid format"; - } } #include diff --git a/include/Nazara/Utility/SimpleTextDrawer.hpp b/include/Nazara/Utility/SimpleTextDrawer.hpp index f5532ca8f..b2f2bf4f9 100644 --- a/include/Nazara/Utility/SimpleTextDrawer.hpp +++ b/include/Nazara/Utility/SimpleTextDrawer.hpp @@ -32,10 +32,10 @@ namespace Nz unsigned int GetCharacterSize() const; const Color& GetColor() const; Font* GetFont() const; - Font* GetFont(unsigned int index) const override; - unsigned int GetFontCount() const override; - const Glyph& GetGlyph(unsigned int index) const override; - unsigned int GetGlyphCount() const override; + Font* GetFont(std::size_t index) const override; + std::size_t GetFontCount() const override; + const Glyph& GetGlyph(std::size_t index) const override; + std::size_t GetGlyphCount() const override; UInt32 GetStyle() const; const String& GetText() const; diff --git a/include/Nazara/Utility/TriangleIterator.hpp b/include/Nazara/Utility/TriangleIterator.hpp index 0addb1836..32c9ceb07 100644 --- a/include/Nazara/Utility/TriangleIterator.hpp +++ b/include/Nazara/Utility/TriangleIterator.hpp @@ -19,7 +19,7 @@ namespace Nz { public: TriangleIterator(PrimitiveMode primitiveMode, const IndexBuffer* indexBuffer); - TriangleIterator(SubMesh* subMesh); + TriangleIterator(const SubMesh* subMesh); ~TriangleIterator() = default; bool Advance(); diff --git a/include/Nazara/Utility/VertexDeclaration.hpp b/include/Nazara/Utility/VertexDeclaration.hpp index f5dea6457..8c1e4b55a 100644 --- a/include/Nazara/Utility/VertexDeclaration.hpp +++ b/include/Nazara/Utility/VertexDeclaration.hpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace Nz { @@ -69,14 +70,14 @@ namespace Nz */ }; - Component m_components[VertexComponent_Max+1]; + std::array m_components; std::size_t m_stride; - static VertexDeclaration s_declarations[VertexLayout_Max+1]; + static std::array s_declarations; static VertexDeclarationLibrary::LibraryMap s_library; }; } -#include +#include #endif // NAZARA_VERTEXDECLARATION_HPP diff --git a/include/Nazara/Utility/VertexMapper.hpp b/include/Nazara/Utility/VertexMapper.hpp index 3dc9accdb..e18250d7c 100644 --- a/include/Nazara/Utility/VertexMapper.hpp +++ b/include/Nazara/Utility/VertexMapper.hpp @@ -22,6 +22,8 @@ namespace Nz public: VertexMapper(SubMesh* subMesh, BufferAccess access = BufferAccess_ReadWrite); VertexMapper(VertexBuffer* vertexBuffer, BufferAccess access = BufferAccess_ReadWrite); + VertexMapper(const SubMesh* subMesh, BufferAccess access = BufferAccess_ReadOnly); + VertexMapper(const VertexBuffer* vertexBuffer, BufferAccess access = BufferAccess_ReadOnly); ~VertexMapper(); template SparsePtr GetComponentPtr(VertexComponent component); diff --git a/include/Nazara/Vulkan.hpp b/include/Nazara/Vulkan.hpp new file mode 100644 index 000000000..a3e363272 --- /dev/null +++ b/include/Nazara/Vulkan.hpp @@ -0,0 +1,44 @@ +// This file was automatically generated on 15 May 2016 at 00:11:13 + +/* + Nazara Engine - Vulkan + + Copyright (C) 2015 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#ifndef NAZARA_GLOBAL_VULKAN_HPP +#define NAZARA_GLOBAL_VULKAN_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // NAZARA_GLOBAL_VULKAN_HPP diff --git a/include/Nazara/Vulkan/Config.hpp b/include/Nazara/Vulkan/Config.hpp new file mode 100644 index 000000000..d5f616214 --- /dev/null +++ b/include/Nazara/Vulkan/Config.hpp @@ -0,0 +1,53 @@ +/* + Nazara Engine - Vulkan + + Copyright (C) 2015 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#ifndef NAZARA_CONFIG_VULKAN_HPP +#define NAZARA_CONFIG_VULKAN_HPP + +/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci + +// Utilise le MemoryManager pour gérer les allocations dynamiques (détecte les leaks au prix d'allocations/libérations dynamiques plus lentes) +#define NAZARA_VULKAN_MANAGE_MEMORY 0 + +// Active les tests de sécurité basés sur le code (Conseillé pour le développement) +#define NAZARA_VULKAN_SAFE 1 + +/// Chaque modification d'un paramètre ci-dessous implique une modification (souvent mineure) du code + +/// Vérification des valeurs et types de certaines constantes +#include + +#if !defined(NAZARA_STATIC) + #ifdef NAZARA_VULKAN_BUILD + #define NAZARA_VULKAN_API NAZARA_EXPORT + #else + #define NAZARA_VULKAN_API NAZARA_IMPORT + #endif +#else + #define NAZARA_VULKAN_API +#endif + +#endif // NAZARA_CONFIG_MODULENAME_HPP diff --git a/include/Nazara/Vulkan/ConfigCheck.hpp b/include/Nazara/Vulkan/ConfigCheck.hpp new file mode 100644 index 000000000..22f334c6f --- /dev/null +++ b/include/Nazara/Vulkan/ConfigCheck.hpp @@ -0,0 +1,22 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CONFIG_CHECK_VULKANE_HPP +#define NAZARA_CONFIG_CHECK_VULKANE_HPP + +/// Ce fichier sert à vérifier la valeur des constantes du fichier Config.hpp + +#include +#define CheckType(name, type, err) static_assert(std::is_ ##type ::value, #type err) +#define CheckTypeAndVal(name, type, op, val, err) static_assert(std::is_ ##type ::value && name op val, #type err) + +// On force la valeur de MANAGE_MEMORY en mode debug +#if defined(NAZARA_DEBUG) && !NAZARA_VULKAN_MANAGE_MEMORY + #undef NAZARA_MODULENAME_MANAGE_MEMORY + #define NAZARA_MODULENAME_MANAGE_MEMORY 0 +#endif + +#endif // NAZARA_CONFIG_CHECK_VULKAN_HPP diff --git a/include/Nazara/Vulkan/Debug.hpp b/include/Nazara/Vulkan/Debug.hpp new file mode 100644 index 000000000..b5e44efae --- /dev/null +++ b/include/Nazara/Vulkan/Debug.hpp @@ -0,0 +1,8 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#if NAZARA_MODULENAME_MANAGE_MEMORY + #include +#endif diff --git a/include/Nazara/Vulkan/DebugOff.hpp b/include/Nazara/Vulkan/DebugOff.hpp new file mode 100644 index 000000000..e57a2b946 --- /dev/null +++ b/include/Nazara/Vulkan/DebugOff.hpp @@ -0,0 +1,9 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +// On suppose que Debug.hpp a déjà été inclus, tout comme Config.hpp +#if NAZARA_MODULENAME_MANAGE_MEMORY + #undef delete + #undef new +#endif diff --git a/include/Nazara/Vulkan/VkCommandBuffer.hpp b/include/Nazara/Vulkan/VkCommandBuffer.hpp new file mode 100644 index 000000000..2dea341dd --- /dev/null +++ b/include/Nazara/Vulkan/VkCommandBuffer.hpp @@ -0,0 +1,58 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKCOMMANDBUFFER_HPP +#define NAZARA_VULKAN_VKCOMMANDBUFFER_HPP + +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + class NAZARA_VULKAN_API CommandBuffer + { + friend CommandPool; + + public: + CommandBuffer(const CommandBuffer&) = delete; + CommandBuffer(CommandBuffer&& commandBuffer); + inline ~CommandBuffer(); + + inline bool Begin(const VkCommandBufferBeginInfo& info); + inline bool Begin(VkCommandBufferUsageFlags flags); + inline bool Begin(VkCommandBufferUsageFlags flags, const VkCommandBufferInheritanceInfo& inheritanceInfo); + inline bool Begin(VkCommandBufferUsageFlags flags, VkRenderPass renderPass, UInt32 subpass, VkFramebuffer framebuffer, bool occlusionQueryEnable, VkQueryControlFlags queryFlags, VkQueryPipelineStatisticFlags pipelineStatistics); + inline bool Begin(VkCommandBufferUsageFlags flags, bool occlusionQueryEnable, VkQueryControlFlags queryFlags, VkQueryPipelineStatisticFlags pipelineStatistics); + + inline bool End(); + + inline void Free(); + + inline VkResult GetLastErrorCode() const; + + CommandBuffer& operator=(const CommandBuffer&) = delete; + CommandBuffer& operator=(CommandBuffer&&) = delete; + + inline operator VkCommandBuffer(); + + private: + inline CommandBuffer(CommandPool& pool, VkCommandBuffer handle); + + CommandPoolHandle m_pool; + VkAllocationCallbacks m_allocator; + VkCommandBuffer m_handle; + VkResult m_lastErrorCode; + + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKCOMMANDBUFFER_HPP diff --git a/include/Nazara/Vulkan/VkCommandBuffer.inl b/include/Nazara/Vulkan/VkCommandBuffer.inl new file mode 100644 index 000000000..d2d6f50d7 --- /dev/null +++ b/include/Nazara/Vulkan/VkCommandBuffer.inl @@ -0,0 +1,150 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + inline CommandBuffer::CommandBuffer(CommandPool& pool, VkCommandBuffer handle) : + m_pool(&pool), + m_handle(handle) + { + } + + inline CommandBuffer::CommandBuffer(CommandBuffer&& commandBuffer) : + m_pool(std::move(commandBuffer.m_pool)), + m_allocator(commandBuffer.m_allocator), + m_handle(commandBuffer.m_handle), + m_lastErrorCode(commandBuffer.m_lastErrorCode) + { + commandBuffer.m_handle = VK_NULL_HANDLE; + } + + inline CommandBuffer::~CommandBuffer() + { + Free(); + } + + inline bool CommandBuffer::Begin(const VkCommandBufferBeginInfo& info) + { + m_lastErrorCode = m_pool->GetDevice().vkBeginCommandBuffer(m_handle, &info); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to begin command buffer"); + return false; + } + + return true; + } + + inline bool CommandBuffer::Begin(VkCommandBufferUsageFlags flags) + { + VkCommandBufferBeginInfo beginInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + nullptr, + flags, + nullptr + }; + + return Begin(beginInfo); + } + + inline bool CommandBuffer::Begin(VkCommandBufferUsageFlags flags, const VkCommandBufferInheritanceInfo& inheritanceInfo) + { + VkCommandBufferBeginInfo beginInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + nullptr, + flags, + &inheritanceInfo + }; + + return Begin(beginInfo); + } + + inline bool CommandBuffer::Begin(VkCommandBufferUsageFlags flags, VkRenderPass renderPass, UInt32 subpass, VkFramebuffer framebuffer, bool occlusionQueryEnable, VkQueryControlFlags queryFlags, VkQueryPipelineStatisticFlags pipelineStatistics) + { + NazaraAssert(flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, "Continue bit is required to ignore renderPass, subpass and framebuffer"); + + VkCommandBufferInheritanceInfo inheritanceInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, + nullptr, + renderPass, + subpass, + framebuffer, + VkBool32((occlusionQueryEnable) ? VK_TRUE : VK_FALSE), + queryFlags, + pipelineStatistics + }; + + VkCommandBufferBeginInfo beginInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + nullptr, + flags, + &inheritanceInfo + }; + + return Begin(beginInfo); + } + + inline bool CommandBuffer::Begin(VkCommandBufferUsageFlags flags, bool occlusionQueryEnable, VkQueryControlFlags queryFlags, VkQueryPipelineStatisticFlags pipelineStatistics) + { + NazaraAssert(flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, "Continue bit is required to ignore renderPass, subpass and framebuffer"); + + VkCommandBufferInheritanceInfo inheritanceInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, + nullptr, + VK_NULL_HANDLE, + 0, + VK_NULL_HANDLE, + VkBool32((occlusionQueryEnable) ? VK_TRUE : VK_FALSE), + queryFlags, + pipelineStatistics + }; + + VkCommandBufferBeginInfo beginInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + nullptr, + flags, + &inheritanceInfo + }; + + return Begin(beginInfo); + } + + inline bool CommandBuffer::End() + { + m_lastErrorCode = m_pool->GetDevice().vkEndCommandBuffer(m_handle); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to end command buffer"); + return false; + } + + return true; + } + + inline void CommandBuffer::Free() + { + if (m_handle) + m_pool->GetDevice().vkFreeCommandBuffers(m_pool->GetDevice(), *m_pool, 1, &m_handle); + } + + inline VkResult CommandBuffer::GetLastErrorCode() const + { + return m_lastErrorCode; + } + + inline CommandBuffer::operator VkCommandBuffer() + { + return m_handle; + } + } +} + +#include diff --git a/include/Nazara/Vulkan/VkCommandPool.hpp b/include/Nazara/Vulkan/VkCommandPool.hpp new file mode 100644 index 000000000..b51061bb2 --- /dev/null +++ b/include/Nazara/Vulkan/VkCommandPool.hpp @@ -0,0 +1,53 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKCOMMANDPOOL_HPP +#define NAZARA_VULKAN_VKCOMMANDPOOL_HPP + +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + class CommandBuffer; + class CommandPool; + + using CommandPoolHandle = ObjectHandle; + + class NAZARA_VULKAN_API CommandPool : public DeviceObject, public HandledObject + { + friend DeviceObject; + + public: + inline CommandPool(Device& instance); + CommandPool(const CommandPool&) = delete; + CommandPool(CommandPool&&) = default; + ~CommandPool() = default; + + CommandBuffer AllocateCommandBuffer(VkCommandBufferLevel level); + std::vector AllocateCommandBuffers(UInt32 commandBufferCount, VkCommandBufferLevel level); + + using DeviceObject::Create; + inline bool Create(UInt32 queueFamilyIndex, VkCommandPoolCreateFlags flags = 0, const VkAllocationCallbacks* allocator = nullptr); + + inline bool Reset(VkCommandPoolResetFlags flags); + + CommandPool& operator=(const CommandPool&) = delete; + CommandPool& operator=(CommandPool&&) = delete; + + private: + static inline VkResult CreateHelper(Device& device, const VkCommandPoolCreateInfo* createInfo, const VkAllocationCallbacks* allocator, VkCommandPool* handle); + static inline void DestroyHelper(Device& device, VkCommandPool handle, const VkAllocationCallbacks* allocator); + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKCOMMANDPOOL_HPP diff --git a/include/Nazara/Vulkan/VkCommandPool.inl b/include/Nazara/Vulkan/VkCommandPool.inl new file mode 100644 index 000000000..61cfeaa43 --- /dev/null +++ b/include/Nazara/Vulkan/VkCommandPool.inl @@ -0,0 +1,53 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + inline CommandPool::CommandPool(Device& device) : + DeviceObject(device) + { + } + + inline bool CommandPool::Create(UInt32 queueFamilyIndex, VkCommandPoolCreateFlags flags, const VkAllocationCallbacks* allocator) + { + VkCommandPoolCreateInfo createInfo = + { + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + nullptr, + flags, + queueFamilyIndex + }; + + return Create(createInfo, allocator); + } + + inline bool CommandPool::Reset(VkCommandPoolResetFlags flags) + { + m_lastErrorCode = m_device.vkResetCommandPool(m_device, m_handle, flags); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + return false; + + return true; + } + + inline VkResult CommandPool::CreateHelper(Device& device, const VkCommandPoolCreateInfo* createInfo, const VkAllocationCallbacks* allocator, VkCommandPool* handle) + { + return device.vkCreateCommandPool(device, createInfo, allocator, handle); + } + + inline void CommandPool::DestroyHelper(Device& device, VkCommandPool handle, const VkAllocationCallbacks* allocator) + { + return device.vkDestroyCommandPool(device, handle, allocator); + } + } +} + +#include diff --git a/include/Nazara/Vulkan/VkDevice.hpp b/include/Nazara/Vulkan/VkDevice.hpp new file mode 100644 index 000000000..1b9d3a470 --- /dev/null +++ b/include/Nazara/Vulkan/VkDevice.hpp @@ -0,0 +1,202 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKDEVICE_HPP +#define NAZARA_VULKAN_VKDEVICE_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + class Device; + class Queue; + class Instance; + + using DeviceHandle = ObjectHandle; + + class NAZARA_VULKAN_API Device : public HandledObject + { + public: + inline Device(Instance& instance); + Device(const Device&) = delete; + Device(Device&&) = delete; + inline ~Device(); + + bool Create(VkPhysicalDevice device, const VkDeviceCreateInfo& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline void Destroy(); + + inline Queue GetQueue(UInt32 queueFamilyIndex, UInt32 queueIndex); + inline Instance& GetInstance(); + inline const Instance& GetInstance() const; + inline VkResult GetLastErrorCode() const; + + inline bool IsExtensionLoaded(const String& extensionName); + inline bool IsLayerLoaded(const String& layerName); + + inline bool WaitForIdle(); + + Device& operator=(const Device&) = delete; + Device& operator=(Device&&) = delete; + + inline operator VkDevice(); + + // Vulkan functions + #define NAZARA_VULKAN_DEVICE_FUNCTION(func) PFN_##func func + + // Vulkan core + NAZARA_VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers); + NAZARA_VULKAN_DEVICE_FUNCTION(vkAllocateMemory); + NAZARA_VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkBindBufferMemory); + NAZARA_VULKAN_DEVICE_FUNCTION(vkBindImageMemory); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdBeginQuery); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdBeginRenderPass); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdBindDescriptorSets); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdBindIndexBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdBindPipeline); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdBindVertexBuffers); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdBlitImage); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdClearAttachments); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdClearColorImage); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdClearDepthStencilImage); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdCopyBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdCopyBufferToImage); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdCopyImage); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdCopyImageToBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdCopyQueryPoolResults); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdDispatch); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdDispatchIndirect); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdDraw); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdDrawIndexed); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdDrawIndexedIndirect); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdDrawIndirect); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdEndQuery); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdEndRenderPass); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdExecuteCommands); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdFillBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdNextSubpass); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdPushConstants); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdResetEvent); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdResetQueryPool); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdResolveImage); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetBlendConstants); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetDepthBias); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetDepthBounds); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetEvent); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetLineWidth); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetScissor); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetStencilCompareMask); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetStencilReference); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetStencilWriteMask); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdSetViewport); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdUpdateBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdWaitEvents); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCmdWriteTimestamp); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateBufferView); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateCommandPool); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateComputePipelines); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateDescriptorPool); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateDescriptorSetLayout); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateEvent); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateFramebuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateGraphicsPipelines); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateImage); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateImageView); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreatePipelineLayout); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateRenderPass); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateSampler); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateSemaphore); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateShaderModule); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyBufferView); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorPool); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorSetLayout); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyDevice); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyEvent); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyFramebuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyImage); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyImageView); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyPipeline); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyPipelineLayout); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyRenderPass); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroySampler); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroySemaphore); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroyShaderModule); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle); + NAZARA_VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers); + NAZARA_VULKAN_DEVICE_FUNCTION(vkFreeDescriptorSets); + NAZARA_VULKAN_DEVICE_FUNCTION(vkFreeMemory); + NAZARA_VULKAN_DEVICE_FUNCTION(vkFlushMappedMemoryRanges); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetBufferMemoryRequirements); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetDeviceMemoryCommitment); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetEventStatus); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetFenceStatus); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetImageMemoryRequirements); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetImageSparseMemoryRequirements); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetImageSubresourceLayout); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetRenderAreaGranularity); + NAZARA_VULKAN_DEVICE_FUNCTION(vkInvalidateMappedMemoryRanges); + NAZARA_VULKAN_DEVICE_FUNCTION(vkMapMemory); + NAZARA_VULKAN_DEVICE_FUNCTION(vkMergePipelineCaches); + NAZARA_VULKAN_DEVICE_FUNCTION(vkQueueSubmit); + NAZARA_VULKAN_DEVICE_FUNCTION(vkQueueWaitIdle); + NAZARA_VULKAN_DEVICE_FUNCTION(vkResetCommandBuffer); + NAZARA_VULKAN_DEVICE_FUNCTION(vkResetCommandPool); + NAZARA_VULKAN_DEVICE_FUNCTION(vkResetDescriptorPool); + NAZARA_VULKAN_DEVICE_FUNCTION(vkResetFences); + NAZARA_VULKAN_DEVICE_FUNCTION(vkResetEvent); + NAZARA_VULKAN_DEVICE_FUNCTION(vkSetEvent); + NAZARA_VULKAN_DEVICE_FUNCTION(vkUnmapMemory); + NAZARA_VULKAN_DEVICE_FUNCTION(vkUpdateDescriptorSets); + NAZARA_VULKAN_DEVICE_FUNCTION(vkWaitForFences); + + // VK_KHR_display_swapchain + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateSharedSwapchainsKHR); + + // VK_KHR_surface + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroySurfaceKHR); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR); + + // VK_KHR_swapchain + NAZARA_VULKAN_DEVICE_FUNCTION(vkAcquireNextImageKHR); + NAZARA_VULKAN_DEVICE_FUNCTION(vkCreateSwapchainKHR); + NAZARA_VULKAN_DEVICE_FUNCTION(vkDestroySwapchainKHR); + NAZARA_VULKAN_DEVICE_FUNCTION(vkGetSwapchainImagesKHR); + NAZARA_VULKAN_DEVICE_FUNCTION(vkQueuePresentKHR); + + #undef NAZARA_VULKAN_DEVICE_FUNCTION + + private: + inline PFN_vkVoidFunction GetProcAddr(const char* name); + + Instance& m_instance; + VkAllocationCallbacks m_allocator; + VkDevice m_device; + VkResult m_lastErrorCode; + std::unordered_set m_loadedExtensions; + std::unordered_set m_loadedLayers; + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKDEVICE_HPP diff --git a/include/Nazara/Vulkan/VkDevice.inl b/include/Nazara/Vulkan/VkDevice.inl new file mode 100644 index 000000000..77dc1e254 --- /dev/null +++ b/include/Nazara/Vulkan/VkDevice.inl @@ -0,0 +1,96 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + inline Device::Device(Instance& instance) : + m_instance(instance), + m_device(nullptr) + { + } + + inline Device::~Device() + { + Destroy(); + } + + inline void Device::Destroy() + { + if (m_device) + { + vkDeviceWaitIdle(m_device); + vkDestroyDevice(m_device, (m_allocator.pfnAllocation) ? &m_allocator : nullptr); + } + } + + inline Queue Device::GetQueue(UInt32 queueFamilyIndex, UInt32 queueIndex) + { + VkQueue queue; + vkGetDeviceQueue(m_device, queueFamilyIndex, queueIndex, &queue); + + return Queue(*this, queue); + } + + inline Instance& Device::GetInstance() + { + return m_instance; + } + + inline const Instance& Device::GetInstance() const + { + return m_instance; + } + + inline VkResult Device::GetLastErrorCode() const + { + return m_lastErrorCode; + } + + inline bool Device::IsExtensionLoaded(const String& extensionName) + { + return m_loadedExtensions.count(extensionName) > 0; + } + + inline bool Device::IsLayerLoaded(const String& layerName) + { + return m_loadedLayers.count(layerName) > 0; + } + + inline bool Device::WaitForIdle() + { + m_lastErrorCode = vkDeviceWaitIdle(m_device); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to wait for device idle"); + return false; + } + + return true; + } + + inline Device::operator VkDevice() + { + return m_device; + } + + inline PFN_vkVoidFunction Device::GetProcAddr(const char* name) + { + PFN_vkVoidFunction func = m_instance.GetDeviceProcAddr(m_device, name); + if (!func) + NazaraError("Failed to get " + String(name) + " address"); + + return func; + } + } +} + +#include diff --git a/include/Nazara/Vulkan/VkDeviceObject.hpp b/include/Nazara/Vulkan/VkDeviceObject.hpp new file mode 100644 index 000000000..dd9da38c9 --- /dev/null +++ b/include/Nazara/Vulkan/VkDeviceObject.hpp @@ -0,0 +1,50 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKDEVICEOBJECT_HPP +#define NAZARA_VULKAN_VKDEVICEOBJECT_HPP + +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + template + class DeviceObject + { + public: + inline DeviceObject(Device& instance); + DeviceObject(const DeviceObject&) = delete; + DeviceObject(DeviceObject&&); + inline ~DeviceObject(); + + inline bool Create(const CreateInfo& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline void Destroy(); + + inline Device& GetDevice(); + inline const Device& GetDevice() const; + inline VkResult GetLastErrorCode() const; + + DeviceObject& operator=(const DeviceObject&) = delete; + DeviceObject& operator=(DeviceObject&&) = delete; + + inline operator VkType(); + + protected: + Device& m_device; + VkAllocationCallbacks m_allocator; + VkType m_handle; + VkResult m_lastErrorCode; + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKDEVICEOBJECT_HPP diff --git a/include/Nazara/Vulkan/VkDeviceObject.inl b/include/Nazara/Vulkan/VkDeviceObject.inl new file mode 100644 index 000000000..a3136156c --- /dev/null +++ b/include/Nazara/Vulkan/VkDeviceObject.inl @@ -0,0 +1,89 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + template + inline DeviceObject::DeviceObject(Device& device) : + m_device(device), + m_handle(VK_NULL_HANDLE) + { + } + + template + inline DeviceObject::DeviceObject(DeviceObject&& object) : + m_device(object.m_device), + m_allocator(object.m_allocator), + m_handle(object.m_handle), + m_lastErrorCode(object.m_lastErrorCode) + { + object.m_handle = VK_NULL_HANDLE; + } + + template + inline DeviceObject::~DeviceObject() + { + Destroy(); + } + + template + inline bool DeviceObject::Create(const CreateInfo& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = C::CreateHelper(m_device, &createInfo, allocator, &m_handle); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to create Vulkan object"); + return false; + } + + // Store the allocator to access them when needed + if (allocator) + m_allocator = *allocator; + else + m_allocator.pfnAllocation = nullptr; + + return true; + } + + template + inline void DeviceObject::Destroy() + { + if (m_handle != VK_NULL_HANDLE) + C::DestroyHelper(m_device, m_handle, (m_allocator.pfnAllocation) ? &m_allocator : nullptr); + } + + template + inline Device& DeviceObject::GetDevice() + { + return m_device; + } + + template + inline const Device& DeviceObject::GetDevice() const + { + return m_device; + } + + template + inline VkResult DeviceObject::GetLastErrorCode() const + { + return m_lastErrorCode; + } + + template + inline DeviceObject::operator VkType() + { + return m_handle; + } + } +} + +#include diff --git a/include/Nazara/Vulkan/VkInstance.hpp b/include/Nazara/Vulkan/VkInstance.hpp new file mode 100644 index 000000000..ded2b0ef7 --- /dev/null +++ b/include/Nazara/Vulkan/VkInstance.hpp @@ -0,0 +1,141 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKINSTANCE_HPP +#define NAZARA_VULKAN_VKINSTANCE_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + class NAZARA_VULKAN_API Instance + { + public: + inline Instance(); + Instance(const Instance&) = delete; + Instance(Instance&&) = delete; + inline ~Instance(); + + bool Create(const VkInstanceCreateInfo& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline bool Create(const String& appName, UInt32 appVersion, const String& engineName, UInt32 engineVersion, const std::vector& layers, const std::vector& extensions, const VkAllocationCallbacks* allocator = nullptr); + inline void Destroy(); + + bool EnumeratePhysicalDevices(std::vector* physicalDevices); + + inline PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* name); + + inline void GetPhysicalDeviceFeatures(VkPhysicalDevice device, VkPhysicalDeviceFeatures* features); + inline void GetPhysicalDeviceFormatProperties(VkPhysicalDevice device, VkFormat format, VkFormatProperties* formatProperties); + inline bool GetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* imageFormatProperties); + inline void GetPhysicalDeviceMemoryProperties(VkPhysicalDevice device, VkPhysicalDeviceMemoryProperties* properties); + inline void GetPhysicalDeviceProperties(VkPhysicalDevice device, VkPhysicalDeviceProperties* properties); + bool GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice device, std::vector* queueFamilyProperties); + + inline VkResult GetLastErrorCode() const; + + inline bool IsExtensionLoaded(const String& extensionName); + inline bool IsLayerLoaded(const String& layerName); + + Instance& operator=(const Instance&) = delete; + Instance& operator=(Instance&&) = delete; + + inline operator VkInstance(); + + // Vulkan functions + #define NAZARA_VULKAN_INSTANCE_FUNCTION(func) PFN_##func func + + // Vulkan core + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateDevice); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkDestroyInstance); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFormatProperties); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceImageFormatProperties); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties); + + // VK_KHR_display + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateDisplayModeKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateDisplayPlaneSurfaceKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetDisplayModePropertiesKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetDisplayPlaneCapabilitiesKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetDisplayPlaneSupportedDisplaysKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceDisplayPlanePropertiesKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceDisplayPropertiesKHR); + + // VK_KHR_surface + NAZARA_VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR); + + // VK_EXT_debug_report + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateDebugReportCallbackEXT); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkDestroyDebugReportCallbackEXT); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkDebugReportMessageEXT); + + #ifdef VK_USE_PLATFORM_ANDROID_KHR + // VK_KHR_android_surface + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateAndroidSurfaceKHR); + #endif + + #ifdef VK_USE_PLATFORM_MIR_KHR + // VK_KHR_mir_surface + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateMirSurfaceKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceMirPresentationSupportKHR); + #endif + + #ifdef VK_USE_PLATFORM_XCB_KHR + // VK_KHR_xcb_surface + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateXcbSurfaceKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceXcbPresentationSupportKHR); + #endif + + #ifdef VK_USE_PLATFORM_XLIB_KHR + // VK_KHR_xlib_surface + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateXlibSurfaceKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceXlibPresentationSupportKHR); + #endif + + #ifdef VK_USE_PLATFORM_WAYLAND_KHR + // VK_KHR_wayland_surface + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateWaylandSurfaceKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceWaylandPresentationSupportKHR); + #endif + + #ifdef VK_USE_PLATFORM_WIN32_KHR + // VK_KHR_win32_surface + NAZARA_VULKAN_INSTANCE_FUNCTION(vkCreateWin32SurfaceKHR); + NAZARA_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceWin32PresentationSupportKHR); + #endif + + #undef NAZARA_VULKAN_INSTANCE_FUNCTION + + private: + inline PFN_vkVoidFunction GetProcAddr(const char* name); + + VkAllocationCallbacks m_allocator; + VkInstance m_instance; + VkResult m_lastErrorCode; + std::unordered_set m_loadedExtensions; + std::unordered_set m_loadedLayers; + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKINSTANCE_HPP diff --git a/include/Nazara/Vulkan/VkInstance.inl b/include/Nazara/Vulkan/VkInstance.inl new file mode 100644 index 000000000..801691855 --- /dev/null +++ b/include/Nazara/Vulkan/VkInstance.inl @@ -0,0 +1,128 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + inline Instance::Instance() : + m_instance(nullptr) + { + } + + inline Instance::~Instance() + { + Destroy(); + } + + inline bool Instance::Create(const String& appName, UInt32 appVersion, const String& engineName, UInt32 engineVersion, const std::vector& layers, const std::vector& extensions, const VkAllocationCallbacks* allocator) + { + VkApplicationInfo appInfo = + { + VK_STRUCTURE_TYPE_APPLICATION_INFO, + nullptr, + appName.GetConstBuffer(), + appVersion, + engineName.GetConstBuffer(), + engineVersion + }; + + VkInstanceCreateInfo instanceInfo = + { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + nullptr, + 0, + &appInfo, + static_cast(layers.size()), + (!layers.empty()) ? layers.data() : nullptr, + static_cast(extensions.size()), + (!extensions.empty()) ? extensions.data() : nullptr + }; + + return Create(instanceInfo, allocator); + } + + inline void Instance::Destroy() + { + if (m_instance) + vkDestroyInstance(m_instance, (m_allocator.pfnAllocation) ? &m_allocator : nullptr); + } + + inline PFN_vkVoidFunction Instance::GetDeviceProcAddr(VkDevice device, const char* name) + { + PFN_vkVoidFunction func = vkGetDeviceProcAddr(device, name); + if (!func) + NazaraError("Failed to get " + String(name) + " address"); + + return func; + } + + inline VkResult Instance::GetLastErrorCode() const + { + return m_lastErrorCode; + } + + inline bool Instance::IsExtensionLoaded(const String& extensionName) + { + return m_loadedExtensions.count(extensionName) > 0; + } + + inline bool Instance::IsLayerLoaded(const String& layerName) + { + return m_loadedLayers.count(layerName) > 0; + } + + inline Instance::operator VkInstance() + { + return m_instance; + } + + inline void Instance::GetPhysicalDeviceFeatures(VkPhysicalDevice device, VkPhysicalDeviceFeatures* features) + { + return vkGetPhysicalDeviceFeatures(device, features); + } + + inline void Instance::GetPhysicalDeviceFormatProperties(VkPhysicalDevice device, VkFormat format, VkFormatProperties* formatProperties) + { + return vkGetPhysicalDeviceFormatProperties(device, format, formatProperties); + } + + inline bool Instance::GetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* imageFormatProperties) + { + m_lastErrorCode = vkGetPhysicalDeviceImageFormatProperties(physicalDevice, format, type, tiling, usage, flags, imageFormatProperties); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to get physical device image format properties"); + return false; + } + + return true; + } + + inline void Instance::GetPhysicalDeviceMemoryProperties(VkPhysicalDevice device, VkPhysicalDeviceMemoryProperties* memoryProperties) + { + return vkGetPhysicalDeviceMemoryProperties(device, memoryProperties); + } + + inline void Instance::GetPhysicalDeviceProperties(VkPhysicalDevice device, VkPhysicalDeviceProperties* properties) + { + return vkGetPhysicalDeviceProperties(device, properties); + } + + inline PFN_vkVoidFunction Instance::GetProcAddr(const char* name) + { + PFN_vkVoidFunction func = Loader::GetInstanceProcAddr(m_instance, name); + if (!func) + NazaraError("Failed to get " + String(name) + " address"); + + return func; + } + } +} + +#include diff --git a/include/Nazara/Vulkan/VkLoader.hpp b/include/Nazara/Vulkan/VkLoader.hpp new file mode 100644 index 000000000..f12429b28 --- /dev/null +++ b/include/Nazara/Vulkan/VkLoader.hpp @@ -0,0 +1,52 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKLOADER_HPP +#define NAZARA_VULKAN_VKLOADER_HPP + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + class NAZARA_VULKAN_API Loader + { + public: + Loader() = delete; + ~Loader() = delete; + + static bool EnumerateInstanceExtensionProperties(std::vector* properties, const char* layerName = nullptr); + static bool EnumerateInstanceLayerProperties(std::vector* properties); + + static inline PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* name); + + static bool Initialize(); + static void Uninitialize(); + + // Vulkan functions + #define NAZARA_VULKAN_GLOBAL_FUNCTION(func) static PFN_##func func + + NAZARA_VULKAN_GLOBAL_FUNCTION(vkCreateInstance); + NAZARA_VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties); + NAZARA_VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties); + NAZARA_VULKAN_GLOBAL_FUNCTION(vkGetInstanceProcAddr); + + #undef NAZARA_VULKAN_GLOBAL_FUNCTION + + private: + static DynLib s_vulkanLib; + static VkResult s_lastErrorCode; + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKLOADER_HPP diff --git a/include/Nazara/Vulkan/VkLoader.inl b/include/Nazara/Vulkan/VkLoader.inl new file mode 100644 index 000000000..49467366d --- /dev/null +++ b/include/Nazara/Vulkan/VkLoader.inl @@ -0,0 +1,19 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + namespace Vk + { + inline PFN_vkVoidFunction Loader::GetInstanceProcAddr(VkInstance instance, const char* name) + { + return vkGetInstanceProcAddr(instance, name); + } + } +} + +#include diff --git a/include/Nazara/Vulkan/VkQueue.hpp b/include/Nazara/Vulkan/VkQueue.hpp new file mode 100644 index 000000000..35c8d16e0 --- /dev/null +++ b/include/Nazara/Vulkan/VkQueue.hpp @@ -0,0 +1,52 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKQUEUE_HPP +#define NAZARA_VULKAN_VKQUEUE_HPP + +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + class Queue + { + public: + inline Queue(Device& device, VkQueue queue); + inline Queue(const Queue& queue); + inline Queue(Queue&& queue); + inline ~Queue() = default; + + inline Device& GetDevice(); + inline VkResult GetLastErrorCode() const; + + inline bool Present(const VkPresentInfoKHR& presentInfo); + inline bool Present(VkSwapchainKHR swapchain, UInt32 imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE); + + inline bool Submit(const VkSubmitInfo& submit, VkFence fence = VK_NULL_HANDLE); + inline bool Submit(UInt32 submitCount, const VkSubmitInfo* submits, VkFence fence = VK_NULL_HANDLE); + + inline bool WaitIdle(); + + Queue& operator=(const Queue& queue) = delete; + Queue& operator=(Queue&&) = delete; + + inline operator VkQueue(); + + protected: + Device& m_device; + VkQueue m_handle; + VkResult m_lastErrorCode; + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKQUEUE_HPP diff --git a/include/Nazara/Vulkan/VkQueue.inl b/include/Nazara/Vulkan/VkQueue.inl new file mode 100644 index 000000000..a47918da3 --- /dev/null +++ b/include/Nazara/Vulkan/VkQueue.inl @@ -0,0 +1,102 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + inline Queue::Queue(Device& device, VkQueue queue) : + m_device(device), + m_handle(queue), + m_lastErrorCode(VkResult::VK_SUCCESS) + { + } + + inline Queue::Queue(const Queue& queue) : + m_device(queue.m_device), + m_handle(queue.m_handle), + m_lastErrorCode(queue.m_lastErrorCode) + { + } + + inline Queue::Queue(Queue&& queue) : + m_device(queue.m_device), + m_handle(queue.m_handle), + m_lastErrorCode(queue.m_lastErrorCode) + { + } + + inline Device& Queue::GetDevice() + { + return m_device; + } + + inline VkResult Queue::GetLastErrorCode() const + { + return m_lastErrorCode; + } + + inline bool Queue::Present(const VkPresentInfoKHR& presentInfo) + { + m_lastErrorCode = m_device.vkQueuePresentKHR(m_handle, &presentInfo); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + return false; + + return true; + } + + inline bool Queue::Present(VkSwapchainKHR swapchain, UInt32 imageIndex, VkSemaphore waitSemaphore) + { + VkPresentInfoKHR presentInfo = + { + VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + nullptr, + (waitSemaphore) ? 1U : 0U, + &waitSemaphore, + 1U, + &swapchain, + &imageIndex, + nullptr + }; + + return Present(presentInfo); + } + + inline bool Queue::Submit(const VkSubmitInfo& submit, VkFence fence) + { + return Submit(1, &submit, fence); + } + + inline bool Queue::Submit(UInt32 submitCount, const VkSubmitInfo* submits, VkFence fence) + { + m_lastErrorCode = m_device.vkQueueSubmit(m_handle, submitCount, submits, fence); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + return false; + + return true; + } + + inline bool Queue::WaitIdle() + { + m_lastErrorCode = m_device.vkQueueWaitIdle(m_handle); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + return false; + + return true; + } + + inline Queue::operator VkQueue() + { + return m_handle; + } + + } +} + +#include diff --git a/include/Nazara/Vulkan/VkSemaphore.hpp b/include/Nazara/Vulkan/VkSemaphore.hpp new file mode 100644 index 000000000..138d726be --- /dev/null +++ b/include/Nazara/Vulkan/VkSemaphore.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKSEMAPHORE_HPP +#define NAZARA_VULKAN_VKSEMAPHORE_HPP + +#include +#include + +namespace Nz +{ + namespace Vk + { + class Semaphore : public DeviceObject + { + friend DeviceObject; + + public: + inline Semaphore(Device& instance); + Semaphore(const Semaphore&) = delete; + Semaphore(Semaphore&&) = default; + ~Semaphore() = default; + + using DeviceObject::Create; + inline bool Create(VkSemaphoreCreateFlags flags = 0, const VkAllocationCallbacks* allocator = nullptr); + + Semaphore& operator=(const Semaphore&) = delete; + Semaphore& operator=(Semaphore&&) = delete; + + private: + static VkResult CreateHelper(Device& device, const VkSemaphoreCreateInfo* createInfo, const VkAllocationCallbacks* allocator, VkSemaphore* handle); + static void DestroyHelper(Device& device, VkSemaphore handle, const VkAllocationCallbacks* allocator); + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKSEMAPHORE_HPP diff --git a/include/Nazara/Vulkan/VkSemaphore.inl b/include/Nazara/Vulkan/VkSemaphore.inl new file mode 100644 index 000000000..fd2bf3427 --- /dev/null +++ b/include/Nazara/Vulkan/VkSemaphore.inl @@ -0,0 +1,41 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + namespace Vk + { + inline Semaphore::Semaphore(Device& device) : + DeviceObject(device) + { + } + + inline bool Semaphore::Create(VkSemaphoreCreateFlags flags, const VkAllocationCallbacks* allocator) + { + VkSemaphoreCreateInfo createInfo = + { + VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + nullptr, + flags + }; + + return Create(createInfo, allocator); + } + + VkResult Semaphore::CreateHelper(Device& device, const VkSemaphoreCreateInfo* createInfo, const VkAllocationCallbacks* allocator, VkSemaphore* handle) + { + return device.vkCreateSemaphore(device, createInfo, allocator, handle); + } + + void Semaphore::DestroyHelper(Device& device, VkSemaphore handle, const VkAllocationCallbacks* allocator) + { + return device.vkDestroySemaphore(device, handle, allocator); + } + } +} + +#include diff --git a/include/Nazara/Vulkan/VkSurface.hpp b/include/Nazara/Vulkan/VkSurface.hpp new file mode 100644 index 000000000..03a57d2fe --- /dev/null +++ b/include/Nazara/Vulkan/VkSurface.hpp @@ -0,0 +1,94 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKSURFACE_HPP +#define NAZARA_VULKAN_VKSURFACE_HPP + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + class Instance; + + class Surface + { + public: + inline Surface(Instance& instance); + Surface(const Surface&) = delete; + Surface(Surface&& surface); + inline ~Surface(); + + #ifdef VK_USE_PLATFORM_ANDROID_KHR + // VK_KHR_android_surface + inline bool Create(const VkAndroidSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline bool Create(ANativeWindow* window, VkAndroidSurfaceCreateFlagsKHR flags = 0, const VkAllocationCallbacks* allocator = nullptr); + #endif + + #ifdef VK_USE_PLATFORM_MIR_KHR + // VK_KHR_mir_surface + inline bool Create(const VkMirSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline bool Create(MirConnection* connection, MirSurface* surface, VkMirSurfaceCreateFlagsKHR flags = 0, const VkAllocationCallbacks* allocator = nullptr); + #endif + + #ifdef VK_USE_PLATFORM_XCB_KHR + // VK_KHR_xcb_surface + inline bool Create(const VkXcbSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline bool Create(xcb_connection_t* connection, xcb_window_t window, VkXcbSurfaceCreateFlagsKHR flags = 0, const VkAllocationCallbacks* allocator = nullptr); + #endif + + #ifdef VK_USE_PLATFORM_XLIB_KHR + // VK_KHR_xlib_surface + inline bool Create(const VkXlibSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline bool Create(Display* display, Window window, VkXlibSurfaceCreateFlagsKHR flags = 0, const VkAllocationCallbacks* allocator = nullptr); + #endif + + #ifdef VK_USE_PLATFORM_WAYLAND_KHR + // VK_KHR_wayland_surface + inline bool Create(const VkWaylandSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline bool Create(wl_display* display, wl_surface* surface, VkWaylandSurfaceCreateFlagsKHR flags = 0, const VkAllocationCallbacks* allocator = nullptr); + #endif + + #ifdef VK_USE_PLATFORM_WIN32_KHR + // VK_KHR_win32_surface + inline bool Create(const VkWin32SurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator = nullptr); + inline bool Create(HINSTANCE instance, HWND handle, VkWin32SurfaceCreateFlagsKHR flags = 0, const VkAllocationCallbacks* allocator = nullptr); + #endif + + inline void Destroy(); + + bool GetCapabilities(VkPhysicalDevice physicalDevice, VkSurfaceCapabilitiesKHR* surfaceCapabilities); + bool GetFormats(VkPhysicalDevice physicalDevice, std::vector* surfaceFormats); + bool GetPresentModes(VkPhysicalDevice physicalDevice, std::vector* presentModes); + bool GetSupportPresentation(VkPhysicalDevice physicalDevice, UInt32 queueFamilyIndex, bool* supported); + + inline bool IsSupported() const; + + inline VkResult GetLastErrorCode() const; + + Surface& operator=(const Surface&) = delete; + Surface& operator=(Surface&&) = delete; + + inline operator VkSurfaceKHR(); + + private: + inline bool Create(const VkAllocationCallbacks* allocator); + + Instance& m_instance; + VkAllocationCallbacks m_allocator; + VkSurfaceKHR m_surface; + VkResult m_lastErrorCode; + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKSURFACE_HPP diff --git a/include/Nazara/Vulkan/VkSurface.inl b/include/Nazara/Vulkan/VkSurface.inl new file mode 100644 index 000000000..f64c24af0 --- /dev/null +++ b/include/Nazara/Vulkan/VkSurface.inl @@ -0,0 +1,311 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + inline Surface::Surface(Instance& instance) : + m_instance(instance), + m_surface(VK_NULL_HANDLE) + { + } + + inline Surface::Surface(Surface&& surface) : + m_instance(surface.m_instance), + m_allocator(surface.m_allocator), + m_surface(surface.m_surface), + m_lastErrorCode(surface.m_lastErrorCode) + { + surface.m_surface = VK_NULL_HANDLE; + } + + inline Surface::~Surface() + { + Destroy(); + } + + #ifdef VK_USE_PLATFORM_ANDROID_KHR + inline bool Surface::Create(const VkAndroidSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = m_instance.vkCreateAndroidSurfaceKHR(m_instance, &createInfo, allocator, &m_surface); + return Create(allocator); + } + + inline bool Surface::Create(ANativeWindow* window, VkAndroidSurfaceCreateFlagsKHR flags, const VkAllocationCallbacks* allocator) + { + VkAndroidSurfaceCreateInfoKHR createInfo = + { + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, + nullptr, + flags, + window + }; + + return Create(createInfo, allocator); + } + #endif + + #ifdef VK_USE_PLATFORM_MIR_KHR + inline bool Surface::Create(const VkMirSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = m_instance.vkCreateMirSurfaceKHR(m_instance, &createInfo, allocator, &m_surface); + return Create(allocator); + } + + inline bool Surface::Create(MirConnection* connection, MirSurface* surface, VkMirSurfaceCreateFlagsKHR flags, const VkAllocationCallbacks* allocator) + { + VkMirSurfaceCreateInfoKHR createInfo = + { + VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR, + nullptr, + flags, + connection, + surface + }; + + return Create(createInfo, allocator); + } + #endif + + #ifdef VK_USE_PLATFORM_XCB_KHR + inline bool Surface::Create(const VkXcbSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = m_instance.vkCreateXcbSurfaceKHR(m_instance, &createInfo, allocator, &m_surface); + return Create(allocator); + } + + inline bool Surface::Create(xcb_connection_t* connection, xcb_window_t window, VkXcbSurfaceCreateFlagsKHR flags, const VkAllocationCallbacks* allocator) + { + VkXcbSurfaceCreateInfoKHR createInfo = + { + VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR, + nullptr, + flags, + connection, + window + }; + + return Create(createInfo, allocator); + } + #endif + + #ifdef VK_USE_PLATFORM_XLIB_KHR + inline bool Surface::Create(const VkXlibSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = m_instance.vkCreateXlibSurfaceKHR(m_instance, &createInfo, allocator, &m_surface); + return Create(allocator); + } + + inline bool Surface::Create(Display* display, Window window, VkXlibSurfaceCreateFlagsKHR flags, const VkAllocationCallbacks* allocator) + { + VkXlibSurfaceCreateInfoKHR createInfo = + { + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + nullptr, + flags, + display, + window + }; + + return Create(createInfo, allocator); + } + #endif + + #ifdef VK_USE_PLATFORM_WAYLAND_KHR + inline bool Surface::Create(const VkWaylandSurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = m_instance.vkCreateWaylandSurfaceKHR(m_instance, &createInfo, allocator, &m_surface); + return Create(allocator); + } + + inline bool Surface::Create(wl_display* display, wl_surface* surface, VkWaylandSurfaceCreateFlagsKHR flags, const VkAllocationCallbacks* allocator) + { + VkWaylandSurfaceCreateInfoKHR createInfo = + { + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, + nullptr, + flags, + display, + surface + }; + + return Create(createInfo, allocator); + } + #endif + + #ifdef VK_USE_PLATFORM_WIN32_KHR + inline bool Surface::Create(const VkWin32SurfaceCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = m_instance.vkCreateWin32SurfaceKHR(m_instance, &createInfo, allocator, &m_surface); + return Create(allocator); + } + + inline bool Surface::Create(HINSTANCE instance, HWND handle, VkWin32SurfaceCreateFlagsKHR flags, const VkAllocationCallbacks* allocator) + { + VkWin32SurfaceCreateInfoKHR createInfo = + { + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + nullptr, + flags, + instance, + handle + }; + + return Create(createInfo, allocator); + } + #endif + + inline void Surface::Destroy() + { + if (m_surface != VK_NULL_HANDLE) + m_instance.vkDestroySurfaceKHR(m_instance, m_surface, (m_allocator.pfnAllocation) ? &m_allocator : nullptr); + } + + inline VkResult Surface::GetLastErrorCode() const + { + return m_lastErrorCode; + } + + inline bool Surface::GetCapabilities(VkPhysicalDevice physicalDevice, VkSurfaceCapabilitiesKHR* surfaceCapabilities) + { + m_lastErrorCode = m_instance.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, m_surface, surfaceCapabilities); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to query surface capabilities"); + return false; + } + + return true; + } + + inline bool Surface::GetFormats(VkPhysicalDevice physicalDevice, std::vector* surfaceFormats) + { + // First, query format count + UInt32 surfaceCount = 0; // Remember, Nz::UInt32 is a typedef on uint32_t + m_lastErrorCode = m_instance.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, m_surface, &surfaceCount, nullptr); + if (m_lastErrorCode != VkResult::VK_SUCCESS || surfaceCount == 0) + { + NazaraError("Failed to query format count"); + return false; + } + + // Now we can get the list of the available physical device + surfaceFormats->resize(surfaceCount); + m_lastErrorCode = m_instance.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, m_surface, &surfaceCount, surfaceFormats->data()); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to query formats"); + return false; + } + + return true; + } + + inline bool Surface::GetPresentModes(VkPhysicalDevice physicalDevice, std::vector* presentModes) + { + // First, query present modes count + UInt32 presentModeCount = 0; // Remember, Nz::UInt32 is a typedef on uint32_t + m_lastErrorCode = m_instance.vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, m_surface, &presentModeCount, nullptr); + if (m_lastErrorCode != VkResult::VK_SUCCESS || presentModeCount == 0) + { + NazaraError("Failed to query present mode count"); + return false; + } + + // Now we can get the list of the available physical device + presentModes->resize(presentModeCount); + m_lastErrorCode = m_instance.vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, m_surface, &presentModeCount, presentModes->data()); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to query present modes"); + return false; + } + + return true; + } + + inline bool Surface::GetSupportPresentation(VkPhysicalDevice physicalDevice, UInt32 queueFamilyIndex, bool* supported) + { + VkBool32 presentationSupported = VK_FALSE; + m_lastErrorCode = m_instance.vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, m_surface, &presentationSupported); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to query surface capabilities"); + return false; + } + + *supported = (presentationSupported == VK_TRUE); + + return true; + } + + inline bool Surface::IsSupported() const + { + if (!m_instance.IsExtensionLoaded("VK_KHR_surface")) + return false; + + #ifdef VK_USE_PLATFORM_ANDROID_KHR + if (m_instance.IsExtensionLoaded("VK_KHR_android_surface")) + return true; + #endif + + #ifdef VK_USE_PLATFORM_MIR_KHR + if (m_instance.IsExtensionLoaded("VK_KHR_mir_surface")) + return true; + #endif + + #ifdef VK_USE_PLATFORM_XCB_KHR + if (m_instance.IsExtensionLoaded("VK_KHR_xcb_surface")) + return true; + #endif + + #ifdef VK_USE_PLATFORM_XLIB_KHR + if (m_instance.IsExtensionLoaded("VK_KHR_xlib_surface")) + return true; + #endif + + #ifdef VK_USE_PLATFORM_WAYLAND_KHR + if (m_instance.IsExtensionLoaded("VK_KHR_wayland_surface")) + return true; + #endif + + #ifdef VK_USE_PLATFORM_WIN32_KHR + if (m_instance.IsExtensionLoaded("VK_KHR_win32_surface")) + return true; + #endif + + return false; + } + + inline Surface::operator VkSurfaceKHR() + { + return m_surface; + } + + inline bool Surface::Create(const VkAllocationCallbacks* allocator) + { + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to create Vulkan surface"); + return false; + } + + // Store the allocator to access them when needed + if (allocator) + m_allocator = *allocator; + else + m_allocator.pfnAllocation = nullptr; + + return true; + } + } +} + +#include diff --git a/include/Nazara/Vulkan/VkSwapchain.hpp b/include/Nazara/Vulkan/VkSwapchain.hpp new file mode 100644 index 000000000..374ad88e6 --- /dev/null +++ b/include/Nazara/Vulkan/VkSwapchain.hpp @@ -0,0 +1,51 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_VKSWAPCHAIN_HPP +#define NAZARA_VULKAN_VKSWAPCHAIN_HPP + +#include +#include + +namespace Nz +{ + namespace Vk + { + class Swapchain : public DeviceObject + { + friend DeviceObject; + + public: + inline Swapchain(Device& instance); + Swapchain(const Swapchain&) = delete; + Swapchain(Swapchain&&) = default; + ~Swapchain() = default; + + inline bool AcquireNextImage(Nz::UInt64 timeout, VkSemaphore semaphore, VkFence fence, UInt32* imageIndex); + + inline bool Create(const VkSwapchainCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator = nullptr); + + inline VkImage GetImage(UInt32 index) const; + inline const std::vector& GetImages() const; + inline UInt32 GetImageCount() const; + + inline bool IsSupported() const; + + Swapchain& operator=(const Swapchain&) = delete; + Swapchain& operator=(Swapchain&&) = delete; + + private: + static VkResult CreateHelper(Device& device, const VkSwapchainCreateInfoKHR* createInfo, const VkAllocationCallbacks* allocator, VkSwapchainKHR* handle); + static void DestroyHelper(Device& device, VkSwapchainKHR handle, const VkAllocationCallbacks* allocator); + + std::vector m_images; + }; + } +} + +#include + +#endif // NAZARA_VULKAN_VKSWAPCHAIN_HPP diff --git a/include/Nazara/Vulkan/VkSwapchain.inl b/include/Nazara/Vulkan/VkSwapchain.inl new file mode 100644 index 000000000..8865012e4 --- /dev/null +++ b/include/Nazara/Vulkan/VkSwapchain.inl @@ -0,0 +1,90 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + inline Swapchain::Swapchain(Device& device) : + DeviceObject(device) + { + } + + inline bool Swapchain::AcquireNextImage(Nz::UInt64 timeout, VkSemaphore semaphore, VkFence fence, UInt32* imageIndex) + { + m_lastErrorCode = m_device.vkAcquireNextImageKHR(m_device, m_handle, timeout, semaphore, fence, imageIndex); + switch (m_lastErrorCode) + { + case VkResult::VK_SUBOPTIMAL_KHR: + case VkResult::VK_SUCCESS: + return true; + + default: + return false; + } + } + + inline bool Swapchain::Create(const VkSwapchainCreateInfoKHR& createInfo, const VkAllocationCallbacks* allocator) + { + if (!DeviceObject::Create(createInfo, allocator)) + return false; + + UInt32 imageCount = 0; + m_lastErrorCode = m_device.vkGetSwapchainImagesKHR(m_device, m_handle, &imageCount, nullptr); + if (m_lastErrorCode != VkResult::VK_SUCCESS || imageCount == 0) + { + NazaraError("Failed to query swapchain image count"); + return false; + } + + m_images.resize(imageCount); + m_lastErrorCode = m_device.vkGetSwapchainImagesKHR(m_device, m_handle, &imageCount, m_images.data()); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to query swapchain images"); + return false; + } + + return true; + } + + inline VkImage Swapchain::GetImage(UInt32 index) const + { + return m_images[index]; + } + + inline const std::vector& Swapchain::GetImages() const + { + return m_images; + } + + inline UInt32 Swapchain::GetImageCount() const + { + return m_images.size(); + } + + inline bool Swapchain::IsSupported() const + { + if (!m_device.IsExtensionLoaded("VK_KHR_swapchain")) + return false; + } + + VkResult Swapchain::CreateHelper(Device& device, const VkSwapchainCreateInfoKHR* createInfo, const VkAllocationCallbacks* allocator, VkSwapchainKHR* handle) + { + return device.vkCreateSwapchainKHR(device, createInfo, allocator, handle); + } + + void Swapchain::DestroyHelper(Device& device, VkSwapchainKHR handle, const VkAllocationCallbacks* allocator) + { + return device.vkDestroySwapchainKHR(device, handle, allocator); + } + } +} + +#include diff --git a/include/Nazara/Vulkan/Vulkan.hpp b/include/Nazara/Vulkan/Vulkan.hpp new file mode 100644 index 000000000..9d90202c2 --- /dev/null +++ b/include/Nazara/Vulkan/Vulkan.hpp @@ -0,0 +1,33 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKAN_HPP +#define NAZARA_VULKAN_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_VULKAN_API Vulkan + { + public: + Vulkan() = delete; + ~Vulkan() = delete; + + static bool Initialize(); + + static bool IsInitialized(); + + static void Uninitialize(); + + private: + static unsigned int s_moduleReferenceCounter; + }; +} + +#endif // NAZARA_VULKAN_HPP diff --git a/plugins/Assimp/Plugin.cpp b/plugins/Assimp/Plugin.cpp index 395ca6305..d1d0ace26 100644 --- a/plugins/Assimp/Plugin.cpp +++ b/plugins/Assimp/Plugin.cpp @@ -23,6 +23,7 @@ SOFTWARE. */ #include +#include #include #include #include @@ -104,7 +105,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) | aiProcess_OptimizeGraph | aiProcess_FlipWindingOrder | aiProcess_Debone; - if (!parameters.flipUVs) + if (parameters.flipUVs) postProcess |= aiProcess_FlipUVs; if (parameters.optimizeIndexBuffers) @@ -130,6 +131,12 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) const aiScene* scene = aiImportFileExWithProperties(userdata.originalFilePath, postProcess, &fileIO, properties); aiReleasePropertyStore(properties); + if (!scene) + { + NazaraError("Assimp failed to import file: " + Nz::String(aiGetErrorString())); + return false; + } + std::set joints; bool animatedMesh = false; @@ -206,10 +213,10 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) { aiVector3D position = iMesh->mVertices[j]; aiVector3D normal = iMesh->mNormals[j]; - aiVector3D tangent = iMesh->mTangents[j]; - aiVector3D uv = iMesh->mTextureCoords[0][j]; + aiVector3D tangent = (iMesh->HasTangentsAndBitangents()) ? iMesh->mTangents[j] : aiVector3D(0.f, 1.f, 0.f); + aiVector3D uv = (iMesh->HasTextureCoords(0)) ? iMesh->mTextureCoords[0][j] : aiVector3D(0.f); - vertex->position = parameters.scale * Vector3f(position.x, position.y, position.z); + vertex->position = parameters.matrix * Vector3f(position.x, position.y, position.z); vertex->normal.Set(normal.x, normal.y, normal.z); vertex->tangent.Set(tangent.x, tangent.y, tangent.z); vertex->uv.Set(uv.x, uv.y); @@ -237,8 +244,6 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) aiColor4D color; if (aiGetMaterialColor(aiMat, aiKey, aiType, aiIndex, &color) == aiReturn_SUCCESS) { - matData.SetParameter(MaterialData::CustomDefined); - matData.SetParameter(colorKey, Color(static_cast(color.r * 255), static_cast(color.g * 255), static_cast(color.b * 255), static_cast(color.a * 255))); } }; @@ -246,16 +251,15 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) auto ConvertTexture = [&] (aiTextureType aiType, const char* textureKey, const char* wrapKey = nullptr) { aiString path; - aiTextureMapMode mapMode; - if (aiGetMaterialTexture(aiMat, aiType, 0, &path, nullptr, nullptr, nullptr, nullptr, &mapMode, nullptr) == aiReturn_SUCCESS) + aiTextureMapMode mapMode[3]; + if (aiGetMaterialTexture(aiMat, aiType, 0, &path, nullptr, nullptr, nullptr, nullptr, &mapMode[0], nullptr) == aiReturn_SUCCESS) { - matData.SetParameter(MaterialData::CustomDefined); matData.SetParameter(textureKey, stream.GetDirectory() + String(path.data, path.length)); if (wrapKey) { SamplerWrap wrap = SamplerWrap_Default; - switch (mapMode) + switch (mapMode[0]) { case aiTextureMapMode_Clamp: case aiTextureMapMode_Decal: @@ -271,7 +275,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) break; default: - NazaraWarning("Assimp texture map mode 0x" + String::Number(mapMode, 16) + " not handled"); + NazaraWarning("Assimp texture map mode 0x" + String::Number(mapMode[0], 16) + " not handled"); break; } @@ -291,8 +295,12 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters) ConvertTexture(aiTextureType_OPACITY, MaterialData::AlphaTexturePath); ConvertTexture(aiTextureType_SPECULAR, MaterialData::SpecularTexturePath, MaterialData::SpecularWrap); + aiString name; + if (aiGetMaterialString(aiMat, AI_MATKEY_NAME, &name) == aiReturn_SUCCESS) + matData.SetParameter(MaterialData::Name, String(name.data, name.length)); + int iValue; - if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue)) + if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue) == aiReturn_SUCCESS) matData.SetParameter(MaterialData::FaceCulling, !iValue); matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(materials.size(), std::move(matData)))).first; diff --git a/src/Nazara/Audio/Audio.cpp b/src/Nazara/Audio/Audio.cpp index c0e785865..6eaa86c39 100644 --- a/src/Nazara/Audio/Audio.cpp +++ b/src/Nazara/Audio/Audio.cpp @@ -16,6 +16,21 @@ namespace Nz { + /*! + * \ingroup audio + * \class Nz::Audio + * \brief Audio class that represents the module initializer of Audio + */ + + /*! + * \brief Gets the format of the audio + * \return AudioFormat Enumeration type for the format + * + * \param channelCount Number of channels + * + * \remark Produces a NazaraError if the number of channels is erroneous (3 or 5) and AudioFormat_Unknown is returned + */ + AudioFormat Audio::GetAudioFormat(unsigned int channelCount) { switch (channelCount) @@ -34,19 +49,36 @@ namespace Nz } } + /*! + * \brief Gets the factor of the doppler effect + * \return Global factor of the doppler effect + */ + float Audio::GetDopplerFactor() { return alGetFloat(AL_DOPPLER_FACTOR); } + /*! + * \brief Gets the global volume + * \return Float between [0, inf) with 100.f being the default + */ + float Audio::GetGlobalVolume() { ALfloat gain = 0.f; alGetListenerf(AL_GAIN, &gain); - return gain*100.f; + return gain * 100.f; } + /*! + * \brief Gets the direction of the listener + * \return Direction of the listener, in front of the listener + * + * \see GetListenerRotation + */ + Vector3f Audio::GetListenerDirection() { ALfloat orientation[6]; @@ -55,6 +87,13 @@ namespace Nz return Vector3f(orientation[0], orientation[1], orientation[2]); } + /*! + * \brief Gets the position of the listener + * \return Position of the listener + * + * \see GetListenerVelocity + */ + Vector3f Audio::GetListenerPosition() { Vector3f position; @@ -63,6 +102,11 @@ namespace Nz return position; } + /*! + * \brief Gets the rotation of the listener + * \return Rotation of the listener + */ + Quaternionf Audio::GetListenerRotation() { ALfloat orientation[6]; @@ -73,6 +117,13 @@ namespace Nz return Quaternionf::RotationBetween(Vector3f::Forward(), forward); } + /*! + * \brief Gets the velocity of the listener + * \return Velocity of the listener + * + * \see GetListenerPosition + */ + Vector3f Audio::GetListenerVelocity() { Vector3f velocity; @@ -81,20 +132,33 @@ namespace Nz return velocity; } + /*! + * \brief Gets the speed of sound + * \return Speed of sound + */ + float Audio::GetSpeedOfSound() { return alGetFloat(AL_SPEED_OF_SOUND); } + /*! + * \brief Initializes the Audio module + * \return true if initialization is successful + * + * \remark Produces a NazaraError if initialization of modules Core, OpenAL or SoundBuffer failed + * \remark Produces a NazaraNotice + */ + bool Audio::Initialize() { - if (s_moduleReferenceCounter > 0) + if (IsInitialized()) { s_moduleReferenceCounter++; - return true; // Déjà initialisé + return true; // Already initialized } - // Initialisation des dépendances + // Initialisation of dependencies if (!Core::Initialize()) { NazaraError("Failed to initialize core module"); @@ -103,10 +167,10 @@ namespace Nz s_moduleReferenceCounter++; - // Initialisation du module + // Initialisation of the module CallOnExit onExit(Audio::Uninitialize); - // Initialisation d'OpenAL + // Initialisation of OpenAL if (!OpenAL::Initialize()) { NazaraError("Failed to initialize OpenAL"); @@ -119,7 +183,7 @@ namespace Nz return false; } - // Définition de l'orientation par défaut + // Definition of the orientation by default SetListenerDirection(Vector3f::Forward()); // Loaders @@ -131,6 +195,13 @@ namespace Nz return true; } + /*! + * \brief Checks whether the format is supported by the engine + * \return true if it is the case + * + * \param format Format to check + */ + bool Audio::IsFormatSupported(AudioFormat format) { if (format == AudioFormat_Unknown) @@ -139,21 +210,46 @@ namespace Nz return OpenAL::AudioFormat[format] != 0; } + /*! + * \brief Checks whether the module is initialized + * \return true if module is initialized + */ + bool Audio::IsInitialized() { return s_moduleReferenceCounter != 0; } + /*! + * \brief Sets the factor of the doppler effect + * + * \param dopplerFactor Global factor of the doppler effect + */ + void Audio::SetDopplerFactor(float dopplerFactor) { alDopplerFactor(dopplerFactor); } + /*! + * \brief Sets the global volume + * + * \param volume Float between [0, inf) with 100.f being the default + */ + void Audio::SetGlobalVolume(float volume) { - alListenerf(AL_GAIN, volume*0.01f); + alListenerf(AL_GAIN, volume * 0.01f); } + /*! + * \brief Sets the direction of the listener + * + * \param direction Direction of the listener, in front of the listener + * + * \see SetListenerDirection, SetListenerRotation + */ + void Audio::SetListenerDirection(const Vector3f& direction) { Vector3f up = Vector3f::Up(); @@ -167,6 +263,14 @@ namespace Nz alListenerfv(AL_ORIENTATION, orientation); } + /*! + * \brief Sets the direction of the listener + * + * \param (dirX, dirY, dirZ) Direction of the listener, in front of the listener + * + * \see SetListenerDirection, SetListenerRotation + */ + void Audio::SetListenerDirection(float dirX, float dirY, float dirZ) { Vector3f up = Vector3f::Up(); @@ -180,16 +284,38 @@ namespace Nz alListenerfv(AL_ORIENTATION, orientation); } + /*! + * \brief Sets the position of the listener + * + * \param position Position of the listener + * + * \see SetListenerVelocity + */ + void Audio::SetListenerPosition(const Vector3f& position) { alListenerfv(AL_POSITION, position); } + /*! + * \brief Sets the position of the listener + * + * \param (x, y, z) Position of the listener + * + * \see SetListenerVelocity + */ + void Audio::SetListenerPosition(float x, float y, float z) { alListener3f(AL_POSITION, x, y, z); } + /*! + * \brief Sets the rotation of the listener + * + * \param rotation Rotation of the listener + */ + void Audio::SetListenerRotation(const Quaternionf& rotation) { Vector3f forward = rotation * Vector3f::Forward(); @@ -204,33 +330,61 @@ namespace Nz alListenerfv(AL_ORIENTATION, orientation); } + /*! + * \brief Sets the velocity of the listener + * + * \param velocity Velocity of the listener + * + * \see SetListenerPosition + */ + void Audio::SetListenerVelocity(const Vector3f& velocity) { alListenerfv(AL_VELOCITY, velocity); } + /*! + * \brief Sets the velocity of the listener + * + * \param (velX, velY, velZ) Velocity of the listener + * + * \see SetListenerPosition + */ + void Audio::SetListenerVelocity(float velX, float velY, float velZ) { alListener3f(AL_VELOCITY, velX, velY, velZ); } + /*! + * \brief Sets the speed of sound + * + * \param speed Speed of sound + */ + void Audio::SetSpeedOfSound(float speed) { alSpeedOfSound(speed); } + /*! + * \brief Uninitializes the Audio module + * + * \remark Produces a NazaraNotice + */ + void Audio::Uninitialize() { if (s_moduleReferenceCounter != 1) { - // Le module est soit encore utilisé, soit pas initialisé + // The module is still in use, or can not be uninitialized if (s_moduleReferenceCounter > 1) s_moduleReferenceCounter--; return; } - // Libération du module + // Free of module s_moduleReferenceCounter = 0; // Loaders @@ -241,7 +395,7 @@ namespace Nz NazaraNotice("Uninitialized: Audio module"); - // Libération des dépendances + // Free of dependencies Core::Uninitialize(); } diff --git a/src/Nazara/Audio/Music.cpp b/src/Nazara/Audio/Music.cpp index bbcb7d813..9e38c7114 100644 --- a/src/Nazara/Audio/Music.cpp +++ b/src/Nazara/Audio/Music.cpp @@ -14,6 +14,19 @@ namespace Nz { + /*! + * \ingroup audio + * \class Nz::Music + * \brief Audio class that represents a music + * + * \remark Module Audio needs to be initialized to use this class + */ + + /*! + * \brief Checks whether the parameters for the loading of the music are correct + * \return true If parameters are valid + */ + bool MusicParams::IsValid() const { return true; @@ -32,11 +45,26 @@ namespace Nz unsigned int sampleRate; }; + /*! + * \brief Destructs the object and calls Destroy + * + * \see Destroy + */ + Music::~Music() { Destroy(); } + /*! + * \brief Creates a music with a sound stream + * \return true if creation was succesful + * + * \param soundStream Sound stream which is the source for the music + * + * \remark Produces a NazaraError if soundStream is invalid with NAZARA_AUDIO_SAFE defined + */ + bool Music::Create(SoundStream* soundStream) { NazaraAssert(soundStream, "Invalid stream"); @@ -48,7 +76,7 @@ namespace Nz m_impl = new MusicImpl; m_impl->sampleRate = soundStream->GetSampleRate(); m_impl->audioFormat = OpenAL::AudioFormat[format]; - m_impl->chunkSamples.resize(format * m_impl->sampleRate); // Une seconde de samples + m_impl->chunkSamples.resize(format * m_impl->sampleRate); // One second of samples m_impl->stream.reset(soundStream); SetPlayingOffset(0); @@ -56,6 +84,10 @@ namespace Nz return true; } + /*! + * \brief Destroys the current music and frees resources + */ + void Music::Destroy() { if (m_impl) @@ -67,6 +99,14 @@ namespace Nz } } + /*! + * \brief Enables the looping of the music + * + * \param loop Should music loop + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + void Music::EnableLooping(bool loop) { #if NAZARA_AUDIO_SAFE @@ -80,6 +120,13 @@ namespace Nz m_impl->loop = loop; } + /*! + * \brief Gets the duration of the music + * \return Duration of the music in milliseconds + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + UInt32 Music::GetDuration() const { #if NAZARA_AUDIO_SAFE @@ -93,6 +140,13 @@ namespace Nz return m_impl->stream->GetDuration(); } + /*! + * \brief Gets the format of the music + * \return Enumeration of type AudioFormat (mono, stereo, ...) + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + AudioFormat Music::GetFormat() const { #if NAZARA_AUDIO_SAFE @@ -106,6 +160,13 @@ namespace Nz return m_impl->stream->GetFormat(); } + /*! + * \brief Gets the current offset in the music + * \return Offset in milliseconds (works with entire seconds) + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + UInt32 Music::GetPlayingOffset() const { #if NAZARA_AUDIO_SAFE @@ -125,6 +186,13 @@ namespace Nz return static_cast((1000ULL * (samples + (m_impl->processedSamples / m_impl->stream->GetFormat()))) / m_impl->sampleRate); } + /*! + * \brief Gets the number of samples in the music + * \return Count of samples (number of seconds * sample rate * channel count) + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + UInt32 Music::GetSampleCount() const { #if NAZARA_AUDIO_SAFE @@ -138,6 +206,13 @@ namespace Nz return m_impl->stream->GetSampleCount(); } + /*! + * \brief Gets the rates of sample in the music + * \return Rate of sample in Hertz (Hz) + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + UInt32 Music::GetSampleRate() const { #if NAZARA_AUDIO_SAFE @@ -151,6 +226,14 @@ namespace Nz return m_impl->sampleRate; } + /*! + * \brief Gets the status of the music + * \return Enumeration of type SoundStatus (Playing, Stopped, ...) + * + * \remark If the music is not playing, Stopped is returned + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + SoundStatus Music::GetStatus() const { #if NAZARA_AUDIO_SAFE @@ -163,13 +246,20 @@ namespace Nz SoundStatus status = GetInternalStatus(); - // Pour compenser les éventuels retards (ou le laps de temps entre Play() et la mise en route du thread) + // To compensate any delays (or the timelaps between Play() and the thread startup) if (m_impl->streaming && status == SoundStatus_Stopped) status = SoundStatus_Playing; return status; } + /*! + * \brief Checks whether the music is looping + * \return true if it is the case + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + bool Music::IsLooping() const { #if NAZARA_AUDIO_SAFE @@ -183,26 +273,61 @@ namespace Nz return m_impl->loop; } + /*! + * \brief Loads the music from file + * \return true if loading is successful + * + * \param filePath Path to the file + * \param params Parameters for the music + */ + bool Music::OpenFromFile(const String& filePath, const MusicParams& params) { return MusicLoader::LoadFromFile(this, filePath, params); } + /*! + * \brief Loads the music from memory + * \return true if loading is successful + * + * \param data Raw memory + * \param size Size of the memory + * \param params Parameters for the music + */ + bool Music::OpenFromMemory(const void* data, std::size_t size, const MusicParams& params) { return MusicLoader::LoadFromMemory(this, data, size, params); } + /*! + * \brief Loads the music from stream + * \return true if loading is successful + * + * \param stream Stream to the music + * \param params Parameters for the music + */ + bool Music::OpenFromStream(Stream& stream, const MusicParams& params) { return MusicLoader::LoadFromStream(this, stream, params); } + /*! + * \brief Pauses the music + */ + void Music::Pause() { alSourcePause(m_source); } + /*! + * \brief Plays the music + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + void Music::Play() { #if NAZARA_AUDIO_SAFE @@ -238,6 +363,14 @@ namespace Nz } } + /*! + * \brief Sets the playing offset for the music + * + * \param offset Offset in the music in milliseconds + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + void Music::SetPlayingOffset(UInt32 offset) { #if NAZARA_AUDIO_SAFE @@ -260,6 +393,12 @@ namespace Nz Play(); } + /*! + * \brief Stops the music + * + * \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined + */ + void Music::Stop() { #if NAZARA_AUDIO_SAFE @@ -277,6 +416,13 @@ namespace Nz } } + /*! + * \brief Fills the buffer and queues it up + * \return true if operation was successful + * + * \param buffer Index of the buffer + */ + bool Music::FillAndQueueBuffer(unsigned int buffer) { unsigned int sampleCount = m_impl->chunkSamples.size(); @@ -304,27 +450,31 @@ namespace Nz alSourceQueueBuffers(m_source, 1, &buffer); } - return sampleRead != sampleCount; // Fin du stream (N'arrive pas en cas de loop) + return sampleRead != sampleCount; // End of stream (Does not happen when looping) } + /*! + * \brief Thread function for the music + */ + void Music::MusicThread() { - // Allocation des buffers de streaming + // Allocation of streaming buffers ALuint buffers[NAZARA_AUDIO_STREAMED_BUFFER_COUNT]; alGenBuffers(NAZARA_AUDIO_STREAMED_BUFFER_COUNT, buffers); for (unsigned int i = 0; i < NAZARA_AUDIO_STREAMED_BUFFER_COUNT; ++i) { if (FillAndQueueBuffer(buffers[i])) - break; // Nous avons atteint la fin du stream, inutile de rajouter des buffers + break; // We have reached the end of the stream, there is no use to add new buffers } alSourcePlay(m_source); - // Boucle de lecture (remplissage de nouveaux buffers au fur et à mesure) + // Reading loop (Filling new buffers as playing) while (m_impl->streaming) { - // La lecture s'est arrêtée, nous avons atteint la fin du stream + // The reading has stopped, we have reached the end of the stream SoundStatus status = GetInternalStatus(); if (status == SoundStatus_Stopped) { @@ -334,7 +484,7 @@ namespace Nz Nz::LockGuard lock(m_impl->bufferLock); - // On traite les buffers lus + // We treat read buffers ALint processedCount = 0; alGetSourcei(m_source, AL_BUFFERS_PROCESSED, &processedCount); while (processedCount--) @@ -355,14 +505,14 @@ namespace Nz lock.Unlock(); - // On retourne dormir un peu + // We go back to sleep Thread::Sleep(50); } - // Arrêt de la lecture du son (dans le cas où ça ne serait pas déjà fait) + // Stop playing of the sound (in the case where it has not been already done) alSourceStop(m_source); - // On supprime les buffers du stream + // We delete buffers from the stream ALint queuedBufferCount; alGetSourcei(m_source, AL_BUFFERS_QUEUED, &queuedBufferCount); diff --git a/src/Nazara/Audio/OpenAL.cpp b/src/Nazara/Audio/OpenAL.cpp index f527b682d..489855022 100644 --- a/src/Nazara/Audio/OpenAL.cpp +++ b/src/Nazara/Audio/OpenAL.cpp @@ -23,6 +23,14 @@ namespace Nz ALCcontext* s_context = nullptr; unsigned int s_version; + /*! + * \brief Parses the devices + * \return Number of devices + * + * \param deviceString String for the device (input / output) + * \param devices List of names of the devices + */ + std::size_t ParseDevices(const char* deviceString, std::vector& devices) { if (!deviceString) @@ -41,35 +49,77 @@ namespace Nz } } + /*! + * \ingroup audio + * \class Nz::OpenAL + * \brief Audio class that represents the link with OpenAL + * + * \remark This class is meant to be used by Module Audio + */ + + /*! + * \brief Gets the entry for the function name + * \return Pointer to the function + * + * \param entryPoint Name of the entry + * + * \remark This does not produces a NazaraError if entry does not exist + */ + OpenALFunc OpenAL::GetEntry(const String& entryPoint) { return LoadEntry(entryPoint.GetConstBuffer(), false); } + /*! + * \brief Gets the name of the renderer + * \return Name of the renderer + */ + String OpenAL::GetRendererName() { return s_rendererName; } + /*! + * \brief Gets the name of the vendor + * \return Name of the vendor + */ + String OpenAL::GetVendorName() { return s_vendorName; } + /*! + * \brief Gets the version of OpenAL + * \return Version of OpenAL + */ + unsigned int OpenAL::GetVersion() { return s_version; } + /*! + * \brief Initializes the module OpenAL + * \return true if initialization is successful + * + * \param openDevice True to get information from the device + * + * \remark Produces a NazaraError if one of the entry failed + * \remark Produces a NazaraError if opening device failed with openDevice parameter set to true + */ + bool OpenAL::Initialize(bool openDevice) { - if (s_library.IsLoaded()) + if (IsInitialized()) return true; #if defined(NAZARA_PLATFORM_WINDOWS) - ///FIXME: Est-ce qu'OpenAL Soft est une meilleure implémentation que Creative ? - /// Si on pouvait se résigner à utiliser OpenAL Soft tout le temps, cela nous permettrait d'utiliser les extensions sonores - /// et de donner plus de possibilités techniques au niveau de l'audio. + ///FIXME: Is OpenAL Soft a better implementation than Creative ? + /// If we could use OpenAL Soft everytime, this would allow us to use sonorous extensions + /// and give us more technical possibilities with audio const char* libs[] = { "soft_oal.dll", "wrap_oal.dll", @@ -217,11 +267,23 @@ namespace Nz return true; } + /*! + * \brief Checks whether the module is initialized + * \return true if it is the case + */ + bool OpenAL::IsInitialized() { return s_library.IsLoaded(); } + /*! + * \brief Queries the input devices + * \return Number of devices + * + * \param devices List of names of the input devices + */ + std::size_t OpenAL::QueryInputDevices(std::vector& devices) { const char* deviceString = reinterpret_cast(alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER)); @@ -231,6 +293,13 @@ namespace Nz return ParseDevices(deviceString, devices); } + /*! + * \brief Queries the output devices + * \return Number of devices + * + * \param devices List of names of the output devices + */ + std::size_t OpenAL::QueryOutputDevices(std::vector& devices) { const char* deviceString = reinterpret_cast(alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER)); @@ -240,6 +309,13 @@ namespace Nz return ParseDevices(deviceString, devices); } + /*! + * \brief Sets the active device + * \return true if device is successfully opened + * + * \param deviceName Name of the device + */ + bool OpenAL::SetDevice(const String& deviceName) { s_deviceName = deviceName; @@ -253,6 +329,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the module + */ + void OpenAL::Uninitialize() { CloseDevice(); @@ -262,8 +342,14 @@ namespace Nz s_library.Unload(); } - ///ATTENTION: La valeur entière est le nombre de canaux possédés par ce format - ALenum OpenAL::AudioFormat[AudioFormat_Max+1] = {0}; // Valeur ajoutées au chargement d'OpenAL + ///WARNING: The integer value is the number of canals owned by the format + ALenum OpenAL::AudioFormat[AudioFormat_Max+1] = {0}; // Added values with loading of OpenAL + + /*! + * \brief Closes the device + * + * \remark Produces a NazaraWarning if you try to close an active device + */ void OpenAL::CloseDevice() { @@ -277,24 +363,31 @@ namespace Nz } if (!alcCloseDevice(s_device)) - // Nous n'avons pas pu fermer le device, ce qui signifie qu'il est en cours d'utilisation + // We could not close the close, this means that it's still in use NazaraWarning("Failed to close device"); s_device = nullptr; } } + /*! + * \brief Opens the device + * \return true if open is successful + * + * \remark Produces a NazaraError if it could not create the context + */ + bool OpenAL::OpenDevice() { - // Initialisation du module - s_device = alcOpenDevice(s_deviceName.IsEmpty() ? nullptr : s_deviceName.GetConstBuffer()); // On choisit le device par défaut + // Initialisation of the module + s_device = alcOpenDevice(s_deviceName.IsEmpty() ? nullptr : s_deviceName.GetConstBuffer()); // We choose the default device if (!s_device) { NazaraError("Failed to open default device"); return false; } - // Un seul contexte nous suffira + // One context is enough s_context = alcCreateContext(s_device, nullptr); if (!s_context) { @@ -341,7 +434,7 @@ namespace Nz s_version = 0; } - // On complète le tableau de formats + // We complete the formats table AudioFormat[AudioFormat_Mono] = AL_FORMAT_MONO16; AudioFormat[AudioFormat_Stereo] = AL_FORMAT_STEREO16; @@ -359,6 +452,16 @@ namespace Nz return true; } + /*! + * \brief Loads the entry for the function name + * \return Pointer to the function + * + * \param name Name of the entry + * \param throwException Should throw exception if failed ? + * + * \remark Produces a std::runtime_error if entry does not exist and throwException is set to true + */ + OpenALFunc OpenAL::LoadEntry(const char* name, bool throwException) { OpenALFunc entry = reinterpret_cast(s_library.GetSymbol(name)); diff --git a/src/Nazara/Audio/Sound.cpp b/src/Nazara/Audio/Sound.cpp index 7198a01d6..d2b71318b 100644 --- a/src/Nazara/Audio/Sound.cpp +++ b/src/Nazara/Audio/Sound.cpp @@ -14,32 +14,76 @@ namespace Nz { + /*! + * \ingroup audio + * \class Nz::Sound + * \brief Audio class that represents a sound + * + * \remark Module Audio needs to be initialized to use this class + */ + + /*! + * \brief Constructs a Sound object + * + * \param soundBuffer Buffer to read sound from + */ + Sound::Sound(const SoundBuffer* soundBuffer) { SetBuffer(soundBuffer); } + /*! + * \brief Constructs a Sound object which is a copy of another + * + * \param sound Sound to copy + */ + Sound::Sound(const Sound& sound) : SoundEmitter(sound) { SetBuffer(sound.m_buffer); } + /*! + * \brief Destructs the object and calls Stop + * + * \see Stop + */ + Sound::~Sound() { Stop(); } + /*! + * \brief Enables the looping of the music + * + * \param loop Should sound loop + */ + void Sound::EnableLooping(bool loop) { alSourcei(m_source, AL_LOOPING, loop); } + /*! + * \brief Gets the internal buffer + * \return Internal buffer + */ + const SoundBuffer* Sound::GetBuffer() const { return m_buffer; } + /*! + * \brief Gets the duration of the sound + * \return Duration of the music in milliseconds + * + * \remark Produces a NazaraError if there is no buffer + */ + UInt32 Sound::GetDuration() const { NazaraAssert(m_buffer, "Invalid sound buffer"); @@ -47,6 +91,11 @@ namespace Nz return m_buffer->GetDuration(); } + /*! + * \brief Gets the current offset in the sound + * \return Offset in milliseconds (works with entire seconds) + */ + UInt32 Sound::GetPlayingOffset() const { ALint samples = 0; @@ -55,11 +104,21 @@ namespace Nz return static_cast(1000ULL * samples / m_buffer->GetSampleRate()); } + /*! + * \brief Gets the status of the music + * \return Enumeration of type SoundStatus (Playing, Stopped, ...) + */ + SoundStatus Sound::GetStatus() const { return GetInternalStatus(); } + /*! + * \brief Checks whether the sound is looping + * \return true if it is the case + */ + bool Sound::IsLooping() const { ALint loop; @@ -68,16 +127,36 @@ namespace Nz return loop != AL_FALSE; } + /*! + * \brief Checks whether the sound is playable + * \return true if it is the case + */ + bool Sound::IsPlayable() const { return m_buffer != nullptr; } + /*! + * \brief Checks whether the sound is playing + * \return true if it is the case + */ + bool Sound::IsPlaying() const { return GetStatus() == SoundStatus_Playing; } + /*! + * \brief Loads the sound from file + * \return true if loading is successful + * + * \param filePath Path to the file + * \param params Parameters for the sound + * + * \remark Produces a NazaraError if loading failed + */ + bool Sound::LoadFromFile(const String& filePath, const SoundBufferParams& params) { SoundBufferRef buffer = SoundBuffer::New(); @@ -91,6 +170,17 @@ namespace Nz return true; } + /*! + * \brief Loads the sound from memory + * \return true if loading is successful + * + * \param data Raw memory + * \param size Size of the memory + * \param params Parameters for the sound + * + * \remark Produces a NazaraError if loading failed + */ + bool Sound::LoadFromMemory(const void* data, std::size_t size, const SoundBufferParams& params) { SoundBufferRef buffer = SoundBuffer::New(); @@ -104,6 +194,16 @@ namespace Nz return true; } + /*! + * \brief Loads the sound from stream + * \return true if loading is successful + * + * \param stream Stream to the sound + * \param params Parameters for the sound + * + * \remark Produces a NazaraError if loading failed + */ + bool Sound::LoadFromStream(Stream& stream, const SoundBufferParams& params) { SoundBufferRef buffer = SoundBuffer::New(); @@ -117,15 +217,25 @@ namespace Nz return true; } + /*! + * \brief Pauses the sound + */ + void Sound::Pause() { alSourcePause(m_source); } + /*! + * \brief Plays the music + * + * \remark Produces a NazaraError if the sound is not playable with NAZARA_AUDIO_SAFE defined + */ + void Sound::Play() { #if NAZARA_AUDIO_SAFE - if (!m_buffer) + if (!IsPlayable()) { NazaraError("Invalid sound buffer"); return; @@ -135,6 +245,14 @@ namespace Nz alSourcePlay(m_source); } + /*! + * \brief Sets the internal buffer + * + * \param buffer Internal buffer + * + * \remark Produces a NazaraError if buffer is invalid with NAZARA_AUDIO_SAFE defined + */ + void Sound::SetBuffer(const SoundBuffer* buffer) { #if NAZARA_AUDIO_SAFE @@ -158,11 +276,21 @@ namespace Nz alSourcei(m_source, AL_BUFFER, AL_NONE); } + /*! + * \brief Sets the playing offset for the sound + * + * \param offset Offset in the sound in milliseconds + */ + void Sound::SetPlayingOffset(UInt32 offset) { alSourcei(m_source, AL_SAMPLE_OFFSET, static_cast(offset/1000.f * m_buffer->GetSampleRate())); } + /*! + * \brief Stops the sound + */ + void Sound::Stop() { alSourceStop(m_source); diff --git a/src/Nazara/Audio/SoundBuffer.cpp b/src/Nazara/Audio/SoundBuffer.cpp index c01878383..b59e74e23 100644 --- a/src/Nazara/Audio/SoundBuffer.cpp +++ b/src/Nazara/Audio/SoundBuffer.cpp @@ -12,10 +12,23 @@ #include #include -///FIXME: Adapter la création +///FIXME: Adapt the creation namespace Nz { + /*! + * \ingroup audio + * \class Nz::SoundBuffer + * \brief Audio class that represents a buffer for sound + * + * \remark Module Audio needs to be initialized to use this class + */ + + /*! + * \brief Checks whether the parameters for the buffer' sound are correct + * \return true If parameters are valid + */ + bool SoundBufferParams::IsValid() const { return true; @@ -31,6 +44,20 @@ namespace Nz UInt32 sampleRate; }; + /*! + * \brief Constructs a SoundBuffer object + * + * \param format Format for the audio + * \param sampleCount Number of samples + * \param sampleRate Rate of samples + * \param samples Samples raw data + * + * \remark Produces a NazaraError if creation went wrong with NAZARA_AUDIO_SAFE defined + * \remark Produces a std::runtime_error if creation went wrong with NAZARA_AUDIO_SAFE defined + * + * \see Create + */ + SoundBuffer::SoundBuffer(AudioFormat format, unsigned int sampleCount, unsigned int sampleRate, const Int16* samples) { Create(format, sampleCount, sampleRate, samples); @@ -44,6 +71,12 @@ namespace Nz #endif } + /*! + * \brief Destructs the object and calls Destroy + * + * \see Destroy + */ + SoundBuffer::~SoundBuffer() { OnSoundBufferRelease(this); @@ -51,6 +84,19 @@ namespace Nz Destroy(); } + /*! + * \brief Creates the SoundBuffer object + * \return true if creation is successful + * + * \param format Format for the audio + * \param sampleCount Number of samples + * \param sampleRate Rate of samples + * \param samples Samples raw data + * + * \remark Produces a NazaraError if creation went wrong with NAZARA_AUDIO_SAFE defined, + * this could happen if parameters are invalid or creation of OpenAL buffers failed + */ + bool SoundBuffer::Create(AudioFormat format, unsigned int sampleCount, unsigned int sampleRate, const Int16* samples) { Destroy(); @@ -81,7 +127,7 @@ namespace Nz } #endif - // On vide le stack d'erreurs + // We empty the error stack while (alGetError() != AL_NO_ERROR); ALuint buffer; @@ -115,6 +161,10 @@ namespace Nz return true; } + /*! + * \brief Destroys the current sound buffer and frees resources + */ + void SoundBuffer::Destroy() { if (m_impl) @@ -126,6 +176,13 @@ namespace Nz } } + /*! + * \brief Gets the duration of the sound buffer + * \return Duration of the sound buffer in milliseconds + * + * \remark Produces a NazaraError if there is no sound buffer with NAZARA_AUDIO_SAFE defined + */ + UInt32 SoundBuffer::GetDuration() const { #if NAZARA_AUDIO_SAFE @@ -139,6 +196,13 @@ namespace Nz return m_impl->duration; } + /*! + * \brief Gets the format of the sound buffer + * \return Enumeration of type AudioFormat (mono, stereo, ...) + * + * \remark Produces a NazaraError if there is no sound buffer with NAZARA_AUDIO_SAFE defined + */ + AudioFormat SoundBuffer::GetFormat() const { #if NAZARA_AUDIO_SAFE @@ -152,6 +216,13 @@ namespace Nz return m_impl->format; } + /*! + * \brief Gets the internal raw samples + * \return Pointer to raw data + * + * \remark Produces a NazaraError if there is no sound buffer with NAZARA_AUDIO_SAFE defined + */ + const Int16* SoundBuffer::GetSamples() const { #if NAZARA_AUDIO_SAFE @@ -165,6 +236,13 @@ namespace Nz return m_impl->samples.get(); } + /*! + * \brief Gets the number of samples in the sound buffer + * \return Count of samples (number of seconds * sample rate * channel count) + * + * \remark Produces a NazaraError if there is no sound buffer with NAZARA_AUDIO_SAFE defined + */ + unsigned int SoundBuffer::GetSampleCount() const { #if NAZARA_AUDIO_SAFE @@ -178,6 +256,13 @@ namespace Nz return m_impl->sampleCount; } + /*! + * \brief Gets the rates of sample in the sound buffer + * \return Rate of sample in Hertz (Hz) + * + * \remark Produces a NazaraError if there is no sound buffer with NAZARA_AUDIO_SAFE defined + */ + unsigned int SoundBuffer::GetSampleRate() const { #if NAZARA_AUDIO_SAFE @@ -191,31 +276,75 @@ namespace Nz return m_impl->sampleRate; } + /*! + * \brief Checks whether the sound buffer is valid + * \return true if it is the case + */ + bool SoundBuffer::IsValid() const { return m_impl != nullptr; } + /*! + * \brief Loads the sound buffer from file + * \return true if loading is successful + * + * \param filePath Path to the file + * \param params Parameters for the sound buffer + */ + bool SoundBuffer::LoadFromFile(const String& filePath, const SoundBufferParams& params) { return SoundBufferLoader::LoadFromFile(this, filePath, params); } + /*! + * \brief Loads the sound buffer from memory + * \return true if loading is successful + * + * \param data Raw memory + * \param size Size of the memory + * \param params Parameters for the sound buffer + */ + bool SoundBuffer::LoadFromMemory(const void* data, std::size_t size, const SoundBufferParams& params) { return SoundBufferLoader::LoadFromMemory(this, data, size, params); } + /*! + * \brief Loads the sound buffer from stream + * \return true if loading is successful + * + * \param stream Stream to the sound buffer + * \param params Parameters for the sound buffer + */ + bool SoundBuffer::LoadFromStream(Stream& stream, const SoundBufferParams& params) { return SoundBufferLoader::LoadFromStream(this, stream, params); } + /*! + * \brief Checks whether the format is supported by the engine + * \return true if it is the case + * + * \param format Format to check + */ + bool SoundBuffer::IsFormatSupported(AudioFormat format) { return Audio::IsFormatSupported(format); } + /*! + * \brief Gets the internal OpenAL buffer + * \return The index of the OpenAL buffer + * + * \remark Produces a NazaraError if there is no sound buffer with NAZARA_AUDIO_SAFE defined + */ + unsigned int SoundBuffer::GetOpenALBuffer() const { #ifdef NAZARA_DEBUG @@ -229,6 +358,13 @@ namespace Nz return m_impl->buffer; } + /*! + * \brief Initializes the libraries and managers + * \return true if initialization is successful + * + * \remark Produces a NazaraError if sub-initialization failed + */ + bool SoundBuffer::Initialize() { if (!SoundBufferLibrary::Initialize()) @@ -246,6 +382,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the libraries and managers + */ + void SoundBuffer::Uninitialize() { SoundBufferManager::Uninitialize(); diff --git a/src/Nazara/Audio/SoundEmitter.cpp b/src/Nazara/Audio/SoundEmitter.cpp index cbee8a8f9..840c4f47d 100644 --- a/src/Nazara/Audio/SoundEmitter.cpp +++ b/src/Nazara/Audio/SoundEmitter.cpp @@ -11,11 +11,32 @@ namespace Nz { + /*! + * \ingroup audio + * \class Nz::SoundEmitter + * \brief Audio class that represents a sound source, that emits sound + * + * \remark Module Audio needs to be initialized to use this class + * \remark This class is abstract + */ + + /*! + * \brief Constructs a SoundEmitter object + */ + SoundEmitter::SoundEmitter() { alGenSources(1, &m_source); } + /*! + * \brief Constructs a SoundEmitter object which is a copy of another + * + * \param emitter SoundEmitter to copy + * + * \remark Position and velocity are not copied + */ + SoundEmitter::SoundEmitter(const SoundEmitter& emitter) { alGenSources(1, &m_source); @@ -23,20 +44,35 @@ namespace Nz SetAttenuation(emitter.GetAttenuation()); SetMinDistance(emitter.GetMinDistance()); SetPitch(emitter.GetPitch()); - // Pas de copie de position ou de vitesse + // No copy for position or velocity SetVolume(emitter.GetVolume()); } + /*! + * \brief Destructs the object + */ + SoundEmitter::~SoundEmitter() { alDeleteSources(1, &m_source); } + /*! + * \brief Enables spatialization + * + * \param spatialization True if spatialization is enabled + */ + void SoundEmitter::EnableSpatialization(bool spatialization) { alSourcei(m_source, AL_SOURCE_RELATIVE, !spatialization); } + /*! + * \brief Gets the attenuation + * \return Amount that your sound will drop off as by the inverse square law + */ + float SoundEmitter::GetAttenuation() const { ALfloat attenuation; @@ -45,6 +81,11 @@ namespace Nz return attenuation; } + /*! + * \brief Gets the minimum distance to hear + * \return Distance to begin to hear + */ + float SoundEmitter::GetMinDistance() const { ALfloat distance; @@ -53,6 +94,11 @@ namespace Nz return distance; } + /*! + * \brief Gets the pitch + * \return Pitch of the sound + */ + float SoundEmitter::GetPitch() const { ALfloat pitch; @@ -61,6 +107,11 @@ namespace Nz return pitch; } + /*! + * \brief Gets the position of the emitter + * \return Position of the sound + */ + Vector3f SoundEmitter::GetPosition() const { Vector3f position; @@ -69,6 +120,11 @@ namespace Nz return position; } + /*! + * \brief Gets the velocity of the emitter + * \return Velocity of the sound + */ + Vector3f SoundEmitter::GetVelocity() const { Vector3f velocity; @@ -77,6 +133,11 @@ namespace Nz return velocity; } + /*! + * \brief Gets the volume of the emitter + * \param volume Float between [0, inf) with 100.f being the default + */ + float SoundEmitter::GetVolume() const { ALfloat gain; @@ -85,6 +146,11 @@ namespace Nz return gain * 100.f; } + /*! + * \brief Checks whether the sound emitter has spatialization enabled + * \return true if it the case + */ + bool SoundEmitter::IsSpatialized() const { ALint relative; @@ -93,46 +159,99 @@ namespace Nz return relative == AL_FALSE; } + /*! + * \brief Sets the attenuation + * + * \param attenuation Amount that your sound will drop off as by the inverse square law + */ + void SoundEmitter::SetAttenuation(float attenuation) { alSourcef(m_source, AL_ROLLOFF_FACTOR, attenuation); } + /*! + * \brief Sets the minimum distance to hear + * + * \param minDistance to begin to hear + */ + void SoundEmitter::SetMinDistance(float minDistance) { alSourcef(m_source, AL_REFERENCE_DISTANCE, minDistance); } + /*! + * \brief Sets the pitch + * + * \param pitch of the sound + */ + void SoundEmitter::SetPitch(float pitch) { alSourcef(m_source, AL_PITCH, pitch); } + /*! + * \brief Sets the position of the emitter + * + * \param position Position of the sound + */ + void SoundEmitter::SetPosition(const Vector3f& position) { alSourcefv(m_source, AL_POSITION, position); } + /*! + * \brief Sets the position of the emitter + * + * \param position Position of the sound with (x, y, z) + */ + void SoundEmitter::SetPosition(float x, float y, float z) { alSource3f(m_source, AL_POSITION, x, y, z); } + /*! + * \brief Sets the velocity of the emitter + * + * \param velocity Velocity of the sound + */ + void SoundEmitter::SetVelocity(const Vector3f& velocity) { alSourcefv(m_source, AL_VELOCITY, velocity); } + /*! + * \brief Sets the velocity of the emitter + * + * \param velocity Velocity with (velX, velY, velZ) + */ + void SoundEmitter::SetVelocity(float velX, float velY, float velZ) { alSource3f(m_source, AL_VELOCITY, velX, velY, velZ); } + /*! + * \brief Sets the volume of the emitter + * + * \param volume Float between [0, inf) with 100.f being the default + */ + void SoundEmitter::SetVolume(float volume) { - alSourcef(m_source, AL_GAIN, volume*0.01f); + alSourcef(m_source, AL_GAIN, volume * 0.01f); } + /*! + * \brief Gets the status of the sound emitter + * \return Enumeration of type SoundStatus (Playing, Stopped, ...) + */ + SoundStatus SoundEmitter::GetInternalStatus() const { ALint state; diff --git a/src/Nazara/Audio/SoundStream.cpp b/src/Nazara/Audio/SoundStream.cpp index c243e875f..88bc0069b 100644 --- a/src/Nazara/Audio/SoundStream.cpp +++ b/src/Nazara/Audio/SoundStream.cpp @@ -6,5 +6,13 @@ namespace Nz { + /*! + * \ingroup audio + * \class Nz::SoundStream + * \brief Audio class that represents a sound stream + * + * \remark This class is abstract + */ + SoundStream::~SoundStream() = default; } diff --git a/src/Nazara/Core/AlgorithmCore.cpp b/src/Nazara/Core/AlgorithmCore.cpp new file mode 100644 index 000000000..1a9d8a171 --- /dev/null +++ b/src/Nazara/Core/AlgorithmCore.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2016 Jérôme Leclercq +// 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 +{ + namespace Detail + { + const UInt8 BitReverseTable256[256] = + { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF + }; + } +} + +#include diff --git a/src/Nazara/Core/Core.cpp b/src/Nazara/Core/Core.cpp index 1ccb35212..026e4a247 100644 --- a/src/Nazara/Core/Core.cpp +++ b/src/Nazara/Core/Core.cpp @@ -28,7 +28,7 @@ namespace Nz bool Core::Initialize() { - if (s_moduleReferenceCounter > 0) + if (IsInitialized()) { s_moduleReferenceCounter++; return true; // Already initialized diff --git a/src/Nazara/Core/File.cpp b/src/Nazara/Core/File.cpp index 97504b0ee..131ceb7f3 100644 --- a/src/Nazara/Core/File.cpp +++ b/src/Nazara/Core/File.cpp @@ -385,7 +385,7 @@ namespace Nz } /*! - * \brief Sets the position of the cursor + * \brief Sets the position of the cursor * \return true if cursor is successfully positioned * * \param pos Position of the cursor @@ -404,7 +404,7 @@ namespace Nz } /*! - * \brief Sets the position of the cursor + * \brief Sets the position of the cursor * \return true if cursor is successfully positioned * * \param offset Offset according to the cursor begin position @@ -725,7 +725,7 @@ namespace Nz return true; else if (path.Match("\\\\*")) // Ex: \\Laptop return true; - else if (path.StartsWith('\\')) // Special : '\' refering to the root + else if (path.StartsWith('\\')) // Special : '\' referring to the root return true; else return false; diff --git a/src/Nazara/Core/ParameterList.cpp b/src/Nazara/Core/ParameterList.cpp index 91426a0b7..bda37b4d4 100644 --- a/src/Nazara/Core/ParameterList.cpp +++ b/src/Nazara/Core/ParameterList.cpp @@ -94,6 +94,7 @@ namespace Nz break; } + case ParameterType_Color: case ParameterType_Float: case ParameterType_None: case ParameterType_Pointer: @@ -198,6 +199,7 @@ namespace Nz } case ParameterType_Boolean: + case ParameterType_Color: case ParameterType_None: case ParameterType_Pointer: case ParameterType_Userdata: @@ -207,7 +209,7 @@ namespace Nz NazaraError("Parameter value is not representable as a float"); return false; } - + /*! * \brief Gets a parameter as an integer * \return true if the parameter could be represented as an integer @@ -247,7 +249,7 @@ namespace Nz case ParameterType_Integer: *value = it->second.value.intVal; - return false; + return true; case ParameterType_String: { @@ -263,6 +265,7 @@ namespace Nz break; } + case ParameterType_Color: case ParameterType_None: case ParameterType_Pointer: case ParameterType_Userdata: @@ -331,6 +334,7 @@ namespace Nz return true; case ParameterType_Boolean: + case ParameterType_Color: case ParameterType_Float: case ParameterType_Integer: case ParameterType_None: @@ -411,7 +415,7 @@ namespace Nz NazaraInternalError("Parameter value is not valid"); return false; } - + /*! * \brief Gets a parameter as an userdata * \return true if the parameter could be represented as a userdata @@ -584,7 +588,7 @@ namespace Nz parameter.type = ParameterType_Integer; parameter.value.intVal = value; } - + /*! * \brief Sets a pointer parameter named `name` * @@ -593,7 +597,7 @@ namespace Nz * \param name Name of the parameter * \param value The pointer value * - * \remark This sets a raw pointer, this class takes no responsibility toward it, + * \remark This sets a raw pointer, this class takes no responsibility toward it, if you wish to destroy the pointed variable along with the parameter list, you should set a userdata */ void ParameterList::SetParameter(const String& name, void* value) @@ -603,6 +607,54 @@ namespace Nz parameter.value.ptrVal = value; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "ParameterList(Name: Type(value), ...)" + */ + String ParameterList::ToString() const + { + StringStream ss; + + ss << "ParameterList("; + for (auto it = m_parameters.cbegin(); it != m_parameters.cend();) + { + ss << it->first << ": "; + switch (it->second.type) + { + case ParameterType_Boolean: + ss << "Boolean(" << String::Boolean(it->second.value.boolVal) << ")"; + break; + case ParameterType_Color: + ss << "Color(" << it->second.value.colorVal.ToString() << ")"; + break; + case ParameterType_Float: + ss << "Float(" << it->second.value.floatVal << ")"; + break; + case ParameterType_Integer: + ss << "Integer(" << it->second.value.intVal << ")"; + break; + case ParameterType_String: + ss << "String(" << it->second.value.stringVal << ")"; + break; + case ParameterType_Pointer: + ss << "Pointer(" << String::Pointer(it->second.value.ptrVal) << ")"; + break; + case ParameterType_Userdata: + ss << "Userdata(" << String::Pointer(it->second.value.userdataVal->ptr) << ")"; + break; + case ParameterType_None: + ss << "None"; + break; + } + + if (++it != m_parameters.cend()) + ss << ", "; + } + ss << ")"; + + return ss; + } + /*! * \brief Sets a userdata parameter named `name` * @@ -639,6 +691,7 @@ namespace Nz switch (it->second.type) { case ParameterType_Boolean: + case ParameterType_Color: case ParameterType_Float: case ParameterType_Integer: case ParameterType_Pointer: @@ -719,3 +772,17 @@ namespace Nz } } } + +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param parameterList The ParameterList to output +*/ + +std::ostream& operator<<(std::ostream& out, const Nz::ParameterList& parameterList) +{ + out << parameterList.ToString(); + return out; +} diff --git a/src/Nazara/Core/PluginManager.cpp b/src/Nazara/Core/PluginManager.cpp index 24fbafcfb..b71af6677 100644 --- a/src/Nazara/Core/PluginManager.cpp +++ b/src/Nazara/Core/PluginManager.cpp @@ -19,8 +19,7 @@ namespace Nz String s_pluginFiles[] = { - "NazaraAssimp", // Plugin_Assimp - "NazaraFreetype" // Plugin_FreeType + "PluginAssimp", // Plugin_Assimp }; } @@ -80,7 +79,13 @@ namespace Nz bool PluginManager::Mount(Plugin plugin) { - return Mount(s_pluginFiles[plugin]); + Nz::String pluginName = s_pluginFiles[plugin]; + #ifdef NAZARA_DEBUG + if (Mount(pluginName + "-d", true)) + return true; + #endif + + return Mount(pluginName, true); } /*! diff --git a/src/Nazara/Core/Posix/FileImpl.cpp b/src/Nazara/Core/Posix/FileImpl.cpp index f6e8b3bcc..b9a6b0a31 100644 --- a/src/Nazara/Core/Posix/FileImpl.cpp +++ b/src/Nazara/Core/Posix/FileImpl.cpp @@ -143,7 +143,18 @@ namespace Nz return false; } - mode_t permissions; // TODO : get permission from first file + mode_t permissions; + struct stat sb; + if (fstat(fd1, &sb) == -1) // get permission from first file + { + NazaraWarning("Could not get permissions of source file"); + permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + } + else + { + permissions = sb.st_mode & ~S_IFMT; // S_IFMT: bit mask for the file type bit field -> ~S_IFMT: general permissions + } + int fd2 = open64(targetPath.GetConstBuffer(), O_WRONLY | O_TRUNC, permissions); if (fd2 == -1) { diff --git a/src/Nazara/Core/String.cpp b/src/Nazara/Core/String.cpp index a57d89861..fbd29c97c 100644 --- a/src/Nazara/Core/String.cpp +++ b/src/Nazara/Core/String.cpp @@ -23,6 +23,11 @@ namespace Nz { namespace Detail { + inline bool IsSpace(char32_t character) + { + return character == '\t' || Unicode::GetCategory(character) & Unicode::Category_Separator; + } + // This algorithm is inspired by the documentation of Qt inline std::size_t GetNewSize(std::size_t newSize) { @@ -211,7 +216,7 @@ namespace Nz */ String::String(const std::string& string) : - String(string.c_str(), string.size()) + String(string.data(), string.size()) { } @@ -1379,7 +1384,7 @@ namespace Nz if (it.base() != m_sharedString->string.get()) { --it; - if (!(Unicode::GetCategory(*it++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*it++)) continue; } @@ -1391,7 +1396,7 @@ namespace Nz { if (*p == '\0') { - if (*tIt == '\0' || Unicode::GetCategory(*tIt) & Unicode::Category_Separator) + if (*tIt == '\0' || Detail::IsSpace(*tIt)) return it.base() - m_sharedString->string.get(); else break; @@ -1421,7 +1426,7 @@ namespace Nz if (it.base() != m_sharedString->string.get()) { --it; - if (!(Unicode::GetCategory(*it++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*it++)) continue; } @@ -1433,7 +1438,7 @@ namespace Nz { if (*p == '\0') { - if (*tIt == '\0' || Unicode::GetCategory(*tIt) & Unicode::Category_Separator) + if (*tIt == '\0' || Detail::IsSpace(*tIt)) return it.base() - m_sharedString->string.get(); else break; @@ -1465,7 +1470,7 @@ namespace Nz if (ptr != m_sharedString->string.get()) { --ptr; - if (!(Unicode::GetCategory(*ptr++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*ptr++)) continue; } @@ -1475,7 +1480,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator) + if (*tPtr == '\0' || Detail::IsSpace(*tPtr)) return ptr-m_sharedString->string.get(); else break; @@ -1503,7 +1508,7 @@ namespace Nz if (ptr != m_sharedString->string.get()) { --ptr; - if (!(Unicode::GetCategory(*ptr++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*ptr++)) continue; } @@ -1513,7 +1518,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator) + if (*tPtr == '\0' || Detail::IsSpace(*tPtr)) return ptr-m_sharedString->string.get(); else break; @@ -1579,7 +1584,7 @@ namespace Nz if (it.base() != m_sharedString->string.get()) { --it; - if (!(Unicode::GetCategory(*it++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*it++)) continue; } @@ -1591,7 +1596,7 @@ namespace Nz { if (*p == '\0') { - if (*tIt == '\0' || Unicode::GetCategory(*tIt) & Unicode::Category_Separator) + if (*tIt == '\0' || Detail::IsSpace(*tIt)) return it.base() - m_sharedString->string.get(); else break; @@ -1621,7 +1626,7 @@ namespace Nz if (it.base() != m_sharedString->string.get()) { --it; - if (!(Unicode::GetCategory(*it++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*it++)) continue; } @@ -1633,7 +1638,7 @@ namespace Nz { if (*p == '\0') { - if (*tIt == '\0' || Unicode::GetCategory(*tIt) & Unicode::Category_Separator) + if (*tIt == '\0' || Detail::IsSpace(*tIt)) return it.base() - m_sharedString->string.get(); else break; @@ -1664,7 +1669,7 @@ namespace Nz if (Detail::ToLower(*ptr) == c) { char nextC = *(ptr + 1); - if (nextC != '\0' && (Unicode::GetCategory(nextC) & Unicode::Category_Separator_Space) == 0) + if (nextC != '\0' && (Detail::IsSpace(nextC)) == 0) continue; const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; @@ -1675,7 +1680,7 @@ namespace Nz if (p == &string.m_sharedString->string[0]) { - if (ptr == m_sharedString->string.get() || Unicode::GetCategory(*(ptr-1)) & Unicode::Category_Separator_Space) + if (ptr == m_sharedString->string.get() || Detail::IsSpace(*(ptr-1))) return ptr-m_sharedString->string.get(); else break; @@ -1695,7 +1700,7 @@ namespace Nz if (*ptr == string.m_sharedString->string[string.m_sharedString->size-1]) { char nextC = *(ptr + 1); - if (nextC != '\0' && (Unicode::GetCategory(nextC) & Unicode::Category_Separator_Space) == 0) + if (nextC != '\0' && !Detail::IsSpace(nextC)) continue; const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; @@ -1706,7 +1711,7 @@ namespace Nz if (p == &string.m_sharedString->string[0]) { - if (ptr == m_sharedString->string.get() || Unicode::GetCategory(*(ptr - 1)) & Unicode::Category_Separator_Space) + if (ptr == m_sharedString->string.get() || Detail::IsSpace(*(ptr - 1))) return ptr-m_sharedString->string.get(); else break; @@ -1766,7 +1771,7 @@ namespace Nz if (it.base() != m_sharedString->string.get()) { --it; - if (!(Unicode::GetCategory(*it++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*it++)) continue; } @@ -1778,7 +1783,7 @@ namespace Nz { if (*p == '\0') { - if (*tIt == '\0' || Unicode::GetCategory(*it++) & Unicode::Category_Separator) + if (*tIt == '\0' || Detail::IsSpace(*it++)) return it.base() - m_sharedString->string.get(); else break; @@ -1806,7 +1811,7 @@ namespace Nz if (it.base() != m_sharedString->string.get()) { --it; - if (!(Unicode::GetCategory(*it++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*it++)) continue; } @@ -1818,7 +1823,7 @@ namespace Nz { if (*p == '\0') { - if (*tIt == '\0' || Unicode::GetCategory(*it++) & Unicode::Category_Separator) + if (*tIt == '\0' || Detail::IsSpace(*it++)) return it.base() - m_sharedString->string.get(); else break; @@ -1844,7 +1849,7 @@ namespace Nz { if (Detail::ToLower(*ptr) == c) { - if (ptr != m_sharedString->string.get() && (Unicode::GetCategory(*(ptr - 1)) & Unicode::Category_Separator) == 0) + if (ptr != m_sharedString->string.get() && !Detail::IsSpace(*(ptr - 1))) continue; const char* p = &string[1]; @@ -1853,7 +1858,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator) + if (*tPtr == '\0' || Detail::IsSpace(*tPtr)) return ptr - m_sharedString->string.get(); else break; @@ -1875,7 +1880,7 @@ namespace Nz { if (*ptr == string[0]) { - if (ptr != m_sharedString->string.get() && (Unicode::GetCategory(*(ptr-1)) & Unicode::Category_Separator) == 0) + if (ptr != m_sharedString->string.get() && !Detail::IsSpace(*(ptr-1))) continue; const char* p = &string[1]; @@ -1884,7 +1889,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator) + if (*tPtr == '\0' || Detail::IsSpace(*tPtr)) return ptr - m_sharedString->string.get(); else break; @@ -1947,7 +1952,7 @@ namespace Nz if (it.base() != m_sharedString->string.get()) { --it; - if (!(Unicode::GetCategory(*it++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*it++)) continue; } @@ -1959,7 +1964,7 @@ namespace Nz { if (*p == '\0') { - if (*tIt == '\0' || Unicode::GetCategory(*it++) & Unicode::Category_Separator) + if (*tIt == '\0' || Detail::IsSpace(*it++)) return it.base() - m_sharedString->string.get(); else break; @@ -1987,7 +1992,7 @@ namespace Nz if (it.base() != m_sharedString->string.get()) { --it; - if (!(Unicode::GetCategory(*it++) & Unicode::Category_Separator)) + if (!Detail::IsSpace(*it++)) continue; } @@ -1999,7 +2004,7 @@ namespace Nz { if (*p == '\0') { - if (*tIt == '\0' || Unicode::GetCategory(*it++) & Unicode::Category_Separator) + if (*tIt == '\0' || Detail::IsSpace(*it++)) return it.base() - m_sharedString->string.get(); else break; @@ -2026,7 +2031,7 @@ namespace Nz { if (Detail::ToLower(*ptr) == c) { - if (ptr != m_sharedString->string.get() && (Unicode::GetCategory(*(ptr-1)) & Unicode::Category_Separator_Space) == 0) + if (ptr != m_sharedString->string.get() && !Detail::IsSpace(*(ptr-1))) continue; const char* p = &string.m_sharedString->string[1]; @@ -2035,7 +2040,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator_Space) + if (*tPtr == '\0' || Detail::IsSpace(*tPtr)) return ptr - m_sharedString->string.get(); else break; @@ -2056,7 +2061,7 @@ namespace Nz while ((ptr = std::strstr(ptr, string.GetConstBuffer())) != nullptr) { // If the word is really alone - if ((ptr == m_sharedString->string.get() || Unicode::GetCategory(*(ptr-1)) & Unicode::Category_Separator_Space) && (*(ptr+m_sharedString->size) == '\0' || Unicode::GetCategory(*(ptr+m_sharedString->size)) & Unicode::Category_Separator_Space)) + if ((ptr == m_sharedString->string.get() || Detail::IsSpace(*(ptr-1))) && (*(ptr+m_sharedString->size) == '\0' || Detail::IsSpace(*(ptr+m_sharedString->size)))) return ptr - m_sharedString->string.get(); ptr++; @@ -2219,7 +2224,7 @@ namespace Nz utf8::unchecked::iterator it(ptr); do { - if (Unicode::GetCategory(*it) & Unicode::Category_Separator) + if (Detail::IsSpace(*it)) { endPos = static_cast(it.base() - m_sharedString->string.get() - 1); break; @@ -2231,7 +2236,7 @@ namespace Nz { do { - if (Unicode::GetCategory(*ptr) & Unicode::Category_Separator) + if (Detail::IsSpace(*ptr)) { endPos = static_cast(ptr - m_sharedString->string.get() - 1); break; @@ -2265,7 +2270,7 @@ namespace Nz utf8::unchecked::iterator it(ptr); do { - if (Unicode::GetCategory(*it) & Unicode::Category_Separator) + if (Detail::IsSpace(*it)) inWord = false; else { @@ -2283,7 +2288,7 @@ namespace Nz { do { - if (Unicode::GetCategory(*ptr) & Unicode::Category_Separator) + if (Detail::IsSpace(*ptr)) inWord = false; else { @@ -3414,7 +3419,7 @@ namespace Nz utf8::unchecked::iterator it(ptr); do { - if (Unicode::GetCategory(*it) & Unicode::Category_Separator) + if (Detail::IsSpace(*it)) { if (inword) { @@ -3435,7 +3440,7 @@ namespace Nz const char* limit = &m_sharedString->string[m_sharedString->size]; do { - if (Unicode::GetCategory(*ptr) & Unicode::Category_Separator) + if (Detail::IsSpace(*ptr)) { if (inword) { @@ -4240,7 +4245,7 @@ namespace Nz utf8::unchecked::iterator it(m_sharedString->string.get()); do { - if (*it != '\t' && (Unicode::GetCategory(*it) & Unicode::Category_Separator) == 0) + if (!Detail::IsSpace(*it)) break; } while (*++it); @@ -4255,7 +4260,7 @@ namespace Nz utf8::unchecked::iterator it(&m_sharedString->string[m_sharedString->size]); while ((it--).base() != m_sharedString->string.get()) { - if (*it != '\t' && (Unicode::GetCategory(*it) & Unicode::Category_Separator) == 0) + if (!Detail::IsSpace(*it)) break; } @@ -4271,8 +4276,8 @@ namespace Nz { for (; startPos < m_sharedString->size; ++startPos) { - char c = m_sharedString->string[startPos]; - if (c != '\t' && (Unicode::GetCategory(c) & Unicode::Category_Separator) == 0) + char c = m_sharedString->string[startPos]; + if (!Detail::IsSpace(c)) break; } } @@ -4282,8 +4287,8 @@ namespace Nz { for (; endPos > 0; --endPos) { - char c = m_sharedString->string[endPos]; - if (c != '\t' && (Unicode::GetCategory(c) & Unicode::Category_Separator) == 0) + char c = m_sharedString->string[endPos]; + if (!Detail::IsSpace(c)) break; } } diff --git a/src/Nazara/Core/StringStream.cpp b/src/Nazara/Core/StringStream.cpp index ebd509c65..0b27077e5 100644 --- a/src/Nazara/Core/StringStream.cpp +++ b/src/Nazara/Core/StringStream.cpp @@ -16,7 +16,6 @@ namespace Nz /*! * \brief Constructs a StringStream object by default */ - StringStream::StringStream() : m_bufferSize(0) { @@ -27,18 +26,34 @@ namespace Nz * * \param str First value of the stream */ - StringStream::StringStream(const String& str) : m_bufferSize(str.GetSize()) { m_strings.push_back(str); } + /*! + * \brief Resets the state of the stream, erasing every contained text + */ + void StringStream::Clear() + { + m_bufferSize = 0; + m_strings.clear(); + } + + /*! + * \brief Get the current buffer size + * \return The internal accumulation buffer size, this is equivalent to the size of the final string + */ + std::size_t StringStream::GetBufferSize() const + { + return m_bufferSize; + } + /*! * \brief Gives a string representation * \return A string representation of the object where every objects of the stream has been converted with Nz::String */ - String StringStream::ToString() const { String string; @@ -56,7 +71,6 @@ namespace Nz * * \param boolean Boolean value */ - StringStream& StringStream::operator<<(bool boolean) { m_strings.push_back(String::Boolean(boolean)); @@ -71,7 +85,6 @@ namespace Nz * * \param number Short value */ - StringStream& StringStream::operator<<(short number) { m_strings.push_back(String::Number(number)); @@ -86,7 +99,6 @@ namespace Nz * * \param number Short value */ - StringStream& StringStream::operator<<(unsigned short number) { m_strings.push_back(String::Number(number)); @@ -101,7 +113,6 @@ namespace Nz * * \param number Int value */ - StringStream& StringStream::operator<<(int number) { m_strings.push_back(String::Number(number)); @@ -116,7 +127,6 @@ namespace Nz * * \param number Int value */ - StringStream& StringStream::operator<<(unsigned int number) { m_strings.push_back(String::Number(number)); @@ -131,7 +141,6 @@ namespace Nz * * \param number Long value */ - StringStream& StringStream::operator<<(long number) { m_strings.push_back(String::Number(number)); @@ -146,7 +155,6 @@ namespace Nz * * \param number Long value */ - StringStream& StringStream::operator<<(unsigned long number) { m_strings.push_back(String::Number(number)); @@ -161,7 +169,6 @@ namespace Nz * * \param number Long long value */ - StringStream& StringStream::operator<<(long long number) { m_strings.push_back(String::Number(number)); @@ -176,7 +183,6 @@ namespace Nz * * \param number Long long value */ - StringStream& StringStream::operator<<(unsigned long long number) { m_strings.push_back(String::Number(number)); @@ -191,7 +197,6 @@ namespace Nz * * \param number Float value */ - StringStream& StringStream::operator<<(float number) { m_strings.push_back(String::Number(number)); @@ -206,7 +211,6 @@ namespace Nz * * \param number Double value */ - StringStream& StringStream::operator<<(double number) { m_strings.push_back(String::Number(number)); @@ -221,7 +225,6 @@ namespace Nz * * \param number Long double value */ - StringStream& StringStream::operator<<(long double number) { m_strings.push_back(String::Number(number)); @@ -236,7 +239,6 @@ namespace Nz * * \param character Char value */ - StringStream& StringStream::operator<<(char character) { m_strings.push_back(String(character)); @@ -251,7 +253,6 @@ namespace Nz * * \param character Char value */ - StringStream& StringStream::operator<<(unsigned char character) { m_strings.push_back(String(static_cast(character))); @@ -266,7 +267,6 @@ namespace Nz * * \param string String value */ - StringStream& StringStream::operator<<(const char* string) { m_strings.push_back(string); @@ -281,7 +281,6 @@ namespace Nz * * \param string String value */ - StringStream& StringStream::operator<<(const std::string& string) { m_strings.push_back(string); @@ -296,7 +295,6 @@ namespace Nz * * \param string String value */ - StringStream& StringStream::operator<<(const String& string) { m_strings.push_back(string); @@ -311,7 +309,6 @@ namespace Nz * * \param ptr Pointer value */ - StringStream& StringStream::operator<<(const void* ptr) { m_strings.push_back(String::Pointer(ptr)); @@ -324,7 +321,6 @@ namespace Nz * \brief Converts this to Nz::String * \return The string representation of the stream */ - StringStream::operator String() const { return ToString(); diff --git a/src/Nazara/Core/Win32/FileImpl.cpp b/src/Nazara/Core/Win32/FileImpl.cpp index dcce3d29a..5ed051adb 100644 --- a/src/Nazara/Core/Win32/FileImpl.cpp +++ b/src/Nazara/Core/Win32/FileImpl.cpp @@ -96,7 +96,7 @@ namespace Nz //UInt64 oldCursorPos = GetCursorPos(); DWORD read = 0; - if (ReadFile(m_handle, buffer, size, &read, nullptr)) + if (ReadFile(m_handle, buffer, static_cast(size), &read, nullptr)) { m_endOfFile = (read != size); m_endOfFileUpdated = true; @@ -191,9 +191,9 @@ namespace Nz LARGE_INTEGER cursorPos; cursorPos.QuadPart = GetCursorPos(); - LockFile(m_handle, cursorPos.LowPart, cursorPos.HighPart, size, 0); - WriteFile(m_handle, buffer, size, &written, nullptr); - UnlockFile(m_handle, cursorPos.LowPart, cursorPos.HighPart, size, 0); + LockFile(m_handle, cursorPos.LowPart, cursorPos.HighPart, static_cast(size), 0); + WriteFile(m_handle, buffer, static_cast(size), &written, nullptr); + UnlockFile(m_handle, cursorPos.LowPart, cursorPos.HighPart, static_cast(size), 0); m_endOfFileUpdated = false; diff --git a/src/Nazara/Core/Win32/TaskSchedulerImpl.cpp b/src/Nazara/Core/Win32/TaskSchedulerImpl.cpp index 5b5d7153d..ef83e4b28 100644 --- a/src/Nazara/Core/Win32/TaskSchedulerImpl.cpp +++ b/src/Nazara/Core/Win32/TaskSchedulerImpl.cpp @@ -24,7 +24,7 @@ namespace Nz } #endif - s_workerCount = workerCount; + s_workerCount = static_cast(workerCount); s_doneEvents.reset(new HANDLE[workerCount]); s_workers.reset(new Worker[workerCount]); s_workerThreads.reset(new HANDLE[workerCount]); @@ -64,7 +64,7 @@ namespace Nz // On s'assure que des tâches ne sont pas déjà en cours WaitForMultipleObjects(s_workerCount, &s_doneEvents[0], true, INFINITE); - std::ldiv_t div = std::ldiv(count, s_workerCount); // Division et modulo en une opération, y'a pas de petit profit + std::lldiv_t div = std::lldiv(count, s_workerCount); // Division et modulo en une opération, y'a pas de petit profit for (std::size_t i = 0; i < s_workerCount; ++i) { // On va maintenant répartir les tâches entre chaque worker et les envoyer dans la queue de chacun @@ -78,7 +78,7 @@ namespace Nz } // On les lance une fois qu'ils sont tous initialisés (pour éviter qu'un worker ne passe en pause détectant une absence de travaux) - for (unsigned int i = 0; i < s_workerCount; ++i) + for (std::size_t i = 0; i < s_workerCount; ++i) { ResetEvent(s_doneEvents[i]); SetEvent(s_workers[i].wakeEvent); @@ -244,5 +244,5 @@ namespace Nz std::unique_ptr TaskSchedulerImpl::s_doneEvents; // Doivent être contigus std::unique_ptr TaskSchedulerImpl::s_workers; std::unique_ptr TaskSchedulerImpl::s_workerThreads; // Doivent être contigus - std::size_t TaskSchedulerImpl::s_workerCount; + DWORD TaskSchedulerImpl::s_workerCount; } diff --git a/src/Nazara/Core/Win32/TaskSchedulerImpl.hpp b/src/Nazara/Core/Win32/TaskSchedulerImpl.hpp index 00fdec634..aabd58b86 100644 --- a/src/Nazara/Core/Win32/TaskSchedulerImpl.hpp +++ b/src/Nazara/Core/Win32/TaskSchedulerImpl.hpp @@ -44,7 +44,7 @@ namespace Nz static std::unique_ptr s_doneEvents; // Doivent être contigus static std::unique_ptr s_workers; static std::unique_ptr s_workerThreads; // Doivent être contigus - static std::size_t s_workerCount; + static DWORD s_workerCount; }; } diff --git a/src/Nazara/Graphics/AbstractBackground.cpp b/src/Nazara/Graphics/AbstractBackground.cpp index 628819edd..28d0f0d4f 100644 --- a/src/Nazara/Graphics/AbstractBackground.cpp +++ b/src/Nazara/Graphics/AbstractBackground.cpp @@ -7,6 +7,14 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::AbstractBackground + * \brief Graphics class that represents the background for our scene + * + * \remark This class is abstract + */ + AbstractBackground::~AbstractBackground() = default; BackgroundLibrary::LibraryMap AbstractBackground::s_library; diff --git a/src/Nazara/Graphics/AbstractRenderQueue.cpp b/src/Nazara/Graphics/AbstractRenderQueue.cpp index d00816f85..1346e37ff 100644 --- a/src/Nazara/Graphics/AbstractRenderQueue.cpp +++ b/src/Nazara/Graphics/AbstractRenderQueue.cpp @@ -7,23 +7,55 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::AbstractRenderQueue + * \brief Graphics class that represents the rendering queue for our scene + * + * \remark This class is abstract + */ + AbstractRenderQueue::~AbstractRenderQueue() = default; + /*! + * \brief Adds a directional light to the rendering queue + * + * \param light Directional light + */ + void AbstractRenderQueue::AddDirectionalLight(const DirectionalLight& light) { directionalLights.push_back(light); } + /*! + * \brief Adds a point light to the rendering queue + * + * \param light Point light + */ + void AbstractRenderQueue::AddPointLight(const PointLight& light) { pointLights.push_back(light); } + /*! + * \brief Adds a spot light to the rendering queue + * + * \param light Spot light + */ + void AbstractRenderQueue::AddSpotLight(const SpotLight& light) { spotLights.push_back(light); } + /*! + * \brief Clears the rendering queue + * + * \param fully Should everything be cleared ? + */ + void AbstractRenderQueue::Clear(bool fully) { NazaraUnused(fully); diff --git a/src/Nazara/Graphics/AbstractRenderTechnique.cpp b/src/Nazara/Graphics/AbstractRenderTechnique.cpp index 11d894a00..9872178cf 100644 --- a/src/Nazara/Graphics/AbstractRenderTechnique.cpp +++ b/src/Nazara/Graphics/AbstractRenderTechnique.cpp @@ -10,6 +10,18 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::AbstractRenderTechnique + * \brief Graphics class that represents the rendering technique for our scene + * + * \remark This class is abstract + */ + + /*! + * \brief Constructs a AbstractRenderTechnique object + */ + AbstractRenderTechnique::AbstractRenderTechnique() : m_instancingEnabled(true) { @@ -17,16 +29,34 @@ namespace Nz AbstractRenderTechnique::~AbstractRenderTechnique() = default; + /*! + * \brief Enables the instancing + * + * \param instancing Should instancing be enabled + * + * \remark This may improve performances + */ + void AbstractRenderTechnique::EnableInstancing(bool instancing) { m_instancingEnabled = instancing; } + /*! + * \brief Gets the name of the actual technique + * \return Name of the technique being used + */ + String AbstractRenderTechnique::GetName() const { return RenderTechniques::ToString(GetType()); } + /*! + * \brief Checks whether the instancing is enabled + * \return true If it is the case + */ + bool AbstractRenderTechnique::IsInstancingEnabled() const { return m_instancingEnabled; diff --git a/src/Nazara/Graphics/AbstractViewer.cpp b/src/Nazara/Graphics/AbstractViewer.cpp index cc9a4b96a..bd8752bc7 100644 --- a/src/Nazara/Graphics/AbstractViewer.cpp +++ b/src/Nazara/Graphics/AbstractViewer.cpp @@ -7,5 +7,13 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::AbstractViewer + * \brief Graphics class that represents the viewer for our scene + * + * \remark This class is abstract + */ + AbstractViewer::~AbstractViewer() = default; } diff --git a/src/Nazara/Graphics/Billboard.cpp b/src/Nazara/Graphics/Billboard.cpp index 3e5173c89..17a2ad065 100644 --- a/src/Nazara/Graphics/Billboard.cpp +++ b/src/Nazara/Graphics/Billboard.cpp @@ -12,19 +12,37 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::Billboard + * \brief Graphics class that represents a billboard, a 2D surface which simulates a 3D object + */ + + /*! + * \brief Adds this billboard to the render queue + * + * \param renderQueue Queue to be added + * \param instanceData Data used for instance + */ + void Billboard::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const { if (!m_material) return; - renderQueue->AddBillboard(instanceData.renderOrder, m_material, instanceData.transformMatrix.GetTranslation(), m_size, m_sinCos, m_color); + Nz::Vector3f position = instanceData.transformMatrix->GetTranslation(); + renderQueue->AddBillboards(instanceData.renderOrder, m_material, 1, &position, &m_size, &m_sinCos, &m_color); } + /* + * \brief Makes the bounding volume of this billboard + */ + void Billboard::MakeBoundingVolume() const { constexpr float sqrt2 = float(M_SQRT2); - m_boundingVolume.Set(Vector3f(0.f), sqrt2*m_size.x*Vector3f::Right() + sqrt2*m_size.y*Vector3f::Down()); + m_boundingVolume.Set(Vector3f(0.f), sqrt2 * m_size.x * Vector3f::Right() + sqrt2 * m_size.y * Vector3f::Down()); } BillboardLibrary::LibraryMap Billboard::s_library; diff --git a/src/Nazara/Graphics/ColorBackground.cpp b/src/Nazara/Graphics/ColorBackground.cpp index 76f717858..dcacb1a46 100644 --- a/src/Nazara/Graphics/ColorBackground.cpp +++ b/src/Nazara/Graphics/ColorBackground.cpp @@ -11,19 +11,36 @@ namespace Nz { namespace { + /*! + * \brief Defines render states + * \return RenderStates for the color background + */ + RenderStates BuildRenderStates() { RenderStates states; + states.cullingSide = FaceSide_Back; states.depthFunc = RendererComparison_Equal; - states.faceCulling = FaceSide_Back; - states.parameters[RendererParameter_DepthBuffer] = true; - states.parameters[RendererParameter_DepthWrite] = false; - states.parameters[RendererParameter_FaceCulling] = true; + states.depthBuffer = true; + states.depthWrite = false; + states.faceCulling = true; return states; } } + /*! + * \ingroup graphics + * \class Nz::ColorBackground + * \brief Graphics class that represents a background with uniform color + */ + + /*! + * \brief Constructs a ColorBackground object with a color + * + * \param color Uniform color (by default Black) + */ + ColorBackground::ColorBackground(const Color& color) : m_color(color) { @@ -38,6 +55,12 @@ namespace Nz m_vertexDepthUniform = shader->GetUniformLocation("VertexDepth"); } + /*! + * \brief Draws this relatively to the viewer + * + * \param viewer Viewer for the background + */ + void ColorBackground::Draw(const AbstractViewer* viewer) const { NazaraUnused(viewer); @@ -55,16 +78,32 @@ namespace Nz Renderer::DrawFullscreenQuad(); } + /*! + * \brief Gets the background type + * \return Type of background + */ + BackgroundType ColorBackground::GetBackgroundType() const { return BackgroundType_Color; } + /*! + * \brief Gets the color of the background + * \return Background color + */ + Color ColorBackground::GetColor() const { return m_color; } + /*! + * \brief Sets the color of the background + * + * \param color Background color + */ + void ColorBackground::SetColor(const Color& color) { m_color = color; diff --git a/src/Nazara/Graphics/DeferredBloomPass.cpp b/src/Nazara/Graphics/DeferredBloomPass.cpp index 0fc80e750..9cf39831a 100644 --- a/src/Nazara/Graphics/DeferredBloomPass.cpp +++ b/src/Nazara/Graphics/DeferredBloomPass.cpp @@ -9,6 +9,16 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::DeferredBloomPass + * \brief Graphics class that represents the pass for bloom in deferred rendering + */ + + /*! + * \brief Constructs a DeferredBloomPass object by default + */ + DeferredBloomPass::DeferredBloomPass() : m_uniformUpdated(false), m_brightLuminance(0.8f), @@ -22,7 +32,7 @@ namespace Nz m_bloomBrightShader = ShaderLibrary::Get("DeferredBloomBright"); m_bloomFinalShader = ShaderLibrary::Get("DeferredBloomFinal"); - m_bloomStates.parameters[RendererParameter_DepthBuffer] = false; + m_bloomStates.depthBuffer = false; m_gaussianBlurShader = ShaderLibrary::Get("DeferredGaussianBlur"); m_gaussianBlurShaderFilterLocation = m_gaussianBlurShader->GetUniformLocation("Filter"); @@ -32,26 +42,55 @@ namespace Nz DeferredBloomPass::~DeferredBloomPass() = default; + /*! + * \brief Gets the number of pass for blur + * \return Number of pass for blur + */ + unsigned int DeferredBloomPass::GetBlurPassCount() const { return m_blurPassCount; } + /*! + * \brief Gets the coefficiant for luminosity + * \return Luminosity of bright elements + */ + float DeferredBloomPass::GetBrightLuminance() const { return m_brightLuminance; } + /*! + * \brief Gets the coefficiant for the middle grey + * \return Luminosity of grey elements + */ + float DeferredBloomPass::GetBrightMiddleGrey() const { return m_brightMiddleGrey; } + /*! + * \brief Gets the coefficiant for things to be bright + * \return Threshold for bright elements + */ + float DeferredBloomPass::GetBrightThreshold() const { return m_brightThreshold; } + /*! + * \brief Gets the ith texture + * \return Texture computed + * + * \param i Index of the texture + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if index is invalid + */ + Texture* DeferredBloomPass::GetTexture(unsigned int i) const { #if NAZARA_GRAPHICS_SAFE @@ -65,7 +104,16 @@ namespace Nz return m_bloomTextures[i]; } - bool DeferredBloomPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const + /*! + * \brief Processes the work on the data while working with textures + * \return true + * + * \param sceneData Data for the scene + * \param firstWorkTexture Index of the first texture to work with + * \param firstWorkTexture Index of the second texture to work with + */ + + bool DeferredBloomPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const { NazaraUnused(sceneData); @@ -91,7 +139,7 @@ namespace Nz Renderer::DrawFullscreenQuad(); Renderer::SetTarget(&m_bloomRTT); - Renderer::SetViewport(Recti(0, 0, m_dimensions.x/8, m_dimensions.y/8)); + Renderer::SetViewport(Recti(0, 0, m_dimensions.x / 8, m_dimensions.y / 8)); Renderer::SetShader(m_gaussianBlurShader); @@ -124,6 +172,13 @@ namespace Nz return true; } + /*! + * \brief Resizes the texture sizes + * \return true If successful + * + * \param dimensions Dimensions for the compute texture + */ + bool DeferredBloomPass::Resize(const Vector2ui& dimensions) { DeferredRenderPass::Resize(dimensions); @@ -131,7 +186,7 @@ namespace Nz m_bloomRTT.Create(true); for (unsigned int i = 0; i < 2; ++i) { - m_bloomTextures[i]->Create(ImageType_2D, PixelFormatType_RGBA8, dimensions.x/8, dimensions.y/8); + m_bloomTextures[i]->Create(ImageType_2D, PixelFormatType_RGBA8, dimensions.x / 8, dimensions.y / 8); m_bloomRTT.AttachTexture(AttachmentPoint_Color, i, m_bloomTextures[i]); } m_bloomRTT.Unlock(); @@ -145,23 +200,47 @@ namespace Nz return true; } + /*! + * \brief Sets the number of pass for blur + * + * \param passCount Number of pass for blur + */ + void DeferredBloomPass::SetBlurPassCount(unsigned int passCount) { m_blurPassCount = passCount; // N'est pas une uniforme } + /*! + * \brief Sets the coefficiant for luminosity + * + * \param luminance Luminosity of bright elements + */ + void DeferredBloomPass::SetBrightLuminance(float luminance) { m_brightLuminance = luminance; m_uniformUpdated = false; } + /*! + * \brief Sets the coefficiant for the middle grey + * + * \param middleGrey Luminosity of grey elements + */ + void DeferredBloomPass::SetBrightMiddleGrey(float middleGrey) { m_brightMiddleGrey = middleGrey; m_uniformUpdated = false; } + /*! + * \brief Sets the coefficiant for things to be bright + * + * \param threshold Threshold for bright elements + */ + void DeferredBloomPass::SetBrightThreshold(float threshold) { m_brightThreshold = threshold; diff --git a/src/Nazara/Graphics/DeferredDOFPass.cpp b/src/Nazara/Graphics/DeferredDOFPass.cpp index f29529749..b39085400 100644 --- a/src/Nazara/Graphics/DeferredDOFPass.cpp +++ b/src/Nazara/Graphics/DeferredDOFPass.cpp @@ -13,6 +13,10 @@ namespace Nz { namespace { + /*! + * \brief Builds the shader for the depth of field + * \return Reference to the shader newly created + */ // http://digitalerr0r.wordpress.com/2009/05/16/xna-shader-programming-tutorial-20-depth-of-field/ ShaderRef BuildDepthOfFieldShader() { @@ -92,6 +96,16 @@ namespace Nz } } + /*! + * \ingroup graphics + * \class Nz::DeferredDOFPass + * \brief Graphics class that represents the pass for depth of field in deferred rendering + */ + + /*! + * \brief Constructs a DeferredDOFPass object by default + */ + DeferredDOFPass::DeferredDOFPass() { m_dofShader = BuildDepthOfFieldShader(); @@ -113,12 +127,21 @@ namespace Nz m_pointSampler.SetFilterMode(SamplerFilter_Nearest); m_pointSampler.SetWrapMode(SamplerWrap_Clamp); - m_states.parameters[RendererParameter_DepthBuffer] = false; + m_states.depthBuffer = false; } DeferredDOFPass::~DeferredDOFPass() = default; - bool DeferredDOFPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const + /*! + * \brief Processes the work on the data while working with textures + * \return true + * + * \param sceneData Data for the scene + * \param firstWorkTexture Index of the first texture to work with + * \param firstWorkTexture Index of the second texture to work with + */ + + bool DeferredDOFPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const { NazaraUnused(sceneData); @@ -162,6 +185,13 @@ namespace Nz return true; } + /*! + * \brief Resizes the texture sizes + * \return true If successful + * + * \param dimensions Dimensions for the compute texture + */ + bool DeferredDOFPass::Resize(const Vector2ui& dimensions) { DeferredRenderPass::Resize(dimensions); @@ -181,5 +211,5 @@ namespace Nz } return true; -} + } } diff --git a/src/Nazara/Graphics/DeferredFXAAPass.cpp b/src/Nazara/Graphics/DeferredFXAAPass.cpp index 3a300392a..d4ba72ddf 100644 --- a/src/Nazara/Graphics/DeferredFXAAPass.cpp +++ b/src/Nazara/Graphics/DeferredFXAAPass.cpp @@ -10,6 +10,16 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::DeferredFXAAPass + * \brief Graphics class that represents the pass for FXAA in deferred rendering + */ + + /*! + * \brief Constructs a DeferredFXAAPass object by default + */ + DeferredFXAAPass::DeferredFXAAPass() { m_fxaaShader = ShaderLibrary::Get("DeferredFXAA"); @@ -18,12 +28,21 @@ namespace Nz m_pointSampler.SetFilterMode(SamplerFilter_Nearest); m_pointSampler.SetWrapMode(SamplerWrap_Clamp); - m_states.parameters[RendererParameter_DepthBuffer] = false; + m_states.depthBuffer = false; } DeferredFXAAPass::~DeferredFXAAPass() = default; - bool DeferredFXAAPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const + /*! + * \brief Processes the work on the data while working with textures + * \return true + * + * \param sceneData Data for the scene + * \param firstWorkTexture Index of the first texture to work with + * \param firstWorkTexture Index of the second texture to work with + */ + + bool DeferredFXAAPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const { NazaraUnused(sceneData); diff --git a/src/Nazara/Graphics/DeferredFinalPass.cpp b/src/Nazara/Graphics/DeferredFinalPass.cpp index 6ca1e72c4..c4ab348eb 100644 --- a/src/Nazara/Graphics/DeferredFinalPass.cpp +++ b/src/Nazara/Graphics/DeferredFinalPass.cpp @@ -10,13 +10,23 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::DeferredFinalPass + * \brief Graphics class that represents the final pass in deferred rendering + */ + + /*! + * \brief Constructs a DeferredFinalPass object by default + */ + DeferredFinalPass::DeferredFinalPass() { m_pointSampler.SetAnisotropyLevel(1); m_pointSampler.SetFilterMode(SamplerFilter_Nearest); m_pointSampler.SetWrapMode(SamplerWrap_Clamp); - m_states.parameters[RendererParameter_DepthBuffer] = false; + m_states.depthBuffer = false; m_uberShader = UberShaderLibrary::Get("Basic"); @@ -34,7 +44,16 @@ namespace Nz DeferredFinalPass::~DeferredFinalPass() = default; - bool DeferredFinalPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const + /*! + * \brief Processes the work on the data while working with textures + * \return true + * + * \param sceneData Data for the scene + * \param firstWorkTexture Index of the first texture to work with + * \param firstWorkTexture Index of the second texture to work with + */ + + bool DeferredFinalPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); diff --git a/src/Nazara/Graphics/DeferredFogPass.cpp b/src/Nazara/Graphics/DeferredFogPass.cpp index af00a7c1d..f47a4ea56 100644 --- a/src/Nazara/Graphics/DeferredFogPass.cpp +++ b/src/Nazara/Graphics/DeferredFogPass.cpp @@ -13,6 +13,11 @@ namespace Nz { namespace { + /*! + * \brief Builds the shader for the fog + * \return Reference to the shader newly created + */ + ShaderRef BuildFogShader() { /*const UInt8 fragmentSource[] = { @@ -117,6 +122,16 @@ namespace Nz } } + /*! + * \ingroup graphics + * \class Nz::DeferredFogPass + * \brief Graphics class that represents the pass for fog in deferred rendering + */ + + /*! + * \brief Constructs a DeferredFogPass object by default + */ + DeferredFogPass::DeferredFogPass() { m_pointSampler.SetAnisotropyLevel(1); @@ -126,12 +141,21 @@ namespace Nz m_shader = BuildFogShader(); m_shaderEyePositionLocation = m_shader->GetUniformLocation("EyePosition"); - m_states.parameters[RendererParameter_DepthBuffer] = false; + m_states.depthBuffer = false; } DeferredFogPass::~DeferredFogPass() = default; - bool DeferredFogPass::Process( const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const + /*! + * \brief Processes the work on the data while working with textures + * \return true + * + * \param sceneData Data for the scene + * \param firstWorkTexture Index of the first texture to work with + * \param firstWorkTexture Index of the second texture to work with + */ + + bool DeferredFogPass::Process( const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); diff --git a/src/Nazara/Graphics/DeferredForwardPass.cpp b/src/Nazara/Graphics/DeferredForwardPass.cpp index 35daabae8..ed7ad45d1 100644 --- a/src/Nazara/Graphics/DeferredForwardPass.cpp +++ b/src/Nazara/Graphics/DeferredForwardPass.cpp @@ -13,9 +13,21 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::DeferredForwardPass + * \brief Graphics class that represents the forward pass in deferred rendering + */ + DeferredForwardPass::DeferredForwardPass() = default; DeferredForwardPass::~DeferredForwardPass() = default; + /*! + * \brief Initializes the deferred forward pass which needs the forward technique + * + * \param technique Rendering technique + */ + void DeferredForwardPass::Initialize(DeferredRenderTechnique* technique) { DeferredRenderPass::Initialize(technique); @@ -23,7 +35,16 @@ namespace Nz m_forwardTechnique = technique->GetForwardTechnique(); } - bool DeferredForwardPass::Process(const SceneData& sceneData, unsigned int workTexture, unsigned sceneTexture) const + /*! + * \brief Processes the work on the data while working with textures + * \return true + * + * \param sceneData Data for the scene + * \param firstWorkTexture Index of the first texture to work with + * \param firstWorkTexture Index of the second texture to work with + */ + + bool DeferredForwardPass::Process(const SceneData& sceneData, unsigned int workTexture, unsigned int sceneTexture) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); NazaraUnused(workTexture); diff --git a/src/Nazara/Graphics/DeferredGeometryPass.cpp b/src/Nazara/Graphics/DeferredGeometryPass.cpp index 2ae39ed9b..3cdcb99fe 100644 --- a/src/Nazara/Graphics/DeferredGeometryPass.cpp +++ b/src/Nazara/Graphics/DeferredGeometryPass.cpp @@ -18,20 +18,39 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::DeferredGeometryPass + * \brief Graphics class that represents the pass for geometries in deferred rendering + */ + + /*! + * \brief Constructs a DeferredGeometryPass object by default + */ + DeferredGeometryPass::DeferredGeometryPass() { m_clearShader = ShaderLibrary::Get("DeferredGBufferClear"); - m_clearStates.parameters[RendererParameter_DepthBuffer] = true; - m_clearStates.parameters[RendererParameter_FaceCulling] = true; - m_clearStates.parameters[RendererParameter_StencilTest] = true; + m_clearStates.depthBuffer = true; + m_clearStates.faceCulling = true; + m_clearStates.stencilTest = true; m_clearStates.depthFunc = RendererComparison_Always; - m_clearStates.frontFace.stencilCompare = RendererComparison_Always; - m_clearStates.frontFace.stencilPass = StencilOperation_Zero; + m_clearStates.stencilCompare.front = RendererComparison_Always; + m_clearStates.stencilPass.front = StencilOperation_Zero; } DeferredGeometryPass::~DeferredGeometryPass() = default; - bool DeferredGeometryPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const + /*! + * \brief Processes the work on the data while working with textures + * \return false + * + * \param sceneData Data for the scene + * \param firstWorkTexture Index of the first texture to work with + * \param firstWorkTexture Index of the second texture to work with + */ + + bool DeferredGeometryPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned int secondWorkTexture) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); NazaraUnused(firstWorkTexture); @@ -54,129 +73,146 @@ namespace Nz const Shader* lastShader = nullptr; const ShaderUniforms* shaderUniforms = nullptr; - for (auto& pair : m_renderQueue->layers) + for (auto& layerPair : m_renderQueue->layers) { - DeferredRenderQueue::Layer& layer = pair.second; - - for (auto& matIt : layer.opaqueModels) + for (auto& pipelinePair : layerPair.second.opaqueModels) { - auto& matEntry = matIt.second; + const MaterialPipeline* pipeline = pipelinePair.first; + auto& pipelineEntry = pipelinePair.second; - if (matEntry.enabled) + if (pipelineEntry.maxInstanceCount > 0) { - DeferredRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; + bool instancing = (pipelineEntry.maxInstanceCount > NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT); - if (!meshInstances.empty()) + UInt32 flags = ShaderFlags_Deferred; + if (instancing) + flags |= ShaderFlags_Instancing; + + const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(flags); + + const Shader* shader = pipelineInstance.uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change + if (shader != lastShader) { - const Material* material = matIt.first; + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); - bool useInstancing = instancingEnabled && matEntry.instancingEnabled; + // Ambiant color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - // On commence par récupérer le programme du matériau - UInt32 flags = ShaderFlags_Deferred; - if (useInstancing) - flags |= ShaderFlags_Instancing; + lastShader = shader; + } - const Shader* shader = material->Apply(flags); + for (auto& materialPair : pipelineEntry.materialMap) + { + const Material* material = materialPair.first; + auto& matEntry = materialPair.second; - // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas - if (shader != lastShader) + if (matEntry.enabled) { - // Index des uniformes dans le shader - shaderUniforms = GetShaderUniforms(shader); + DeferredRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; - // Couleur ambiante de la scène - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position de la caméra - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - - lastShader = shader; - } - - // Meshes - for (auto& meshIt : meshInstances) - { - const MeshData& meshData = meshIt.first; - auto& meshEntry = meshIt.second; - - std::vector& instances = meshEntry.instances; - if (!instances.empty()) + if (!meshInstances.empty()) { - const IndexBuffer* indexBuffer = meshData.indexBuffer; - const VertexBuffer* vertexBuffer = meshData.vertexBuffer; + material->Apply(pipelineInstance); - // Gestion du draw call avant la boucle de rendu - Renderer::DrawCall drawFunc; - Renderer::DrawCallInstanced instancedDrawFunc; - unsigned int indexCount; - - if (indexBuffer) + // Meshes + for (auto& meshIt : meshInstances) { - drawFunc = Renderer::DrawIndexedPrimitives; - instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; - indexCount = indexBuffer->GetIndexCount(); - } - else - { - drawFunc = Renderer::DrawPrimitives; - instancedDrawFunc = Renderer::DrawPrimitivesInstanced; - indexCount = vertexBuffer->GetVertexCount(); - } + const MeshData& meshData = meshIt.first; + auto& meshEntry = meshIt.second; - Renderer::SetIndexBuffer(indexBuffer); - Renderer::SetVertexBuffer(vertexBuffer); - - if (useInstancing) - { - // On récupère le buffer d'instancing du Renderer et on le configure pour fonctionner avec des matrices - VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); - instanceBuffer->SetVertexDeclaration(VertexDeclaration::Get(VertexLayout_Matrix4)); - - const Matrix4f* instanceMatrices = &instances[0]; - unsigned int instanceCount = instances.size(); - unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Le nombre de matrices que peut contenir le buffer - - while (instanceCount > 0) + std::vector& instances = meshEntry.instances; + if (!instances.empty()) { - // On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing) - unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); - instanceCount -= renderedInstanceCount; + const IndexBuffer* indexBuffer = meshData.indexBuffer; + const VertexBuffer* vertexBuffer = meshData.vertexBuffer; - // On remplit l'instancing buffer avec nos matrices world - instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true); - instanceMatrices += renderedInstanceCount; + // Handle draw call before rendering loop + Renderer::DrawCall drawFunc; + Renderer::DrawCallInstanced instancedDrawFunc; + unsigned int indexCount; - // Et on affiche - instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); + if (indexBuffer) + { + drawFunc = Renderer::DrawIndexedPrimitives; + instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; + indexCount = indexBuffer->GetIndexCount(); + } + else + { + drawFunc = Renderer::DrawPrimitives; + instancedDrawFunc = Renderer::DrawPrimitivesInstanced; + indexCount = vertexBuffer->GetVertexCount(); + } + + Renderer::SetIndexBuffer(indexBuffer); + Renderer::SetVertexBuffer(vertexBuffer); + + if (instancing) + { + // We get the buffer for instance of Renderer and we configure it to work with matrices + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(VertexDeclaration::Get(VertexLayout_Matrix4)); + + const Matrix4f* instanceMatrices = &instances[0]; + unsigned int instanceCount = instances.size(); + unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // The number of matrices that can be hold in the buffer + + while (instanceCount > 0) + { + // We compute the number of instances that we will be able to show this time (Depending on the instance buffer size) + unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); + instanceCount -= renderedInstanceCount; + + // We fill the instancing buffer with our world matrices + instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true); + instanceMatrices += renderedInstanceCount; + + // And we show + instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); + } + } + else + { + // Without instancing, we must do one draw call for each instance + // This may be faster than instancing under a threshold + // Due to the time to modify the instancing buffer + for (const Matrix4f& matrix : instances) + { + Renderer::SetMatrix(MatrixType_World, matrix); + drawFunc(meshData.primitiveMode, 0, indexCount); + } + } + + instances.clear(); } } - else - { - // Sans instancing, on doit effectuer un draw call pour chaque instance - // Cela reste néanmoins plus rapide que l'instancing en dessous d'un certain nombre d'instances - // À cause du temps de modification du buffer d'instancing - for (const Matrix4f& matrix : instances) - { - Renderer::SetMatrix(MatrixType_World, matrix); - drawFunc(meshData.primitiveMode, 0, indexCount); - } - } - - instances.clear(); } + + // And we set it back data to zero + matEntry.enabled = false; } } - // Et on remet à zéro les données - matEntry.enabled = false; - matEntry.instancingEnabled = false; + pipelineEntry.maxInstanceCount = 0; } } } - return false; // On ne fait que remplir le G-Buffer, les work texture ne sont pas affectées + return false; // We only fill the G-Buffer, the work texture are unchanged } + /*! + * \brief Resizes the texture sizes + * \return true If successful + * + * \param dimensions Dimensions for the compute texture + */ + bool DeferredGeometryPass::Resize(const Vector2ui& dimensions) { DeferredRenderPass::Resize(dimensions); @@ -241,6 +277,13 @@ namespace Nz } } + /*! + * \brief Gets the uniforms of a shader + * \return Uniforms of the shader + * + * \param shader Shader to get uniforms from + */ + const DeferredGeometryPass::ShaderUniforms* DeferredGeometryPass::GetShaderUniforms(const Shader* shader) const { auto it = m_shaderUniforms.find(shader); @@ -260,6 +303,12 @@ namespace Nz return &it->second; } + /*! + * \brief Handle the invalidation of a shader + * + * \param shader Shader being invalidated + */ + void DeferredGeometryPass::OnShaderInvalidated(const Shader* shader) const { m_shaderUniforms.erase(shader); diff --git a/src/Nazara/Graphics/DeferredPhongLightingPass.cpp b/src/Nazara/Graphics/DeferredPhongLightingPass.cpp index c340d89a2..afe17ed13 100644 --- a/src/Nazara/Graphics/DeferredPhongLightingPass.cpp +++ b/src/Nazara/Graphics/DeferredPhongLightingPass.cpp @@ -13,6 +13,16 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::DeferredPhongLightingPass + * \brief Graphics class that represents the pass for phong lighting in deferred rendering + */ + + /*! + * \brief Constructs a DeferredPhongLightingPass object by default + */ + DeferredPhongLightingPass::DeferredPhongLightingPass() : m_lightMeshesDrawing(false) { @@ -21,7 +31,7 @@ namespace Nz m_directionalLightShaderSceneAmbientLocation = m_directionalLightShader->GetUniformLocation("SceneAmbient"); m_directionalLightUniforms.ubo = false; - m_directionalLightUniforms.locations.type = -1; // Type déjà connu + m_directionalLightUniforms.locations.type = -1; // Type already known m_directionalLightUniforms.locations.color = m_directionalLightShader->GetUniformLocation("LightColor"); m_directionalLightUniforms.locations.factors = m_directionalLightShader->GetUniformLocation("LightFactors"); m_directionalLightUniforms.locations.parameters1 = m_directionalLightShader->GetUniformLocation("LightDirection"); @@ -56,16 +66,36 @@ namespace Nz DeferredPhongLightingPass::~DeferredPhongLightingPass() = default; + /*! + * \brief Enables the drawing of meshes with light + * + * \param enable Should meshes with light parameter be drawed + */ + void DeferredPhongLightingPass::EnableLightMeshesDrawing(bool enable) { m_lightMeshesDrawing = enable; } + /*! + * \brief Checks whether the drawing of meshes with light is enabled + * \return true If it is the case + */ + bool DeferredPhongLightingPass::IsLightMeshesDrawingEnabled() const { return m_lightMeshesDrawing; } + /*! + * \brief Processes the work on the data while working with textures + * \return true + * + * \param sceneData Data for the scene + * \param firstWorkTexture Index of the first texture to work with + * \param firstWorkTexture Index of the second texture to work with + */ + bool DeferredPhongLightingPass::Process(const SceneData& sceneData, unsigned int firstWorkTexture, unsigned secondWorkTexture) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); @@ -90,9 +120,9 @@ namespace Nz RenderStates lightStates; lightStates.dstBlend = BlendFunc_One; lightStates.srcBlend = BlendFunc_One; - lightStates.parameters[RendererParameter_Blend] = true; - lightStates.parameters[RendererParameter_DepthBuffer] = false; - lightStates.parameters[RendererParameter_DepthWrite] = false; + lightStates.blending = true; + lightStates.depthBuffer = false; + lightStates.depthWrite = false; // Directional lights if (!m_renderQueue->directionalLights.empty()) @@ -116,18 +146,18 @@ namespace Nz if (!m_renderQueue->pointLights.empty() || !m_renderQueue->spotLights.empty()) { // http://www.altdevblogaday.com/2011/08/08/stencil-buffer-optimisation-for-deferred-lights/ - lightStates.parameters[RendererParameter_StencilTest] = true; - lightStates.faceCulling = FaceSide_Front; - lightStates.backFace.stencilMask = 0xFF; - lightStates.backFace.stencilReference = 0; - lightStates.backFace.stencilFail = StencilOperation_Keep; - lightStates.backFace.stencilPass = StencilOperation_Keep; - lightStates.backFace.stencilZFail = StencilOperation_Invert; - lightStates.frontFace.stencilMask = 0xFF; - lightStates.frontFace.stencilReference = 0; - lightStates.frontFace.stencilFail = StencilOperation_Keep; - lightStates.frontFace.stencilPass = StencilOperation_Keep; - lightStates.frontFace.stencilZFail = StencilOperation_Invert; + lightStates.cullingSide = FaceSide_Front; + lightStates.stencilTest = true; + lightStates.stencilDepthFail.back = StencilOperation_Invert; + lightStates.stencilDepthFail.front = StencilOperation_Invert; + lightStates.stencilFail.back = StencilOperation_Keep; + lightStates.stencilFail.front = StencilOperation_Keep; + lightStates.stencilPass.back = StencilOperation_Keep; + lightStates.stencilPass.front = StencilOperation_Keep; + lightStates.stencilReference.back = 0; + lightStates.stencilReference.front = 0; + lightStates.stencilWriteMask.back = 0xFF; + lightStates.stencilWriteMask.front = 0xFF; Renderer::SetRenderStates(lightStates); @@ -151,12 +181,12 @@ namespace Nz m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters1, Vector4f(light.position, light.attenuation)); m_pointSpotLightShader->SendVector(m_pointSpotLightUniforms.locations.parameters2, Vector4f(0.f, 0.f, 0.f, light.invRadius)); - lightMatrix.SetScale(Vector3f(light.radius * 1.1f)); // Pour corriger les imperfections liées à la sphère + lightMatrix.SetScale(Vector3f(light.radius * 1.1f)); // To correct imperfections due to the sphere lightMatrix.SetTranslation(light.position); Renderer::SetMatrix(MatrixType_World, lightMatrix); - // Rendu de la sphère dans le stencil buffer + // Sphere rendering in the stencil buffer Renderer::Enable(RendererParameter_ColorWrite, false); Renderer::Enable(RendererParameter_DepthBuffer, true); Renderer::Enable(RendererParameter_FaceCulling, false); @@ -166,7 +196,7 @@ namespace Nz Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount()); - // Rendu de la sphère comme zone d'effet + // Sphere rendering as effect zone Renderer::Enable(RendererParameter_ColorWrite, true); Renderer::Enable(RendererParameter_DepthBuffer, false); Renderer::Enable(RendererParameter_FaceCulling, true); @@ -192,7 +222,7 @@ namespace Nz Renderer::SetShader(shader); for (const auto& light : m_renderQueue->pointLights) { - lightMatrix.SetScale(Vector3f(light.radius * 1.1f)); // Pour corriger les imperfections liées à la sphère + lightMatrix.SetScale(Vector3f(light.radius * 1.1f)); // To correct imperfections due to the sphere lightMatrix.SetTranslation(light.position); Renderer::SetMatrix(MatrixType_World, lightMatrix); @@ -230,7 +260,7 @@ namespace Nz Renderer::SetMatrix(MatrixType_World, lightMatrix); - // Rendu de la sphère dans le stencil buffer + // Sphere rendering in the stencil buffer Renderer::Enable(RendererParameter_ColorWrite, false); Renderer::Enable(RendererParameter_DepthBuffer, true); Renderer::Enable(RendererParameter_FaceCulling, false); @@ -240,7 +270,7 @@ namespace Nz Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, indexBuffer->GetIndexCount()); - // Rendu de la sphère comme zone d'effet + // Sphere rendering as effect zone Renderer::Enable(RendererParameter_ColorWrite, true); Renderer::Enable(RendererParameter_DepthBuffer, false); Renderer::Enable(RendererParameter_FaceCulling, true); diff --git a/src/Nazara/Graphics/DeferredRenderPass.cpp b/src/Nazara/Graphics/DeferredRenderPass.cpp index 8d688c871..da40c01da 100644 --- a/src/Nazara/Graphics/DeferredRenderPass.cpp +++ b/src/Nazara/Graphics/DeferredRenderPass.cpp @@ -9,6 +9,16 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::DeferredRenderPass + * \brief Graphics class that represents the pass for rendering in deferred rendering + */ + + /*! + * \brief Constructs a DeferredRenderPass object by default + */ + DeferredRenderPass::DeferredRenderPass() : m_enabled(true) { @@ -16,11 +26,23 @@ namespace Nz DeferredRenderPass::~DeferredRenderPass() = default; + /*! + * \brief Enables the deferred rendering + * + * \param enable Should deferred rendering be activated + */ + void DeferredRenderPass::Enable(bool enable) { m_enabled = enable; } + /*! + * \brief Initializes the deferred forward pass which needs the deferred technique + * + * \param technique Rendering technique + */ + void DeferredRenderPass::Initialize(DeferredRenderTechnique* technique) { m_deferredTechnique = technique; @@ -37,11 +59,23 @@ namespace Nz m_workTextures[i] = technique->GetWorkTexture(i); } + /*! + * \brief Checks whether the deferred rendering is enabled + * \return true If it the case + */ + bool DeferredRenderPass::IsEnabled() const { return m_enabled; } + /*! + * \brief Resizes the texture sizes + * \return true If successful + * + * \param dimensions Dimensions for the compute texture + */ + bool DeferredRenderPass::Resize(const Vector2ui& dimensions) { m_dimensions = dimensions; diff --git a/src/Nazara/Graphics/DeferredRenderQueue.cpp b/src/Nazara/Graphics/DeferredRenderQueue.cpp index 5bd0931e3..8623904a6 100644 --- a/src/Nazara/Graphics/DeferredRenderQueue.cpp +++ b/src/Nazara/Graphics/DeferredRenderQueue.cpp @@ -8,88 +8,222 @@ #include #include -///TODO: Rendre les billboards via Deferred Shading si possible +///TODO: Render billboards using Deferred Shading if possible namespace Nz { + /*! + * \ingroup graphics + * \class Nz::DeferredRenderQueue + * \brief Graphics class that represents the rendering queue for deferred rendering + */ + + /*! + * \brief Constructs a DeferredRenderQueue object with the rendering queue of forward rendering + * + * \param forwardQueue Queue of data to render + */ + DeferredRenderQueue::DeferredRenderQueue(ForwardRenderQueue* forwardQueue) : m_forwardQueue(forwardQueue) { } - void DeferredRenderQueue::AddBillboard(int renderOrder, const Material* material, const Vector3f& position, const Vector2f& size, const Vector2f& sinCos, const Color& color) - { - m_forwardQueue->AddBillboard(renderOrder, material, position, size, sinCos, color); - } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + */ void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) { m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + */ + void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) { m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + */ + void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) { m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, anglePtr, colorPtr); } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + */ + void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) { m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, anglePtr, alphaPtr); } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + */ + void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) { m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + */ + void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) { m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + */ + void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) { m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, anglePtr, colorPtr); } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + */ + void DeferredRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) { m_forwardQueue->AddBillboards(renderOrder, material, count, positionPtr, sizePtr, anglePtr, alphaPtr); } + /*! + * \brief Adds drawable to the queue + * + * \param renderOrder Order of rendering + * \param drawable Drawable user defined + * + * \remark Produces a NazaraError if drawable is invalid + */ + void DeferredRenderQueue::AddDrawable(int renderOrder, const Drawable* drawable) { m_forwardQueue->AddDrawable(renderOrder, drawable); } + /*! + * \brief Adds mesh to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the mesh + * \param meshData Data of the mesh + * \param meshAABB Box of the mesh + * \param transformMatrix Matrix of the mesh + */ + void DeferredRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) { - if (material->IsEnabled(RendererParameter_Blend)) - // Un matériau transparent ? J'aime pas, va voir dans la forward queue si j'y suis + if (material->IsBlendingEnabled()) + // One transparent material ? I don't like it, go see if I'm in the forward queue m_forwardQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix); else { Layer& currentLayer = GetLayer(renderOrder); - auto& opaqueModels = currentLayer.opaqueModels; + MeshPipelineBatches& opaqueModels = currentLayer.opaqueModels; - auto it = opaqueModels.find(material); - if (it == opaqueModels.end()) + const MaterialPipeline* materialPipeline = material->GetPipeline(); + + auto pipelineIt = opaqueModels.find(materialPipeline); + if (pipelineIt == opaqueModels.end()) + { + BatchedMaterialEntry materialEntry; + pipelineIt = opaqueModels.insert(MeshPipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; + } + + BatchedMaterialEntry& materialEntry = pipelineIt->second; + MeshMaterialBatches& materialMap = materialEntry.materialMap; + + auto materialIt = materialMap.find(material); + if (materialIt == materialMap.end()) { BatchedModelEntry entry; entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &DeferredRenderQueue::OnMaterialInvalidation); - it = opaqueModels.insert(std::make_pair(material, std::move(entry))).first; + materialIt = materialMap.insert(MeshMaterialBatches::value_type(material, std::move(entry))).first; } - BatchedModelEntry& entry = it->second; + BatchedModelEntry& entry = materialIt->second; entry.enabled = true; - auto& meshMap = entry.meshMap; + MeshInstanceContainer& meshMap = entry.meshMap; auto it2 = meshMap.find(meshData); if (it2 == meshMap.end()) @@ -103,21 +237,32 @@ namespace Nz it2 = meshMap.insert(std::make_pair(meshData, std::move(instanceEntry))).first; } - // On ajoute la matrice à la liste des instances de cet objet std::vector& instances = it2->second.instances; instances.push_back(transformMatrix); - - // Avons-nous suffisamment d'instances pour que le coût d'utilisation de l'instancing soit payé ? - if (instances.size() >= NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT) - entry.instancingEnabled = true; // Apparemment oui, activons l'instancing avec ce matériau } } + /*! + * \brief Adds sprites to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the sprites + * \param vertices Buffer of data for the sprites + * \param spriteCount Number of sprites + * \param overlay Texture of the sprites + */ + void DeferredRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay) { m_forwardQueue->AddSprites(renderOrder, material, vertices, spriteCount, overlay); } + /*! + * \brief Clears the queue + * + * \param fully Should everything be cleared or we can keep layers + */ + void DeferredRenderQueue::Clear(bool fully) { AbstractRenderQueue::Clear(fully); @@ -137,6 +282,13 @@ namespace Nz m_forwardQueue->Clear(fully); } + /*! + * \brief Gets the ith layer + * \return Reference to the ith layer for the queue + * + * \param i Index of the layer + */ + DeferredRenderQueue::Layer& DeferredRenderQueue::GetLayer(unsigned int i) { auto it = layers.find(i); @@ -148,6 +300,12 @@ namespace Nz return layer; } + + /*! + * \brief Handle the invalidation of an index buffer + * + * \param indexBuffer Index buffer being invalidated + */ void DeferredRenderQueue::OnIndexBufferInvalidation(const IndexBuffer* indexBuffer) { @@ -155,87 +313,67 @@ namespace Nz { Layer& layer = pair.second; - for (auto& modelPair : layer.opaqueModels) + for (auto& pipelineEntry : layer.opaqueModels) { - MeshInstanceContainer& meshes = modelPair.second.meshMap; - for (auto it = meshes.begin(); it != meshes.end();) + for (auto& materialEntry : pipelineEntry.second.materialMap) { - const MeshData& renderData = it->first; - if (renderData.indexBuffer == indexBuffer) - it = meshes.erase(it); - else - ++it; + MeshInstanceContainer& meshes = materialEntry.second.meshMap; + for (auto it = meshes.begin(); it != meshes.end();) + { + const MeshData& renderData = it->first; + if (renderData.indexBuffer == indexBuffer) + it = meshes.erase(it); + else + ++it; + } } } } } + /*! + * \brief Handle the invalidation of a material + * + * \param material Material being invalidated + */ + void DeferredRenderQueue::OnMaterialInvalidation(const Material* material) { for (auto& pair : layers) { Layer& layer = pair.second; - layer.opaqueModels.erase(material); + for (auto& pipelineEntry : layer.opaqueModels) + pipelineEntry.second.materialMap.erase(material); } } + /*! + * \brief Handle the invalidation of a vertex buffer + * + * \param vertexBuffer Vertex buffer being invalidated + */ + void DeferredRenderQueue::OnVertexBufferInvalidation(const VertexBuffer* vertexBuffer) { for (auto& pair : layers) { Layer& layer = pair.second; - - for (auto& modelPair : layer.opaqueModels) + for (auto& pipelineEntry : layer.opaqueModels) { - MeshInstanceContainer& meshes = modelPair.second.meshMap; - for (auto it = meshes.begin(); it != meshes.end();) + for (auto& materialEntry : pipelineEntry.second.materialMap) { - const MeshData& renderData = it->first; - if (renderData.vertexBuffer == vertexBuffer) - it = meshes.erase(it); - else - ++it; + MeshInstanceContainer& meshes = materialEntry.second.meshMap; + for (auto it = meshes.begin(); it != meshes.end();) + { + const MeshData& renderData = it->first; + if (renderData.vertexBuffer == vertexBuffer) + it = meshes.erase(it); + else + ++it; + } } } } } - - bool DeferredRenderQueue::BatchedModelMaterialComparator::operator()(const Material* mat1, const Material* mat2) const - { - const UberShader* uberShader1 = mat1->GetShader(); - const UberShader* uberShader2 = mat2->GetShader(); - if (uberShader1 != uberShader2) - return uberShader1 < uberShader2; - - const Shader* shader1 = mat1->GetShaderInstance(ShaderFlags_Deferred)->GetShader(); - const Shader* shader2 = mat2->GetShaderInstance(ShaderFlags_Deferred)->GetShader(); - if (shader1 != shader2) - return shader1 < shader2; - - const Texture* diffuseMap1 = mat1->GetDiffuseMap(); - const Texture* diffuseMap2 = mat2->GetDiffuseMap(); - if (diffuseMap1 != diffuseMap2) - return diffuseMap1 < diffuseMap2; - - return mat1 < mat2; - } - - bool DeferredRenderQueue::MeshDataComparator::operator()(const MeshData& data1, const MeshData& data2) const - { - const Buffer* buffer1; - const Buffer* buffer2; - - buffer1 = (data1.indexBuffer) ? data1.indexBuffer->GetBuffer() : nullptr; - buffer2 = (data2.indexBuffer) ? data2.indexBuffer->GetBuffer() : nullptr; - if (buffer1 != buffer2) - return buffer1 < buffer2; - - buffer1 = data1.vertexBuffer->GetBuffer(); - buffer2 = data2.vertexBuffer->GetBuffer(); - if (buffer1 != buffer2) - return buffer1 < buffer2; - - return data1.primitiveMode < data2.primitiveMode; - } } diff --git a/src/Nazara/Graphics/DeferredRenderTechnique.cpp b/src/Nazara/Graphics/DeferredRenderTechnique.cpp index 88335d92b..585091406 100644 --- a/src/Nazara/Graphics/DeferredRenderTechnique.cpp +++ b/src/Nazara/Graphics/DeferredRenderTechnique.cpp @@ -77,7 +77,18 @@ namespace Nz 3, // RenderPassType_SSAO }; - static_assert(sizeof(RenderPassPriority)/sizeof(unsigned int) == RenderPassType_Max+1, "Render pass priority array is incomplete"); + static_assert(sizeof(RenderPassPriority) / sizeof(unsigned int) == RenderPassType_Max + 1, "Render pass priority array is incomplete"); + + /*! + * \brief Registers the deferred shader + * \return Reference to the newly created shader + * + * \param name Name of the shader + * \param fragmentSource Raw data to fragment shader + * \param fragmentSourceLength Size of the fragment source + * \param vertexStage Stage of the shader + * \param err Pointer to string to contain error message + */ inline ShaderRef RegisterDeferredShader(const String& name, const UInt8* fragmentSource, unsigned int fragmentSourceLength, const ShaderStage& vertexStage, String* err) { @@ -109,6 +120,18 @@ namespace Nz } } + /*! + * \ingroup graphics + * \class Nz::DeferredRenderTechnique + * \brief Graphics class that represents the technique used in deferred rendering + */ + + /*! + * \brief Constructs a DeferredRenderTechnique object by default + * + * \remark Produces a NazaraError if one pass could not be created + */ + DeferredRenderTechnique::DeferredRenderTechnique() : m_renderQueue(static_cast(m_forwardTechnique.GetRenderQueue())), m_GBufferSize(0U) @@ -204,11 +227,27 @@ namespace Nz DeferredRenderTechnique::~DeferredRenderTechnique() = default; + /*! + * \brief Clears the data + * + * \param sceneData Data of the scene + */ + void DeferredRenderTechnique::Clear(const SceneData& sceneData) const { NazaraUnused(sceneData); } + /*! + * \brief Draws the data of the scene + * \return true If successful + * + * \param sceneData Data of the scene + * + * \remark Produces a NazaraAssert if viewer of the scene is invalid + * \remark Produces a NazaraError if updating viewport dimensions failed + */ + bool DeferredRenderTechnique::Draw(const SceneData& sceneData) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); @@ -242,6 +281,14 @@ namespace Nz return true; } + /*! + * \brief Enables a pass + * + * \param renderPass Enumeration for the pass + * \param position Position of the pass + * \param enable Should the pass be enabled + */ + void DeferredRenderTechnique::EnablePass(RenderPassType renderPass, int position, bool enable) { auto it = m_passes.find(renderPass); @@ -253,11 +300,25 @@ namespace Nz } } + /*! + * \brief Gets the stencil buffer + * \return Pointer to the rendering buffer + */ + RenderBuffer* DeferredRenderTechnique::GetDepthStencilBuffer() const { return m_depthStencilBuffer; } + /*! + * \brief Gets the G-buffer + * \return Pointer to the ith texture + * + * \param i Index of the G-buffer + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if index is invalid + */ + Texture* DeferredRenderTechnique::GetGBuffer(unsigned int i) const { #if NAZARA_GRAPHICS_SAFE @@ -271,16 +332,34 @@ namespace Nz return m_GBuffer[i]; } + /*! + * \brief Gets the rendering texture of the G-buffer + * \return Pointer to the rendering buffer + */ + RenderTexture* DeferredRenderTechnique::GetGBufferRTT() const { return &m_GBufferRTT; } + /*! + * \brief Gets the forward technique + * \return Constant pointer to the forward technique + */ + const ForwardRenderTechnique* DeferredRenderTechnique::GetForwardTechnique() const { return &m_forwardTechnique; } + /*! + * \brief Gets the pass + * \return Pointer to the deferred render pass + * + * \param renderPass Enumeration for the pass + * \param position Position of the pass + */ + DeferredRenderPass* DeferredRenderTechnique::GetPass(RenderPassType renderPass, int position) { auto it = m_passes.find(renderPass); @@ -294,21 +373,45 @@ namespace Nz return nullptr; } + /*! + * \brief Gets the render queue + * \return Pointer to the render queue + */ + AbstractRenderQueue* DeferredRenderTechnique::GetRenderQueue() { return &m_renderQueue; } + /*! + * \brief Gets the type of the current technique + * \return Type of the render technique + */ + RenderTechniqueType DeferredRenderTechnique::GetType() const { return RenderTechniqueType_DeferredShading; } + /*! + * \brief Gets the render texture used to work + * \return Pointer to the rendering texture + */ + RenderTexture* DeferredRenderTechnique::GetWorkRTT() const { return &m_workRTT; } + /*! + * \brief Gets the ith texture to work + * \return Pointer to the texture + * + * \param i Index of the texture used to work + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if index is invalid + */ + Texture* DeferredRenderTechnique::GetWorkTexture(unsigned int i) const { #if NAZARA_GRAPHICS_SAFE @@ -322,6 +425,14 @@ namespace Nz return m_workTextures[i]; } + /*! + * \brief Checks whether the pass is enable + * \return true If it is the case + * + * \param renderPass Enumeration for the pass + * \param position Position of the pass + */ + bool DeferredRenderTechnique::IsPassEnabled(RenderPassType renderPass, int position) { auto it = m_passes.find(renderPass); @@ -335,9 +446,17 @@ namespace Nz return false; } + /*! + * \brief Resets the pass + * \return Pointer to the new deferred render pass + * + * \param renderPass Enumeration for the pass + * \param position Position of the pass + */ + DeferredRenderPass* DeferredRenderTechnique::ResetPass(RenderPassType renderPass, int position) { - std::unique_ptr smartPtr; // Nous évite un leak en cas d'exception + std::unique_ptr smartPtr; // We avoid to leak in case of exception switch (renderPass) { @@ -386,6 +505,14 @@ namespace Nz return smartPtr.release(); } + /*! + * \brief Sets the pass + * + * \param relativeTo Enumeration for the pass + * \param position Position of the pass + * \param pass Render pass to set + */ + void DeferredRenderTechnique::SetPass(RenderPassType relativeTo, int position, DeferredRenderPass* pass) { if (pass) @@ -400,12 +527,26 @@ namespace Nz m_passes[relativeTo].erase(position); } + /*! + * \brief Checks whether the technique is supported + * \return true if it is the case + */ + bool DeferredRenderTechnique::IsSupported() { - // Depuis qu'OpenGL 3.3 est la version minimale, le Renderer supporte ce qu'il faut, mais par acquis de conscience... + // Since OpenGL 3.3 is the minimal version, the Renderer supports what it needs, but we are never sure... return Renderer::GetMaxColorAttachments() >= 4 && Renderer::GetMaxRenderTargets() >= 4; } + /*! + * \brief Resizes the texture sizes used for the render technique + * \return true If successful + * + * \param dimensions Dimensions for the render technique + * + * \param Produces a NazaraError if one pass could not be resized + */ + bool DeferredRenderTechnique::Resize(const Vector2ui& dimensions) const { try @@ -427,6 +568,13 @@ namespace Nz } } + /*! + * \brief Initializes the deferred render technique + * \return true If successful + * + * \remark Produces a NazaraError if one shader creation failed + */ + bool DeferredRenderTechnique::Initialize() { const char vertexSource_Basic[] = @@ -560,6 +708,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the deferred render technique + */ + void DeferredRenderTechnique::Uninitialize() { ShaderLibrary::Unregister("DeferredGBufferClear"); @@ -571,6 +723,14 @@ namespace Nz ShaderLibrary::Unregister("DeferredGaussianBlur"); } + /*! + * \brief Functor to compare two render pass + * \return true If first render pass is "smaller" than the second one + * + * \param pass1 First render pass to compare + * \param pass2 Second render pass to compare + */ + bool DeferredRenderTechnique::RenderPassComparator::operator()(RenderPassType pass1, RenderPassType pass2) const { return RenderPassPriority[pass1] < RenderPassPriority[pass2]; diff --git a/src/Nazara/Graphics/DepthRenderQueue.cpp b/src/Nazara/Graphics/DepthRenderQueue.cpp new file mode 100644 index 000000000..fbc24bb19 --- /dev/null +++ b/src/Nazara/Graphics/DepthRenderQueue.cpp @@ -0,0 +1,371 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup graphics + * \class Nz::DepthRenderQueue + * \brief Graphics class that represents the rendering queue for depth rendering + */ + + /*! + * \brief Constructs a DepthRenderTechnique object by default + */ + + DepthRenderQueue::DepthRenderQueue() + { + // Material + m_baseMaterial = Material::New(); + m_baseMaterial->EnableColorWrite(false); + m_baseMaterial->EnableFaceCulling(false); + //m_baseMaterial->SetFaceCulling(FaceSide_Front); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, anglePtr, colorPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, anglePtr, alphaPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, anglePtr, colorPtr); + } + + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddBillboards(0, material, count, positionPtr, sizePtr, anglePtr, alphaPtr); + } + + /*! + * \brief Adds a direcitonal light to the queue + * + * \param light Light to add + * + * \remark Produces a NazaraAssert + */ + + void DepthRenderQueue::AddDirectionalLight(const DirectionalLight& light) + { + NazaraAssert(false, "Depth render queue doesn't handle lights"); + NazaraUnused(light); + } + + /*! + * \brief Adds mesh to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the mesh + * \param meshData Data of the mesh + * \param meshAABB Box of the mesh + * \param transformMatrix Matrix of the mesh + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + NazaraUnused(meshAABB); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddMesh(0, material, meshData, meshAABB, transformMatrix); + } + + /*! + * \brief Adds a point light to the queue + * + * \param light Light to add + * + * \remark Produces a NazaraAssert + */ + + void DepthRenderQueue::AddPointLight(const PointLight& light) + { + NazaraAssert(false, "Depth render queue doesn't handle lights"); + NazaraUnused(light); + } + + /*! + * \brief Adds a spot light to the queue + * + * \param light Light to add + * + * \remark Produces a NazaraAssert + */ + + void DepthRenderQueue::AddSpotLight(const SpotLight& light) + { + NazaraAssert(false, "Depth render queue doesn't handle lights"); + NazaraUnused(light); + } + + /*! + * \brief Adds sprites to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the sprites + * \param vertices Buffer of data for the sprites + * \param spriteCount Number of sprites + * \param overlay Texture of the sprites + * + * \remark Produces a NazaraAssert if material is invalid + */ + + void DepthRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay) + { + NazaraAssert(material, "Invalid material"); + NazaraUnused(renderOrder); + NazaraUnused(overlay); + + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + ForwardRenderQueue::AddSprites(0, material, vertices, spriteCount, overlay); + } +} + diff --git a/src/Nazara/Graphics/DepthRenderTechnique.cpp b/src/Nazara/Graphics/DepthRenderTechnique.cpp new file mode 100644 index 000000000..46b1c3407 --- /dev/null +++ b/src/Nazara/Graphics/DepthRenderTechnique.cpp @@ -0,0 +1,671 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace + { + struct BillboardPoint + { + Color color; + Vector3f position; + Vector2f size; + Vector2f sinCos; // must follow `size` (both will be sent as a Vector4f) + Vector2f uv; + }; + + unsigned int s_maxQuads = std::numeric_limits::max() / 6; + unsigned int s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + } + + /*! + * \ingroup graphics + * \class Nz::DepthRenderTechnique + * \brief Graphics class that represents the technique used in depth rendering + */ + + /*! + * \brief Constructs a DepthRenderTechnique object by default + */ + + DepthRenderTechnique::DepthRenderTechnique() : + m_vertexBuffer(BufferType_Vertex) + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + + std::array whitePixel = {255, 255, 255, 255}; + m_whiteTexture.Create(ImageType_2D, PixelFormatType_RGBA8, 1, 1); + m_whiteTexture.Update(whitePixel.data()); + + m_vertexBuffer.Create(s_vertexBufferSize, DataStorage_Hardware, BufferUsage_Dynamic); + + m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer); + m_spriteBuffer.Reset(VertexDeclaration::Get(VertexLayout_XYZ_Color_UV), &m_vertexBuffer); + } + + /*! + * \brief Clears the data + * + * \param sceneData Data of the scene + */ + + void DepthRenderTechnique::Clear(const SceneData& sceneData) const + { + Renderer::Enable(RendererParameter_DepthBuffer, true); + Renderer::Enable(RendererParameter_DepthWrite, true); + Renderer::Clear(RendererBuffer_Depth); + + // Just in case the background does render depth + //if (sceneData.background) + // sceneData.background->Draw(sceneData.viewer); + } + + /*! + * \brief Draws the data of the scene + * \return true If successful + * + * \param sceneData Data of the scene + */ + + bool DepthRenderTechnique::Draw(const SceneData& sceneData) const + { + for (auto& pair : m_renderQueue.layers) + { + ForwardRenderQueue::Layer& layer = pair.second; + + if (!layer.opaqueModels.empty()) + DrawOpaqueModels(sceneData, layer); + + if (!layer.basicSprites.empty()) + DrawBasicSprites(sceneData, layer); + + if (!layer.billboards.empty()) + DrawBillboards(sceneData, layer); + + for (const Drawable* drawable : layer.otherDrawables) + drawable->Draw(); + } + + return true; + } + + /*! + * \brief Gets the render queue + * \return Pointer to the render queue + */ + + AbstractRenderQueue* DepthRenderTechnique::GetRenderQueue() + { + return &m_renderQueue; + } + + /*! + * \brief Gets the type of the current technique + * \return Type of the render technique + */ + + RenderTechniqueType DepthRenderTechnique::GetType() const + { + return RenderTechniqueType_Depth; + } + + /*! + * \brief Initializes the depth render technique + * \return true If successful + * + * \remark Produces a NazaraError if one shader creation failed + */ + + bool DepthRenderTechnique::Initialize() + { + try + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + + s_quadIndexBuffer.Reset(false, s_maxQuads * 6, DataStorage_Hardware, BufferUsage_Static); + + BufferMapper mapper(s_quadIndexBuffer, BufferAccess_WriteOnly); + UInt16* indices = static_cast(mapper.GetPointer()); + + for (unsigned int i = 0; i < s_maxQuads; ++i) + { + *indices++ = i * 4 + 0; + *indices++ = i * 4 + 2; + *indices++ = i * 4 + 1; + + *indices++ = i * 4 + 2; + *indices++ = i * 4 + 3; + *indices++ = i * 4 + 1; + } + + mapper.Unmap(); // Inutile de garder le buffer ouvert plus longtemps + + // Quad buffer (utilisé pour l'instancing de billboard et de sprites) + //Note: Les UV sont calculés dans le shader + s_quadVertexBuffer.Reset(VertexDeclaration::Get(VertexLayout_XY), 4, DataStorage_Hardware, BufferUsage_Static); + + float vertices[2 * 4] = { + -0.5f, -0.5f, + 0.5f, -0.5f, + -0.5f, 0.5f, + 0.5f, 0.5f, + }; + + s_quadVertexBuffer.FillRaw(vertices, 0, sizeof(vertices)); + + // Déclaration lors du rendu des billboards par sommet + s_billboardVertexDeclaration.EnableComponent(VertexComponent_Color, ComponentType_Color, NazaraOffsetOf(BillboardPoint, color)); + s_billboardVertexDeclaration.EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(BillboardPoint, position)); + s_billboardVertexDeclaration.EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(BillboardPoint, uv)); + s_billboardVertexDeclaration.EnableComponent(VertexComponent_Userdata0, ComponentType_Float4, NazaraOffsetOf(BillboardPoint, size)); // Englobe sincos + + // Declaration utilisée lors du rendu des billboards par instancing + // L'avantage ici est la copie directe (std::memcpy) des données de la RenderQueue vers le buffer GPU + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(ForwardRenderQueue::BillboardData, center)); + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(ForwardRenderQueue::BillboardData, size)); // Englobe sincos + s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(ForwardRenderQueue::BillboardData, color)); + } + catch (const std::exception& e) + { + NazaraError("Failed to initialise: " + String(e.what())); + return false; + } + + return true; + } + + /*! + * \brief Uninitializes the depth render technique + */ + + void DepthRenderTechnique::Uninitialize() + { + s_quadIndexBuffer.Reset(); + s_quadVertexBuffer.Reset(); + } + + /*! + * \brief Draws basic sprites + * + * \param sceneData Data of the scene + * \param layer Layer of the rendering + */ + + void DepthRenderTechnique::DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const + { + NazaraAssert(sceneData.viewer, "Invalid viewer"); + + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + + Renderer::SetIndexBuffer(&s_quadIndexBuffer); + Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); + Renderer::SetVertexBuffer(&m_spriteBuffer); + + for (auto& pipelinePair : layer.basicSprites) + { + const MaterialPipeline* pipeline = pipelinePair.first; + auto& pipelineEntry = pipelinePair.second; + + if (pipelineEntry.enabled) + { + const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance.uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambiant color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + lastShader = shader; + } + + for (auto& materialPair : pipelineEntry.materialMap) + { + const Material* material = materialPair.first; + auto& matEntry = materialPair.second; + + if (matEntry.enabled) + { + UInt8 overlayUnit; + material->Apply(pipelineInstance, 0, &overlayUnit); + overlayUnit++; + + shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit); + + Renderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler()); + + auto& overlayMap = matEntry.overlayMap; + for (auto& overlayIt : overlayMap) + { + const Texture* overlay = overlayIt.first; + auto& spriteChainVector = overlayIt.second.spriteChains; + + unsigned int spriteChainCount = spriteChainVector.size(); + if (spriteChainCount > 0) + { + Renderer::SetTexture(overlayUnit, (overlay) ? overlay : &m_whiteTexture); + + unsigned int spriteChain = 0; // Which chain of sprites are we treating + unsigned int spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain + + do + { + // We open the buffer in writing mode + BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); + VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); + + unsigned int spriteCount = 0; + unsigned int maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); + + do + { + ForwardRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain]; + unsigned int count = std::min(maxSpriteCount - spriteCount, currentChain.spriteCount - spriteChainOffset); + + std::memcpy(vertices, currentChain.vertices + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += count * 4; + + spriteCount += count; + spriteChainOffset += count; + + // Have we treated the entire chain ? + if (spriteChainOffset == currentChain.spriteCount) + { + spriteChain++; + spriteChainOffset = 0; + } + } while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); + + vertexMapper.Unmap(); + + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6); + } while (spriteChain < spriteChainCount); + + spriteChainVector.clear(); + } + } + + // We set it back to zero + matEntry.enabled = false; + } + } + pipelineEntry.enabled = false; + } + } + } + + /*! + * \brief Draws billboards + * + * \param sceneData Data of the scene + * \param layer Layer of the rendering + */ + + void DepthRenderTechnique::DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const + { + NazaraAssert(sceneData.viewer, "Invalid viewer"); + + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + + if (Renderer::HasCapability(RendererCap_Instancing)) + { + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); + + Renderer::SetVertexBuffer(&s_quadVertexBuffer); + + for (auto& pipelinePair : layer.billboards) + { + const MaterialPipeline* pipeline = pipelinePair.first; + auto& pipelineEntry = pipelinePair.second; + + if (pipelineEntry.enabled) + { + const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance.uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambiant color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + lastShader = shader; + } + + for (auto& matIt : pipelinePair.second.materialMap) + { + const Material* material = matIt.first; + auto& entry = matIt.second; + auto& billboardVector = entry.billboards; + + unsigned int billboardCount = billboardVector.size(); + if (billboardCount > 0) + { + // We begin to apply the material (and get the shader activated doing so) + material->Apply(pipelineInstance); + + const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; + unsigned int maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + do + { + unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); + billboardCount -= renderedBillboardCount; + + instanceBuffer->Fill(data, 0, renderedBillboardCount, true); + data += renderedBillboardCount; + + Renderer::DrawPrimitivesInstanced(renderedBillboardCount, PrimitiveMode_TriangleStrip, 0, 4); + } + while (billboardCount > 0); + + billboardVector.clear(); + } + } + } + } + } + else + { + Renderer::SetIndexBuffer(&s_quadIndexBuffer); + Renderer::SetVertexBuffer(&m_billboardPointBuffer); + + for (auto& pipelinePair : layer.billboards) + { + const MaterialPipeline* pipeline = pipelinePair.first; + auto& pipelineEntry = pipelinePair.second; + + if (pipelineEntry.enabled) + { + const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance.uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambiant color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + lastShader = shader; + } + + for (auto& matIt : pipelinePair.second.materialMap) + { + const Material* material = matIt.first; + auto& entry = matIt.second; + auto& billboardVector = entry.billboards; + + const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; + unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount() / 4); + + unsigned int billboardCount = billboardVector.size(); + do + { + unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); + billboardCount -= renderedBillboardCount; + + BufferMapper vertexMapper(m_billboardPointBuffer, BufferAccess_DiscardAndWrite, 0, renderedBillboardCount * 4); + BillboardPoint* vertices = static_cast(vertexMapper.GetPointer()); + + for (unsigned int i = 0; i < renderedBillboardCount; ++i) + { + const ForwardRenderQueue::BillboardData& billboard = *data++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(0.f, 1.f); + vertices++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(1.f, 1.f); + vertices++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(0.f, 0.f); + vertices++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(1.f, 0.f); + vertices++; + } + + vertexMapper.Unmap(); + + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, renderedBillboardCount * 6); + } + while (billboardCount > 0); + + billboardVector.clear(); + } + } + } + } + } + + /*! + * \brief Draws opaques models + * + * \param sceneData Data of the scene + * \param layer Layer of the rendering + */ + + void DepthRenderTechnique::DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const + { + NazaraAssert(sceneData.viewer, "Invalid viewer"); + + const Shader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + + for (auto& pipelinePair : layer.opaqueModels) + { + const MaterialPipeline* pipeline = pipelinePair.first; + auto& pipelineEntry = pipelinePair.second; + + if (pipelineEntry.maxInstanceCount > 0) + { + bool instancing = (pipelineEntry.maxInstanceCount > NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT); + const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply((instancing) ? ShaderFlags_Instancing : 0); + + const Shader* shader = pipelineInstance.uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); + + // Ambiant color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + + lastShader = shader; + } + + for (auto& materialPair : pipelineEntry.materialMap) + { + const Material* material = materialPair.first; + auto& matEntry = materialPair.second; + + if (matEntry.enabled) + { + UInt8 freeTextureUnit; + material->Apply(pipelineInstance, 0, &freeTextureUnit); + + ForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; + + // Meshes + for (auto& meshIt : meshInstances) + { + const MeshData& meshData = meshIt.first; + auto& meshEntry = meshIt.second; + + const Spheref& squaredBoundingSphere = meshEntry.squaredBoundingSphere; + std::vector& instances = meshEntry.instances; + + if (!instances.empty()) + { + const IndexBuffer* indexBuffer = meshData.indexBuffer; + const VertexBuffer* vertexBuffer = meshData.vertexBuffer; + + // Handle draw call before rendering loop + Renderer::DrawCall drawFunc; + Renderer::DrawCallInstanced instancedDrawFunc; + unsigned int indexCount; + + if (indexBuffer) + { + drawFunc = Renderer::DrawIndexedPrimitives; + instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; + indexCount = indexBuffer->GetIndexCount(); + } + else + { + drawFunc = Renderer::DrawPrimitives; + instancedDrawFunc = Renderer::DrawPrimitivesInstanced; + indexCount = vertexBuffer->GetVertexCount(); + } + + Renderer::SetIndexBuffer(indexBuffer); + Renderer::SetVertexBuffer(vertexBuffer); + + if (instancing) + { + // We compute the number of instances that we will be able to draw this time (depending on the instancing buffer size) + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(VertexDeclaration::Get(VertexLayout_Matrix4)); + + const Matrix4f* instanceMatrices = &instances[0]; + unsigned int instanceCount = instances.size(); + unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Maximum number of instance in one batch + + while (instanceCount > 0) + { + // We compute the number of instances that we will be able to draw this time (depending on the instancing buffer size) + unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); + instanceCount -= renderedInstanceCount; + + // We fill the instancing buffer with our world matrices + instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true); + instanceMatrices += renderedInstanceCount; + + // And we draw + instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); + } + } + else + { + // Without instancing, we must do a draw call for each instance + // This may be faster than instancing under a certain number + // Due to the time to modify the instancing buffer + for (const Matrix4f& matrix : instances) + { + Renderer::SetMatrix(MatrixType_World, matrix); + drawFunc(meshData.primitiveMode, 0, indexCount); + } + } + instances.clear(); + } + } + + matEntry.enabled = false; + } + } + + pipelineEntry.maxInstanceCount = 0; + } + } + } + + /*! + * \brief Gets the shader uniforms + * \return Uniforms of the shader + * + * \param shader Shader to get uniforms from + */ + + const DepthRenderTechnique::ShaderUniforms* DepthRenderTechnique::GetShaderUniforms(const Shader* shader) const + { + auto it = m_shaderUniforms.find(shader); + if (it == m_shaderUniforms.end()) + { + ShaderUniforms uniforms; + uniforms.shaderReleaseSlot.Connect(shader->OnShaderRelease, this, &DepthRenderTechnique::OnShaderInvalidated); + uniforms.shaderUniformInvalidatedSlot.Connect(shader->OnShaderUniformInvalidated, this, &DepthRenderTechnique::OnShaderInvalidated); + + uniforms.eyePosition = shader->GetUniformLocation("EyePosition"); + uniforms.textureOverlay = shader->GetUniformLocation("TextureOverlay"); + + it = m_shaderUniforms.emplace(shader, std::move(uniforms)).first; + } + + return &it->second; + } + + /*! + * \brief Handle the invalidation of a shader + * + * \param shader Shader being invalidated + */ + + void DepthRenderTechnique::OnShaderInvalidated(const Shader* shader) const + { + m_shaderUniforms.erase(shader); + } + + IndexBuffer DepthRenderTechnique::s_quadIndexBuffer; + VertexBuffer DepthRenderTechnique::s_quadVertexBuffer; + VertexDeclaration DepthRenderTechnique::s_billboardInstanceDeclaration; + VertexDeclaration DepthRenderTechnique::s_billboardVertexDeclaration; +} \ No newline at end of file diff --git a/src/Nazara/Graphics/Drawable.cpp b/src/Nazara/Graphics/Drawable.cpp index baac1defb..7db121225 100644 --- a/src/Nazara/Graphics/Drawable.cpp +++ b/src/Nazara/Graphics/Drawable.cpp @@ -7,5 +7,13 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::Drawable + * \brief Graphics class that represents something drawable for our scene + * + * \remark This class is abstract + */ + Drawable::~Drawable() = default; } diff --git a/src/Nazara/Graphics/Formats/MeshLoader.cpp b/src/Nazara/Graphics/Formats/MeshLoader.cpp index ffd2b423e..38f836e90 100644 --- a/src/Nazara/Graphics/Formats/MeshLoader.cpp +++ b/src/Nazara/Graphics/Formats/MeshLoader.cpp @@ -27,13 +27,19 @@ namespace Nz String filePath; if (matData.GetStringParameter(MaterialData::FilePath, &filePath)) { + if (!File::Exists(filePath)) + { + NazaraWarning("Shader name does not refer to an existing file, \".tga\" is used by default"); + filePath += ".tga"; + } + MaterialRef material = Material::New(); if (material->LoadFromFile(filePath, parameters.material)) model->SetMaterial(i, std::move(material)); else NazaraWarning("Failed to load material from file " + String::Number(i)); } - else if (matData.HasParameter(MaterialData::CustomDefined)) + else { MaterialRef material = Material::New(); material->BuildFromParameters(matData, parameters.material); diff --git a/src/Nazara/Graphics/ForwardRenderQueue.cpp b/src/Nazara/Graphics/ForwardRenderQueue.cpp index a4909e333..882a6f61e 100644 --- a/src/Nazara/Graphics/ForwardRenderQueue.cpp +++ b/src/Nazara/Graphics/ForwardRenderQueue.cpp @@ -7,62 +7,43 @@ #include #include -///TODO: Remplacer les sinus/cosinus par une lookup table (va booster les perfs d'un bon x10) +///TODO: Replace sinus/cosinus by a lookup table (which will lead to a speed up about 10x) namespace Nz { - void ForwardRenderQueue::AddBillboard(int renderOrder, const Material* material, const Vector3f& position, const Vector2f& size, const Vector2f& sinCos, const Color& color) - { - NazaraAssert(material, "Invalid material"); + /*! + * \ingroup graphics + * \class Nz::ForwardRenderQueue + * \brief Graphics class that represents the rendering queue for forward rendering + */ - auto& billboards = GetLayer(renderOrder).billboards; - - auto it = billboards.find(material); - if (it == billboards.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = billboards.insert(std::make_pair(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - billboardVector.push_back(BillboardData{color, position, size, sinCos}); - } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) { NazaraAssert(material, "Invalid material"); - ///DOC: sinCosPtr et colorPtr peuvent être nuls, ils seont remplacés respectivement par Vector2f(0.f, 1.f) et Color::White Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 if (!sinCosPtr) - sinCosPtr.Reset(&defaultSinCos, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile if (!colorPtr) - colorPtr.Reset(&Color::White, 0); // Pareil + colorPtr.Reset(&Color::White, 0); // Same - auto& billboards = GetLayer(renderOrder).billboards; - - auto it = billboards.find(material); - if (it == billboards.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = billboards.insert(std::make_pair(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - unsigned int prevSize = billboardVector.size(); - billboardVector.resize(prevSize + count); - - BillboardData* billboardData = &billboardVector[prevSize]; + BillboardData* billboardData = GetBillboardData(renderOrder, material, count); for (unsigned int i = 0; i < count; ++i) { billboardData->center = *positionPtr++; @@ -73,39 +54,35 @@ namespace Nz } } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) { NazaraAssert(material, "Invalid material"); - ///DOC: sinCosPtr et alphaPtr peuvent être nuls, ils seont remplacés respectivement par Vector2f(0.f, 1.f) et Color::White Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 if (!sinCosPtr) - sinCosPtr.Reset(&defaultSinCos, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile float defaultAlpha = 1.f; if (!alphaPtr) - alphaPtr.Reset(&defaultAlpha, 0); // Pareil + alphaPtr.Reset(&defaultAlpha, 0); // Same - auto& billboards = GetLayer(renderOrder).billboards; - - auto it = billboards.find(material); - if (it == billboards.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = billboards.insert(std::make_pair(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - unsigned int prevSize = billboardVector.size(); - billboardVector.resize(prevSize + count); - - BillboardData* billboardData = &billboardVector[prevSize]; + BillboardData* billboardData = GetBillboardData(renderOrder, material, count); for (unsigned int i = 0; i < count; ++i) { billboardData->center = *positionPtr++; @@ -116,37 +93,33 @@ namespace Nz } } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) { NazaraAssert(material, "Invalid material"); - ///DOC: sinCosPtr et colorPtr peuvent être nuls, ils seont remplacés respectivement par Vector2f(0.f, 1.f) et Color::White float defaultRotation = 0.f; if (!anglePtr) - anglePtr.Reset(&defaultRotation, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile if (!colorPtr) - colorPtr.Reset(&Color::White, 0); // Pareil + colorPtr.Reset(&Color::White, 0); // Same - auto& billboards = GetLayer(renderOrder).billboards; - - auto it = billboards.find(material); - if (it == billboards.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = billboards.insert(std::make_pair(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - unsigned int prevSize = billboardVector.size(); - billboardVector.resize(prevSize + count); - - BillboardData* billboardData = &billboardVector[prevSize]; + BillboardData* billboardData = GetBillboardData(renderOrder, material, count); for (unsigned int i = 0; i < count; ++i) { float sin = std::sin(ToRadians(*anglePtr)); @@ -161,39 +134,35 @@ namespace Nz } } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Sizes of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) { NazaraAssert(material, "Invalid material"); - ///DOC: sinCosPtr et alphaPtr peuvent être nuls, ils seont remplacés respectivement par Vector2f(0.f, 1.f) et Color::White float defaultRotation = 0.f; if (!anglePtr) - anglePtr.Reset(&defaultRotation, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile float defaultAlpha = 1.f; if (!alphaPtr) - alphaPtr.Reset(&defaultAlpha, 0); // Pareil + alphaPtr.Reset(&defaultAlpha, 0); // Same - auto& billboards = GetLayer(renderOrder).billboards; - - auto it = billboards.find(material); - if (it == billboards.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = billboards.insert(std::make_pair(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - unsigned int prevSize = billboardVector.size(); - billboardVector.resize(prevSize + count); - - BillboardData* billboardData = &billboardVector[prevSize]; + BillboardData* billboardData = GetBillboardData(renderOrder, material, count); for (unsigned int i = 0; i < count; ++i) { float sin = std::sin(ToRadians(*anglePtr)); @@ -208,37 +177,33 @@ namespace Nz } } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr colorPtr) { NazaraAssert(material, "Invalid material"); - ///DOC: sinCosPtr et colorPtr peuvent être nuls, ils seont remplacés respectivement par Vector2f(0.f, 1.f) et Color::White Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 if (!sinCosPtr) - sinCosPtr.Reset(&defaultSinCos, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile if (!colorPtr) - colorPtr.Reset(&Color::White, 0); // Pareil + colorPtr.Reset(&Color::White, 0); // Same - auto& billboards = GetLayer(renderOrder).billboards; - - auto it = billboards.find(material); - if (it == billboards.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = billboards.insert(std::make_pair(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - unsigned int prevSize = billboardVector.size(); - billboardVector.resize(prevSize + count); - - BillboardData* billboardData = &billboardVector[prevSize]; + BillboardData* billboardData = GetBillboardData(renderOrder, material, count); for (unsigned int i = 0; i < count; ++i) { billboardData->center = *positionPtr++; @@ -249,39 +214,35 @@ namespace Nz } } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param sinCosPtr Rotation of the billboards if null, Vector2f(0.f, 1.f) is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr sinCosPtr, SparsePtr alphaPtr) { NazaraAssert(material, "Invalid material"); - ///DOC: sinCosPtr et alphaPtr peuvent être nuls, ils seont remplacés respectivement par Vector2f(0.f, 1.f) et Color::White Vector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 if (!sinCosPtr) - sinCosPtr.Reset(&defaultSinCos, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + sinCosPtr.Reset(&defaultSinCos, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile float defaultAlpha = 1.f; if (!alphaPtr) - alphaPtr.Reset(&defaultAlpha, 0); // Pareil + alphaPtr.Reset(&defaultAlpha, 0); // Same - auto& billboards = GetLayer(renderOrder).billboards; - - auto it = billboards.find(material); - if (it == billboards.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = billboards.insert(std::make_pair(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - unsigned int prevSize = billboardVector.size(); - billboardVector.resize(prevSize + count); - - BillboardData* billboardData = &billboardVector[prevSize]; + BillboardData* billboardData = GetBillboardData(renderOrder, material, count); for (unsigned int i = 0; i < count; ++i) { billboardData->center = *positionPtr++; @@ -292,37 +253,33 @@ namespace Nz } } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param colorPtr Color of the billboards if null, Color::White is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr colorPtr) { NazaraAssert(material, "Invalid material"); - ///DOC: sinCosPtr et colorPtr peuvent être nuls, ils seont remplacés respectivement par Vector2f(0.f, 1.f) et Color::White float defaultRotation = 0.f; if (!anglePtr) - anglePtr.Reset(&defaultRotation, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile if (!colorPtr) - colorPtr.Reset(&Color::White, 0); // Pareil + colorPtr.Reset(&Color::White, 0); // Same - auto& billboards = GetLayer(renderOrder).billboards; - - auto it = billboards.find(material); - if (it == billboards.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = billboards.insert(std::make_pair(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - unsigned int prevSize = billboardVector.size(); - billboardVector.resize(prevSize + count); - - BillboardData* billboardData = &billboardVector[prevSize]; + BillboardData* billboardData = GetBillboardData(renderOrder, material, count); for (unsigned int i = 0; i < count; ++i) { float sin = std::sin(ToRadians(*anglePtr)); @@ -337,39 +294,35 @@ namespace Nz } } + /*! + * \brief Adds multiple billboards to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the billboards + * \param count Number of billboards + * \param positionPtr Position of the billboards + * \param sizePtr Size of the billboards + * \param anglePtr Rotation of the billboards if null, 0.f is used + * \param alphaPtr Alpha parameters of the billboards if null, 1.f is used + * + * \remark Produces a NazaraAssert if material is invalid + */ + void ForwardRenderQueue::AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr positionPtr, SparsePtr sizePtr, SparsePtr anglePtr, SparsePtr alphaPtr) { NazaraAssert(material, "Invalid material"); - ///DOC: sinCosPtr et alphaPtr peuvent être nuls, ils seont remplacés respectivement par Vector2f(0.f, 1.f) et Color::White float defaultRotation = 0.f; if (!anglePtr) - anglePtr.Reset(&defaultRotation, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + anglePtr.Reset(&defaultRotation, 0); // The trick here is to put the stride to zero, which leads the pointer to be immobile float defaultAlpha = 1.f; if (!alphaPtr) - alphaPtr.Reset(&defaultAlpha, 0); // Pareil + alphaPtr.Reset(&defaultAlpha, 0); // Same - auto& billboards = GetLayer(renderOrder).billboards; - - auto it = billboards.find(material); - if (it == billboards.end()) - { - BatchedBillboardEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - - it = billboards.insert(std::make_pair(material, std::move(entry))).first; - } - - BatchedBillboardEntry& entry = it->second; - - auto& billboardVector = entry.billboards; - unsigned int prevSize = billboardVector.size(); - billboardVector.resize(prevSize + count); - - BillboardData* billboardData = &billboardVector[prevSize]; + BillboardData* billboardData = GetBillboardData(renderOrder, material, count); for (unsigned int i = 0; i < count; ++i) { float sin = std::sin(ToRadians(*anglePtr)); @@ -384,6 +337,15 @@ namespace Nz } } + /*! + * \brief Adds drawable to the queue + * + * \param renderOrder Order of rendering + * \param drawable Drawable user defined + * + * \remark Produces a NazaraError if drawable is invalid + */ + void ForwardRenderQueue::AddDrawable(int renderOrder, const Drawable* drawable) { #if NAZARA_GRAPHICS_SAFE @@ -399,15 +361,28 @@ namespace Nz otherDrawables.push_back(drawable); } + /*! + * \brief Adds mesh to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the mesh + * \param meshData Data of the mesh + * \param meshAABB Box of the mesh + * \param transformMatrix Matrix of the mesh + * + * \remark Produces a NazaraAssert if material is invalid + */ void ForwardRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) { - if (material->IsEnabled(RendererParameter_Blend)) + NazaraAssert(material, "Invalid material"); + + if (material->IsBlendingEnabled()) { Layer& currentLayer = GetLayer(renderOrder); auto& transparentModels = currentLayer.transparentModels; auto& transparentModelData = currentLayer.transparentModelData; - // Le matériau est transparent, nous devons rendre ce mesh d'une autre façon (après le rendu des objets opaques et en les triant) + // The material is transparent, we must draw this mesh using another way (after the rendering of opages objects while sorting them) unsigned int index = transparentModelData.size(); transparentModelData.resize(index+1); @@ -422,21 +397,33 @@ namespace Nz else { Layer& currentLayer = GetLayer(renderOrder); - auto& opaqueModels = currentLayer.opaqueModels; + MeshPipelineBatches& opaqueModels = currentLayer.opaqueModels; - auto it = opaqueModels.find(material); - if (it == opaqueModels.end()) + const MaterialPipeline* materialPipeline = material->GetPipeline(); + + auto pipelineIt = opaqueModels.find(materialPipeline); + if (pipelineIt == opaqueModels.end()) + { + BatchedMaterialEntry materialEntry; + pipelineIt = opaqueModels.insert(MeshPipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; + } + + BatchedMaterialEntry& materialEntry = pipelineIt->second; + MeshMaterialBatches& materialMap = materialEntry.materialMap; + + auto materialIt = materialMap.find(material); + if (materialIt == materialMap.end()) { BatchedModelEntry entry; entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - it = opaqueModels.insert(std::make_pair(material, std::move(entry))).first; + materialIt = materialMap.insert(MeshMaterialBatches::value_type(material, std::move(entry))).first; } - BatchedModelEntry& entry = it->second; + BatchedModelEntry& entry = materialIt->second; entry.enabled = true; - auto& meshMap = entry.meshMap; + MeshInstanceContainer& meshMap = entry.meshMap; auto it2 = meshMap.find(meshData); if (it2 == meshMap.end()) @@ -455,24 +442,49 @@ namespace Nz std::vector& instances = it2->second.instances; instances.push_back(transformMatrix); - // Avons-nous suffisamment d'instances pour que le coût d'utilisation de l'instancing soit payé ? - if (instances.size() >= NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT) - entry.instancingEnabled = true; // Apparemment oui, activons l'instancing avec ce matériau + materialEntry.maxInstanceCount = std::max(materialEntry.maxInstanceCount, instances.size()); } } + /*! + * \brief Adds sprites to the queue + * + * \param renderOrder Order of rendering + * \param material Material of the sprites + * \param vertices Buffer of data for the sprites + * \param spriteCount Number of sprites + * \param overlay Texture of the sprites + * + * \remark Produces a NazaraAssert if material is invalid + */ void ForwardRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay) { - Layer& currentLayer = GetLayer(renderOrder); - auto& basicSprites = currentLayer.basicSprites; + NazaraAssert(material, "Invalid material"); - auto matIt = basicSprites.find(material); - if (matIt == basicSprites.end()) + Layer& currentLayer = GetLayer(renderOrder); + SpritePipelineBatches& basicSprites = currentLayer.basicSprites; + + const MaterialPipeline* materialPipeline = material->GetPipeline(); + + auto pipelineIt = basicSprites.find(materialPipeline); + if (pipelineIt == basicSprites.end()) + { + BatchedSpritePipelineEntry materialEntry; + pipelineIt = basicSprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; + } + + BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second; + pipelineEntry.enabled = true; + + SpriteMaterialBatches& materialMap = pipelineEntry.materialMap; + + auto matIt = materialMap.find(material); + if (matIt == materialMap.end()) { BatchedBasicSpriteEntry entry; entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); - matIt = basicSprites.insert(std::make_pair(material, std::move(entry))).first; + matIt = materialMap.insert(SpriteMaterialBatches::value_type(material, std::move(entry))).first; } BatchedBasicSpriteEntry& entry = matIt->second; @@ -494,6 +506,12 @@ namespace Nz spriteVector.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount})); } + /*! + * \brief Clears the queue + * + * \param fully Should everything be cleared or we can keep layers + */ + void ForwardRenderQueue::Clear(bool fully) { AbstractRenderQueue::Clear(fully); @@ -518,6 +536,12 @@ namespace Nz } } + /*! + * \brief Sorts the object according to the viewer position, furthest to nearest + * + * \param viewer Viewer of the scene + */ + void ForwardRenderQueue::Sort(const AbstractViewer* viewer) { Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near); @@ -539,24 +563,77 @@ namespace Nz return nearPlane.Distance(position1) > nearPlane.Distance(position2); }); - for (auto& pair : layer.billboards) + for (auto& pipelinePair : layer.billboards) { - const Material* mat = pair.first; - - if (mat->IsDepthSortingEnabled()) + for (auto& matPair : pipelinePair.second.materialMap) { - BatchedBillboardEntry& entry = pair.second; - auto& billboardVector = entry.billboards; + const Material* mat = matPair.first; - std::sort(billboardVector.begin(), billboardVector.end(), [&viewerPos] (const BillboardData& data1, const BillboardData& data2) + if (mat->IsDepthSortingEnabled()) { - return viewerPos.SquaredDistance(data1.center) > viewerPos.SquaredDistance(data2.center); - }); + BatchedBillboardEntry& entry = matPair.second; + auto& billboardVector = entry.billboards; + + std::sort(billboardVector.begin(), billboardVector.end(), [&viewerPos] (const BillboardData& data1, const BillboardData& data2) + { + return viewerPos.SquaredDistance(data1.center) > viewerPos.SquaredDistance(data2.center); + }); + } } } } } + /*! + * \brief Gets the billboard data + * \return Pointer to the data of the billboards + * + * \param renderOrder Order of rendering + * \param material Material of the billboard + */ + + ForwardRenderQueue::BillboardData* ForwardRenderQueue::GetBillboardData(int renderOrder, const Material* material, unsigned int count) + { + auto& billboards = GetLayer(renderOrder).billboards; + + const MaterialPipeline* materialPipeline = material->GetPipeline(); + + auto pipelineIt = billboards.find(materialPipeline); + if (pipelineIt == billboards.end()) + { + BatchedBillboardPipelineEntry pipelineEntry; + pipelineIt = billboards.insert(BillboardPipelineBatches::value_type(materialPipeline, std::move(pipelineEntry))).first; + } + BatchedBillboardPipelineEntry& pipelineEntry = pipelineIt->second; + pipelineEntry.enabled = true; + + BatchedBillboardContainer& materialMap = pipelineEntry.materialMap; + + auto it = materialMap.find(material); + if (it == materialMap.end()) + { + BatchedBillboardEntry entry; + entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); + + it = materialMap.insert(BatchedBillboardContainer::value_type(material, std::move(entry))).first; + } + + BatchedBillboardEntry& entry = it->second; + + auto& billboardVector = entry.billboards; + unsigned int prevSize = billboardVector.size(); + billboardVector.resize(prevSize + count); + + return &billboardVector[prevSize]; + } + + /*! + * \brief Gets the ith layer + * \return Reference to the ith layer for the queue + * + * \param i Index of the layer + */ + ForwardRenderQueue::Layer& ForwardRenderQueue::GetLayer(int i) { auto it = layers.find(i); @@ -569,84 +646,109 @@ namespace Nz return layer; } + /*! + * \brief Handle the invalidation of an index buffer + * + * \param indexBuffer Index buffer being invalidated + */ + void ForwardRenderQueue::OnIndexBufferInvalidation(const IndexBuffer* indexBuffer) { for (auto& pair : layers) { Layer& layer = pair.second; - for (auto& modelPair : layer.opaqueModels) + for (auto& pipelineEntry : layer.opaqueModels) { - MeshInstanceContainer& meshes = modelPair.second.meshMap; - for (auto it = meshes.begin(); it != meshes.end();) + for (auto& materialEntry : pipelineEntry.second.materialMap) { - const MeshData& renderData = it->first; - if (renderData.indexBuffer == indexBuffer) - it = meshes.erase(it); - else - ++it; + MeshInstanceContainer& meshes = materialEntry.second.meshMap; + for (auto it = meshes.begin(); it != meshes.end();) + { + const MeshData& renderData = it->first; + if (renderData.indexBuffer == indexBuffer) + it = meshes.erase(it); + else + ++it; + } } } } } + /*! + * \brief Handle the invalidation of a material + * + * \param material Material being invalidated + */ + void ForwardRenderQueue::OnMaterialInvalidation(const Material* material) { for (auto& pair : layers) { Layer& layer = pair.second; - layer.basicSprites.erase(material); - layer.billboards.erase(material); - layer.opaqueModels.erase(material); + for (auto& pipelineEntry : layer.basicSprites) + pipelineEntry.second.materialMap.erase(material); + + for (auto& pipelineEntry : layer.billboards) + pipelineEntry.second.materialMap.erase(material); + + for (auto& pipelineEntry : layer.opaqueModels) + pipelineEntry.second.materialMap.erase(material); } } + /*! + * \brief Handle the invalidation of a texture + * + * \param texture Texture being invalidated + */ + void ForwardRenderQueue::OnTextureInvalidation(const Texture* texture) { for (auto& pair : layers) { Layer& layer = pair.second; - for (auto matIt = layer.basicSprites.begin(); matIt != layer.basicSprites.end(); ++matIt) + for (auto& pipelineEntry : layer.basicSprites) { - auto& overlayMap = matIt->second.overlayMap; - overlayMap.erase(texture); + for (auto& materialEntry : pipelineEntry.second.materialMap) + materialEntry.second.overlayMap.erase(texture); } } } + /*! + * \brief Handle the invalidation of a vertex buffer + * + * \param vertexBuffer Vertex buffer being invalidated + */ + void ForwardRenderQueue::OnVertexBufferInvalidation(const VertexBuffer* vertexBuffer) { for (auto& pair : layers) { Layer& layer = pair.second; - for (auto& modelPair : layer.opaqueModels) + for (auto& pipelineEntry : layer.opaqueModels) { - MeshInstanceContainer& meshes = modelPair.second.meshMap; - for (auto it = meshes.begin(); it != meshes.end();) + for (auto& materialEntry : pipelineEntry.second.materialMap) { - const MeshData& renderData = it->first; - if (renderData.vertexBuffer == vertexBuffer) - it = meshes.erase(it); - else - ++it; + MeshInstanceContainer& meshes = materialEntry.second.meshMap; + for (auto it = meshes.begin(); it != meshes.end();) + { + const MeshData& renderData = it->first; + if (renderData.vertexBuffer == vertexBuffer) + it = meshes.erase(it); + else + ++it; + } } } } } - bool ForwardRenderQueue::BatchedBillboardComparator::operator()(const Material* mat1, const Material* mat2) const + bool ForwardRenderQueue::MaterialComparator::operator()(const Material* mat1, const Material* mat2) const { - const UberShader* uberShader1 = mat1->GetShader(); - const UberShader* uberShader2 = mat2->GetShader(); - if (uberShader1 != uberShader2) - return uberShader1 < uberShader2; - - const Shader* shader1 = mat1->GetShaderInstance(ShaderFlags_Billboard | ShaderFlags_VertexColor)->GetShader(); - const Shader* shader2 = mat2->GetShaderInstance(ShaderFlags_Billboard | ShaderFlags_VertexColor)->GetShader(); - if (shader1 != shader2) - return shader1 < shader2; - const Texture* diffuseMap1 = mat1->GetDiffuseMap(); const Texture* diffuseMap2 = mat2->GetDiffuseMap(); if (diffuseMap1 != diffuseMap2) @@ -655,45 +757,23 @@ namespace Nz return mat1 < mat2; } - bool ForwardRenderQueue::BatchedModelMaterialComparator::operator()(const Material* mat1, const Material* mat2) const + bool ForwardRenderQueue::MaterialPipelineComparator::operator()(const MaterialPipeline* pipeline1, const MaterialPipeline* pipeline2) const { - const UberShader* uberShader1 = mat1->GetShader(); - const UberShader* uberShader2 = mat2->GetShader(); - if (uberShader1 != uberShader2) - return uberShader1 < uberShader2; - - const Shader* shader1 = mat1->GetShaderInstance()->GetShader(); - const Shader* shader2 = mat2->GetShaderInstance()->GetShader(); + const Shader* shader1 = pipeline1->GetInstance().renderPipeline.GetInfo().shader; + const Shader* shader2 = pipeline2->GetInstance().renderPipeline.GetInfo().shader; if (shader1 != shader2) return shader1 < shader2; - const Texture* diffuseMap1 = mat1->GetDiffuseMap(); - const Texture* diffuseMap2 = mat2->GetDiffuseMap(); - if (diffuseMap1 != diffuseMap2) - return diffuseMap1 < diffuseMap2; - - return mat1 < mat2; + return pipeline1 < pipeline2; } - bool ForwardRenderQueue::BatchedSpriteMaterialComparator::operator()(const Material* mat1, const Material* mat2) - { - const UberShader* uberShader1 = mat1->GetShader(); - const UberShader* uberShader2 = mat2->GetShader(); - if (uberShader1 != uberShader2) - return uberShader1 < uberShader2; - - const Shader* shader1 = mat1->GetShaderInstance()->GetShader(); - const Shader* shader2 = mat2->GetShaderInstance()->GetShader(); - if (shader1 != shader2) - return shader1 < shader2; - - const Texture* diffuseMap1 = mat1->GetDiffuseMap(); - const Texture* diffuseMap2 = mat2->GetDiffuseMap(); - if (diffuseMap1 != diffuseMap2) - return diffuseMap1 < diffuseMap2; - - return mat1 < mat2; - } + /*! + * \brief Functor to compare two mesh data + * \return true If first mesh is "smaller" than the second one + * + * \param data1 First mesh to compare + * \param data2 Second mesh to compare + */ bool ForwardRenderQueue::MeshDataComparator::operator()(const MeshData& data1, const MeshData& data2) const { diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index 21423fdc8..91507ee68 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -29,26 +29,46 @@ namespace Nz Color color; Vector3f position; Vector2f size; - Vector2f sinCos; // doit suivre size + Vector2f sinCos; // must follow `size` (both will be sent as a Vector4f) Vector2f uv; }; - unsigned int s_maxQuads = std::numeric_limits::max()/6; - unsigned int s_vertexBufferSize = 4*1024*1024; // 4 MiB + unsigned int s_maxQuads = std::numeric_limits::max() / 6; + unsigned int s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB } + /*! + * \ingroup graphics + * \class Nz::ForwardRenderTechnique + * \brief Graphics class that represents the technique used in forward rendering + */ + + /*! + * \brief Constructs a ForwardRenderTechnique object by default + */ + ForwardRenderTechnique::ForwardRenderTechnique() : m_vertexBuffer(BufferType_Vertex), m_maxLightPassPerObject(3) { ErrorFlags flags(ErrorFlag_ThrowException, true); + std::array whitePixel = {255, 255, 255, 255}; + m_whiteTexture.Create(ImageType_2D, PixelFormatType_RGBA8, 1, 1); + m_whiteTexture.Update(whitePixel.data()); + m_vertexBuffer.Create(s_vertexBufferSize, DataStorage_Hardware, BufferUsage_Dynamic); m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer); m_spriteBuffer.Reset(VertexDeclaration::Get(VertexLayout_XYZ_Color_UV), &m_vertexBuffer); } + /*! + * \brief Clears the data + * + * \param sceneData Data of the scene + */ + void ForwardRenderTechnique::Clear(const SceneData& sceneData) const { Renderer::Enable(RendererParameter_DepthBuffer, true); @@ -59,6 +79,15 @@ namespace Nz sceneData.background->Draw(sceneData.viewer); } + /*! + * \brief Draws the data of the scene + * \return true If successful + * + * \param sceneData Data of the scene + * + * \remark Produces a NazaraAssert if viewer of the scene is invalid + */ + bool ForwardRenderTechnique::Draw(const SceneData& sceneData) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); @@ -88,55 +117,83 @@ namespace Nz return true; } + /*! + * \brief Gets the maximum number of lights available per pass per object + * \return Maximum number of light simulatenously per object + */ + unsigned int ForwardRenderTechnique::GetMaxLightPassPerObject() const { return m_maxLightPassPerObject; } + /*! + * \brief Gets the render queue + * \return Pointer to the render queue + */ + AbstractRenderQueue* ForwardRenderTechnique::GetRenderQueue() { return &m_renderQueue; } + /*! + * \brief Gets the type of the current technique + * \return Type of the render technique + */ + RenderTechniqueType ForwardRenderTechnique::GetType() const { return RenderTechniqueType_BasicForward; } - void ForwardRenderTechnique::SetMaxLightPassPerObject(unsigned int passCount) + /*! + * \brief Sets the maximum number of lights available per pass per object + * + * \param passCount Maximum number of light simulatenously per object + */ + + void ForwardRenderTechnique::SetMaxLightPassPerObject(unsigned int maxLightPassPerObject) { - m_maxLightPassPerObject = passCount; + m_maxLightPassPerObject = maxLightPassPerObject; } + /*! + * \brief Initializes the forward render technique + * \return true If successful + * + * \remark Produces a NazaraError if one shader creation failed + */ + bool ForwardRenderTechnique::Initialize() { try { ErrorFlags flags(ErrorFlag_ThrowException, true); - s_quadIndexBuffer.Reset(false, s_maxQuads*6, DataStorage_Hardware, BufferUsage_Static); + s_quadIndexBuffer.Reset(false, s_maxQuads * 6, DataStorage_Hardware, BufferUsage_Static); BufferMapper mapper(s_quadIndexBuffer, BufferAccess_WriteOnly); UInt16* indices = static_cast(mapper.GetPointer()); for (unsigned int i = 0; i < s_maxQuads; ++i) { - *indices++ = i*4 + 0; - *indices++ = i*4 + 2; - *indices++ = i*4 + 1; + *indices++ = i * 4 + 0; + *indices++ = i * 4 + 2; + *indices++ = i * 4 + 1; - *indices++ = i*4 + 2; - *indices++ = i*4 + 3; - *indices++ = i*4 + 1; + *indices++ = i * 4 + 2; + *indices++ = i * 4 + 3; + *indices++ = i * 4 + 1; } - mapper.Unmap(); // Inutile de garder le buffer ouvert plus longtemps + mapper.Unmap(); // No point to keep the buffer open any longer - // Quad buffer (utilisé pour l'instancing de billboard et de sprites) - //Note: Les UV sont calculés dans le shader + // Quad buffer (used for instancing of billboards and sprites) + //Note: UV are computed in the shader s_quadVertexBuffer.Reset(VertexDeclaration::Get(VertexLayout_XY), 4, DataStorage_Hardware, BufferUsage_Static); - float vertices[2*4] = { + float vertices[2 * 4] = { -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, @@ -145,17 +202,20 @@ namespace Nz s_quadVertexBuffer.FillRaw(vertices, 0, sizeof(vertices)); - // Déclaration lors du rendu des billboards par sommet + // Declaration used when rendering the vertex billboards s_billboardVertexDeclaration.EnableComponent(VertexComponent_Color, ComponentType_Color, NazaraOffsetOf(BillboardPoint, color)); s_billboardVertexDeclaration.EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(BillboardPoint, position)); s_billboardVertexDeclaration.EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(BillboardPoint, uv)); - s_billboardVertexDeclaration.EnableComponent(VertexComponent_Userdata0, ComponentType_Float4, NazaraOffsetOf(BillboardPoint, size)); // Englobe sincos + s_billboardVertexDeclaration.EnableComponent(VertexComponent_Userdata0, ComponentType_Float4, NazaraOffsetOf(BillboardPoint, size)); // Includes sincos - // Declaration utilisée lors du rendu des billboards par instancing - // L'avantage ici est la copie directe (std::memcpy) des données de la RenderQueue vers le buffer GPU + // Declaration used when rendering the billboards with intancing + // The main advantage is the direct copy (std::memcpy) of data in the RenderQueue to the GPU buffer s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData0, ComponentType_Float3, NazaraOffsetOf(ForwardRenderQueue::BillboardData, center)); s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(ForwardRenderQueue::BillboardData, size)); // Englobe sincos s_billboardInstanceDeclaration.EnableComponent(VertexComponent_InstanceData2, ComponentType_Color, NazaraOffsetOf(ForwardRenderQueue::BillboardData, color)); + + s_shadowSampler.SetFilterMode(SamplerFilter_Bilinear); + s_shadowSampler.SetWrapMode(SamplerWrap_Clamp); } catch (const std::exception& e) { @@ -166,12 +226,23 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the forward render technique + */ + void ForwardRenderTechnique::Uninitialize() { s_quadIndexBuffer.Reset(); s_quadVertexBuffer.Reset(); } + /*! + * \brief Chooses the nearest lights for one object + * + * \param object Sphere symbolising the object + * \param includeDirectionalLights Should directional lights be included in the computation + */ + void ForwardRenderTechnique::ChooseLights(const Spheref& object, bool includeDirectionalLights) const { m_lights.clear(); @@ -210,6 +281,15 @@ namespace Nz }); } + /*! + * \brief Draws basic sprites + * + * \param sceneData Data of the scene + * \param layer Layer of the rendering + * + * \remark Produces a NazaraAssert is viewer is invalid + */ + void ForwardRenderTechnique::DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); @@ -221,101 +301,115 @@ namespace Nz Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetVertexBuffer(&m_spriteBuffer); - for (auto& matIt : layer.basicSprites) + for (auto& pipelinePair : layer.basicSprites) { - const Material* material = matIt.first; - auto& matEntry = matIt.second; + const MaterialPipeline* pipeline = pipelinePair.first; + auto& pipelineEntry = pipelinePair.second; - if (matEntry.enabled) + if (pipelineEntry.enabled) { - auto& overlayMap = matEntry.overlayMap; - for (auto& overlayIt : overlayMap) + const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance.uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change + if (shader != lastShader) { - const Texture* overlay = overlayIt.first; - auto& spriteChainVector = overlayIt.second.spriteChains; + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); - unsigned int spriteChainCount = spriteChainVector.size(); - if (spriteChainCount > 0) - { - // On commence par appliquer du matériau (et récupérer le shader ainsi activé) - UInt32 flags = ShaderFlags_VertexColor; - if (overlay) - flags |= ShaderFlags_TextureOverlay; + // Ambiant color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - UInt8 overlayUnit; - const Shader* shader = material->Apply(flags, 0, &overlayUnit); - - if (overlay) - { - overlayUnit++; - Renderer::SetTexture(overlayUnit, overlay); - Renderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler()); - } - - // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas - if (shader != lastShader) - { - // Index des uniformes dans le shader - shaderUniforms = GetShaderUniforms(shader); - - // Couleur ambiante de la scène - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Overlay - shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit); - // Position de la caméra - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - - lastShader = shader; - } - - unsigned int spriteChain = 0; // Quelle chaîne de sprite traitons-nous - unsigned int spriteChainOffset = 0; // À quel offset dans la dernière chaîne nous sommes-nous arrêtés - - do - { - // On ouvre le buffer en écriture - BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); - - unsigned int spriteCount = 0; - unsigned int maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount()/4); - - do - { - ForwardRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain]; - unsigned int count = std::min(maxSpriteCount - spriteCount, currentChain.spriteCount - spriteChainOffset); - - std::memcpy(vertices, currentChain.vertices + spriteChainOffset*4, 4*count*sizeof(VertexStruct_XYZ_Color_UV)); - vertices += count*4; - - spriteCount += count; - spriteChainOffset += count; - - // Avons-nous traité la chaîne entière ? - if (spriteChainOffset == currentChain.spriteCount) - { - spriteChain++; - spriteChainOffset = 0; - } - } - while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); - - vertexMapper.Unmap(); - - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount*6); - } - while (spriteChain < spriteChainCount); - - spriteChainVector.clear(); - } + lastShader = shader; } - // On remet à zéro - matEntry.enabled = false; + for (auto& materialPair : pipelineEntry.materialMap) + { + const Material* material = materialPair.first; + auto& matEntry = materialPair.second; + + if (matEntry.enabled) + { + UInt8 overlayUnit; + material->Apply(pipelineInstance, 0, &overlayUnit); + overlayUnit++; + + shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit); + + Renderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler()); + + auto& overlayMap = matEntry.overlayMap; + for (auto& overlayIt : overlayMap) + { + const Texture* overlay = overlayIt.first; + auto& spriteChainVector = overlayIt.second.spriteChains; + + unsigned int spriteChainCount = spriteChainVector.size(); + if (spriteChainCount > 0) + { + Renderer::SetTexture(overlayUnit, (overlay) ? overlay : &m_whiteTexture); + + unsigned int spriteChain = 0; // Which chain of sprites are we treating + unsigned int spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain + + do + { + // We open the buffer in writing mode + BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); + VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); + + unsigned int spriteCount = 0; + unsigned int maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); + + do + { + ForwardRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain]; + unsigned int count = std::min(maxSpriteCount - spriteCount, currentChain.spriteCount - spriteChainOffset); + + std::memcpy(vertices, currentChain.vertices + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += count * 4; + + spriteCount += count; + spriteChainOffset += count; + + // Have we treated the entire chain ? + if (spriteChainOffset == currentChain.spriteCount) + { + spriteChain++; + spriteChainOffset = 0; + } + } while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); + + vertexMapper.Unmap(); + + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6); + } while (spriteChain < spriteChainCount); + + spriteChainVector.clear(); + } + } + + // We set it back to zero + matEntry.enabled = false; + } + } + pipelineEntry.enabled = false; } } } + /*! + * \brief Draws billboards + * + * \param sceneData Data of the scene + * \param layer Layer of the rendering + * + * \remark Produces a NazaraAssert is viewer is invalid + */ + void ForwardRenderTechnique::DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); @@ -330,47 +424,60 @@ namespace Nz Renderer::SetVertexBuffer(&s_quadVertexBuffer); - for (auto& matIt : layer.billboards) + for (auto& pipelinePair : layer.billboards) { - const Material* material = matIt.first; - auto& entry = matIt.second; - auto& billboardVector = entry.billboards; + const MaterialPipeline* pipeline = pipelinePair.first; + auto& pipelineEntry = pipelinePair.second; - unsigned int billboardCount = billboardVector.size(); - if (billboardCount > 0) + if (pipelineEntry.enabled) { - // On commence par appliquer du matériau (et récupérer le shader ainsi activé) - const Shader* shader = material->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor); + const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_Instancing | ShaderFlags_VertexColor); - // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas + const Shader* shader = pipelineInstance.uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change if (shader != lastShader) { - // Index des uniformes dans le shader + // Index of uniforms in the shader shaderUniforms = GetShaderUniforms(shader); - // Couleur ambiante de la scène + // Ambiant color of the scene shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position de la caméra + // Position of the camera shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); lastShader = shader; } - const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; - unsigned int maxBillboardPerDraw = instanceBuffer->GetVertexCount(); - do + for (auto& matIt : pipelinePair.second.materialMap) { - unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); - billboardCount -= renderedBillboardCount; + const Material* material = matIt.first; + auto& entry = matIt.second; + auto& billboardVector = entry.billboards; - instanceBuffer->Fill(data, 0, renderedBillboardCount, true); - data += renderedBillboardCount; + unsigned int billboardCount = billboardVector.size(); + if (billboardCount > 0) + { + // We begin to apply the material (and get the shader activated doing so) + material->Apply(pipelineInstance); - Renderer::DrawPrimitivesInstanced(renderedBillboardCount, PrimitiveMode_TriangleStrip, 0, 4); + const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; + unsigned int maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + do + { + unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); + billboardCount -= renderedBillboardCount; + + instanceBuffer->Fill(data, 0, renderedBillboardCount, true); + data += renderedBillboardCount; + + Renderer::DrawPrimitivesInstanced(renderedBillboardCount, PrimitiveMode_TriangleStrip, 0, 4); + } + while (billboardCount > 0); + + billboardVector.clear(); + } } - while (billboardCount > 0); - - billboardVector.clear(); } } } @@ -379,88 +486,104 @@ namespace Nz Renderer::SetIndexBuffer(&s_quadIndexBuffer); Renderer::SetVertexBuffer(&m_billboardPointBuffer); - for (auto& matIt : layer.billboards) + for (auto& pipelinePair : layer.billboards) { - const Material* material = matIt.first; - auto& entry = matIt.second; - auto& billboardVector = entry.billboards; + const MaterialPipeline* pipeline = pipelinePair.first; + auto& pipelineEntry = pipelinePair.second; - unsigned int billboardCount = billboardVector.size(); - if (billboardCount > 0) + if (pipelineEntry.enabled) { - // On commence par appliquer du matériau (et récupérer le shader ainsi activé) - const Shader* shader = material->Apply(ShaderFlags_Billboard | ShaderFlags_VertexColor); + const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply(ShaderFlags_Billboard | ShaderFlags_VertexColor); - // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas + const Shader* shader = pipelineInstance.uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change if (shader != lastShader) { - // Index des uniformes dans le shader + // Index of uniforms in the shader shaderUniforms = GetShaderUniforms(shader); - // Couleur ambiante de la scène + // Ambiant color of the scene shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position de la caméra + // Position of the camera shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); lastShader = shader; } - const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; - unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount()/4); - - do + for (auto& matIt : pipelinePair.second.materialMap) { - unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); - billboardCount -= renderedBillboardCount; + const Material* material = matIt.first; + auto& entry = matIt.second; + auto& billboardVector = entry.billboards; - BufferMapper vertexMapper(m_billboardPointBuffer, BufferAccess_DiscardAndWrite, 0, renderedBillboardCount*4); - BillboardPoint* vertices = static_cast(vertexMapper.GetPointer()); + const ForwardRenderQueue::BillboardData* data = &billboardVector[0]; + unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount() / 4); - for (unsigned int i = 0; i < renderedBillboardCount; ++i) + unsigned int billboardCount = billboardVector.size(); + do { - const ForwardRenderQueue::BillboardData& billboard = *data++; + unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); + billboardCount -= renderedBillboardCount; - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(0.f, 1.f); - vertices++; + BufferMapper vertexMapper(m_billboardPointBuffer, BufferAccess_DiscardAndWrite, 0, renderedBillboardCount * 4); + BillboardPoint* vertices = static_cast(vertexMapper.GetPointer()); - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(1.f, 1.f); - vertices++; + for (unsigned int i = 0; i < renderedBillboardCount; ++i) + { + const ForwardRenderQueue::BillboardData& billboard = *data++; - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(0.f, 0.f); - vertices++; + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(0.f, 1.f); + vertices++; - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(1.f, 0.f); - vertices++; + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(1.f, 1.f); + vertices++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(0.f, 0.f); + vertices++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(1.f, 0.f); + vertices++; + } + + vertexMapper.Unmap(); + + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, renderedBillboardCount * 6); } + while (billboardCount > 0); - vertexMapper.Unmap(); - - Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, renderedBillboardCount*6); + billboardVector.clear(); } - while (billboardCount > 0); - - billboardVector.clear(); } } } } + /*! + * \brief Draws opaques models + * + * \param sceneData Data of the scene + * \param layer Layer of the rendering + * + * \remark Produces a NazaraAssert is viewer is invalid + */ + void ForwardRenderTechnique::DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); @@ -468,206 +591,221 @@ namespace Nz const Shader* lastShader = nullptr; const ShaderUniforms* shaderUniforms = nullptr; - for (auto& matIt : layer.opaqueModels) + for (auto& pipelinePair : layer.opaqueModels) { - auto& matEntry = matIt.second; + const MaterialPipeline* pipeline = pipelinePair.first; + auto& pipelineEntry = pipelinePair.second; - if (matEntry.enabled) + if (pipelineEntry.maxInstanceCount > 0) { - ForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; + bool instancing = (pipelineEntry.maxInstanceCount > NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT); + const MaterialPipeline::Instance& pipelineInstance = pipeline->Apply((instancing) ? ShaderFlags_Instancing : 0); - if (!meshInstances.empty()) + const Shader* shader = pipelineInstance.uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change + if (shader != lastShader) { - const Material* material = matIt.first; + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); - // Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active - // Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches - // (Le deferred shading n'a pas ce problème) - bool noPointSpotLight = m_renderQueue.pointLights.empty() && m_renderQueue.spotLights.empty(); - bool instancing = m_instancingEnabled && (!material->IsLightingEnabled() || noPointSpotLight) && matEntry.instancingEnabled; + // Ambiant color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - // On commence par appliquer du matériau (et récupérer le shader ainsi activé) - const Shader* shader = material->Apply((instancing) ? ShaderFlags_Instancing : 0); + lastShader = shader; + } - // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas - if (shader != lastShader) + for (auto& materialPair : pipelineEntry.materialMap) + { + const Material* material = materialPair.first; + auto& matEntry = materialPair.second; + + if (matEntry.enabled) { - // Index des uniformes dans le shader - shaderUniforms = GetShaderUniforms(shader); + UInt8 freeTextureUnit; + material->Apply(pipelineInstance, 0, &freeTextureUnit); - // Couleur ambiante de la scène - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position de la caméra - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + ForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; - lastShader = shader; - } - - // Meshes - for (auto& meshIt : meshInstances) - { - const MeshData& meshData = meshIt.first; - auto& meshEntry = meshIt.second; - - const Spheref& squaredBoundingSphere = meshEntry.squaredBoundingSphere; - std::vector& instances = meshEntry.instances; - - if (!instances.empty()) + // Meshes + for (auto& meshIt : meshInstances) { - const IndexBuffer* indexBuffer = meshData.indexBuffer; - const VertexBuffer* vertexBuffer = meshData.vertexBuffer; + const MeshData& meshData = meshIt.first; + auto& meshEntry = meshIt.second; - // Gestion du draw call avant la boucle de rendu - Renderer::DrawCall drawFunc; - Renderer::DrawCallInstanced instancedDrawFunc; - unsigned int indexCount; + const Spheref& squaredBoundingSphere = meshEntry.squaredBoundingSphere; + std::vector& instances = meshEntry.instances; - if (indexBuffer) + if (!instances.empty()) { - drawFunc = Renderer::DrawIndexedPrimitives; - instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; - indexCount = indexBuffer->GetIndexCount(); - } - else - { - drawFunc = Renderer::DrawPrimitives; - instancedDrawFunc = Renderer::DrawPrimitivesInstanced; - indexCount = vertexBuffer->GetVertexCount(); - } + const IndexBuffer* indexBuffer = meshData.indexBuffer; + const VertexBuffer* vertexBuffer = meshData.vertexBuffer; - Renderer::SetIndexBuffer(indexBuffer); - Renderer::SetVertexBuffer(vertexBuffer); + // Handle draw call before rendering loop + Renderer::DrawCall drawFunc; + Renderer::DrawCallInstanced instancedDrawFunc; + unsigned int indexCount; - if (instancing) - { - // On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing) - VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); - instanceBuffer->SetVertexDeclaration(VertexDeclaration::Get(VertexLayout_Matrix4)); - - // Avec l'instancing, impossible de sélectionner les lumières pour chaque objet - // Du coup, il n'est activé que pour les lumières directionnelles - unsigned int lightCount = m_renderQueue.directionalLights.size(); - unsigned int lightIndex = 0; - RendererComparison oldDepthFunc = Renderer::GetDepthFunc(); - - unsigned int passCount = (lightCount == 0) ? 1 : (lightCount-1)/NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; - for (unsigned int pass = 0; pass < passCount; ++pass) + if (indexBuffer) { - if (shaderUniforms->hasLightUniforms) - { - unsigned int renderedLightCount = std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U)); - lightCount -= renderedLightCount; - - if (pass == 1) - { - // Pour additionner le résultat des calculs de lumière - // Aucune chance d'interférer avec les paramètres du matériau car nous ne rendons que les objets opaques - // (Autrement dit, sans blending) - // Quant à la fonction de profondeur, elle ne doit être appliquée que la première fois - Renderer::Enable(RendererParameter_Blend, true); - Renderer::SetBlendFunc(BlendFunc_One, BlendFunc_One); - Renderer::SetDepthFunc(RendererComparison_Equal); - } - - // Sends the uniforms - for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) - SendLightUniforms(shader, shaderUniforms->lightUniforms, lightIndex++, i*shaderUniforms->lightOffset); - } - - const Matrix4f* instanceMatrices = &instances[0]; - unsigned int instanceCount = instances.size(); - unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Le nombre maximum d'instances en une fois - - while (instanceCount > 0) - { - // On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing) - unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); - instanceCount -= renderedInstanceCount; - - // On remplit l'instancing buffer avec nos matrices world - instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true); - instanceMatrices += renderedInstanceCount; - - // Et on affiche - instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); - } + drawFunc = Renderer::DrawIndexedPrimitives; + instancedDrawFunc = Renderer::DrawIndexedPrimitivesInstanced; + indexCount = indexBuffer->GetIndexCount(); + } + else + { + drawFunc = Renderer::DrawPrimitives; + instancedDrawFunc = Renderer::DrawPrimitivesInstanced; + indexCount = vertexBuffer->GetVertexCount(); } - // On n'oublie pas de désactiver le blending pour ne pas interférer sur le reste du rendu - Renderer::Enable(RendererParameter_Blend, false); - Renderer::SetDepthFunc(oldDepthFunc); - } - else - { - if (shaderUniforms->hasLightUniforms) + Renderer::SetIndexBuffer(indexBuffer); + Renderer::SetVertexBuffer(vertexBuffer); + + if (instancing) { - for (const Matrix4f& matrix : instances) + // We compute the number of instances that we will be able to draw this time (depending on the instancing buffer size) + VertexBuffer* instanceBuffer = Renderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(VertexDeclaration::Get(VertexLayout_Matrix4)); + + // With instancing, impossible to select the lights for each object + // So, it's only activated for directional lights + unsigned int lightCount = m_renderQueue.directionalLights.size(); + unsigned int lightIndex = 0; + RendererComparison oldDepthFunc = Renderer::GetDepthFunc(); + + unsigned int passCount = (lightCount == 0) ? 1 : (lightCount - 1) / NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; + for (unsigned int pass = 0; pass < passCount; ++pass) { - // Choose the lights depending on an object position and apparent radius - ChooseLights(Spheref(matrix.GetTranslation() + squaredBoundingSphere.GetPosition(), squaredBoundingSphere.radius)); - - unsigned int lightCount = m_lights.size(); - - Renderer::SetMatrix(MatrixType_World, matrix); - unsigned int lightIndex = 0; - RendererComparison oldDepthFunc = Renderer::GetDepthFunc(); // Dans le cas où nous aurions à le changer - - unsigned int passCount = (lightCount == 0) ? 1 : (lightCount-1)/NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; - for (unsigned int pass = 0; pass < passCount; ++pass) + if (shaderUniforms->hasLightUniforms) { - lightCount -= std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U)); + unsigned int renderedLightCount = std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U)); + lightCount -= renderedLightCount; if (pass == 1) { - // Pour additionner le résultat des calculs de lumière - // Aucune chance d'interférer avec les paramètres du matériau car nous ne rendons que les objets opaques - // (Autrement dit, sans blending) - // Quant à la fonction de profondeur, elle ne doit être appliquée que la première fois + // To add the result of light computations + // We won't interfeer with materials parameters because we only render opaques objects + // (A.K.A., without blending) + // About the depth function, it must be applied only the first time Renderer::Enable(RendererParameter_Blend, true); Renderer::SetBlendFunc(BlendFunc_One, BlendFunc_One); Renderer::SetDepthFunc(RendererComparison_Equal); } - // Sends the light uniforms to the shader + // Sends the uniforms for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) - SendLightUniforms(shader, shaderUniforms->lightUniforms, lightIndex++, shaderUniforms->lightOffset*i); - - // Et on passe à l'affichage - drawFunc(meshData.primitiveMode, 0, indexCount); + SendLightUniforms(shader, shaderUniforms->lightUniforms, lightIndex++, shaderUniforms->lightOffset * i, freeTextureUnit + i); } - Renderer::Enable(RendererParameter_Blend, false); - Renderer::SetDepthFunc(oldDepthFunc); + const Matrix4f* instanceMatrices = &instances[0]; + unsigned int instanceCount = instances.size(); + unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Maximum number of instance in one batch + + while (instanceCount > 0) + { + // We compute the number of instances that we will be able to draw this time (depending on the instancing buffer size) + unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); + instanceCount -= renderedInstanceCount; + + // We fill the instancing buffer with our world matrices + instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true); + instanceMatrices += renderedInstanceCount; + + // And we draw + instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); + } } + + // We don't forget to disable the blending to avoid to interferering with the rest of the rendering + Renderer::Enable(RendererParameter_Blend, false); + Renderer::SetDepthFunc(oldDepthFunc); } else { - // Sans instancing, on doit effectuer un draw call pour chaque instance - // Cela reste néanmoins plus rapide que l'instancing en dessous d'un certain nombre d'instances - // À cause du temps de modification du buffer d'instancing - for (const Matrix4f& matrix : instances) + if (shaderUniforms->hasLightUniforms) { - Renderer::SetMatrix(MatrixType_World, matrix); - drawFunc(meshData.primitiveMode, 0, indexCount); + for (const Matrix4f& matrix : instances) + { + // Choose the lights depending on an object position and apparent radius + ChooseLights(Spheref(matrix.GetTranslation() + squaredBoundingSphere.GetPosition(), squaredBoundingSphere.radius)); + + unsigned int lightCount = m_lights.size(); + + Renderer::SetMatrix(MatrixType_World, matrix); + unsigned int lightIndex = 0; + RendererComparison oldDepthFunc = Renderer::GetDepthFunc(); // In the case where we have to change it + + unsigned int passCount = (lightCount == 0) ? 1 : (lightCount - 1) / NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; + for (unsigned int pass = 0; pass < passCount; ++pass) + { + lightCount -= std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U)); + + if (pass == 1) + { + // To add the result of light computations + // We won't interfeer with materials parameters because we only render opaques objects + // (A.K.A., without blending) + // About the depth function, it must be applied only the first time + Renderer::Enable(RendererParameter_Blend, true); + Renderer::SetBlendFunc(BlendFunc_One, BlendFunc_One); + Renderer::SetDepthFunc(RendererComparison_Equal); + } + + // Sends the light uniforms to the shader + for (unsigned int i = 0; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) + SendLightUniforms(shader, shaderUniforms->lightUniforms, lightIndex++, shaderUniforms->lightOffset*i, freeTextureUnit + i); + + // And we draw + drawFunc(meshData.primitiveMode, 0, indexCount); + } + + Renderer::Enable(RendererParameter_Blend, false); + Renderer::SetDepthFunc(oldDepthFunc); + } + } + else + { + // Without instancing, we must do a draw call for each instance + // This may be faster than instancing under a certain number + // Due to the time to modify the instancing buffer + for (const Matrix4f& matrix : instances) + { + Renderer::SetMatrix(MatrixType_World, matrix); + drawFunc(meshData.primitiveMode, 0, indexCount); + } } } + instances.clear(); } - instances.clear(); } + + matEntry.enabled = false; } } - // Et on remet à zéro les données - matEntry.enabled = false; - matEntry.instancingEnabled = false; + pipelineEntry.maxInstanceCount = 0; } } } + /*! + * \brief Draws transparent models + * + * \param sceneData Data of the scene + * \param layer Layer of the rendering + * + * \remark Produces a NazaraAssert is viewer is invalid + */ + void ForwardRenderTechnique::DrawTransparentModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); + const MaterialPipeline* lastPipeline = nullptr; + const MaterialPipeline::Instance* pipelineInstance = nullptr; const Shader* lastShader = nullptr; const ShaderUniforms* shaderUniforms = nullptr; unsigned int lightCount = 0; @@ -676,30 +814,39 @@ namespace Nz { const ForwardRenderQueue::TransparentModelData& modelData = layer.transparentModelData[index]; - // Matériau + // Material const Material* material = modelData.material; - // On commence par appliquer du matériau (et récupérer le shader ainsi activé) - const Shader* shader = material->Apply(); + const MaterialPipeline* pipeline = material->GetPipeline(); + if (pipeline != lastPipeline) + { + pipelineInstance = &pipeline->Apply(); + lastPipeline = pipeline; + } - // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas + // We begin to apply the material + UInt8 freeTextureUnit; + material->Apply(*pipelineInstance, 0, &freeTextureUnit); + + // Uniforms are conserved in our program, there's no point to send them back until they change + const Shader* shader = pipelineInstance->uberInstance->GetShader(); if (shader != lastShader) { - // Index des uniformes dans le shader + // Index of uniforms in the shader shaderUniforms = GetShaderUniforms(shader); - // Couleur ambiante de la scène + // Ambiant color of the scene shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position de la caméra + // Position of the camera shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) + // We send the directional lights if there is one (same for all) if (shaderUniforms->hasLightUniforms) { lightCount = std::min(m_renderQueue.directionalLights.size(), static_cast(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS)); for (unsigned int i = 0; i < lightCount; ++i) - SendLightUniforms(shader, shaderUniforms->lightUniforms, i, shaderUniforms->lightOffset * i); + SendLightUniforms(shader, shaderUniforms->lightUniforms, i, shaderUniforms->lightOffset * i, freeTextureUnit++); } lastShader = shader; @@ -712,7 +859,7 @@ namespace Nz const IndexBuffer* indexBuffer = meshData.indexBuffer; const VertexBuffer* vertexBuffer = meshData.vertexBuffer; - // Gestion du draw call avant la boucle de rendu + // Handle draw call before the rendering loop Renderer::DrawCall drawFunc; unsigned int indexCount; @@ -738,7 +885,7 @@ namespace Nz ChooseLights(Spheref(position, radius), false); for (unsigned int i = lightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) - SendLightUniforms(shader, shaderUniforms->lightUniforms, i, shaderUniforms->lightOffset*i); + SendLightUniforms(shader, shaderUniforms->lightUniforms, i, shaderUniforms->lightOffset*i, freeTextureUnit++); } Renderer::SetMatrix(MatrixType_World, matrix); @@ -746,6 +893,13 @@ namespace Nz } } + /*! + * \brief Gets the shader uniforms + * \return Uniforms of the shader + * + * \param shader Shader to get uniforms from + */ + const ForwardRenderTechnique::ShaderUniforms* ForwardRenderTechnique::GetShaderUniforms(const Shader* shader) const { auto it = m_shaderUniforms.find(shader); @@ -770,9 +924,13 @@ namespace Nz uniforms.lightUniforms.locations.type = type0Location; uniforms.lightUniforms.locations.color = shader->GetUniformLocation("Lights[0].color"); uniforms.lightUniforms.locations.factors = shader->GetUniformLocation("Lights[0].factors"); + uniforms.lightUniforms.locations.lightViewProjMatrix = shader->GetUniformLocation("LightViewProjMatrix[0]"); uniforms.lightUniforms.locations.parameters1 = shader->GetUniformLocation("Lights[0].parameters1"); uniforms.lightUniforms.locations.parameters2 = shader->GetUniformLocation("Lights[0].parameters2"); uniforms.lightUniforms.locations.parameters3 = shader->GetUniformLocation("Lights[0].parameters3"); + uniforms.lightUniforms.locations.pointLightShadowMap = shader->GetUniformLocation("PointLightShadowMap[0]"); + uniforms.lightUniforms.locations.shadowMapping = shader->GetUniformLocation("Lights[0].shadowMapping"); + uniforms.lightUniforms.locations.directionalSpotLightShadowMap = shader->GetUniformLocation("DirectionalSpotLightShadowMap[0]"); } else uniforms.hasLightUniforms = false; @@ -783,12 +941,19 @@ namespace Nz return &it->second; } + /*! + * \brief Handle the invalidation of a shader + * + * \param shader Shader being invalidated + */ + void ForwardRenderTechnique::OnShaderInvalidated(const Shader* shader) const { m_shaderUniforms.erase(shader); } IndexBuffer ForwardRenderTechnique::s_quadIndexBuffer; + TextureSampler ForwardRenderTechnique::s_shadowSampler; VertexBuffer ForwardRenderTechnique::s_quadVertexBuffer; VertexDeclaration ForwardRenderTechnique::s_billboardInstanceDeclaration; VertexDeclaration ForwardRenderTechnique::s_billboardVertexDeclaration; diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 03c343ed3..6e1c3ee5f 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -27,15 +29,29 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::Graphics + * \brief Graphics class that represents the module initializer of Graphics + */ + + /*! + * \brief Initializes the Graphics module + * \return true if initialization is successful + * + * \remark Produces a NazaraNotice + * \remark Produces a NazaraError if one submodule failed + */ + bool Graphics::Initialize() { - if (s_moduleReferenceCounter > 0) + if (IsInitialized()) { s_moduleReferenceCounter++; - return true; // Déjà initialisé + return true; // Already initialized } - // Initialisation des dépendances + // Initialisation of dependances if (!Renderer::Initialize()) { NazaraError("Failed to initialize Renderer module"); @@ -44,15 +60,23 @@ namespace Nz s_moduleReferenceCounter++; - // Initialisation du module + // Initialisation of the module CallOnExit onExit(Graphics::Uninitialize); + // Materials + if (!MaterialPipeline::Initialize()) + { + NazaraError("Failed to initialize material pipelines"); + return false; + } + if (!Material::Initialize()) { NazaraError("Failed to initialize materials"); return false; } + // Renderables if (!ParticleController::Initialize()) { NazaraError("Failed to initialize particle controllers"); @@ -95,11 +119,23 @@ namespace Nz return false; } - // Loaders génériques + if (!TileMap::Initialize()) + { + NazaraError("Failed to initialize tilemaps"); + return false; + } + + // Generic loaders Loaders::RegisterMesh(); Loaders::RegisterTexture(); - // RenderTechniques + // Render techniques + if (!DepthRenderTechnique::Initialize()) + { + NazaraError("Failed to initialize Depth Rendering"); + return false; + } + if (!ForwardRenderTechnique::Initialize()) { NazaraError("Failed to initialize Forward Rendering"); @@ -126,43 +162,54 @@ namespace Nz return true; } + /*! + * \brief Checks whether the module is initialized + * \return true if module is initialized + */ + bool Graphics::IsInitialized() { return s_moduleReferenceCounter != 0; } + /*! + * \brief Uninitializes the Core module + * + * \remark Produces a NazaraNotice + */ + void Graphics::Uninitialize() { if (s_moduleReferenceCounter != 1) { - // Le module est soit encore utilisé, soit pas initialisé + // The module is still in use, or can not be uninitialized if (s_moduleReferenceCounter > 1) s_moduleReferenceCounter--; return; } - // Libération du module + // Free of module s_moduleReferenceCounter = 0; - // Libération de l'atlas s'il vient de nous + // Free of atlas if it is ours std::shared_ptr defaultAtlas = Font::GetDefaultAtlas(); if (defaultAtlas && defaultAtlas->GetStorage() & DataStorage_Hardware) { Font::SetDefaultAtlas(nullptr); - // La police par défaut peut faire vivre un atlas hardware après la libération du module (ce qui va être problématique) - // du coup, si la police par défaut utilise un atlas hardware, on lui enlève. - // Je n'aime pas cette solution mais je n'en ai pas de meilleure sous la main pour l'instant + // The default police can make live one hardware atlas after the free of a module (which could be problematic) + // So, if the default police use a hardware atlas, we stole it. + // I don't like this solution, but I don't have any better if (!defaultAtlas.unique()) { - // Encore au moins une police utilise l'atlas + // Still at least one police use the atlas Font* defaultFont = Font::GetDefault(); defaultFont->SetAtlas(nullptr); if (!defaultAtlas.unique()) { - // Toujours pas seuls propriétaires ? Ah ben zut. + // Still not the only one to own it ? Then crap. NazaraWarning("Default font atlas uses hardware storage and is still used"); } } @@ -174,20 +221,28 @@ namespace Nz Loaders::UnregisterMesh(); Loaders::UnregisterTexture(); - DeferredRenderTechnique::Uninitialize(); - ForwardRenderTechnique::Uninitialize(); - SkinningManager::Uninitialize(); + // Renderables ParticleRenderer::Uninitialize(); ParticleGenerator::Uninitialize(); ParticleDeclaration::Uninitialize(); ParticleController::Uninitialize(); - Material::Uninitialize(); SkyboxBackground::Uninitialize(); Sprite::Uninitialize(); + TileMap::Uninitialize(); + + // Render techniques + DeferredRenderTechnique::Uninitialize(); + DepthRenderTechnique::Uninitialize(); + ForwardRenderTechnique::Uninitialize(); + SkinningManager::Uninitialize(); + + // Materials + Material::Uninitialize(); + MaterialPipeline::Uninitialize(); NazaraNotice("Uninitialized: Graphics module"); - // Libération des dépendances + // Free of dependances Renderer::Uninitialize(); } diff --git a/src/Nazara/Graphics/GuillotineTextureAtlas.cpp b/src/Nazara/Graphics/GuillotineTextureAtlas.cpp index c8034d7b9..7c2122281 100644 --- a/src/Nazara/Graphics/GuillotineTextureAtlas.cpp +++ b/src/Nazara/Graphics/GuillotineTextureAtlas.cpp @@ -9,11 +9,32 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::GuillotineTextureAtlas + * \brief Graphics class that represents an atlas texture for guillotine + */ + + /*! + * \brief Gets the underlying data storage + * \return Value of the enumeration of the underlying data storage + */ + UInt32 GuillotineTextureAtlas::GetStorage() const { return DataStorage_Hardware; } + /*! + * \brief Resizes the image + * \return Updated texture + * + * \param oldImage Old image to resize + * \param size New image size + * + * \remark Produces a NazaraError if resize failed + */ + AbstractImage* GuillotineTextureAtlas::ResizeImage(AbstractImage* oldImage, const Vector2ui& size) const { std::unique_ptr newTexture(new Texture); @@ -23,8 +44,8 @@ namespace Nz { Texture* oldTexture = static_cast(oldImage); - // Copie des anciennes données - ///TODO: Copie de texture à texture + // Copy of old data + ///TODO: Copy from texture to texture Image image; if (!oldTexture->Download(&image)) { @@ -43,8 +64,7 @@ namespace Nz } else { - // Si on arrive ici c'est que la taille demandée est trop grande pour la carte graphique - // ou que nous manquons de mémoire + // If we are here, it is that the size is too big for the graphic card or we don't have enough return nullptr; } } diff --git a/src/Nazara/Graphics/InstancedRenderable.cpp b/src/Nazara/Graphics/InstancedRenderable.cpp index 780f11a67..03da17984 100644 --- a/src/Nazara/Graphics/InstancedRenderable.cpp +++ b/src/Nazara/Graphics/InstancedRenderable.cpp @@ -7,16 +7,43 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::InstancedRenderable + * \brief Graphics class that represents an instancer renderable + * + * \remark This class is abstract + */ + + /*! + * \brief Destructs the object and calls OnInstancedRenderableRelease + * + * \see OnInstancedRenderableRelease + */ + InstancedRenderable::~InstancedRenderable() { OnInstancedRenderableRelease(this); } + /*! + * \brief Culls the instanced if not in the frustum + * \return true If instanced is in the frustum + * + * \param frustum Symbolizing the field of view + * \param transformMatrix Matrix transformation for our object + */ + bool InstancedRenderable::Cull(const Frustumf& frustum, const InstanceData& instanceData) const { return frustum.Contains(instanceData.volume); } + /*! + * \brief Gets the bounding volume + * \return Bounding volume of the instanced + */ + const BoundingVolumef& InstancedRenderable::GetBoundingVolume() const { EnsureBoundingVolumeUpdated(); @@ -24,19 +51,46 @@ namespace Nz return m_boundingVolume; } + /*! + * \brief Invalidates data for instanced + * + * \param instanceData Pointer to data of instances + * \param flags Flags for the instances + * + * \remark Produces a NazaraAssert if instanceData is invalid + */ + void InstancedRenderable::InvalidateData(InstanceData* instanceData, UInt32 flags) const { + NazaraAssert(instanceData, "Invalid instance data"); + instanceData->flags |= flags; } + /*! + * \brief Updates the bounding volume + * + * \param instanceData Pointer to data of instances + * + * \remark Produces a NazaraAssert if instanceData is invalid + */ + void InstancedRenderable::UpdateBoundingVolume(InstanceData* instanceData) const { NazaraAssert(instanceData, "Invalid instance data"); NazaraUnused(instanceData); - instanceData->volume.Update(instanceData->transformMatrix); + instanceData->volume.Update(*instanceData->transformMatrix); } + /*! + * \brief Updates the instance data + * + * \param instanceData Pointer to data of instances + * + * \remark Produces a NazaraAssert if instanceData is invalid + */ + void InstancedRenderable::UpdateData(InstanceData* instanceData) const { NazaraAssert(instanceData, "Invalid instance data"); diff --git a/src/Nazara/Graphics/Light.cpp b/src/Nazara/Graphics/Light.cpp index 4e8822e02..275d78a79 100644 --- a/src/Nazara/Graphics/Light.cpp +++ b/src/Nazara/Graphics/Light.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -12,13 +13,29 @@ #include #include -///TODO: Utilisation des UBOs +///TODO: Use of UBOs ///TODO: Scale ? namespace Nz { + /*! + * \ingroup graphics + * \class Nz::Light + * \brief Graphics class that represents a light + */ + + /*! + * \brief Constructs a Light object with a type + * + * \param type Type of the light + */ + Light::Light(LightType type) : - m_type(type) + m_type(type), + m_shadowMapFormat(PixelFormatType_Depth16), + m_shadowMapSize(512, 512), + m_shadowCastingEnabled(false), + m_shadowMapUpdated(false) { SetAmbientFactor((type == LightType_Directional) ? 0.2f : 0.f); SetAttenuation(0.9f); @@ -29,8 +46,22 @@ namespace Nz SetRadius(5.f); } + /*! + * \brief Adds this light to the render queue + * + * \param renderQueue Queue to be added + * \param transformMatrix Matrix transformation for this light + * + * \remark Produces a NazaraError if type is invalid + */ + void Light::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const { + static Matrix4f biasMatrix(0.5f, 0.f, 0.f, 0.f, + 0.f, 0.5f, 0.f, 0.f, + 0.f, 0.f, 0.5f, 0.f, + 0.5f, 0.5f, 0.5f, 1.f); + switch (m_type) { case LightType_Directional: @@ -40,6 +71,8 @@ namespace Nz light.color = m_color; light.diffuseFactor = m_diffuseFactor; light.direction = transformMatrix.Transform(Vector3f::Forward(), 0.f); + light.shadowMap = m_shadowMap.Get(); + light.transformMatrix = Matrix4f::ViewMatrix(transformMatrix.GetRotation() * Vector3f::Forward() * 100.f, transformMatrix.GetRotation()) * Matrix4f::Ortho(0.f, 100.f, 100.f, 0.f, 1.f, 100.f) * biasMatrix; renderQueue->AddDirectionalLight(light); break; @@ -55,6 +88,7 @@ namespace Nz light.invRadius = m_invRadius; light.position = transformMatrix.GetTranslation(); light.radius = m_radius; + light.shadowMap = m_shadowMap.Get(); renderQueue->AddPointLight(light); break; @@ -74,6 +108,8 @@ namespace Nz light.outerAngleTangent = m_outerAngleTangent; light.position = transformMatrix.GetTranslation(); light.radius = m_radius; + light.shadowMap = m_shadowMap.Get(); + light.transformMatrix = Matrix4f::ViewMatrix(transformMatrix.GetTranslation(), transformMatrix.GetRotation()) * Matrix4f::Perspective(m_outerAngle*2.f, 1.f, 0.1f, m_radius) * biasMatrix; renderQueue->AddSpotLight(light); break; @@ -85,16 +121,36 @@ namespace Nz } } + /*! + * \brief Clones this light + * \return Pointer to newly allocated Light + */ + Light* Light::Clone() const { return new Light(*this); } + /*! + * \brief Creates a default light + * \return Pointer to newly allocated light + */ + Light* Light::Create() const { return new Light; } + /*! + * \brief Culls the light if not in the frustum + * \return true If light is in the frustum + * + * \param frustum Symbolizing the field of view + * \param transformMatrix Matrix transformation for our object + * + * \remark Produces a NazaraError if type is invalid + */ + bool Light::Cull(const Frustumf& frustum, const Matrix4f& transformMatrix) const { switch (m_type) @@ -113,6 +169,14 @@ namespace Nz return false; } + /*! + * \brief Updates the bounding volume by a matrix + * + * \param transformMatrix Matrix transformation for our bounding volume + * + * \remark Produces a NazaraError if type is invalid + */ + void Light::UpdateBoundingVolume(const Matrix4f& transformMatrix) { switch (m_type) @@ -134,6 +198,12 @@ namespace Nz } } + /* + * \brief Makes the bounding volume of this light + * + * \remark Produces a NazaraError if type is invalid + */ + void Light::MakeBoundingVolume() const { switch (m_type) @@ -151,19 +221,19 @@ namespace Nz case LightType_Spot: { - // On forme une boite sur l'origine + // We make a box center in the origin Boxf box(Vector3f::Zero()); - // On calcule le reste des points - Vector3f base(Vector3f::Forward()*m_radius); + // We compute the other points + Vector3f base(Vector3f::Forward() * m_radius); - // Il nous faut maintenant le rayon du cercle projeté à cette distance - // Tangente = Opposé/Adjaçent <=> Opposé = Adjaçent*Tangente + // Now we need the radius of the projected circle depending on the distance + // Tangent = Opposite/Adjacent <=> Opposite = Adjacent * Tangent float radius = m_radius * m_outerAngleTangent; - Vector3f lExtend = Vector3f::Left()*radius; - Vector3f uExtend = Vector3f::Up()*radius; + Vector3f lExtend = Vector3f::Left() * radius; + Vector3f uExtend = Vector3f::Up() * radius; - // Et on ajoute ensuite les quatres extrémités de la pyramide + // And we add the four extremities of our pyramid box.ExtendTo(base + lExtend + uExtend); box.ExtendTo(base + lExtend - uExtend); box.ExtendTo(base - lExtend + uExtend); @@ -178,4 +248,23 @@ namespace Nz break; } } + + /*! + * \brief Updates the shadow map + */ + + void Light::UpdateShadowMap() const + { + if (m_shadowCastingEnabled) + { + if (!m_shadowMap) + m_shadowMap = Texture::New(); + + m_shadowMap->Create((m_type == LightType_Point) ? ImageType_Cubemap : ImageType_2D, m_shadowMapFormat, m_shadowMapSize.x, m_shadowMapSize.y); + } + else + m_shadowMap.Reset(); + + m_shadowMapUpdated = true; + } } diff --git a/src/Nazara/Graphics/Material.cpp b/src/Nazara/Graphics/Material.cpp index c646d940b..b81bd82b4 100644 --- a/src/Nazara/Graphics/Material.cpp +++ b/src/Nazara/Graphics/Material.cpp @@ -2,40 +2,25 @@ // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp -#ifndef NAZARA_RENDERER_OPENGL -#define NAZARA_RENDERER_OPENGL // Nécessaire pour inclure les headers OpenGL -#endif - #include #include -#include #include -#include #include #include #include namespace Nz { - namespace - { - const UInt8 r_basicFragmentShader[] = { - #include - }; - - const UInt8 r_basicVertexShader[] = { - #include - }; - - const UInt8 r_phongLightingFragmentShader[] = { - #include - }; - - const UInt8 r_phongLightingVertexShader[] = { - #include - }; - } + /*! + * \ingroup graphics + * \class Nz::Material + * \brief Graphics class that represents a material + */ + /*! + * \brief Checks whether the parameters for the material are correct + * \return true If parameters are valid + */ bool MaterialParams::IsValid() const { if (!UberShaderLibrary::Has(shaderName)) @@ -44,51 +29,37 @@ namespace Nz return true; } - Material::Material() + /*! + * \brief Applies shader to the material + * + * \param instance Pipeline instance to update + * \param textureUnit Unit for the texture GL_TEXTURE"i" + * \param lastUsedUnit Optional argument to get the last texture unit + */ + void Material::Apply(const MaterialPipeline::Instance& instance, UInt8 textureUnit, UInt8* lastUsedUnit) const { - Reset(); - } - - Material::Material(const Material& material) : - RefCounted(), - Resource(material) - { - Copy(material); - } - - Material::~Material() - { - OnMaterialRelease(this); - } - - const Shader* Material::Apply(UInt32 shaderFlags, UInt8 textureUnit, UInt8* lastUsedUnit) const - { - const ShaderInstance& instance = m_shaders[shaderFlags]; - if (!instance.uberInstance) - GenerateShader(shaderFlags); - - instance.uberInstance->Activate(); + const Shader* shader = instance.renderPipeline.GetInfo().shader; if (instance.uniforms[MaterialUniform_AlphaThreshold] != -1) - instance.shader->SendFloat(instance.uniforms[MaterialUniform_AlphaThreshold], m_alphaThreshold); + shader->SendFloat(instance.uniforms[MaterialUniform_AlphaThreshold], m_alphaThreshold); if (instance.uniforms[MaterialUniform_Ambient] != -1) - instance.shader->SendColor(instance.uniforms[MaterialUniform_Ambient], m_ambientColor); + shader->SendColor(instance.uniforms[MaterialUniform_Ambient], m_ambientColor); if (instance.uniforms[MaterialUniform_Diffuse] != -1) - instance.shader->SendColor(instance.uniforms[MaterialUniform_Diffuse], m_diffuseColor); + shader->SendColor(instance.uniforms[MaterialUniform_Diffuse], m_diffuseColor); if (instance.uniforms[MaterialUniform_Shininess] != -1) - instance.shader->SendFloat(instance.uniforms[MaterialUniform_Shininess], m_shininess); + shader->SendFloat(instance.uniforms[MaterialUniform_Shininess], m_shininess); if (instance.uniforms[MaterialUniform_Specular] != -1) - instance.shader->SendColor(instance.uniforms[MaterialUniform_Specular], m_specularColor); + shader->SendColor(instance.uniforms[MaterialUniform_Specular], m_specularColor); if (m_alphaMap && instance.uniforms[MaterialUniform_AlphaMap] != -1) { Renderer::SetTexture(textureUnit, m_alphaMap); Renderer::SetTextureSampler(textureUnit, m_diffuseSampler); - instance.shader->SendInteger(instance.uniforms[MaterialUniform_AlphaMap], textureUnit); + shader->SendInteger(instance.uniforms[MaterialUniform_AlphaMap], textureUnit); textureUnit++; } @@ -96,7 +67,7 @@ namespace Nz { Renderer::SetTexture(textureUnit, m_diffuseMap); Renderer::SetTextureSampler(textureUnit, m_diffuseSampler); - instance.shader->SendInteger(instance.uniforms[MaterialUniform_DiffuseMap], textureUnit); + shader->SendInteger(instance.uniforms[MaterialUniform_DiffuseMap], textureUnit); textureUnit++; } @@ -104,7 +75,7 @@ namespace Nz { Renderer::SetTexture(textureUnit, m_emissiveMap); Renderer::SetTextureSampler(textureUnit, m_diffuseSampler); - instance.shader->SendInteger(instance.uniforms[MaterialUniform_EmissiveMap], textureUnit); + shader->SendInteger(instance.uniforms[MaterialUniform_EmissiveMap], textureUnit); textureUnit++; } @@ -112,7 +83,7 @@ namespace Nz { Renderer::SetTexture(textureUnit, m_heightMap); Renderer::SetTextureSampler(textureUnit, m_diffuseSampler); - instance.shader->SendInteger(instance.uniforms[MaterialUniform_HeightMap], textureUnit); + shader->SendInteger(instance.uniforms[MaterialUniform_HeightMap], textureUnit); textureUnit++; } @@ -120,7 +91,7 @@ namespace Nz { Renderer::SetTexture(textureUnit, m_normalMap); Renderer::SetTextureSampler(textureUnit, m_diffuseSampler); - instance.shader->SendInteger(instance.uniforms[MaterialUniform_NormalMap], textureUnit); + shader->SendInteger(instance.uniforms[MaterialUniform_NormalMap], textureUnit); textureUnit++; } @@ -128,18 +99,20 @@ namespace Nz { Renderer::SetTexture(textureUnit, m_specularMap); Renderer::SetTextureSampler(textureUnit, m_specularSampler); - instance.shader->SendInteger(instance.uniforms[MaterialUniform_SpecularMap], textureUnit); + shader->SendInteger(instance.uniforms[MaterialUniform_SpecularMap], textureUnit); textureUnit++; } - Renderer::SetRenderStates(m_states); - if (lastUsedUnit) *lastUsedUnit = textureUnit; - - return instance.shader; } + /*! + * \brief Builds the material from a parameter list + * + * \param matData Data information for the material + * \param matParams Additional parameters for the material + */ void Material::BuildFromParameters(const ParameterList& matData, const MaterialParams& matParams) { Color color; @@ -150,7 +123,6 @@ namespace Nz ErrorFlags errFlags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled, true); - if (matData.GetFloatParameter(MaterialData::AlphaThreshold, &fValue)) SetAlphaThreshold(fValue); @@ -160,6 +132,9 @@ namespace Nz if (matData.GetColorParameter(MaterialData::AmbientColor, &color)) SetAmbientColor(color); + if (matData.GetIntegerParameter(MaterialData::CullingSide, &iValue)) + SetFaceCulling(static_cast(iValue)); + if (matData.GetIntegerParameter(MaterialData::DepthFunc, &iValue)) SetDepthFunc(static_cast(iValue)); @@ -172,20 +147,14 @@ namespace Nz if (matData.GetIntegerParameter(MaterialData::DstBlend, &iValue)) SetDstBlend(static_cast(iValue)); - if (matData.GetIntegerParameter(MaterialData::FaceCulling, &iValue)) - SetFaceCulling(static_cast(iValue)); - if (matData.GetIntegerParameter(MaterialData::FaceFilling, &iValue)) SetFaceFilling(static_cast(iValue)); - if (matData.GetBooleanParameter(MaterialData::Lighting, &isEnabled)) - EnableLighting(isEnabled); - if (matData.GetFloatParameter(MaterialData::LineWidth, &fValue)) - m_states.lineWidth = fValue; + SetLineWidth(fValue); if (matData.GetFloatParameter(MaterialData::PointSize, &fValue)) - m_states.pointSize = fValue; + SetPointSize(fValue); if (matData.GetColorParameter(MaterialData::SpecularColor, &color)) SetSpecularColor(color); @@ -196,30 +165,27 @@ namespace Nz if (matData.GetIntegerParameter(MaterialData::SrcBlend, &iValue)) SetSrcBlend(static_cast(iValue)); - if (matData.GetBooleanParameter(MaterialData::Transform, &isEnabled)) - EnableTransform(isEnabled); - // RendererParameter if (matData.GetBooleanParameter(MaterialData::Blending, &isEnabled)) - Enable(RendererParameter_Blend, isEnabled); + EnableBlending(isEnabled); if (matData.GetBooleanParameter(MaterialData::ColorWrite, &isEnabled)) - Enable(RendererParameter_ColorWrite, isEnabled); + EnableColorWrite(isEnabled); if (matData.GetBooleanParameter(MaterialData::DepthBuffer, &isEnabled)) - Enable(RendererParameter_DepthBuffer, isEnabled); + EnableDepthBuffer(isEnabled); if (matData.GetBooleanParameter(MaterialData::DepthWrite, &isEnabled)) - Enable(RendererParameter_DepthWrite, isEnabled); + EnableDepthWrite(isEnabled); if (matData.GetBooleanParameter(MaterialData::FaceCulling, &isEnabled)) - Enable(RendererParameter_FaceCulling, isEnabled); + EnableFaceCulling(isEnabled); if (matData.GetBooleanParameter(MaterialData::ScissorTest, &isEnabled)) - Enable(RendererParameter_ScissorTest, isEnabled); + EnableScissorTest(isEnabled); if (matData.GetBooleanParameter(MaterialData::StencilTest, &isEnabled)) - Enable(RendererParameter_StencilTest, isEnabled); + EnableStencilTest(isEnabled); // Samplers if (matData.GetIntegerParameter(MaterialData::DiffuseAnisotropyLevel, &iValue)) @@ -242,41 +208,43 @@ namespace Nz // Stencil if (matData.GetIntegerParameter(MaterialData::StencilCompare, &iValue)) - m_states.frontFace.stencilCompare = static_cast(iValue); + m_pipelineInfo.stencilCompare.front = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::StencilFail, &iValue)) - m_states.frontFace.stencilFail = static_cast(iValue); + m_pipelineInfo.stencilFail.front = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::StencilPass, &iValue)) - m_states.frontFace.stencilPass = static_cast(iValue); + m_pipelineInfo.stencilPass.front = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::StencilZFail, &iValue)) - m_states.frontFace.stencilZFail = static_cast(iValue); + m_pipelineInfo.stencilDepthFail.front = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::StencilMask, &iValue)) - m_states.frontFace.stencilMask = static_cast(iValue); + m_pipelineInfo.stencilWriteMask.front = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::StencilReference, &iValue)) - m_states.frontFace.stencilReference = static_cast(iValue); + m_pipelineInfo.stencilReference.front = static_cast(iValue); // Stencil (back) if (matData.GetIntegerParameter(MaterialData::BackFaceStencilCompare, &iValue)) - m_states.backFace.stencilCompare = static_cast(iValue); + m_pipelineInfo.stencilCompare.back = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::BackFaceStencilFail, &iValue)) - m_states.backFace.stencilFail = static_cast(iValue); + m_pipelineInfo.stencilFail.back = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::BackFaceStencilPass, &iValue)) - m_states.backFace.stencilPass = static_cast(iValue); + m_pipelineInfo.stencilPass.back = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::BackFaceStencilZFail, &iValue)) - m_states.backFace.stencilZFail = static_cast(iValue); + m_pipelineInfo.stencilDepthFail.back = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::BackFaceStencilMask, &iValue)) - m_states.backFace.stencilMask = static_cast(iValue); + m_pipelineInfo.stencilWriteMask.back = static_cast(iValue); if (matData.GetIntegerParameter(MaterialData::BackFaceStencilReference, &iValue)) - m_states.backFace.stencilReference = static_cast(iValue); + m_pipelineInfo.stencilReference.back = static_cast(iValue); + + InvalidatePipeline(); // Textures if (matParams.loadAlphaMap && matData.GetStringParameter(MaterialData::AlphaTexturePath, &path)) @@ -300,577 +268,178 @@ namespace Nz SetShader(matParams.shaderName); } - void Material::Enable(RendererParameter renderParameter, bool enable) + /*! + * \brief Builds a ParameterList with material data + * + * \param matData Destination parameter list which will receive material data + */ + void Material::SaveToParameters(ParameterList* matData) { - #ifdef NAZARA_DEBUG - if (renderParameter > RendererParameter_Max) + NazaraAssert(matData, "Invalid ParameterList"); + + matData->SetParameter(MaterialData::AlphaTest, IsAlphaTestEnabled()); + matData->SetParameter(MaterialData::AlphaThreshold, GetAlphaThreshold()); + matData->SetParameter(MaterialData::AmbientColor, GetAmbientColor()); + matData->SetParameter(MaterialData::CullingSide, int(GetFaceCulling())); + matData->SetParameter(MaterialData::DepthFunc, int(GetDepthFunc())); + matData->SetParameter(MaterialData::DepthSorting, IsDepthSortingEnabled()); + matData->SetParameter(MaterialData::DiffuseColor, GetDiffuseColor()); + matData->SetParameter(MaterialData::DstBlend, int(GetDstBlend())); + matData->SetParameter(MaterialData::FaceFilling, int(GetFaceFilling())); + matData->SetParameter(MaterialData::LineWidth, GetLineWidth()); + matData->SetParameter(MaterialData::PointSize, GetPointSize()); + matData->SetParameter(MaterialData::Shininess, GetShininess()); + matData->SetParameter(MaterialData::SpecularColor, GetSpecularColor()); + matData->SetParameter(MaterialData::SrcBlend, int(GetSrcBlend())); + + // RendererParameter + matData->SetParameter(MaterialData::Blending, IsBlendingEnabled()); + matData->SetParameter(MaterialData::ColorWrite, IsColorWriteEnabled()); + matData->SetParameter(MaterialData::DepthBuffer, IsDepthBufferEnabled()); + matData->SetParameter(MaterialData::DepthWrite, IsDepthWriteEnabled()); + matData->SetParameter(MaterialData::FaceCulling, IsFaceCullingEnabled()); + matData->SetParameter(MaterialData::ScissorTest, IsScissorTestEnabled()); + matData->SetParameter(MaterialData::StencilTest, IsStencilTestEnabled()); + + // Samplers + matData->SetParameter(MaterialData::DiffuseAnisotropyLevel, int(GetDiffuseSampler().GetAnisotropicLevel())); + matData->SetParameter(MaterialData::DiffuseFilter, int(GetDiffuseSampler().GetFilterMode())); + matData->SetParameter(MaterialData::DiffuseWrap, int(GetDiffuseSampler().GetWrapMode())); + + matData->SetParameter(MaterialData::SpecularAnisotropyLevel, int(GetSpecularSampler().GetAnisotropicLevel())); + matData->SetParameter(MaterialData::SpecularFilter, int(GetSpecularSampler().GetFilterMode())); + matData->SetParameter(MaterialData::SpecularWrap, int(GetSpecularSampler().GetWrapMode())); + + // Stencil + matData->SetParameter(MaterialData::StencilCompare, int(GetPipelineInfo().stencilCompare.front)); + matData->SetParameter(MaterialData::StencilFail, int(GetPipelineInfo().stencilFail.front)); + matData->SetParameter(MaterialData::StencilPass, int(GetPipelineInfo().stencilPass.front)); + matData->SetParameter(MaterialData::StencilZFail, int(GetPipelineInfo().stencilDepthFail.front)); + matData->SetParameter(MaterialData::StencilMask, int(GetPipelineInfo().stencilWriteMask.front)); + matData->SetParameter(MaterialData::StencilReference, int(GetPipelineInfo().stencilReference.front)); + + // Stencil (back) + matData->SetParameter(MaterialData::BackFaceStencilCompare, int(GetPipelineInfo().stencilCompare.back)); + matData->SetParameter(MaterialData::BackFaceStencilFail, int(GetPipelineInfo().stencilFail.back)); + matData->SetParameter(MaterialData::BackFaceStencilPass, int(GetPipelineInfo().stencilPass.back)); + matData->SetParameter(MaterialData::BackFaceStencilZFail, int(GetPipelineInfo().stencilDepthFail.back)); + matData->SetParameter(MaterialData::BackFaceStencilMask, int(GetPipelineInfo().stencilWriteMask.back)); + matData->SetParameter(MaterialData::BackFaceStencilReference, int(GetPipelineInfo().stencilReference.back)); + + // Textures + if (HasAlphaMap()) { - NazaraError("Renderer parameter out of enum"); - return; + const String& path = GetAlphaMap()->GetFilePath(); + if (!path.IsEmpty()) + matData->SetParameter(MaterialData::AlphaTexturePath, path); } - #endif - m_states.parameters[renderParameter] = enable; - } - - void Material::EnableAlphaTest(bool alphaTest) - { - m_alphaTestEnabled = alphaTest; - - InvalidateShaders(); - } - - void Material::EnableDepthSorting(bool depthSorting) - { - m_depthSortingEnabled = depthSorting; - } - - void Material::EnableLighting(bool lighting) - { - m_lightingEnabled = lighting; - - InvalidateShaders(); - } - - void Material::EnableTransform(bool transform) - { - m_transformEnabled = transform; - - InvalidateShaders(); - } - - Texture* Material::GetAlphaMap() const - { - return m_alphaMap; - } - - float Material::GetAlphaThreshold() const - { - return m_alphaThreshold; - } - - Color Material::GetAmbientColor() const - { - return m_ambientColor; - } - - RendererComparison Material::GetDepthFunc() const - { - return m_states.depthFunc; - } - - Color Material::GetDiffuseColor() const - { - return m_diffuseColor; - } - - TextureSampler& Material::GetDiffuseSampler() - { - return m_diffuseSampler; - } - - const TextureSampler& Material::GetDiffuseSampler() const - { - return m_diffuseSampler; - } - - Texture* Material::GetDiffuseMap() const - { - return m_diffuseMap; - } - - BlendFunc Material::GetDstBlend() const - { - return m_states.dstBlend; - } - - Texture* Material::GetEmissiveMap() const - { - return m_emissiveMap; - } - - FaceSide Material::GetFaceCulling() const - { - return m_states.faceCulling; - } - - FaceFilling Material::GetFaceFilling() const - { - return m_states.faceFilling; - } - - Texture* Material::GetHeightMap() const - { - return m_heightMap; - } - - Texture* Material::GetNormalMap() const - { - return m_normalMap; - } - - const RenderStates& Material::GetRenderStates() const - { - return m_states; - } - - const UberShader* Material::GetShader() const - { - return m_uberShader; - } - - const UberShaderInstance* Material::GetShaderInstance(UInt32 flags) const - { - const ShaderInstance& instance = m_shaders[flags]; - if (!instance.uberInstance) - GenerateShader(flags); - - return instance.uberInstance; - } - - float Material::GetShininess() const - { - return m_shininess; - } - - Color Material::GetSpecularColor() const - { - return m_specularColor; - } - - Texture* Material::GetSpecularMap() const - { - return m_specularMap; - } - - TextureSampler& Material::GetSpecularSampler() - { - return m_specularSampler; - } - - const TextureSampler& Material::GetSpecularSampler() const - { - return m_specularSampler; - } - - BlendFunc Material::GetSrcBlend() const - { - return m_states.srcBlend; - } - - bool Material::HasAlphaMap() const - { - return m_alphaMap.IsValid(); - } - - bool Material::HasDiffuseMap() const - { - return m_diffuseMap.IsValid(); - } - - bool Material::HasEmissiveMap() const - { - return m_emissiveMap.IsValid(); - } - - bool Material::HasHeightMap() const - { - return m_heightMap.IsValid(); - } - - bool Material::HasNormalMap() const - { - return m_normalMap.IsValid(); - } - - bool Material::HasSpecularMap() const - { - return m_specularMap.IsValid(); - } - - bool Material::IsAlphaTestEnabled() const - { - return m_alphaTestEnabled; - } - - bool Material::IsDepthSortingEnabled() const - { - return m_depthSortingEnabled; - } - - bool Material::IsEnabled(RendererParameter parameter) const - { - #ifdef NAZARA_DEBUG - if (parameter > RendererParameter_Max) + if (HasDiffuseMap()) { - NazaraError("Renderer parameter out of enum"); - return false; + const String& path = GetDiffuseMap()->GetFilePath(); + if (!path.IsEmpty()) + matData->SetParameter(MaterialData::DiffuseTexturePath, path); } - #endif - return m_states.parameters[parameter]; - } - - bool Material::IsLightingEnabled() const - { - return m_lightingEnabled; - } - - bool Material::IsTransformEnabled() const - { - return m_transformEnabled; - } - - bool Material::LoadFromFile(const String& filePath, const MaterialParams& params) - { - return MaterialLoader::LoadFromFile(this, filePath, params); - } - - bool Material::LoadFromMemory(const void* data, std::size_t size, const MaterialParams& params) - { - return MaterialLoader::LoadFromMemory(this, data, size, params); - } - - bool Material::LoadFromStream(Stream& stream, const MaterialParams& params) - { - return MaterialLoader::LoadFromStream(this, stream, params); + if (HasEmissiveMap()) + { + const String& path = GetEmissiveMap()->GetFilePath(); + if (!path.IsEmpty()) + matData->SetParameter(MaterialData::EmissiveTexturePath, path); + } + + if (HasHeightMap()) + { + const String& path = GetHeightMap()->GetFilePath(); + if (!path.IsEmpty()) + matData->SetParameter(MaterialData::HeightTexturePath, path); + } + + if (HasNormalMap()) + { + const String& path = GetNormalMap()->GetFilePath(); + if (!path.IsEmpty()) + matData->SetParameter(MaterialData::NormalTexturePath, path); + } + + if (HasSpecularMap()) + { + const String& path = GetSpecularMap()->GetFilePath(); + if (!path.IsEmpty()) + matData->SetParameter(MaterialData::SpecularTexturePath, path); + } } + /*! + * \brief Resets the material, cleans everything + * + * \remark Invalidates the pipeline + */ void Material::Reset() { OnMaterialReset(this); m_alphaMap.Reset(); + m_depthMaterial.Reset(); m_diffuseMap.Reset(); m_emissiveMap.Reset(); m_heightMap.Reset(); m_normalMap.Reset(); m_specularMap.Reset(); - m_uberShader.Reset(); - - for (ShaderInstance& instance : m_shaders) - instance.uberInstance = nullptr; m_alphaThreshold = 0.2f; - m_alphaTestEnabled = false; m_ambientColor = Color(128, 128, 128); - m_depthSortingEnabled = false; m_diffuseColor = Color::White; m_diffuseSampler = TextureSampler(); - m_lightingEnabled = true; + m_shadowCastingEnabled = true; m_shininess = 50.f; m_specularColor = Color::White; m_specularSampler = TextureSampler(); - m_states = RenderStates(); - m_states.parameters[RendererParameter_DepthBuffer] = true; - m_states.parameters[RendererParameter_FaceCulling] = true; - m_transformEnabled = true; + m_pipelineInfo = MaterialPipelineInfo(); + m_pipelineInfo.depthBuffer = true; + m_pipelineInfo.faceCulling = true; SetShader("Basic"); + + InvalidatePipeline(); } - bool Material::SetAlphaMap(const String& textureName) - { - TextureRef texture = TextureLibrary::Query(textureName); - if (!texture) - { - texture = TextureManager::Get(textureName); - if (!texture) - return false; - } - - SetAlphaMap(std::move(texture)); - return true; - } - - void Material::SetAlphaMap(TextureRef alphaMap) - { - m_alphaMap = std::move(alphaMap); - - InvalidateShaders(); - } - - void Material::SetAlphaThreshold(float alphaThreshold) - { - m_alphaThreshold = alphaThreshold; - } - - void Material::SetAmbientColor(const Color& ambient) - { - m_ambientColor = ambient; - } - - void Material::SetDepthFunc(RendererComparison depthFunc) - { - m_states.depthFunc = depthFunc; - } - - void Material::SetDiffuseColor(const Color& diffuse) - { - m_diffuseColor = diffuse; - } - - bool Material::SetDiffuseMap(const String& textureName) - { - TextureRef texture = TextureLibrary::Query(textureName); - if (!texture) - { - texture = TextureManager::Get(textureName); - if (!texture) - return false; - } - - SetDiffuseMap(std::move(texture)); - return true; - } - - void Material::SetDiffuseMap(TextureRef diffuseMap) - { - m_diffuseMap = std::move(diffuseMap); - - InvalidateShaders(); - } - - void Material::SetDiffuseSampler(const TextureSampler& sampler) - { - m_diffuseSampler = sampler; - } - - void Material::SetDstBlend(BlendFunc func) - { - m_states.dstBlend = func; - } - - bool Material::SetEmissiveMap(const String& textureName) - { - TextureRef texture = TextureLibrary::Query(textureName); - if (!texture) - { - texture = TextureManager::Get(textureName); - if (!texture) - return false; - } - - SetEmissiveMap(std::move(texture)); - return true; - } - - void Material::SetEmissiveMap(TextureRef emissiveMap) - { - m_emissiveMap = std::move(emissiveMap); - - InvalidateShaders(); - } - - void Material::SetFaceCulling(FaceSide faceSide) - { - m_states.faceCulling = faceSide; - } - - void Material::SetFaceFilling(FaceFilling filling) - { - m_states.faceFilling = filling; - } - - bool Material::SetHeightMap(const String& textureName) - { - TextureRef texture = TextureLibrary::Query(textureName); - if (!texture) - { - texture = TextureManager::Get(textureName); - if (!texture) - return false; - } - - SetHeightMap(std::move(texture)); - return true; - } - - void Material::SetHeightMap(TextureRef heightMap) - { - m_heightMap = std::move(heightMap); - - InvalidateShaders(); - } - - bool Material::SetNormalMap(const String& textureName) - { - TextureRef texture = TextureLibrary::Query(textureName); - if (!texture) - { - texture = TextureManager::Get(textureName); - if (!texture) - return false; - } - - SetNormalMap(std::move(texture)); - return true; - } - - void Material::SetNormalMap(TextureRef normalMap) - { - m_normalMap = std::move(normalMap); - - InvalidateShaders(); - } - - void Material::SetRenderStates(const RenderStates& states) - { - m_states = states; - } - - void Material::SetShader(UberShaderConstRef uberShader) - { - m_uberShader = std::move(uberShader); - - InvalidateShaders(); - } - - bool Material::SetShader(const String& uberShaderName) - { - UberShaderConstRef uberShader = UberShaderLibrary::Get(uberShaderName); - if (!uberShader) - return false; - - SetShader(std::move(uberShader)); - return true; - } - - void Material::SetShininess(float shininess) - { - m_shininess = shininess; - } - - void Material::SetSpecularColor(const Color& specular) - { - m_specularColor = specular; - } - - bool Material::SetSpecularMap(const String& textureName) - { - TextureRef texture = TextureLibrary::Query(textureName); - if (!texture) - { - texture = TextureManager::Get(textureName); - if (!texture) - return false; - } - - SetSpecularMap(std::move(texture)); - return true; - } - - void Material::SetSpecularMap(TextureRef specularMap) - { - m_specularMap = std::move(specularMap); - - InvalidateShaders(); - } - - void Material::SetSpecularSampler(const TextureSampler& sampler) - { - m_specularSampler = sampler; - } - - void Material::SetSrcBlend(BlendFunc func) - { - m_states.srcBlend = func; - } - - Material& Material::operator=(const Material& material) - { - Resource::operator=(material); - - Copy(material); - return *this; - } - - MaterialRef Material::GetDefault() - { - return s_defaultMaterial; - } - + /*! + * \brief Copies the other material + * + * \param material Material to copy into this + */ void Material::Copy(const Material& material) { - // Copie des états de base - m_alphaTestEnabled = material.m_alphaTestEnabled; - m_alphaThreshold = material.m_alphaThreshold; - m_ambientColor = material.m_ambientColor; - m_depthSortingEnabled = material.m_depthSortingEnabled; - m_diffuseColor = material.m_diffuseColor; - m_diffuseSampler = material.m_diffuseSampler; - m_lightingEnabled = material.m_lightingEnabled; - m_shininess = material.m_shininess; - m_specularColor = material.m_specularColor; - m_specularSampler = material.m_specularSampler; - m_states = material.m_states; - m_transformEnabled = material.m_transformEnabled; + // Copy of base states + m_alphaThreshold = material.m_alphaThreshold; + m_ambientColor = material.m_ambientColor; + m_diffuseColor = material.m_diffuseColor; + m_diffuseSampler = material.m_diffuseSampler; + m_pipelineInfo = material.m_pipelineInfo; + m_shininess = material.m_shininess; + m_shadowCastingEnabled = material.m_shadowCastingEnabled; + m_specularColor = material.m_specularColor; + m_specularSampler = material.m_specularSampler; - // Copie des références de texture - m_alphaMap = material.m_alphaMap; - m_diffuseMap = material.m_diffuseMap; - m_emissiveMap = material.m_emissiveMap; - m_heightMap = material.m_heightMap; - m_normalMap = material.m_normalMap; - m_specularMap = material.m_specularMap; + // Copy of reference to the textures + m_alphaMap = material.m_alphaMap; + m_depthMaterial = material.m_depthMaterial; + m_diffuseMap = material.m_diffuseMap; + m_emissiveMap = material.m_emissiveMap; + m_heightMap = material.m_heightMap; + m_normalMap = material.m_normalMap; + m_specularMap = material.m_specularMap; - // Copie de la référence vers l'Über-Shader - m_uberShader = material.m_uberShader; - - // On copie les instances de shader par la même occasion - std::memcpy(&m_shaders[0], &material.m_shaders[0], (ShaderFlags_Max+1)*sizeof(ShaderInstance)); - } - - void Material::GenerateShader(UInt32 flags) const - { - ParameterList list; - list.SetParameter("ALPHA_MAPPING", m_alphaMap.IsValid()); - list.SetParameter("ALPHA_TEST", m_alphaTestEnabled); - list.SetParameter("COMPUTE_TBNMATRIX", m_normalMap.IsValid() || m_heightMap.IsValid()); - list.SetParameter("DIFFUSE_MAPPING", m_diffuseMap.IsValid()); - list.SetParameter("EMISSIVE_MAPPING", m_emissiveMap.IsValid()); - list.SetParameter("LIGHTING", m_lightingEnabled); - list.SetParameter("NORMAL_MAPPING", m_normalMap.IsValid()); - list.SetParameter("PARALLAX_MAPPING", m_heightMap.IsValid()); - list.SetParameter("SPECULAR_MAPPING", m_specularMap.IsValid()); - list.SetParameter("TEXTURE_MAPPING", m_alphaMap.IsValid() || m_diffuseMap.IsValid() || m_emissiveMap.IsValid() || - m_normalMap.IsValid() || m_heightMap.IsValid() || m_specularMap.IsValid() || - flags & ShaderFlags_TextureOverlay); - list.SetParameter("TRANSFORM", m_transformEnabled); - - list.SetParameter("FLAG_BILLBOARD", static_cast((flags & ShaderFlags_Billboard) != 0)); - list.SetParameter("FLAG_DEFERRED", static_cast((flags & ShaderFlags_Deferred) != 0)); - list.SetParameter("FLAG_INSTANCING", static_cast((flags & ShaderFlags_Instancing) != 0)); - list.SetParameter("FLAG_TEXTUREOVERLAY", static_cast((flags & ShaderFlags_TextureOverlay) != 0)); - list.SetParameter("FLAG_VERTEXCOLOR", static_cast((flags & ShaderFlags_VertexColor) != 0)); - - ShaderInstance& instance = m_shaders[flags]; - instance.uberInstance = m_uberShader->Get(list); - instance.shader = instance.uberInstance->GetShader(); - - #define CacheUniform(name) instance.uniforms[MaterialUniform_##name] = instance.shader->GetUniformLocation("Material" #name) - - CacheUniform(AlphaMap); - CacheUniform(AlphaThreshold); - CacheUniform(Ambient); - CacheUniform(Diffuse); - CacheUniform(DiffuseMap); - CacheUniform(EmissiveMap); - CacheUniform(HeightMap); - CacheUniform(NormalMap); - CacheUniform(Shininess); - CacheUniform(Specular); - CacheUniform(SpecularMap); - - #undef CacheUniform - } - - void Material::InvalidateShaders() - { - for (ShaderInstance& instance : m_shaders) - instance.uberInstance = nullptr; + InvalidatePipeline(); } + /*! + * \brief Initializes the material librairies + * \return true If successful + * + * \remark Produces a NazaraError if the material library failed to be initialized + */ bool Material::Initialize() { if (!MaterialLibrary::Initialize()) @@ -885,46 +454,21 @@ namespace Nz return false; } - // Basic shader - { - UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New(); - - String fragmentShader(reinterpret_cast(r_basicFragmentShader), sizeof(r_basicFragmentShader)); - String vertexShader(reinterpret_cast(r_basicVertexShader), sizeof(r_basicVertexShader)); - - uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING"); - uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_INSTANCING FLAG_VERTEXCOLOR TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH"); - - UberShaderLibrary::Register("Basic", uberShader); - } - - // PhongLighting shader - { - UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New(); - - String fragmentShader(reinterpret_cast(r_phongLightingFragmentShader), sizeof(r_phongLightingFragmentShader)); - String vertexShader(reinterpret_cast(r_phongLightingVertexShader), sizeof(r_phongLightingVertexShader)); - - uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_DEFERRED FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING EMISSIVE_MAPPING LIGHTING NORMAL_MAPPING PARALLAX_MAPPING SPECULAR_MAPPING"); - uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_DEFERRED FLAG_INSTANCING FLAG_VERTEXCOLOR COMPUTE_TBNMATRIX LIGHTING PARALLAX_MAPPING TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH"); - - UberShaderLibrary::Register("PhongLighting", uberShader); - } - - // Une fois les shaders de base enregistrés, on peut créer le matériau par défaut - s_defaultMaterial = Material::New(); - s_defaultMaterial->Enable(RendererParameter_FaceCulling, false); + s_defaultMaterial = New(); + s_defaultMaterial->EnableFaceCulling(false); s_defaultMaterial->SetFaceFilling(FaceFilling_Line); MaterialLibrary::Register("Default", s_defaultMaterial); return true; } + /*! + * \brief Uninitializes the material librairies + */ void Material::Uninitialize() { s_defaultMaterial.Reset(); - UberShaderLibrary::Unregister("PhongLighting"); - UberShaderLibrary::Unregister("Basic"); + MaterialManager::Uninitialize(); MaterialLibrary::Uninitialize(); } diff --git a/src/Nazara/Graphics/MaterialPipeline.cpp b/src/Nazara/Graphics/MaterialPipeline.cpp new file mode 100644 index 000000000..01e9ffe5d --- /dev/null +++ b/src/Nazara/Graphics/MaterialPipeline.cpp @@ -0,0 +1,168 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include + +#ifndef NAZARA_RENDERER_OPENGL +#define NAZARA_RENDERER_OPENGL // Mandatory to include the OpenGL headers +#endif + +#include +#include +#include + +namespace Nz +{ + namespace + { + const UInt8 r_basicFragmentShader[] = { + #include + }; + + const UInt8 r_basicVertexShader[] = { + #include + }; + + const UInt8 r_phongLightingFragmentShader[] = { + #include + }; + + const UInt8 r_phongLightingVertexShader[] = { + #include + }; + } + + /*! + * \ingroup graphics + * \class Nz::MaterialPipeline + * + * \brief Graphics class used to contains all rendering states that are not allowed to change individually on rendering devices + */ + + /*! + * \brief Returns a reference to a MaterialPipeline built with MaterialPipelineInfo + * + * This function is using a cache, calling it multiples times with the same MaterialPipelineInfo will returns references to a single MaterialPipeline + * + * \param pipelineInfo Pipeline informations used to build/retrieve a MaterialPipeline object + */ + MaterialPipelineRef MaterialPipeline::GetPipeline(const MaterialPipelineInfo& pipelineInfo) + { + auto it = s_pipelineCache.lower_bound(pipelineInfo); + if (it == s_pipelineCache.end() || it->first != pipelineInfo) + it = s_pipelineCache.insert(it, PipelineCache::value_type(pipelineInfo, New(pipelineInfo))); + + return it->second; + } + + void MaterialPipeline::GenerateRenderPipeline(UInt32 flags) const + { + ParameterList list; + list.SetParameter("ALPHA_MAPPING", m_pipelineInfo.hasAlphaMap); + list.SetParameter("ALPHA_TEST", m_pipelineInfo.alphaTest); + list.SetParameter("COMPUTE_TBNMATRIX", m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap); + list.SetParameter("DIFFUSE_MAPPING", m_pipelineInfo.hasDiffuseMap); + list.SetParameter("EMISSIVE_MAPPING", m_pipelineInfo.hasEmissiveMap); + list.SetParameter("NORMAL_MAPPING", m_pipelineInfo.hasNormalMap); + list.SetParameter("PARALLAX_MAPPING", m_pipelineInfo.hasHeightMap); + list.SetParameter("SHADOW_MAPPING", m_pipelineInfo.shadowReceive); + list.SetParameter("SPECULAR_MAPPING", m_pipelineInfo.hasSpecularMap); + list.SetParameter("TEXTURE_MAPPING", m_pipelineInfo.hasAlphaMap || m_pipelineInfo.hasDiffuseMap || m_pipelineInfo.hasEmissiveMap || + m_pipelineInfo.hasNormalMap || m_pipelineInfo.hasHeightMap || m_pipelineInfo.hasSpecularMap || + flags & ShaderFlags_TextureOverlay); + list.SetParameter("TRANSFORM", true); + + list.SetParameter("FLAG_BILLBOARD", static_cast((flags & ShaderFlags_Billboard) != 0)); + list.SetParameter("FLAG_DEFERRED", static_cast((flags & ShaderFlags_Deferred) != 0)); + list.SetParameter("FLAG_INSTANCING", static_cast((flags & ShaderFlags_Instancing) != 0)); + list.SetParameter("FLAG_TEXTUREOVERLAY", static_cast((flags & ShaderFlags_TextureOverlay) != 0)); + list.SetParameter("FLAG_VERTEXCOLOR", static_cast((flags & ShaderFlags_VertexColor) != 0)); + + Instance& instance = m_instances[flags]; + instance.uberInstance = m_pipelineInfo.uberShader->Get(list); + + RenderPipelineInfo renderPipelineInfo; + static_cast(renderPipelineInfo).operator=(m_pipelineInfo); // Not my proudest line + + renderPipelineInfo.shader = instance.uberInstance->GetShader(); + + instance.renderPipeline.Create(renderPipelineInfo); + + #define CacheUniform(name) instance.uniforms[MaterialUniform_##name] = renderPipelineInfo.shader->GetUniformLocation("Material" #name) + + CacheUniform(AlphaMap); + CacheUniform(AlphaThreshold); + CacheUniform(Ambient); + CacheUniform(Diffuse); + CacheUniform(DiffuseMap); + CacheUniform(EmissiveMap); + CacheUniform(HeightMap); + CacheUniform(NormalMap); + CacheUniform(Shininess); + CacheUniform(Specular); + CacheUniform(SpecularMap); + + #undef CacheUniform + } + + bool MaterialPipeline::Initialize() + { + // Basic shader + { + UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New(); + + String fragmentShader(reinterpret_cast(r_basicFragmentShader), sizeof(r_basicFragmentShader)); + String vertexShader(reinterpret_cast(r_basicVertexShader), sizeof(r_basicVertexShader)); + + uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING"); + uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_INSTANCING FLAG_VERTEXCOLOR TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH"); + + UberShaderLibrary::Register("Basic", uberShader); + } + + // PhongLighting shader + { + UberShaderPreprocessorRef uberShader = UberShaderPreprocessor::New(); + + String fragmentShader(reinterpret_cast(r_phongLightingFragmentShader), sizeof(r_phongLightingFragmentShader)); + String vertexShader(reinterpret_cast(r_phongLightingVertexShader), sizeof(r_phongLightingVertexShader)); + + uberShader->SetShader(ShaderStageType_Fragment, fragmentShader, "FLAG_DEFERRED FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING EMISSIVE_MAPPING NORMAL_MAPPING PARALLAX_MAPPING SHADOW_MAPPING SPECULAR_MAPPING"); + uberShader->SetShader(ShaderStageType_Vertex, vertexShader, "FLAG_BILLBOARD FLAG_DEFERRED FLAG_INSTANCING FLAG_VERTEXCOLOR COMPUTE_TBNMATRIX PARALLAX_MAPPING SHADOW_MAPPING TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH"); + + UberShaderLibrary::Register("PhongLighting", uberShader); + } + + // Once the base shaders are registered, we can now set some default materials + MaterialPipelineInfo pipelineInfo; + + // Basic 2D - No depth write/face culling + pipelineInfo.depthWrite = false; + pipelineInfo.faceCulling = false; + + MaterialPipelineLibrary::Register("Basic2D", GetPipeline(pipelineInfo)); + + // Translucent 2D - Alpha blending with no depth write/face culling + pipelineInfo.blending = false; + pipelineInfo.depthWrite = false; + pipelineInfo.faceCulling = false; + pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha; + pipelineInfo.srcBlend = BlendFunc_SrcAlpha; + + MaterialPipelineLibrary::Register("Translucent2D", GetPipeline(pipelineInfo)); + + return true; + } + + void MaterialPipeline::Uninitialize() + { + s_pipelineCache.clear(); + UberShaderLibrary::Unregister("PhongLighting"); + UberShaderLibrary::Unregister("Basic"); + MaterialPipelineLibrary::Uninitialize(); + } + + MaterialPipelineLibrary::LibraryMap MaterialPipeline::s_library; + MaterialPipeline::PipelineCache MaterialPipeline::s_pipelineCache; +} \ No newline at end of file diff --git a/src/Nazara/Graphics/Model.cpp b/src/Nazara/Graphics/Model.cpp index e8954a886..2b9e03cc1 100644 --- a/src/Nazara/Graphics/Model.cpp +++ b/src/Nazara/Graphics/Model.cpp @@ -12,11 +12,26 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::Model + * \brief Graphics class that represents a model + */ + + /*! + * \brief Constructs a ModelParameters object by default + */ + ModelParameters::ModelParameters() { material.shaderName = "PhongLighting"; } + /*! + * \brief Checks whether the parameters for the model are correct + * \return true If parameters are valid + */ + bool ModelParameters::IsValid() const { if (loadMaterials && !material.IsValid()) @@ -25,6 +40,10 @@ namespace Nz return mesh.IsValid(); } + /*! + * \brief Constructs a Model object by default + */ + Model::Model() : m_matCount(0), m_skin(0), @@ -32,11 +51,24 @@ namespace Nz { } + /*! + * \brief Destructs the object and calls Reset + * + * \see Reset + */ + Model::~Model() { Reset(); } + /*! + * \brief Adds this model to the render queue + * + * \param renderQueue Queue to be added + * \param instanceData Data used for this instance + */ + void Model::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const { unsigned int submeshCount = m_mesh->GetSubMeshCount(); @@ -50,10 +82,21 @@ namespace Nz meshData.primitiveMode = mesh->GetPrimitiveMode(); meshData.vertexBuffer = mesh->GetVertexBuffer(); - renderQueue->AddMesh(instanceData.renderOrder, material, meshData, mesh->GetAABB(), instanceData.transformMatrix); + renderQueue->AddMesh(instanceData.renderOrder, material, meshData, mesh->GetAABB(), *instanceData.transformMatrix); } } + /*! + * \brief Gets the material of the named submesh + * \return Pointer to the current material + * + * \param subMeshName Name of the subMesh + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no mesh + * \remark Produces a NazaraError if there is no subMesh with that name + * \remark Produces a NazaraError if material is invalid + */ + Material* Model::GetMaterial(const String& subMeshName) const { #if NAZARA_GRAPHICS_SAFE @@ -78,9 +121,18 @@ namespace Nz return nullptr; } - return m_materials[m_skin*m_matCount + matIndex]; + return m_materials[m_skin * m_matCount + matIndex]; } + /*! + * \brief Gets the material by index + * \return Pointer to the current material + * + * \param matIndex Index of the material + * + * \remark Produces a NazaraError if index is invalid + */ + Material* Model::GetMaterial(unsigned int matIndex) const { #if NAZARA_GRAPHICS_SAFE @@ -91,9 +143,21 @@ namespace Nz } #endif - return m_materials[m_skin*m_matCount + matIndex]; + return m_materials[m_skin * m_matCount + matIndex]; } + /*! + * \brief Gets the material by index of the named submesh + * \return Pointer to the current material + * + * \param skinIndex Index of the skin + * \param subMeshName Name of the subMesh + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if skinIndex is invalid + * \remark Produces a NazaraError if there is no subMesh with that name + * \remark Produces a NazaraError if material index is invalid + */ + Material* Model::GetMaterial(unsigned int skinIndex, const String& subMeshName) const { #if NAZARA_GRAPHICS_SAFE @@ -118,9 +182,20 @@ namespace Nz return nullptr; } - return m_materials[skinIndex*m_matCount + matIndex]; + return m_materials[skinIndex * m_matCount + matIndex]; } + /*! + * \brief Gets the material by index with skin + * \return Pointer to the current material + * + * \param skinIndex Index of the skin + * \param matIndex Index of the material + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if skinIndex is invalid + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if matIndex is invalid + */ + Material* Model::GetMaterial(unsigned int skinIndex, unsigned int matIndex) const { #if NAZARA_GRAPHICS_SAFE @@ -137,49 +212,103 @@ namespace Nz } #endif - return m_materials[skinIndex*m_matCount + matIndex]; + return m_materials[skinIndex * m_matCount + matIndex]; } + /*! + * \brief Gets the number of materials + * \return Current number of materials + */ + unsigned int Model::GetMaterialCount() const { return m_matCount; } + /*! + * \brief Gets the mesh + * \return Current mesh + */ + Mesh* Model::GetMesh() const { return m_mesh; } + /*! + * \brief Gets the skin + * \return Current skin + */ + unsigned int Model::GetSkin() const { return m_skin; } + /*! + * \brief Gets the number of skins + * \return Current number of skins + */ + unsigned int Model::GetSkinCount() const { return m_skinCount; } + /*! + * \brief Checks whether the model is animated + * \return false + */ + bool Model::IsAnimated() const { return false; } + /*! + * \brief Loads the model from file + * \return true if loading is successful + * + * \param filePath Path to the file + * \param params Parameters for the model + */ + bool Model::LoadFromFile(const String& filePath, const ModelParameters& params) { return ModelLoader::LoadFromFile(this, filePath, params); } + /*! + * \brief Loads the model from memory + * \return true if loading is successful + * + * \param data Raw memory + * \param size Size of the memory + * \param params Parameters for the model + */ + bool Model::LoadFromMemory(const void* data, std::size_t size, const ModelParameters& params) { return ModelLoader::LoadFromMemory(this, data, size, params); } + /*! + * \brief Loads the model from stream + * \return true if loading is successful + * + * \param stream Stream to the model + * \param params Parameters for the model + */ + bool Model::LoadFromStream(Stream& stream, const ModelParameters& params) { return ModelLoader::LoadFromStream(this, stream, params); } + /*! + * \brief Resets the model, cleans everything + */ + void Model::Reset() { m_matCount = 0; @@ -192,6 +321,17 @@ namespace Nz } } + /*! + * \brief Sets the material of the named submesh + * \return true If successful + * + * \param subMeshName Name of the subMesh + * \param material Pointer to the material + * + * \remark Produces a NazaraError if there is no subMesh with that name + * \remark Produces a NazaraError if material index is invalid + */ + bool Model::SetMaterial(const String& subMeshName, Material* material) { SubMesh* subMesh = m_mesh->GetSubMesh(subMeshName); @@ -208,7 +348,7 @@ namespace Nz return false; } - unsigned int index = m_skin*m_matCount + matIndex; + unsigned int index = m_skin * m_matCount + matIndex; if (material) m_materials[index] = material; @@ -218,6 +358,16 @@ namespace Nz return true; } + /*! + * \brief Sets the material by index + * \return true If successful + * + * \param matIndex Index of the material + * \param material Pointer to the material + * + * \remark Produces a NazaraError with if NAZARA_GRAPHICS_SAFE defined index is invalid + */ + void Model::SetMaterial(unsigned int matIndex, Material* material) { #if NAZARA_GRAPHICS_SAFE @@ -228,7 +378,7 @@ namespace Nz } #endif - unsigned int index = m_skin*m_matCount + matIndex; + unsigned int index = m_skin * m_matCount + matIndex; if (material) m_materials[index] = material; @@ -236,6 +386,19 @@ namespace Nz m_materials[index] = Material::GetDefault(); } + /*! + * \brief Sets the material by index of the named submesh + * \return true If successful + * + * \param skinIndex Index of the skin + * \param subMeshName Name of the subMesh + * \param material Pointer to the material + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if skinIndex is invalid + * \remark Produces a NazaraError if there is no subMesh with that name + * \remark Produces a NazaraError if material index is invalid + */ + bool Model::SetMaterial(unsigned int skinIndex, const String& subMeshName, Material* material) { #if NAZARA_GRAPHICS_SAFE @@ -260,7 +423,7 @@ namespace Nz return false; } - unsigned int index = skinIndex*m_matCount + matIndex; + unsigned int index = skinIndex * m_matCount + matIndex; if (material) m_materials[index] = material; @@ -270,6 +433,18 @@ namespace Nz return true; } + /*! + * \brief Sets the material by index with skin + * \return true If successful + * + * \param skinIndex Index of the skin + * \param matIndex Index of the material + * \param material Pointer to the material + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if skinIndex is invalid + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if matIndex is invalid + */ + void Model::SetMaterial(unsigned int skinIndex, unsigned int matIndex, Material* material) { #if NAZARA_GRAPHICS_SAFE @@ -286,7 +461,7 @@ namespace Nz } #endif - unsigned int index = skinIndex*m_matCount + matIndex; + unsigned int index = skinIndex * m_matCount + matIndex; if (material) m_materials[index] = material; @@ -294,6 +469,14 @@ namespace Nz m_materials[index] = Material::GetDefault(); } + /*! + * \brief Sets the mesh + * + * \param pointer to the mesh + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if mesh is invalid + */ + void Model::SetMesh(Mesh* mesh) { #if NAZARA_GRAPHICS_SAFE @@ -323,6 +506,14 @@ namespace Nz InvalidateBoundingVolume(); } + /*! + * \brief Sets the skin + * + * \param skin Skin to use + * + * \remark Produces a NazaraError if skin is invalid + */ + void Model::SetSkin(unsigned int skin) { #if NAZARA_GRAPHICS_SAFE @@ -336,6 +527,14 @@ namespace Nz m_skin = skin; } + /*! + * \brief Sets the number of skins + * + * \param skinCount Number of skins + * + * \remark Produces a NazaraError if skinCount equals zero + */ + void Model::SetSkinCount(unsigned int skinCount) { #if NAZARA_GRAPHICS_SAFE @@ -350,6 +549,10 @@ namespace Nz m_skinCount = skinCount; } + /* + * \brief Makes the bounding volume of this billboard + */ + void Model::MakeBoundingVolume() const { if (m_mesh) diff --git a/src/Nazara/Graphics/ParticleController.cpp b/src/Nazara/Graphics/ParticleController.cpp index 252887246..d46bb1621 100644 --- a/src/Nazara/Graphics/ParticleController.cpp +++ b/src/Nazara/Graphics/ParticleController.cpp @@ -7,17 +7,44 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::ParticleController + * \brief Graphics class which controls a flow of particles + * + * \remark This class is abstract + */ + + /*! + * \brief Constructs a ParticleController object by assignation + * + * \param controller ParticleController to copy into this + */ + ParticleController::ParticleController(const ParticleController& controller) : RefCounted() { NazaraUnused(controller); } + /*! + * \brief Destructs the object and calls OnParticleControllerRelease + * + * \see OnParticleControllerRelease + */ + ParticleController::~ParticleController() { OnParticleControllerRelease(this); } + /*! + * \brief Initializes the particle controller librairies + * \return true If successful + * + * \remark Produces a NazaraError if the particle controller library failed to be initialized + */ + bool ParticleController::Initialize() { if (!ParticleControllerLibrary::Initialize()) @@ -29,6 +56,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the particle controller librairies + */ + void ParticleController::Uninitialize() { ParticleControllerLibrary::Uninitialize(); diff --git a/src/Nazara/Graphics/ParticleDeclaration.cpp b/src/Nazara/Graphics/ParticleDeclaration.cpp index 8d68b88ce..dff382d2c 100644 --- a/src/Nazara/Graphics/ParticleDeclaration.cpp +++ b/src/Nazara/Graphics/ParticleDeclaration.cpp @@ -15,23 +15,54 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::ParticleDeclaration + * \brief Graphics class that represents the declaration of the particle, works like an ECS + */ + + /*! + * \brief Constructs a ParticleDeclaration object by default + */ + ParticleDeclaration::ParticleDeclaration() : m_stride(0) { } + /*! + * \brief Constructs a ParticleDeclaration object by assignation + * + * \param declaration ParticleDeclaration to copy into this + */ + ParticleDeclaration::ParticleDeclaration(const ParticleDeclaration& declaration) : RefCounted(), + m_components(declaration.m_components), m_stride(declaration.m_stride) { - std::memcpy(m_components, declaration.m_components, sizeof(Component)*(ParticleComponent_Max+1)); } + /*! + * \brief Destructs the object and calls OnParticleDeclarationRelease + * + * \see OnParticleDeclarationRelease + */ + ParticleDeclaration::~ParticleDeclaration() { OnParticleDeclarationRelease(this); } + /*! + * \brief Disables a component + * + * \param component Component to disable in the declaration + * + * \remark Produces a NazaraError with NAZARA_DEBUG defined if enumeration is invalid + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if enumeration is equal to ParticleComponent_Unused + */ + void ParticleDeclaration::DisableComponent(ParticleComponent component) { #ifdef NAZARA_DEBUG @@ -58,6 +89,17 @@ namespace Nz } } + /*! + * \brief Enables a component + * + * \param component Component to enable in the declaration + * \param type Type of this component + * \param offset Offset in the declaration + * + * \remark Produces a NazaraError with NAZARA_DEBUG defined if enumeration is invalid + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if type is not supported + */ + void ParticleDeclaration::EnableComponent(ParticleComponent component, ComponentType type, unsigned int offset) { #ifdef NAZARA_DEBUG @@ -91,6 +133,18 @@ namespace Nz m_stride += Utility::ComponentStride[type]; } + /*! + * \brief Gets a component + * + * \param component Component in the declaration + * \param enabled Optional argument to get if this component is enabled + * \param type Optional argument to get if the type of the component + * \param offset Optional argument to get if the offset in the declaration + * + * \remark Produces a NazaraError with NAZARA_DEBUG defined if enumeration is invalid + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if enumeration is equal to ParticleComponent_Unused + */ + void ParticleDeclaration::GetComponent(ParticleComponent component, bool* enabled, ComponentType* type, unsigned int* offset) const { #ifdef NAZARA_DEBUG @@ -121,37 +175,67 @@ namespace Nz *offset = particleComponent.offset; } + /*! + * \brief Gets the stride of the declaration + * \return Stride of the declaration + */ + unsigned int ParticleDeclaration::GetStride() const { return m_stride; } + /*! + * \brief Sets the stride of the declaration + * + * \param stride Stride of the declaration + */ + void ParticleDeclaration::SetStride(unsigned int stride) { m_stride = stride; } + /*! + * \brief Sets the current particle declaration with the content of the other one + * \return A reference to this + * + * \param declaration The other ParticleDeclaration + */ + ParticleDeclaration& ParticleDeclaration::operator=(const ParticleDeclaration& declaration) { - std::memcpy(m_components, declaration.m_components, sizeof(Component)*(ParticleComponent_Max+1)); + m_components = declaration.m_components; m_stride = declaration.m_stride; return *this; } + /*! + * \brief Gets the particle declaration based on the layout + * \return Pointer to the declaration + * + * \param layout Layout of the particle declaration + * + * \remark Produces a NazaraError with NAZARA_DEBUG if enumeration is invalid + */ + ParticleDeclaration* ParticleDeclaration::Get(ParticleLayout layout) { - #ifdef NAZARA_DEBUG - if (layout > ParticleLayout_Max) - { - NazaraError("Particle layout out of enum"); - return nullptr; - } - #endif + NazaraAssert(layout <= ParticleLayout_Max, "Particle layout out of enum"); return &s_declarations[layout]; } + /*! + * \brief Checks whether the type is supported + * \return true If it is the case + * + * \param type Type of the component + * + * \remark Produces a NazaraError if enumeration is invalid + */ + bool ParticleDeclaration::IsTypeSupported(ComponentType type) { switch (type) @@ -177,6 +261,14 @@ namespace Nz return false; } + /*! + * \brief Initializes the particle declaration librairies + * \return true If successful + * + * \remark Produces a NazaraError if the particle declaration library failed to be initialized + * \remark Produces a NazaraAssert if memory layout of declaration does not match the corresponding structure + */ + bool ParticleDeclaration::Initialize() { if (!ParticleDeclarationLibrary::Initialize()) @@ -216,9 +308,9 @@ namespace Nz declaration = &s_declarations[ParticleLayout_Sprite]; declaration->EnableComponent(ParticleComponent_Color, ComponentType_Color, NazaraOffsetOf(ParticleStruct_Sprite, color)); declaration->EnableComponent(ParticleComponent_Life, ComponentType_Int1, NazaraOffsetOf(ParticleStruct_Sprite, life)); - declaration->EnableComponent(ParticleComponent_Position, ComponentType_Float2, NazaraOffsetOf(ParticleStruct_Sprite, position)); + declaration->EnableComponent(ParticleComponent_Position, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Sprite, position)); declaration->EnableComponent(ParticleComponent_Rotation, ComponentType_Float1, NazaraOffsetOf(ParticleStruct_Sprite, rotation)); - declaration->EnableComponent(ParticleComponent_Velocity, ComponentType_Float2, NazaraOffsetOf(ParticleStruct_Sprite, velocity)); + declaration->EnableComponent(ParticleComponent_Velocity, ComponentType_Float3, NazaraOffsetOf(ParticleStruct_Sprite, velocity)); NazaraAssert(declaration->GetStride() == sizeof(ParticleStruct_Sprite), "Invalid stride for declaration ParticleLayout_Sprite"); } @@ -231,11 +323,15 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the particle declaration librairies + */ + void ParticleDeclaration::Uninitialize() { ParticleDeclarationLibrary::Uninitialize(); } - ParticleDeclaration ParticleDeclaration::s_declarations[ParticleLayout_Max+1]; + std::array ParticleDeclaration::s_declarations; ParticleDeclarationLibrary::LibraryMap ParticleDeclaration::s_library; } diff --git a/src/Nazara/Graphics/ParticleEmitter.cpp b/src/Nazara/Graphics/ParticleEmitter.cpp index 9e87eb7c0..fe27575e7 100644 --- a/src/Nazara/Graphics/ParticleEmitter.cpp +++ b/src/Nazara/Graphics/ParticleEmitter.cpp @@ -7,13 +7,23 @@ #include #include #include -#include +#include #include #include #include namespace Nz { + /*! + * \ingroup graphics + * \class Nz::ParticleEmitter + * \brief Graphics class that represents an emitter of particles + */ + + /*! + * \brief Constructs a ParticleEmitter object by default + */ + ParticleEmitter::ParticleEmitter() : m_lagCompensationEnabled(false), m_emissionAccumulator(0.f), @@ -22,30 +32,57 @@ namespace Nz { } - ParticleEmitter::~ParticleEmitter() = default; + ParticleEmitter::ParticleEmitter(const ParticleEmitter& emitter) : + m_lagCompensationEnabled(emitter.m_lagCompensationEnabled), + m_emissionAccumulator(0.f), + m_emissionRate(emitter.m_emissionRate), + m_emissionCount(emitter.m_emissionCount) + { + } - void ParticleEmitter::Emit(ParticleSystem& system, float elapsedTime) const + ParticleEmitter::ParticleEmitter(ParticleEmitter&& emitter) : + m_lagCompensationEnabled(emitter.m_lagCompensationEnabled), + m_emissionAccumulator(0.f), + m_emissionRate(emitter.m_emissionRate), + m_emissionCount(emitter.m_emissionCount) + { + OnParticleEmitterMove(&emitter, this); + } + + ParticleEmitter::~ParticleEmitter() + { + OnParticleEmitterRelease(this); + } + + /*! + * \brief Emits particles according to the delta time between the previous frame + * + * \param system Particle system to work on + * \param elapsedTime Delta time between the previous frame + */ + + void ParticleEmitter::Emit(ParticleGroup& system, float elapsedTime) const { if (m_emissionRate > 0.f) { - // On accumule la partie réelle (pour éviter qu'un taux d'update élevé empêche des particules de se former) - m_emissionAccumulator += elapsedTime*m_emissionRate; + // We accumulate the real part (to avoid that a high emission rate prevents particles to form) + m_emissionAccumulator += elapsedTime * m_emissionRate; - float emissionCount = std::floor(m_emissionAccumulator); // Le nombre d'émissions de cette mise à jour - m_emissionAccumulator -= emissionCount; // On enlève la partie entière + float emissionCount = std::floor(m_emissionAccumulator); // The number of emissions in this update + m_emissionAccumulator -= emissionCount; // We get rid off the integer part if (emissionCount >= 1.f) { - // On calcule le nombre maximum de particules pouvant être émises cette fois-ci + // We compute the maximum number of particles which can be emitted unsigned int emissionCountInt = static_cast(emissionCount); - unsigned int maxParticleCount = emissionCountInt*m_emissionCount; + unsigned int maxParticleCount = emissionCountInt * m_emissionCount; - // On récupère le nombre de particules qu'il est possible de créer selon l'espace libre + // We get the number of particles that we are able to create (depending on the free space) unsigned int particleCount = std::min(maxParticleCount, system.GetMaxParticleCount() - system.GetParticleCount()); if (particleCount == 0) return; - // Et on émet nos particules + // And we emit our particles void* particles = system.GenerateParticles(particleCount); ParticleMapper mapper(particles, system.GetDeclaration()); @@ -53,42 +90,85 @@ namespace Nz if (m_lagCompensationEnabled) { - // On va maintenant appliquer les contrôleurs - float invEmissionRate = 1.f/m_emissionRate; + // We will now apply our controllers + float invEmissionRate = 1.f / m_emissionRate; for (unsigned int i = 1; i <= emissionCountInt; ++i) - system.ApplyControllers(mapper, std::min(m_emissionCount*i, particleCount), invEmissionRate); + system.ApplyControllers(mapper, std::min(m_emissionCount * i, particleCount), invEmissionRate); } } } } + /*! + * \brief Enables the lag compensation + * + * \param enable Should lag compensation be enabled + */ + void ParticleEmitter::EnableLagCompensation(bool enable) { m_lagCompensationEnabled = enable; } + /*! + * \brief Gets the emission count + * \return Current emission count + */ + unsigned int ParticleEmitter::GetEmissionCount() const { return m_emissionCount; } + /*! + * \brief Gets the emission rate + * \return Current emission rate + */ + float ParticleEmitter::GetEmissionRate() const { return m_emissionRate; } + /*! + * \brief Checks whether the lag compensation is enabled + * \return true If it is the case + */ + bool ParticleEmitter::IsLagCompensationEnabled() const { return m_lagCompensationEnabled; } + /*! + * \brief Sets the emission count + * + * \param count Emission count + */ + void ParticleEmitter::SetEmissionCount(unsigned int count) { m_emissionCount = count; } + /*! + * \brief Sets the emission rate + * + * \param rate Emission rate + */ + void ParticleEmitter::SetEmissionRate(float rate) { m_emissionRate = rate; } + + ParticleEmitter& ParticleEmitter::operator=(ParticleEmitter && emitter) + { + m_emissionCount = emitter.m_emissionCount; + m_emissionRate = emitter.m_emissionRate; + m_lagCompensationEnabled = emitter.m_lagCompensationEnabled; + + OnParticleEmitterMove(&emitter, this); + return *this; + } } diff --git a/src/Nazara/Graphics/ParticleGenerator.cpp b/src/Nazara/Graphics/ParticleGenerator.cpp index d5e3b80ff..304a99e8c 100644 --- a/src/Nazara/Graphics/ParticleGenerator.cpp +++ b/src/Nazara/Graphics/ParticleGenerator.cpp @@ -7,17 +7,44 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::ParticleGenerator + * \brief Graphics class which generates particles + * + * \remark This class is abstract + */ + + /*! + * \brief Constructs a ParticleGenerator object by assignation + * + * \param generator ParticleGenerator to copy into this + */ + ParticleGenerator::ParticleGenerator(const ParticleGenerator& generator) : RefCounted() { NazaraUnused(generator); } + /*! + * \brief Destructs the object and calls OnParticleGeneratorRelease + * + * \see OnParticleGeneratorRelease + */ + ParticleGenerator::~ParticleGenerator() { OnParticleGeneratorRelease(this); } + /*! + * \brief Initializes the particle generator librairies + * \return true If successful + * + * \remark Produces a NazaraError if the particle generator library failed to be initialized + */ + bool ParticleGenerator::Initialize() { if (!ParticleGeneratorLibrary::Initialize()) @@ -29,6 +56,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the particle generator librairies + */ + void ParticleGenerator::Uninitialize() { ParticleGeneratorLibrary::Uninitialize(); diff --git a/src/Nazara/Graphics/ParticleGroup.cpp b/src/Nazara/Graphics/ParticleGroup.cpp new file mode 100644 index 000000000..5aca6c2b4 --- /dev/null +++ b/src/Nazara/Graphics/ParticleGroup.cpp @@ -0,0 +1,497 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup graphics + * \class Nz::ParticleSystem + * \brief Graphics class that represents the system to handle particles + */ + + /*! + * \brief Constructs a ParticleSystem object with a maximal number of particles and a layout + * + * \param maxParticleCount Maximum number of particles to generate + * \param layout Enumeration for the layout of data information for the particles + */ + + ParticleGroup::ParticleGroup(unsigned int maxParticleCount, ParticleLayout layout) : + ParticleGroup(maxParticleCount, ParticleDeclaration::Get(layout)) + { + } + + /*! + * \brief Constructs a ParticleSystem object with a maximal number of particles and a particle declaration + * + * \param maxParticleCount Maximum number of particles to generate + * \param declaration Data information for the particles + */ + + ParticleGroup::ParticleGroup(unsigned int maxParticleCount, ParticleDeclarationConstRef declaration) : + m_declaration(std::move(declaration)), + m_processing(false), + m_maxParticleCount(maxParticleCount), + m_particleCount(0) + { + // In case of error, the constructor can only throw an exception + ErrorFlags flags(ErrorFlag_ThrowException, true); + + m_particleSize = m_declaration->GetStride(); // The size of each particle + + ResizeBuffer(); + } + + /*! + * \brief Constructs a ParticleSystem object by assignation + * + * \param system ParticleSystem to copy into this + */ + + ParticleGroup::ParticleGroup(const ParticleGroup& system) : + Renderable(system), + m_controllers(system.m_controllers), + m_generators(system.m_generators), + m_declaration(system.m_declaration), + m_renderer(system.m_renderer), + m_processing(false), + m_maxParticleCount(system.m_maxParticleCount), + m_particleCount(system.m_particleCount), + m_particleSize(system.m_particleSize) + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + + ResizeBuffer(); + + // We only copy alive particles + std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize); + } + + ParticleGroup::~ParticleGroup() + { + OnParticleGroupRelease(this); + } + + /*! + * \brief Adds a controller to the particles + * + * \param controller Controller for the particles + * + * \remark Produces a NazaraAssert if controller is invalid + */ + + void ParticleGroup::AddController(ParticleControllerRef controller) + { + NazaraAssert(controller, "Invalid particle controller"); + + m_controllers.emplace_back(std::move(controller)); + } + + /*! + * \brief Adds an emitter to the particles + * + * \param emitter Emitter for the particles + * + * \remark Produces a NazaraAssert if emitter is invalid + */ + + void ParticleGroup::AddEmitter(ParticleEmitter* emitter) + { + NazaraAssert(emitter, "Invalid particle emitter"); + + EmitterEntry entry; + entry.emitter = emitter; + entry.moveSlot.Connect(emitter->OnParticleEmitterMove, this, &ParticleGroup::OnEmitterMove); + entry.releaseSlot.Connect(emitter->OnParticleEmitterRelease, this, &ParticleGroup::OnEmitterRelease); + + m_emitters.emplace_back(std::move(entry)); + } + + /*! + * \brief Adds a generator to the particles + * + * \param generator Generator for the particles + * + * \remark Produces a NazaraAssert if generator is invalid + */ + + void ParticleGroup::AddGenerator(ParticleGeneratorRef generator) + { + NazaraAssert(generator, "Invalid particle generator"); + + m_generators.emplace_back(std::move(generator)); + } + + /*! + * \brief Adds the particle system to the rendering queue + * + * \param renderQueue Queue to be added + * \param transformMatrix Transformation matrix for the system + * + * \remark Produces a NazaraAssert if inner renderer is invalid + * \remark Produces a NazaraAssert if renderQueue is invalid + */ + + void ParticleGroup::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const + { + NazaraAssert(m_renderer, "Invalid particle renderer"); + NazaraAssert(renderQueue, "Invalid renderqueue"); + NazaraUnused(transformMatrix); + + if (m_particleCount > 0) + { + ParticleMapper mapper(m_buffer.data(), m_declaration); + m_renderer->Render(*this, mapper, 0, m_particleCount - 1, renderQueue); + } + } + + /*! + * \brief Applies the controllers + * + * \param mapper Mapper containing layout information of each particle + * \param particleCount Number of particles + * \param elapsedTime Delta time between the previous frame + */ + void ParticleGroup::ApplyControllers(ParticleMapper& mapper, unsigned int particleCount, float elapsedTime) + { + m_processing = true; + + // To avoid a lock in case of exception + CallOnExit onExit([this]() + { + m_processing = false; + }); + + for (ParticleController* controller : m_controllers) + controller->Apply(*this, mapper, 0, particleCount - 1, elapsedTime); + + onExit.CallAndReset(); + + // We only kill now the dead particles during the update + if (m_dyingParticles.size() < m_particleCount) + { + // We kill them in reverse order, std::set sorting them via std::greater + // The reason is simple, as the death of a particle means moving the last particle in the buffer, + // without this solution, certain particles could avoid death + for (unsigned int index : m_dyingParticles) + KillParticle(index); + } + else + KillParticles(); // Every particles are dead, this is way faster + + m_dyingParticles.clear(); + } + + /*! + * \brief Creates one particle + * \return Pointer to the particle memory buffer + */ + + void* ParticleGroup::CreateParticle() + { + return CreateParticles(1); + } + + /*! + * \brief Creates multiple particles + * \return Pointer to the first particle memory buffer + */ + + void* ParticleGroup::CreateParticles(unsigned int count) + { + if (count == 0) + return nullptr; + + if (m_particleCount + count > m_maxParticleCount) + return nullptr; + + unsigned int particlesIndex = m_particleCount; + m_particleCount += count; + + return &m_buffer[particlesIndex * m_particleSize]; + } + + /*! + * \brief Generates one particle + * \return Pointer to the particle memory buffer + */ + + void* ParticleGroup::GenerateParticle() + { + return GenerateParticles(1); + } + + /*! + * \brief Generates multiple particles + * \return Pointer to the first particle memory buffer + */ + + void* ParticleGroup::GenerateParticles(unsigned int count) + { + void* ptr = CreateParticles(count); + if (!ptr) + return nullptr; + + ParticleMapper mapper(ptr, m_declaration); + for (ParticleGenerator* generator : m_generators) + generator->Generate(*this, mapper, 0, count - 1); + + return ptr; + } + + /*! + * \brief Gets the particle declaration + * \return Particle declaration + */ + + const ParticleDeclarationConstRef& ParticleGroup::GetDeclaration() const + { + return m_declaration; + } + + /*! + * \brief Gets the maximum number of particles + * \return Current maximum number + */ + + unsigned int ParticleGroup::GetMaxParticleCount() const + { + return m_maxParticleCount; + } + + /*! + * \brief Gets the number of particles + * \return Current number + */ + + unsigned int ParticleGroup::GetParticleCount() const + { + return m_particleCount; + } + + /*! + * \brief Gets the size of particles + * \return Current size + */ + + unsigned int ParticleGroup::GetParticleSize() const + { + return m_particleSize; + } + + /*! + * \brief Kills one particle + * + * \param index Index of the particle + */ + + void ParticleGroup::KillParticle(unsigned int index) + { + ///FIXME: Verify the index + + if (m_processing) + { + // The buffer is being modified, we can not reduce its size, we put the particle in the waiting list + m_dyingParticles.insert(index); + return; + } + + // We move the last alive particle to the place of this one + if (--m_particleCount > 0) + std::memcpy(&m_buffer[index * m_particleSize], &m_buffer[m_particleCount * m_particleSize], m_particleSize); + } + + /*! + * \brief Kills every particles + */ + + void ParticleGroup::KillParticles() + { + m_particleCount = 0; + } + + /*! + * \brief Removes a controller to the particles + * + * \param controller Controller for the particles to remove + */ + + void ParticleGroup::RemoveController(ParticleController* controller) + { + auto it = std::find(m_controllers.begin(), m_controllers.end(), controller); + if (it != m_controllers.end()) + m_controllers.erase(it); + } + + /*! + * \brief Removes an emitter to the particles + * + * \param emitter Emitter for the particles to remove + */ + + void ParticleGroup::RemoveEmitter(ParticleEmitter* emitter) + { + for (auto it = m_emitters.begin(); it != m_emitters.end(); ++it) + { + if (it->emitter == emitter) + { + m_emitters.erase(it); + break; + } + } + } + + /*! + * \brief Removes a generator to the particles + * + * \param generator Generator for the particles to remove + */ + + void ParticleGroup::RemoveGenerator(ParticleGenerator* generator) + { + auto it = std::find(m_generators.begin(), m_generators.end(), generator); + if (it != m_generators.end()) + m_generators.erase(it); + } + + /*! + * \brief Sets the renderer of the particles + * + * \param renderer Renderer for the particles + */ + + void ParticleGroup::SetRenderer(ParticleRenderer* renderer) + { + m_renderer = renderer; + } + + /*! + * \brief Updates the system + * + * \param elapsedTime Delta time between the previous frame + */ + + void ParticleGroup::Update(float elapsedTime) + { + // Emission + for (const EmitterEntry& entry : m_emitters) + entry.emitter->Emit(*this, elapsedTime); + + // Update + if (m_particleCount > 0) + { + ///TODO: Update using threads + ParticleMapper mapper(m_buffer.data(), m_declaration); + ApplyControllers(mapper, m_particleCount, elapsedTime); + } + } + + /*! + * \brief Updates the bounding volume by a matrix + * + * \param transformMatrix Matrix transformation for our bounding volume + */ + + void ParticleGroup::UpdateBoundingVolume(const Matrix4f& transformMatrix) + { + NazaraUnused(transformMatrix); + + // Nothing to do here (our bounding volume is global) + } + + /*! + * \brief Sets the current particle system with the content of the other one + * \return A reference to this + * + * \param system The other ParticleSystem + */ + + ParticleGroup& ParticleGroup::operator=(const ParticleGroup& system) + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + + Renderable::operator=(system); + + m_controllers = system.m_controllers; + m_declaration = system.m_declaration; + m_generators = system.m_generators; + m_maxParticleCount = system.m_maxParticleCount; + m_particleCount = system.m_particleCount; + m_particleSize = system.m_particleSize; + m_renderer = system.m_renderer; + + // The copy can not (or should not) happen during the update, there is no use to copy + m_dyingParticles.clear(); + m_processing = false; + + m_buffer.clear(); // To avoid a copy due to resize() which will be pointless + ResizeBuffer(); + + // We only copy alive particles + std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount * m_particleSize); + + return *this; + } + + /*! + * \brief Makes the bounding volume of this text + */ + + void ParticleGroup::MakeBoundingVolume() const + { + ///TODO: Compute the AABB (taking into account the size of particles) + m_boundingVolume.MakeInfinite(); + } + + void ParticleGroup::OnEmitterMove(ParticleEmitter* oldEmitter, ParticleEmitter* newEmitter) + { + for (EmitterEntry& entry : m_emitters) + { + if (entry.emitter == oldEmitter) + entry.emitter = newEmitter; + } + } + + void ParticleGroup::OnEmitterRelease(const ParticleEmitter* emitter) + { + for (auto it = m_emitters.begin(); it != m_emitters.end();) + { + if (it->emitter == emitter) + it = m_emitters.erase(it); + else + ++it; + } + } + + /*! + * \brief Resizes the internal buffer + * + * \remark Produces a NazaraError if resize did not work + */ + + void ParticleGroup::ResizeBuffer() + { + // Just to have a better description of our problem in case of error + try + { + m_buffer.resize(m_maxParticleCount*m_particleSize); + } + catch (const std::exception& e) + { + StringStream stream; + stream << "Failed to allocate particle buffer (" << e.what() << ") for " << m_maxParticleCount << " particles of size " << m_particleSize; + + NazaraError(stream.ToString()); + } + } +} diff --git a/src/Nazara/Graphics/ParticleMapper.cpp b/src/Nazara/Graphics/ParticleMapper.cpp index ce883e6e0..27a4b4075 100644 --- a/src/Nazara/Graphics/ParticleMapper.cpp +++ b/src/Nazara/Graphics/ParticleMapper.cpp @@ -8,6 +8,19 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::ParticleMapper + * \brief Graphics class that represents the mapping between the internal buffer and the particle declaration + */ + + /*! + * \brief Constructs a ParticleMapper object with a raw buffer and a particle declaration + * + * \param buffer Raw buffer to store particles data + * \param declaration Declaration of the particle + */ + ParticleMapper::ParticleMapper(void* buffer, const ParticleDeclaration* declaration) : m_declaration(declaration), m_ptr(static_cast(buffer)) diff --git a/src/Nazara/Graphics/ParticleRenderer.cpp b/src/Nazara/Graphics/ParticleRenderer.cpp index 96ff3dd8a..e5fa56c74 100644 --- a/src/Nazara/Graphics/ParticleRenderer.cpp +++ b/src/Nazara/Graphics/ParticleRenderer.cpp @@ -7,17 +7,42 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::ParticleRenderer + * \brief Graphics class that represents the rendering of the particle + */ + + /*! + * \brief Constructs a ParticleRenderer object by assignation + * + * \param renderer ParticleRenderer to copy into this + */ + ParticleRenderer::ParticleRenderer(const ParticleRenderer& renderer) : RefCounted() { NazaraUnused(renderer); } + /*! + * \brief Destructs the object and calls OnParticleRendererRelease + * + * \see OnParticleRendererRelease + */ + ParticleRenderer::~ParticleRenderer() { OnParticleRendererRelease(this); } + /*! + * \brief Initializes the particle renderer librairies + * \return true If successful + * + * \remark Produces a NazaraError if the particle renderer library failed to be initialized + */ + bool ParticleRenderer::Initialize() { if (!ParticleRendererLibrary::Initialize()) @@ -29,6 +54,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the particle renderer librairies + */ + void ParticleRenderer::Uninitialize() { ParticleRendererLibrary::Uninitialize(); diff --git a/src/Nazara/Graphics/ParticleSystem.cpp b/src/Nazara/Graphics/ParticleSystem.cpp deleted file mode 100644 index 2ac54e4af..000000000 --- a/src/Nazara/Graphics/ParticleSystem.cpp +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright (C) 2015 Jérôme Leclercq -// This file is part of the "Nazara Engine - Graphics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - ParticleSystem::ParticleSystem(unsigned int maxParticleCount, ParticleLayout layout) : - ParticleSystem(maxParticleCount, ParticleDeclaration::Get(layout)) - { - } - - ParticleSystem::ParticleSystem(unsigned int maxParticleCount, ParticleDeclarationConstRef declaration) : - m_declaration(std::move(declaration)), - m_processing(false), - m_maxParticleCount(maxParticleCount), - m_particleCount(0) - { - // En cas d'erreur, un constructeur ne peut que lancer une exception - ErrorFlags flags(ErrorFlag_ThrowException, true); - - m_particleSize = m_declaration->GetStride(); // La taille de chaque particule - - ResizeBuffer(); - } - - ParticleSystem::ParticleSystem(const ParticleSystem& system) : - Renderable(system), - m_controllers(system.m_controllers), - m_generators(system.m_generators), - m_declaration(system.m_declaration), - m_renderer(system.m_renderer), - m_processing(false), - m_maxParticleCount(system.m_maxParticleCount), - m_particleCount(system.m_particleCount), - m_particleSize(system.m_particleSize) - { - ErrorFlags flags(ErrorFlag_ThrowException, true); - - ResizeBuffer(); - - // On ne copie que les particules vivantes - std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize); - } - - ParticleSystem::~ParticleSystem() = default; - - void ParticleSystem::AddController(ParticleControllerRef controller) - { - NazaraAssert(controller, "Invalid particle controller"); - - m_controllers.emplace_back(std::move(controller)); - } - - void ParticleSystem::AddEmitter(ParticleEmitter* emitter) - { - NazaraAssert(emitter, "Invalid particle emitter"); - - m_emitters.emplace_back(emitter); - } - - void ParticleSystem::AddGenerator(ParticleGeneratorRef generator) - { - NazaraAssert(generator, "Invalid particle generator"); - - m_generators.emplace_back(std::move(generator)); - } - - void ParticleSystem::AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const - { - NazaraAssert(m_renderer, "Invalid particle renderer"); - NazaraAssert(renderQueue, "Invalid renderqueue"); - NazaraUnused(transformMatrix); - - if (m_particleCount > 0) - { - ParticleMapper mapper(m_buffer.data(), m_declaration); - m_renderer->Render(*this, mapper, 0, m_particleCount-1, renderQueue); - } - } - - void ParticleSystem::ApplyControllers(ParticleMapper& mapper, unsigned int particleCount, float elapsedTime) - { - m_processing = true; - - // Pour éviter un verrouillage en cas d'exception - CallOnExit onExit([this]() - { - m_processing = false; - }); - - for (ParticleController* controller : m_controllers) - controller->Apply(*this, mapper, 0, particleCount-1, elapsedTime); - - onExit.CallAndReset(); - - // On tue maintenant les particules mortes durant la mise à jour - if (m_dyingParticles.size() < m_particleCount) - { - // On tue les particules depuis la dernière vers la première (en terme de place), le std::set étant trié via std::greater - // La raison est simple, étant donné que la mort d'une particule signifie le déplacement de la dernière particule du buffer, - // sans cette solution certaines particules pourraient échapper à la mort - for (unsigned int index : m_dyingParticles) - KillParticle(index); - } - else - KillParticles(); // Toutes les particules sont mortes, ceci est beaucoup plus rapide - - m_dyingParticles.clear(); - } - - void* ParticleSystem::CreateParticle() - { - return CreateParticles(1); - } - - void* ParticleSystem::CreateParticles(unsigned int count) - { - if (count == 0) - return nullptr; - - if (m_particleCount + count > m_maxParticleCount) - return nullptr; - - unsigned int particlesIndex = m_particleCount; - m_particleCount += count; - - return &m_buffer[particlesIndex*m_particleSize]; - } - - void* ParticleSystem::GenerateParticle() - { - return GenerateParticles(1); - } - - void* ParticleSystem::GenerateParticles(unsigned int count) - { - void* ptr = CreateParticles(count); - if (!ptr) - return nullptr; - - ParticleMapper mapper(ptr, m_declaration); - for (ParticleGenerator* generator : m_generators) - generator->Generate(*this, mapper, 0, count-1); - - return ptr; - } - - const ParticleDeclarationConstRef& ParticleSystem::GetDeclaration() const - { - return m_declaration; - } - - float ParticleSystem::GetFixedStepSize() const - { - return m_stepSize; - } - - unsigned int ParticleSystem::GetMaxParticleCount() const - { - return m_maxParticleCount; - } - - unsigned int ParticleSystem::GetParticleCount() const - { - return m_particleCount; - } - - unsigned int ParticleSystem::GetParticleSize() const - { - return m_particleSize; - } - - void ParticleSystem::KillParticle(unsigned int index) - { - ///FIXME: Vérifier index - - if (m_processing) - { - // Le buffer est en train d'être modifié, nous ne pouvons pas réduire sa taille, on place alors la particule dans une liste d'attente - m_dyingParticles.insert(index); - return; - } - - // On déplace la dernière particule vivante à la place de celle-ci - if (--m_particleCount > 0) - std::memcpy(&m_buffer[index*m_particleSize], &m_buffer[m_particleCount*m_particleSize], m_particleSize); - } - - void ParticleSystem::KillParticles() - { - m_particleCount = 0; - } - - void ParticleSystem::RemoveController(ParticleController* controller) - { - auto it = std::find(m_controllers.begin(), m_controllers.end(), controller); - if (it != m_controllers.end()) - m_controllers.erase(it); - } - - void ParticleSystem::RemoveEmitter(ParticleEmitter* emitter) - { - auto it = std::find(m_emitters.begin(), m_emitters.end(), emitter); - if (it != m_emitters.end()) - m_emitters.erase(it); - } - - void ParticleSystem::RemoveGenerator(ParticleGenerator* generator) - { - auto it = std::find(m_generators.begin(), m_generators.end(), generator); - if (it != m_generators.end()) - m_generators.erase(it); - } - - void ParticleSystem::SetFixedStepSize(float stepSize) - { - m_stepSize = stepSize; - } - - void ParticleSystem::SetRenderer(ParticleRenderer* renderer) - { - m_renderer = renderer; - } - - void ParticleSystem::Update(float elapsedTime) - { - // Émission - for (ParticleEmitter* emitter : m_emitters) - emitter->Emit(*this, elapsedTime); - - // Mise à jour - if (m_particleCount > 0) - { - ///TODO: Mettre à jour en utilisant des threads - ParticleMapper mapper(m_buffer.data(), m_declaration); - ApplyControllers(mapper, m_particleCount, elapsedTime); - } - } - - void ParticleSystem::UpdateBoundingVolume(const Matrix4f& transformMatrix) - { - NazaraUnused(transformMatrix); - - // Nothing to do here (our bounding volume is global) - } - - ParticleSystem& ParticleSystem::operator=(const ParticleSystem& system) - { - ErrorFlags flags(ErrorFlag_ThrowException, true); - - Renderable::operator=(system); - - m_controllers = system.m_controllers; - m_declaration = system.m_declaration; - m_generators = system.m_generators; - m_maxParticleCount = system.m_maxParticleCount; - m_particleCount = system.m_particleCount; - m_particleSize = system.m_particleSize; - m_renderer = system.m_renderer; - m_stepSize = system.m_stepSize; - - // La copie ne peut pas (ou plutôt ne devrait pas) avoir lieu pendant une mise à jour, inutile de copier - m_dyingParticles.clear(); - m_processing = false; - m_stepAccumulator = 0.f; - - m_buffer.clear(); // Pour éviter une recopie lors du resize() qui ne servira pas à grand chose - ResizeBuffer(); - - // On ne copie que les particules vivantes - std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize); - - return *this; - } - - void ParticleSystem::MakeBoundingVolume() const - { - ///TODO: Calculer l'AABB (prendre la taille des particules en compte s'il y a) - m_boundingVolume.MakeInfinite(); - } - - void ParticleSystem::ResizeBuffer() - { - // Histoire de décrire un peu mieux l'erreur en cas d'échec - try - { - m_buffer.resize(m_maxParticleCount*m_particleSize); - } - catch (const std::exception& e) - { - StringStream stream; - stream << "Failed to allocate particle buffer (" << e.what() << ") for " << m_maxParticleCount << " particles of size " << m_particleSize; - - NazaraError(stream.ToString()); - } - } -} diff --git a/src/Nazara/Graphics/RenderTechniques.cpp b/src/Nazara/Graphics/RenderTechniques.cpp index b21c31218..46d84d9c4 100644 --- a/src/Nazara/Graphics/RenderTechniques.cpp +++ b/src/Nazara/Graphics/RenderTechniques.cpp @@ -18,11 +18,12 @@ namespace Nz "Advanced Forward", "Basic Forward", "Deferred Shading", + "Depth Pass", "Light Pre-Pass", "User" }; - static_assert(sizeof(techniquesName)/sizeof(const char*) == RenderTechniqueType_Max+1, "Render technique type name array is incomplete"); + static_assert(sizeof(techniquesName) / sizeof(const char*) == RenderTechniqueType_Max + 1, "Render technique type name array is incomplete"); struct RenderTechnique { @@ -33,6 +34,22 @@ namespace Nz std::unordered_map s_renderTechniques; } + /*! + * \ingroup graphics + * \class Nz::RenderTechniques + * \brief Graphics class that represents the techniques used in rendering + */ + + /*! + * \brief Gets the technique by enumeration + * \return A reference to the newly created technique + * + * \param renderTechnique Enumeration of the technique + * \param techniqueRanking Ranking for the technique + * + * \remark Produces a NazaraError if renderTechnique does not exist + */ + AbstractRenderTechnique* RenderTechniques::GetByEnum(RenderTechniqueType renderTechnique, int* techniqueRanking) { #ifdef NAZARA_DEBUG @@ -46,6 +63,16 @@ namespace Nz return GetByName(techniquesName[renderTechnique], techniqueRanking); } + /*! + * \brief Gets the technique by index + * \return A reference to the newly created technique + * + * \param index Index of the technique + * \param techniqueRanking Ranking for the technique + * + * \remark Produces a NazaraError if index is out or range + */ + AbstractRenderTechnique* RenderTechniques::GetByIndex(unsigned int index, int* techniqueRanking) { #if NAZARA_GRAPHICS_SAFE @@ -65,6 +92,16 @@ namespace Nz return it->second.factory(); } + /*! + * \brief Gets the technique by name + * \return A reference to the newly created technique + * + * \param name Name of the technique + * \param techniqueRanking Ranking for the technique + * + * \remark Produces a NazaraError if name does not exist or is invalid + */ + AbstractRenderTechnique* RenderTechniques::GetByName(const String& name, int* techniqueRanking) { #if NAZARA_GRAPHICS_SAFE @@ -88,6 +125,16 @@ namespace Nz return it->second.factory(); } + /*! + * \brief Gets the technique by ranking + * \return A reference to the newly created technique + * + * \param maxRanking Ranking maximum of the technique + * \param techniqueRanking Ranking for the technique + * + * \remark Produces a NazaraError if name does not exist or is invalid + */ + AbstractRenderTechnique* RenderTechniques::GetByRanking(int maxRanking, int* techniqueRanking) { if (maxRanking < 0) @@ -118,11 +165,28 @@ namespace Nz return technique->factory(); } + /*! + * \brief Gets the number of techniques available + * \return Number of techniques + */ + unsigned int RenderTechniques::GetCount() { return s_renderTechniques.size(); } + /*! + * \brief Registers a technique + * + * \param name Name of the technique + * \param ranking Ranking of the technique + * \param factory Factory to create the technique + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if name is empty + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if ranking is negative + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if factory is invalid is invalid + */ + void RenderTechniques::Register(const String& name, int ranking, RenderTechniqueFactory factory) { #if NAZARA_GRAPHICS_SAFE @@ -148,6 +212,15 @@ namespace Nz s_renderTechniques[name] = {factory, ranking}; } + /*! + * \brief Converts the enumeration to string + * \return String symbolizing the technique + * + * \param renderTechnique Enumeration of the technique + * + * \remark Produces a NazaraError if renderTechnique does not exist and returns "Error" + */ + String RenderTechniques::ToString(RenderTechniqueType renderTechnique) { #ifdef NAZARA_DEBUG @@ -161,6 +234,12 @@ namespace Nz return techniquesName[renderTechnique]; } + /*! + * \brief Unregisters a technique + * + * \param name Name of the technique + */ + void RenderTechniques::Unregister(const String& name) { s_renderTechniques.erase(name); diff --git a/src/Nazara/Graphics/Renderable.cpp b/src/Nazara/Graphics/Renderable.cpp index 932a8d127..5b02cd5cb 100644 --- a/src/Nazara/Graphics/Renderable.cpp +++ b/src/Nazara/Graphics/Renderable.cpp @@ -7,8 +7,24 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::Renderable + * \brief Graphics class that represents a renderable element for our scene + * + * \remark This class is abstract + */ + Renderable::~Renderable() = default; + /*! + * \brief Culls the model if not in the frustum + * \return true If renderable is in the frustum + * + * \param frustum Symbolizing the field of view + * \param transformMatrix Matrix transformation for our object + */ + bool Renderable::Cull(const Frustumf& frustum, const Matrix4f& transformMatrix) const { NazaraUnused(transformMatrix); @@ -16,6 +32,11 @@ namespace Nz return frustum.Contains(m_boundingVolume); } + /*! + * \brief Gets the bounding volume + * \return Bounding volume of the renderable element + */ + const BoundingVolumef& Renderable::GetBoundingVolume() const { EnsureBoundingVolumeUpdated(); @@ -23,6 +44,12 @@ namespace Nz return m_boundingVolume; } + /*! + * \brief Updates the bounding volume by a matrix + * + * \param transformMatrix Matrix transformation for our bounding volume + */ + void Renderable::UpdateBoundingVolume(const Matrix4f& transformMatrix) { m_boundingVolume.Update(transformMatrix); diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag b/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag index 80ad84697..a3391be91 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag +++ b/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag @@ -8,6 +8,7 @@ layout(early_fragment_tests) in; /********************Entrant********************/ in vec4 vColor; +in vec4 vLightSpacePos[3]; in mat3 vLightToWorld; in vec3 vNormal; in vec2 vTexCoord; @@ -29,10 +30,13 @@ struct Light vec4 parameters1; vec4 parameters2; vec2 parameters3; + bool shadowMapping; }; // Lumières uniform Light Lights[3]; +uniform samplerCube PointLightShadowMap[3]; +uniform sampler2D DirectionalSpotLightShadowMap[3]; // Matériau uniform sampler2D MaterialAlphaMap; @@ -81,6 +85,43 @@ vec4 EncodeNormal(in vec3 normal) return vec4(vec2(atan(normal.y, normal.x)/kPI, normal.z), 0.0, 0.0); } +float VectorToDepthValue(vec3 vec, float zNear, float zFar) +{ + vec3 absVec = abs(vec); + float localZ = max(absVec.x, max(absVec.y, absVec.z)); + + float normZ = ((zFar + zNear) * localZ - (2.0*zFar*zNear)) / ((zFar - zNear)*localZ); + return (normZ + 1.0) * 0.5; +} + +#if SHADOW_MAPPING +float CalculateDirectionalShadowFactor(int lightIndex) +{ + vec4 lightSpacePos = vLightSpacePos[lightIndex]; + return (texture(DirectionalSpotLightShadowMap[lightIndex], lightSpacePos.xy).x >= (lightSpacePos.z - 0.0005)) ? 1.0 : 0.0; +} + +float CalculatePointShadowFactor(int lightIndex, vec3 lightToWorld, float zNear, float zFar) +{ + return (texture(PointLightShadowMap[lightIndex], vec3(lightToWorld.x, -lightToWorld.y, -lightToWorld.z)).x >= VectorToDepthValue(lightToWorld, zNear, zFar)) ? 1.0 : 0.0; +} + +float CalculateSpotShadowFactor(int lightIndex) +{ + vec4 lightSpacePos = vLightSpacePos[lightIndex]; + + float visibility = 1.0; + float x,y; + for (y = -3.5; y <= 3.5; y+= 1.0) + for (x = -3.5; x <= 3.5; x+= 1.0) + visibility += (textureProj(DirectionalSpotLightShadowMap[lightIndex], lightSpacePos.xyw + vec3(x/1024.0 * lightSpacePos.w, y/1024.0 * lightSpacePos.w, 0.0)).x >= (lightSpacePos.z - 0.0005)/lightSpacePos.w) ? 1.0 : 0.0; + + visibility /= 64.0; + + return visibility; +} +#endif + void main() { vec4 diffuseColor = MaterialDiffuse * vColor; @@ -91,7 +132,7 @@ void main() vec2 texCoord = vTexCoord; #endif -#if LIGHTING && PARALLAX_MAPPING +#if PARALLAX_MAPPING float height = texture(MaterialHeightMap, texCoord).r; float v = height*ParallaxScale + ParallaxBias; @@ -118,17 +159,16 @@ void main() discard; #endif // ALPHA_TEST - #if LIGHTING - #if NORMAL_MAPPING + #if NORMAL_MAPPING vec3 normal = normalize(vLightToWorld * (2.0 * vec3(texture(MaterialNormalMap, texCoord)) - 1.0)); - #else + #else vec3 normal = normalize(vNormal); - #endif // NORMAL_MAPPING + #endif // NORMAL_MAPPING vec3 specularColor = MaterialSpecular.rgb; - #if SPECULAR_MAPPING + #if SPECULAR_MAPPING specularColor *= texture(MaterialSpecularMap, texCoord).rgb; - #endif + #endif /* Texture0: Diffuse Color + Specular @@ -138,9 +178,6 @@ void main() RenderTarget0 = vec4(diffuseColor.rgb, dot(specularColor, vec3(0.3, 0.59, 0.11))); RenderTarget1 = vec4(EncodeNormal(normal)); RenderTarget2 = vec4(FloatToColor(gl_FragCoord.z), (MaterialShininess == 0.0) ? 0.0 : max(log2(MaterialShininess), 0.1)/10.5); // http://www.guerrilla-games.com/publications/dr_kz2_rsx_dev07.pdf - #else // LIGHTING - RenderTarget0 = vec4(diffuseColor.rgb, 0.0); - #endif #else // FLAG_DEFERRED #if ALPHA_MAPPING diffuseColor.a *= texture(MaterialAlphaMap, texCoord).r; @@ -151,16 +188,15 @@ void main() discard; #endif - #if LIGHTING vec3 lightAmbient = vec3(0.0); vec3 lightDiffuse = vec3(0.0); vec3 lightSpecular = vec3(0.0); - #if NORMAL_MAPPING + #if NORMAL_MAPPING vec3 normal = normalize(vLightToWorld * (2.0 * vec3(texture(MaterialNormalMap, texCoord)) - 1.0)); - #else + #else vec3 normal = normalize(vNormal); - #endif + #endif if (MaterialShininess > 0.0) { @@ -168,6 +204,10 @@ void main() for (int i = 0; i < 3; ++i) { + vec4 lightColor = Lights[i].color; + float lightAmbientFactor = Lights[i].factors.x; + float lightDiffuseFactor = Lights[i].factors.y; + switch (Lights[i].type) { case LIGHT_DIRECTIONAL: @@ -175,75 +215,120 @@ void main() vec3 lightDir = -Lights[i].parameters1.xyz; // Ambient - lightAmbient += Lights[i].color.rgb * Lights[i].factors.x * (MaterialAmbient.rgb + SceneAmbient.rgb); + lightAmbient += lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb); + + float att = 1.0; + + #if SHADOW_MAPPING + if (Lights[i].shadowMapping) + { + float shadowFactor = CalculateDirectionalShadowFactor(i); + if (shadowFactor == 0.0) + break; + + att *= shadowFactor; + } + #endif // Diffuse float lambert = max(dot(normal, lightDir), 0.0); - lightDiffuse += lambert * Lights[i].color.rgb * Lights[i].factors.y; + lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor; // Specular vec3 reflection = reflect(-lightDir, normal); float specularFactor = max(dot(reflection, eyeVec), 0.0); specularFactor = pow(specularFactor, MaterialShininess); - lightSpecular += specularFactor * Lights[i].color.rgb; + lightSpecular += att * specularFactor * lightColor.rgb; break; } case LIGHT_POINT: { - vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos; - float lightDirLength = length(lightDir); - lightDir /= lightDirLength; // Normalisation + vec3 lightPos = Lights[i].parameters1.xyz; + float lightAttenuation = Lights[i].parameters1.w; + float lightInvRadius = Lights[i].parameters2.w; - float att = max(Lights[i].parameters1.w - Lights[i].parameters2.w*lightDirLength, 0.0); + vec3 worldToLight = lightPos - vWorldPos; + float lightDirLength = length(worldToLight); + vec3 lightDir = worldToLight / lightDirLength; // Normalisation + + float att = max(lightAttenuation - lightInvRadius * lightDirLength, 0.0); // Ambient - lightAmbient += att * Lights[i].color.rgb * Lights[i].factors.x * (MaterialAmbient.rgb + SceneAmbient.rgb); + lightAmbient += att * lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb); + + #if SHADOW_MAPPING + if (Lights[i].shadowMapping) + { + float shadowFactor = CalculatePointShadowFactor(i, vWorldPos - lightPos, 0.1, 50.0); + if (shadowFactor == 0.0) + break; + + att *= shadowFactor; + } + #endif // Diffuse float lambert = max(dot(normal, lightDir), 0.0); - lightDiffuse += att * lambert * Lights[i].color.rgb * Lights[i].factors.y; + lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor; // Specular vec3 reflection = reflect(-lightDir, normal); float specularFactor = max(dot(reflection, eyeVec), 0.0); specularFactor = pow(specularFactor, MaterialShininess); - lightSpecular += att * specularFactor * Lights[i].color.rgb; + lightSpecular += att * specularFactor * lightColor.rgb; break; } case LIGHT_SPOT: { - vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos; - float lightDirLength = length(lightDir); - lightDir /= lightDirLength; // Normalisation + vec3 lightPos = Lights[i].parameters1.xyz; + vec3 lightDir = Lights[i].parameters2.xyz; + float lightAttenuation = Lights[i].parameters1.w; + float lightInvRadius = Lights[i].parameters2.w; + float lightInnerAngle = Lights[i].parameters3.x; + float lightOuterAngle = Lights[i].parameters3.y; + + vec3 worldToLight = lightPos - vWorldPos; + float lightDistance = length(worldToLight); + worldToLight /= lightDistance; // Normalisation - float att = max(Lights[i].parameters1.w - Lights[i].parameters2.w*lightDirLength, 0.0); + float att = max(lightAttenuation - lightInvRadius * lightDistance, 0.0); // Ambient - lightAmbient += att * Lights[i].color.rgb * Lights[i].factors.x * (MaterialAmbient.rgb + SceneAmbient.rgb); + lightAmbient += att * lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb); + + #if SHADOW_MAPPING + if (Lights[i].shadowMapping) + { + float shadowFactor = CalculateSpotShadowFactor(i); + if (shadowFactor == 0.0) + break; + + att *= shadowFactor; + } + #endif // Modification de l'atténuation pour gérer le spot - float curAngle = dot(Lights[i].parameters2.xyz, -lightDir); - float outerAngle = Lights[i].parameters3.y; - float innerMinusOuterAngle = Lights[i].parameters3.x - outerAngle; - att *= max((curAngle - outerAngle) / innerMinusOuterAngle, 0.0); + float curAngle = dot(lightDir, -worldToLight); + float innerMinusOuterAngle = lightInnerAngle - lightOuterAngle; + att *= max((curAngle - lightOuterAngle) / innerMinusOuterAngle, 0.0); // Diffuse - float lambert = max(dot(normal, lightDir), 0.0); + float lambert = max(dot(normal, worldToLight), 0.0); - lightDiffuse += att * lambert * Lights[i].color.rgb * Lights[i].factors.y; + lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor; // Specular - vec3 reflection = reflect(-lightDir, normal); + vec3 reflection = reflect(-worldToLight, normal); float specularFactor = max(dot(reflection, eyeVec), 0.0); specularFactor = pow(specularFactor, MaterialShininess); - lightSpecular += att * specularFactor * Lights[i].color.rgb; + lightSpecular += att * specularFactor * lightColor.rgb; break; } @@ -256,61 +341,110 @@ void main() { for (int i = 0; i < 3; ++i) { + vec4 lightColor = Lights[i].color; + float lightAmbientFactor = Lights[i].factors.x; + float lightDiffuseFactor = Lights[i].factors.y; + switch (Lights[i].type) { case LIGHT_DIRECTIONAL: { - vec3 lightDir = normalize(-Lights[i].parameters1.xyz); + vec3 lightDir = -Lights[i].parameters1.xyz; // Ambient - lightAmbient += Lights[i].color.rgb * Lights[i].factors.x * (MaterialAmbient.rgb + SceneAmbient.rgb); + lightAmbient += lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb); + + float att = 1.0; + + #if SHADOW_MAPPING + if (Lights[i].shadowMapping) + { + float shadowFactor = CalculateDirectionalShadowFactor(i); + if (shadowFactor == 0.0) + break; + + att *= shadowFactor; + } + #endif // Diffuse float lambert = max(dot(normal, lightDir), 0.0); - lightDiffuse += lambert * Lights[i].color.rgb * Lights[i].factors.y; + lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor; break; } case LIGHT_POINT: { - vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos; - float lightDirLength = length(lightDir); - lightDir /= lightDirLength; // Normalisation + vec3 lightPos = Lights[i].parameters1.xyz; + float lightAttenuation = Lights[i].parameters1.w; + float lightInvRadius = Lights[i].parameters2.w; - float att = max(Lights[i].parameters1.w - Lights[i].parameters2.w*lightDirLength, 0.0); + vec3 worldToLight = lightPos - vWorldPos; + float lightDirLength = length(worldToLight); + vec3 lightDir = worldToLight / lightDirLength; // Normalisation + + float att = max(lightAttenuation - lightInvRadius * lightDirLength, 0.0); // Ambient - lightAmbient += att * Lights[i].color.rgb * Lights[i].factors.x * (MaterialAmbient.rgb + SceneAmbient.rgb); + lightAmbient += att * lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb); + + #if SHADOW_MAPPING + if (Lights[i].shadowMapping) + { + float shadowFactor = CalculatePointShadowFactor(i, vWorldPos - lightPos, 0.1, 50.0); + if (shadowFactor == 0.0) + break; + + att *= shadowFactor; + } + #endif // Diffuse float lambert = max(dot(normal, lightDir), 0.0); - lightDiffuse += att * lambert * Lights[i].color.rgb * Lights[i].factors.y; + lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor; break; } case LIGHT_SPOT: { - vec3 lightDir = Lights[i].parameters1.xyz - vWorldPos; - float lightDirLength = length(lightDir); - lightDir /= lightDirLength; // Normalisation + vec3 lightPos = Lights[i].parameters1.xyz; + vec3 lightDir = Lights[i].parameters2.xyz; + float lightAttenuation = Lights[i].parameters1.w; + float lightInvRadius = Lights[i].parameters2.w; + float lightInnerAngle = Lights[i].parameters3.x; + float lightOuterAngle = Lights[i].parameters3.y; + + vec3 worldToLight = lightPos - vWorldPos; + float lightDistance = length(worldToLight); + worldToLight /= lightDistance; // Normalisation - float att = max(Lights[i].parameters1.w - Lights[i].parameters2.w*lightDirLength, 0.0); + float att = max(lightAttenuation - lightInvRadius * lightDistance, 0.0); // Ambient - lightAmbient += att * Lights[i].color.rgb * Lights[i].factors.x * (MaterialAmbient.rgb + SceneAmbient.rgb); + lightAmbient += att * lightColor.rgb * lightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb); + + #if SHADOW_MAPPING + if (Lights[i].shadowMapping) + { + float shadowFactor = CalculateSpotShadowFactor(i); + if (shadowFactor == 0.0) + break; + + att *= shadowFactor; + } + #endif // Modification de l'atténuation pour gérer le spot - float curAngle = dot(Lights[i].parameters2.xyz, -lightDir); - float outerAngle = Lights[i].parameters3.y; - float innerMinusOuterAngle = Lights[i].parameters3.x - outerAngle; - att *= max((curAngle - outerAngle) / innerMinusOuterAngle, 0.0); + float curAngle = dot(lightDir, -worldToLight); + float innerMinusOuterAngle = lightInnerAngle - lightOuterAngle; + att *= max((curAngle - lightOuterAngle) / innerMinusOuterAngle, 0.0); // Diffuse - float lambert = max(dot(normal, lightDir), 0.0); + float lambert = max(dot(normal, worldToLight), 0.0); - lightDiffuse += att * lambert * Lights[i].color.rgb * Lights[i].factors.y; + lightDiffuse += att * lambert * lightColor.rgb * lightDiffuseFactor; } default: @@ -318,25 +452,23 @@ void main() } } } - + lightSpecular *= MaterialSpecular.rgb; - #if SPECULAR_MAPPING + #if SPECULAR_MAPPING lightSpecular *= texture(MaterialSpecularMap, texCoord).rgb; // Utiliser l'alpha de MaterialSpecular n'aurait aucun sens - #endif + #endif vec3 lightColor = (lightAmbient + lightDiffuse + lightSpecular); vec4 fragmentColor = vec4(lightColor, 1.0) * diffuseColor; - #if EMISSIVE_MAPPING + #if EMISSIVE_MAPPING float lightIntensity = dot(lightColor, vec3(0.3, 0.59, 0.11)); vec3 emissionColor = MaterialDiffuse.rgb * texture(MaterialEmissiveMap, texCoord).rgb; RenderTarget0 = vec4(mix(fragmentColor.rgb, emissionColor, clamp(1.0 - 3.0*lightIntensity, 0.0, 1.0)), fragmentColor.a); - #else - RenderTarget0 = fragmentColor; - #endif // EMISSIVE_MAPPING #else - RenderTarget0 = diffuseColor; - #endif // LIGHTING + RenderTarget0 = fragmentColor; + #endif // EMISSIVE_MAPPING #endif // FLAG_DEFERRED } + diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag.h b/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag.h index 29895eefa..c77a33db2 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag.h +++ b/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.frag.h @@ -1 +1 @@ -35,105,102,32,69,65,82,76,89,95,70,82,65,71,77,69,78,84,95,84,69,83,84,83,32,38,38,32,33,65,76,80,72,65,95,84,69,83,84,13,10,108,97,121,111,117,116,40,101,97,114,108,121,95,102,114,97,103,109,101,110,116,95,116,101,115,116,115,41,32,105,110,59,13,10,35,101,110,100,105,102,13,10,13,10,35,100,101,102,105,110,101,32,76,73,71,72,84,95,68,73,82,69,67,84,73,79,78,65,76,32,48,13,10,35,100,101,102,105,110,101,32,76,73,71,72,84,95,80,79,73,78,84,32,49,13,10,35,100,101,102,105,110,101,32,76,73,71,72,84,95,83,80,79,84,32,50,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,69,110,116,114,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,105,110,32,118,101,99,52,32,118,67,111,108,111,114,59,13,10,105,110,32,109,97,116,51,32,118,76,105,103,104,116,84,111,87,111,114,108,100,59,13,10,105,110,32,118,101,99,51,32,118,78,111,114,109,97,108,59,13,10,105,110,32,118,101,99,50,32,118,84,101,120,67,111,111,114,100,59,13,10,105,110,32,118,101,99,51,32,118,86,105,101,119,68,105,114,59,13,10,105,110,32,118,101,99,51,32,118,87,111,114,108,100,80,111,115,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,83,111,114,116,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,48,59,13,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,49,59,13,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,50,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,85,110,105,102,111,114,109,101,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,115,116,114,117,99,116,32,76,105,103,104,116,13,10,123,13,10,9,105,110,116,32,116,121,112,101,59,13,10,9,118,101,99,52,32,99,111,108,111,114,59,13,10,9,118,101,99,50,32,102,97,99,116,111,114,115,59,13,10,13,10,9,118,101,99,52,32,112,97,114,97,109,101,116,101,114,115,49,59,13,10,9,118,101,99,52,32,112,97,114,97,109,101,116,101,114,115,50,59,13,10,9,118,101,99,50,32,112,97,114,97,109,101,116,101,114,115,51,59,13,10,125,59,13,10,13,10,47,47,32,76,117,109,105,195,168,114,101,115,13,10,117,110,105,102,111,114,109,32,76,105,103,104,116,32,76,105,103,104,116,115,91,51,93,59,13,10,13,10,47,47,32,77,97,116,195,169,114,105,97,117,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,59,13,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,59,13,10,117,110,105,102,111,114,109,32,118,101,99,52,32,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,59,13,10,117,110,105,102,111,114,109,32,118,101,99,52,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,59,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,59,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,69,109,105,115,115,105,118,101,77,97,112,59,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,72,101,105,103,104,116,77,97,112,59,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,78,111,114,109,97,108,77,97,112,59,13,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,59,13,10,117,110,105,102,111,114,109,32,118,101,99,52,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,59,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,77,97,112,59,13,10,13,10,47,47,32,65,117,116,114,101,115,13,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,80,97,114,97,108,108,97,120,66,105,97,115,32,61,32,45,48,46,48,51,59,13,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,80,97,114,97,108,108,97,120,83,99,97,108,101,32,61,32,48,46,48,50,59,13,10,117,110,105,102,111,114,109,32,118,101,99,50,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,13,10,117,110,105,102,111,114,109,32,118,101,99,51,32,69,121,101,80,111,115,105,116,105,111,110,59,13,10,117,110,105,102,111,114,109,32,118,101,99,52,32,83,99,101,110,101,65,109,98,105,101,110,116,59,13,10,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,84,101,120,116,117,114,101,79,118,101,114,108,97,121,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,70,111,110,99,116,105,111,110,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,118,101,99,51,32,70,108,111,97,116,84,111,67,111,108,111,114,40,102,108,111,97,116,32,102,41,13,10,123,13,10,9,118,101,99,51,32,99,111,108,111,114,59,13,10,13,10,9,102,32,42,61,32,50,53,54,46,48,59,13,10,9,99,111,108,111,114,46,120,32,61,32,102,108,111,111,114,40,102,41,59,13,10,13,10,9,102,32,61,32,40,102,32,45,32,99,111,108,111,114,46,120,41,32,42,32,50,53,54,46,48,59,13,10,9,99,111,108,111,114,46,121,32,61,32,102,108,111,111,114,40,102,41,59,13,10,13,10,9,99,111,108,111,114,46,122,32,61,32,102,32,45,32,99,111,108,111,114,46,121,59,13,10,9,99,111,108,111,114,46,120,121,32,42,61,32,48,46,48,48,51,57,48,54,50,53,59,32,47,47,32,42,61,32,49,46,48,47,50,53,54,13,10,13,10,9,114,101,116,117,114,110,32,99,111,108,111,114,59,13,10,125,13,10,13,10,35,100,101,102,105,110,101,32,107,80,73,32,51,46,49,52,49,53,57,50,54,53,51,54,13,10,13,10,118,101,99,52,32,69,110,99,111,100,101,78,111,114,109,97,108,40,105,110,32,118,101,99,51,32,110,111,114,109,97,108,41,13,10,123,13,10,9,47,47,114,101,116,117,114,110,32,118,101,99,52,40,110,111,114,109,97,108,42,48,46,53,32,43,32,48,46,53,44,32,48,46,48,41,59,13,10,9,114,101,116,117,114,110,32,118,101,99,52,40,118,101,99,50,40,97,116,97,110,40,110,111,114,109,97,108,46,121,44,32,110,111,114,109,97,108,46,120,41,47,107,80,73,44,32,110,111,114,109,97,108,46,122,41,44,32,48,46,48,44,32,48,46,48,41,59,13,10,125,13,10,13,10,118,111,105,100,32,109,97,105,110,40,41,13,10,123,13,10,9,118,101,99,52,32,100,105,102,102,117,115,101,67,111,108,111,114,32,61,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,32,42,32,118,67,111,108,111,114,59,13,10,13,10,35,105,102,32,65,85,84,79,95,84,69,88,67,79,79,82,68,83,13,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,103,108,95,70,114,97,103,67,111,111,114,100,46,120,121,32,42,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,13,10,35,101,108,115,101,13,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,118,84,101,120,67,111,111,114,100,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,76,73,71,72,84,73,78,71,32,38,38,32,80,65,82,65,76,76,65,88,95,77,65,80,80,73,78,71,13,10,9,102,108,111,97,116,32,104,101,105,103,104,116,32,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,72,101,105,103,104,116,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,59,13,10,9,102,108,111,97,116,32,118,32,61,32,104,101,105,103,104,116,42,80,97,114,97,108,108,97,120,83,99,97,108,101,32,43,32,80,97,114,97,108,108,97,120,66,105,97,115,59,13,10,13,10,9,118,101,99,51,32,118,105,101,119,68,105,114,32,61,32,110,111,114,109,97,108,105,122,101,40,118,86,105,101,119,68,105,114,41,59,13,10,9,116,101,120,67,111,111,114,100,32,43,61,32,118,32,42,32,118,105,101,119,68,105,114,46,120,121,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,68,73,70,70,85,83,69,95,77,65,80,80,73,78,71,13,10,9,100,105,102,102,117,115,101,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,44,32,116,101,120,67,111,111,114,100,41,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,70,76,65,71,95,84,69,88,84,85,82,69,79,86,69,82,76,65,89,13,10,9,100,105,102,102,117,115,101,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,84,101,120,116,117,114,101,79,118,101,114,108,97,121,44,32,116,101,120,67,111,111,114,100,41,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,70,76,65,71,95,68,69,70,69,82,82,69,68,13,10,9,35,105,102,32,65,76,80,72,65,95,84,69,83,84,13,10,9,9,47,47,32,73,110,117,116,105,108,101,32,100,101,32,102,97,105,114,101,32,100,101,32,108,39,97,108,112,104,97,45,109,97,112,112,105,110,103,32,115,97,110,115,32,97,108,112,104,97,45,116,101,115,116,32,101,110,32,68,101,102,101,114,114,101,100,32,40,108,39,97,108,112,104,97,32,110,39,101,115,116,32,112,97,115,32,115,97,117,118,101,103,97,114,100,195,169,32,100,97,110,115,32,108,101,32,71,45,66,117,102,102,101,114,41,13,10,9,9,35,105,102,32,65,76,80,72,65,95,77,65,80,80,73,78,71,13,10,9,100,105,102,102,117,115,101,67,111,108,111,114,46,97,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,59,13,10,9,9,35,101,110,100,105,102,13,10,9,9,13,10,9,105,102,32,40,100,105,102,102,117,115,101,67,111,108,111,114,46,97,32,60,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,41,13,10,9,9,100,105,115,99,97,114,100,59,13,10,9,35,101,110,100,105,102,32,47,47,32,65,76,80,72,65,95,84,69,83,84,13,10,13,10,9,35,105,102,32,76,73,71,72,84,73,78,71,13,10,9,9,35,105,102,32,78,79,82,77,65,76,95,77,65,80,80,73,78,71,13,10,9,118,101,99,51,32,110,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,118,76,105,103,104,116,84,111,87,111,114,108,100,32,42,32,40,50,46,48,32,42,32,118,101,99,51,40,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,78,111,114,109,97,108,77,97,112,44,32,116,101,120,67,111,111,114,100,41,41,32,45,32,49,46,48,41,41,59,13,10,9,9,35,101,108,115,101,13,10,9,118,101,99,51,32,110,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,118,78,111,114,109,97,108,41,59,13,10,9,9,35,101,110,100,105,102,32,47,47,32,78,79,82,77,65,76,95,77,65,80,80,73,78,71,13,10,13,10,9,118,101,99,51,32,115,112,101,99,117,108,97,114,67,111,108,111,114,32,61,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,46,114,103,98,59,13,10,9,9,35,105,102,32,83,80,69,67,85,76,65,82,95,77,65,80,80,73,78,71,13,10,9,115,112,101,99,117,108,97,114,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,103,98,59,13,10,9,9,35,101,110,100,105,102,13,10,13,10,9,47,42,13,10,9,84,101,120,116,117,114,101,48,58,32,68,105,102,102,117,115,101,32,67,111,108,111,114,32,43,32,83,112,101,99,117,108,97,114,13,10,9,84,101,120,116,117,114,101,49,58,32,78,111,114,109,97,108,32,43,32,83,112,101,99,117,108,97,114,13,10,9,84,101,120,116,117,114,101,50,58,32,69,110,99,111,100,101,100,32,100,101,112,116,104,32,43,32,83,104,105,110,105,110,101,115,115,13,10,9,42,47,13,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,118,101,99,52,40,100,105,102,102,117,115,101,67,111,108,111,114,46,114,103,98,44,32,100,111,116,40,115,112,101,99,117,108,97,114,67,111,108,111,114,44,32,118,101,99,51,40,48,46,51,44,32,48,46,53,57,44,32,48,46,49,49,41,41,41,59,13,10,9,82,101,110,100,101,114,84,97,114,103,101,116,49,32,61,32,118,101,99,52,40,69,110,99,111,100,101,78,111,114,109,97,108,40,110,111,114,109,97,108,41,41,59,13,10,9,82,101,110,100,101,114,84,97,114,103,101,116,50,32,61,32,118,101,99,52,40,70,108,111,97,116,84,111,67,111,108,111,114,40,103,108,95,70,114,97,103,67,111,111,114,100,46,122,41,44,32,40,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,32,61,61,32,48,46,48,41,32,63,32,48,46,48,32,58,32,109,97,120,40,108,111,103,50,40,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,41,44,32,48,46,49,41,47,49,48,46,53,41,59,32,47,47,32,104,116,116,112,58,47,47,119,119,119,46,103,117,101,114,114,105,108,108,97,45,103,97,109,101,115,46,99,111,109,47,112,117,98,108,105,99,97,116,105,111,110,115,47,100,114,95,107,122,50,95,114,115,120,95,100,101,118,48,55,46,112,100,102,13,10,9,35,101,108,115,101,32,47,47,32,76,73,71,72,84,73,78,71,13,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,118,101,99,52,40,100,105,102,102,117,115,101,67,111,108,111,114,46,114,103,98,44,32,48,46,48,41,59,13,10,9,35,101,110,100,105,102,13,10,35,101,108,115,101,32,47,47,32,70,76,65,71,95,68,69,70,69,82,82,69,68,13,10,9,35,105,102,32,65,76,80,72,65,95,77,65,80,80,73,78,71,13,10,9,100,105,102,102,117,115,101,67,111,108,111,114,46,97,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,59,13,10,9,35,101,110,100,105,102,13,10,13,10,9,35,105,102,32,65,76,80,72,65,95,84,69,83,84,13,10,9,105,102,32,40,100,105,102,102,117,115,101,67,111,108,111,114,46,97,32,60,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,41,13,10,9,9,100,105,115,99,97,114,100,59,13,10,9,35,101,110,100,105,102,13,10,13,10,9,35,105,102,32,76,73,71,72,84,73,78,71,13,10,9,118,101,99,51,32,108,105,103,104,116,65,109,98,105,101,110,116,32,61,32,118,101,99,51,40,48,46,48,41,59,13,10,9,118,101,99,51,32,108,105,103,104,116,68,105,102,102,117,115,101,32,61,32,118,101,99,51,40,48,46,48,41,59,13,10,9,118,101,99,51,32,108,105,103,104,116,83,112,101,99,117,108,97,114,32,61,32,118,101,99,51,40,48,46,48,41,59,13,10,13,10,9,9,35,105,102,32,78,79,82,77,65,76,95,77,65,80,80,73,78,71,13,10,9,118,101,99,51,32,110,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,118,76,105,103,104,116,84,111,87,111,114,108,100,32,42,32,40,50,46,48,32,42,32,118,101,99,51,40,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,78,111,114,109,97,108,77,97,112,44,32,116,101,120,67,111,111,114,100,41,41,32,45,32,49,46,48,41,41,59,13,10,9,9,35,101,108,115,101,13,10,9,118,101,99,51,32,110,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,118,78,111,114,109,97,108,41,59,13,10,9,9,35,101,110,100,105,102,13,10,13,10,9,105,102,32,40,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,32,62,32,48,46,48,41,13,10,9,123,13,10,9,9,118,101,99,51,32,101,121,101,86,101,99,32,61,32,110,111,114,109,97,108,105,122,101,40,69,121,101,80,111,115,105,116,105,111,110,32,45,32,118,87,111,114,108,100,80,111,115,41,59,13,10,13,10,9,9,102,111,114,32,40,105,110,116,32,105,32,61,32,48,59,32,105,32,60,32,51,59,32,43,43,105,41,13,10,9,9,123,13,10,9,9,9,115,119,105,116,99,104,32,40,76,105,103,104,116,115,91,105,93,46,116,121,112,101,41,13,10,9,9,9,123,13,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,68,73,82,69,67,84,73,79,78,65,76,58,13,10,9,9,9,9,123,13,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,45,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,59,13,10,13,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,13,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,120,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,13,10,13,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,13,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,108,97,109,98,101,114,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,121,59,13,10,13,10,9,9,9,9,9,47,47,32,83,112,101,99,117,108,97,114,13,10,9,9,9,9,9,118,101,99,51,32,114,101,102,108,101,99,116,105,111,110,32,61,32,114,101,102,108,101,99,116,40,45,108,105,103,104,116,68,105,114,44,32,110,111,114,109,97,108,41,59,13,10,9,9,9,9,9,102,108,111,97,116,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,109,97,120,40,100,111,116,40,114,101,102,108,101,99,116,105,111,110,44,32,101,121,101,86,101,99,41,44,32,48,46,48,41,59,13,10,9,9,9,9,9,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,112,111,119,40,115,112,101,99,117,108,97,114,70,97,99,116,111,114,44,32,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,41,59,13,10,13,10,9,9,9,9,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,43,61,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,59,13,10,9,9,9,9,9,98,114,101,97,107,59,13,10,9,9,9,9,125,13,10,13,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,80,79,73,78,84,58,13,10,9,9,9,9,123,13,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,32,45,32,118,87,111,114,108,100,80,111,115,59,13,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,32,61,32,108,101,110,103,116,104,40,108,105,103,104,116,68,105,114,41,59,13,10,9,9,9,9,9,108,105,103,104,116,68,105,114,32,47,61,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,59,32,47,47,32,78,111,114,109,97,108,105,115,97,116,105,111,110,13,10,9,9,9,9,9,13,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,109,97,120,40,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,119,32,45,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,119,42,108,105,103,104,116,68,105,114,76,101,110,103,116,104,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,13,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,97,116,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,120,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,13,10,13,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,13,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,13,10,9,9,9,9,9,13,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,121,59,13,10,13,10,9,9,9,9,9,47,47,32,83,112,101,99,117,108,97,114,13,10,9,9,9,9,9,118,101,99,51,32,114,101,102,108,101,99,116,105,111,110,32,61,32,114,101,102,108,101,99,116,40,45,108,105,103,104,116,68,105,114,44,32,110,111,114,109,97,108,41,59,13,10,9,9,9,9,9,102,108,111,97,116,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,109,97,120,40,100,111,116,40,114,101,102,108,101,99,116,105,111,110,44,32,101,121,101,86,101,99,41,44,32,48,46,48,41,59,13,10,9,9,9,9,9,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,112,111,119,40,115,112,101,99,117,108,97,114,70,97,99,116,111,114,44,32,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,41,59,13,10,13,10,9,9,9,9,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,43,61,32,97,116,116,32,42,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,59,13,10,9,9,9,9,9,98,114,101,97,107,59,13,10,9,9,9,9,125,13,10,13,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,83,80,79,84,58,13,10,9,9,9,9,123,13,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,32,45,32,118,87,111,114,108,100,80,111,115,59,13,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,32,61,32,108,101,110,103,116,104,40,108,105,103,104,116,68,105,114,41,59,13,10,9,9,9,9,9,108,105,103,104,116,68,105,114,32,47,61,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,59,32,47,47,32,78,111,114,109,97,108,105,115,97,116,105,111,110,13,10,9,9,9,9,9,13,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,109,97,120,40,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,119,32,45,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,119,42,108,105,103,104,116,68,105,114,76,101,110,103,116,104,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,13,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,97,116,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,120,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,13,10,13,10,9,9,9,9,9,47,47,32,77,111,100,105,102,105,99,97,116,105,111,110,32,100,101,32,108,39,97,116,116,195,169,110,117,97,116,105,111,110,32,112,111,117,114,32,103,195,169,114,101,114,32,108,101,32,115,112,111,116,13,10,9,9,9,9,9,102,108,111,97,116,32,99,117,114,65,110,103,108,101,32,61,32,100,111,116,40,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,120,121,122,44,32,45,108,105,103,104,116,68,105,114,41,59,13,10,9,9,9,9,9,102,108,111,97,116,32,111,117,116,101,114,65,110,103,108,101,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,51,46,121,59,13,10,9,9,9,9,9,102,108,111,97,116,32,105,110,110,101,114,77,105,110,117,115,79,117,116,101,114,65,110,103,108,101,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,51,46,120,32,45,32,111,117,116,101,114,65,110,103,108,101,59,13,10,9,9,9,9,9,97,116,116,32,42,61,32,109,97,120,40,40,99,117,114,65,110,103,108,101,32,45,32,111,117,116,101,114,65,110,103,108,101,41,32,47,32,105,110,110,101,114,77,105,110,117,115,79,117,116,101,114,65,110,103,108,101,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,13,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,121,59,13,10,13,10,9,9,9,9,9,47,47,32,83,112,101,99,117,108,97,114,13,10,9,9,9,9,9,118,101,99,51,32,114,101,102,108,101,99,116,105,111,110,32,61,32,114,101,102,108,101,99,116,40,45,108,105,103,104,116,68,105,114,44,32,110,111,114,109,97,108,41,59,13,10,9,9,9,9,9,102,108,111,97,116,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,109,97,120,40,100,111,116,40,114,101,102,108,101,99,116,105,111,110,44,32,101,121,101,86,101,99,41,44,32,48,46,48,41,59,13,10,9,9,9,9,9,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,112,111,119,40,115,112,101,99,117,108,97,114,70,97,99,116,111,114,44,32,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,41,59,13,10,13,10,9,9,9,9,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,43,61,32,97,116,116,32,42,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,59,13,10,9,9,9,9,9,98,114,101,97,107,59,13,10,9,9,9,9,125,13,10,9,9,9,9,13,10,9,9,9,9,100,101,102,97,117,108,116,58,13,10,9,9,9,9,9,98,114,101,97,107,59,13,10,9,9,9,125,13,10,9,9,125,13,10,9,125,13,10,9,101,108,115,101,13,10,9,123,13,10,9,9,102,111,114,32,40,105,110,116,32,105,32,61,32,48,59,32,105,32,60,32,51,59,32,43,43,105,41,13,10,9,9,123,13,10,9,9,9,115,119,105,116,99,104,32,40,76,105,103,104,116,115,91,105,93,46,116,121,112,101,41,13,10,9,9,9,123,13,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,68,73,82,69,67,84,73,79,78,65,76,58,13,10,9,9,9,9,123,13,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,110,111,114,109,97,108,105,122,101,40,45,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,41,59,13,10,13,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,13,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,120,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,13,10,13,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,13,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,108,97,109,98,101,114,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,121,59,13,10,9,9,9,9,9,98,114,101,97,107,59,13,10,9,9,9,9,125,13,10,13,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,80,79,73,78,84,58,13,10,9,9,9,9,123,13,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,32,45,32,118,87,111,114,108,100,80,111,115,59,13,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,32,61,32,108,101,110,103,116,104,40,108,105,103,104,116,68,105,114,41,59,13,10,9,9,9,9,9,108,105,103,104,116,68,105,114,32,47,61,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,59,32,47,47,32,78,111,114,109,97,108,105,115,97,116,105,111,110,13,10,9,9,9,9,9,13,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,109,97,120,40,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,119,32,45,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,119,42,108,105,103,104,116,68,105,114,76,101,110,103,116,104,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,13,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,97,116,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,120,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,13,10,13,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,13,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,13,10,9,9,9,9,9,13,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,121,59,13,10,9,9,9,9,9,98,114,101,97,107,59,13,10,9,9,9,9,125,13,10,13,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,83,80,79,84,58,13,10,9,9,9,9,123,13,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,32,45,32,118,87,111,114,108,100,80,111,115,59,13,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,32,61,32,108,101,110,103,116,104,40,108,105,103,104,116,68,105,114,41,59,13,10,9,9,9,9,9,108,105,103,104,116,68,105,114,32,47,61,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,59,32,47,47,32,78,111,114,109,97,108,105,115,97,116,105,111,110,13,10,9,9,9,9,9,13,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,109,97,120,40,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,119,32,45,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,119,42,108,105,103,104,116,68,105,114,76,101,110,103,116,104,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,13,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,97,116,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,120,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,13,10,13,10,9,9,9,9,9,47,47,32,77,111,100,105,102,105,99,97,116,105,111,110,32,100,101,32,108,39,97,116,116,195,169,110,117,97,116,105,111,110,32,112,111,117,114,32,103,195,169,114,101,114,32,108,101,32,115,112,111,116,13,10,9,9,9,9,9,102,108,111,97,116,32,99,117,114,65,110,103,108,101,32,61,32,100,111,116,40,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,120,121,122,44,32,45,108,105,103,104,116,68,105,114,41,59,13,10,9,9,9,9,9,102,108,111,97,116,32,111,117,116,101,114,65,110,103,108,101,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,51,46,121,59,13,10,9,9,9,9,9,102,108,111,97,116,32,105,110,110,101,114,77,105,110,117,115,79,117,116,101,114,65,110,103,108,101,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,51,46,120,32,45,32,111,117,116,101,114,65,110,103,108,101,59,13,10,9,9,9,9,9,97,116,116,32,42,61,32,109,97,120,40,40,99,117,114,65,110,103,108,101,32,45,32,111,117,116,101,114,65,110,103,108,101,41,32,47,32,105,110,110,101,114,77,105,110,117,115,79,117,116,101,114,65,110,103,108,101,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,13,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,13,10,13,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,46,114,103,98,32,42,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,121,59,13,10,9,9,9,9,125,13,10,9,9,9,9,13,10,9,9,9,9,100,101,102,97,117,108,116,58,13,10,9,9,9,9,9,98,114,101,97,107,59,13,10,9,9,9,125,13,10,9,9,125,13,10,9,125,13,10,13,10,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,42,61,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,46,114,103,98,59,13,10,9,9,35,105,102,32,83,80,69,67,85,76,65,82,95,77,65,80,80,73,78,71,13,10,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,103,98,59,32,47,47,32,85,116,105,108,105,115,101,114,32,108,39,97,108,112,104,97,32,100,101,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,32,110,39,97,117,114,97,105,116,32,97,117,99,117,110,32,115,101,110,115,13,10,9,9,35,101,110,100,105,102,13,10,9,9,13,10,9,118,101,99,51,32,108,105,103,104,116,67,111,108,111,114,32,61,32,40,108,105,103,104,116,65,109,98,105,101,110,116,32,43,32,108,105,103,104,116,68,105,102,102,117,115,101,32,43,32,108,105,103,104,116,83,112,101,99,117,108,97,114,41,59,13,10,9,118,101,99,52,32,102,114,97,103,109,101,110,116,67,111,108,111,114,32,61,32,118,101,99,52,40,108,105,103,104,116,67,111,108,111,114,44,32,49,46,48,41,32,42,32,100,105,102,102,117,115,101,67,111,108,111,114,59,13,10,13,10,9,9,35,105,102,32,69,77,73,83,83,73,86,69,95,77,65,80,80,73,78,71,13,10,9,102,108,111,97,116,32,108,105,103,104,116,73,110,116,101,110,115,105,116,121,32,61,32,100,111,116,40,108,105,103,104,116,67,111,108,111,114,44,32,118,101,99,51,40,48,46,51,44,32,48,46,53,57,44,32,48,46,49,49,41,41,59,13,10,13,10,9,118,101,99,51,32,101,109,105,115,115,105,111,110,67,111,108,111,114,32,61,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,46,114,103,98,32,42,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,69,109,105,115,115,105,118,101,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,103,98,59,13,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,118,101,99,52,40,109,105,120,40,102,114,97,103,109,101,110,116,67,111,108,111,114,46,114,103,98,44,32,101,109,105,115,115,105,111,110,67,111,108,111,114,44,32,99,108,97,109,112,40,49,46,48,32,45,32,51,46,48,42,108,105,103,104,116,73,110,116,101,110,115,105,116,121,44,32,48,46,48,44,32,49,46,48,41,41,44,32,102,114,97,103,109,101,110,116,67,111,108,111,114,46,97,41,59,13,10,9,9,35,101,108,115,101,13,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,102,114,97,103,109,101,110,116,67,111,108,111,114,59,13,10,9,9,35,101,110,100,105,102,32,47,47,32,69,77,73,83,83,73,86,69,95,77,65,80,80,73,78,71,13,10,9,35,101,108,115,101,13,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,100,105,102,102,117,115,101,67,111,108,111,114,59,13,10,9,35,101,110,100,105,102,32,47,47,32,76,73,71,72,84,73,78,71,13,10,35,101,110,100,105,102,32,47,47,32,70,76,65,71,95,68,69,70,69,82,82,69,68,13,10,125,13,10, \ No newline at end of file +35,105,102,32,69,65,82,76,89,95,70,82,65,71,77,69,78,84,95,84,69,83,84,83,32,38,38,32,33,65,76,80,72,65,95,84,69,83,84,10,108,97,121,111,117,116,40,101,97,114,108,121,95,102,114,97,103,109,101,110,116,95,116,101,115,116,115,41,32,105,110,59,10,35,101,110,100,105,102,10,13,10,35,100,101,102,105,110,101,32,76,73,71,72,84,95,68,73,82,69,67,84,73,79,78,65,76,32,48,10,35,100,101,102,105,110,101,32,76,73,71,72,84,95,80,79,73,78,84,32,49,10,35,100,101,102,105,110,101,32,76,73,71,72,84,95,83,80,79,84,32,50,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,69,110,116,114,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,105,110,32,118,101,99,52,32,118,67,111,108,111,114,59,10,105,110,32,118,101,99,52,32,118,76,105,103,104,116,83,112,97,99,101,80,111,115,91,51,93,59,10,105,110,32,109,97,116,51,32,118,76,105,103,104,116,84,111,87,111,114,108,100,59,10,105,110,32,118,101,99,51,32,118,78,111,114,109,97,108,59,10,105,110,32,118,101,99,50,32,118,84,101,120,67,111,111,114,100,59,10,105,110,32,118,101,99,51,32,118,86,105,101,119,68,105,114,59,10,105,110,32,118,101,99,51,32,118,87,111,114,108,100,80,111,115,59,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,83,111,114,116,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,48,59,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,49,59,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,50,59,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,85,110,105,102,111,114,109,101,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,115,116,114,117,99,116,32,76,105,103,104,116,10,123,10,9,105,110,116,32,116,121,112,101,59,10,9,118,101,99,52,32,99,111,108,111,114,59,10,9,118,101,99,50,32,102,97,99,116,111,114,115,59,10,10,9,118,101,99,52,32,112,97,114,97,109,101,116,101,114,115,49,59,10,9,118,101,99,52,32,112,97,114,97,109,101,116,101,114,115,50,59,10,9,118,101,99,50,32,112,97,114,97,109,101,116,101,114,115,51,59,10,9,98,111,111,108,32,115,104,97,100,111,119,77,97,112,112,105,110,103,59,10,125,59,10,10,47,47,32,76,117,109,105,195,168,114,101,115,10,117,110,105,102,111,114,109,32,76,105,103,104,116,32,76,105,103,104,116,115,91,51,93,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,67,117,98,101,32,80,111,105,110,116,76,105,103,104,116,83,104,97,100,111,119,77,97,112,91,51,93,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,68,105,114,101,99,116,105,111,110,97,108,83,112,111,116,76,105,103,104,116,83,104,97,100,111,119,77,97,112,91,51,93,59,10,10,47,47,32,77,97,116,195,169,114,105,97,117,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,59,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,59,10,117,110,105,102,111,114,109,32,118,101,99,52,32,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,59,10,117,110,105,102,111,114,109,32,118,101,99,52,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,69,109,105,115,115,105,118,101,77,97,112,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,72,101,105,103,104,116,77,97,112,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,78,111,114,109,97,108,77,97,112,59,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,59,10,117,110,105,102,111,114,109,32,118,101,99,52,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,59,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,77,97,112,59,10,10,47,47,32,65,117,116,114,101,115,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,80,97,114,97,108,108,97,120,66,105,97,115,32,61,32,45,48,46,48,51,59,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,80,97,114,97,108,108,97,120,83,99,97,108,101,32,61,32,48,46,48,50,59,10,117,110,105,102,111,114,109,32,118,101,99,50,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,117,110,105,102,111,114,109,32,118,101,99,51,32,69,121,101,80,111,115,105,116,105,111,110,59,10,117,110,105,102,111,114,109,32,118,101,99,52,32,83,99,101,110,101,65,109,98,105,101,110,116,59,10,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,84,101,120,116,117,114,101,79,118,101,114,108,97,121,59,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,70,111,110,99,116,105,111,110,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,118,101,99,51,32,70,108,111,97,116,84,111,67,111,108,111,114,40,102,108,111,97,116,32,102,41,10,123,10,9,118,101,99,51,32,99,111,108,111,114,59,10,10,9,102,32,42,61,32,50,53,54,46,48,59,10,9,99,111,108,111,114,46,120,32,61,32,102,108,111,111,114,40,102,41,59,10,10,9,102,32,61,32,40,102,32,45,32,99,111,108,111,114,46,120,41,32,42,32,50,53,54,46,48,59,10,9,99,111,108,111,114,46,121,32,61,32,102,108,111,111,114,40,102,41,59,10,10,9,99,111,108,111,114,46,122,32,61,32,102,32,45,32,99,111,108,111,114,46,121,59,10,9,99,111,108,111,114,46,120,121,32,42,61,32,48,46,48,48,51,57,48,54,50,53,59,32,47,47,32,42,61,32,49,46,48,47,50,53,54,10,10,9,114,101,116,117,114,110,32,99,111,108,111,114,59,10,125,10,10,35,100,101,102,105,110,101,32,107,80,73,32,51,46,49,52,49,53,57,50,54,53,51,54,10,10,118,101,99,52,32,69,110,99,111,100,101,78,111,114,109,97,108,40,105,110,32,118,101,99,51,32,110,111,114,109,97,108,41,10,123,10,9,47,47,114,101,116,117,114,110,32,118,101,99,52,40,110,111,114,109,97,108,42,48,46,53,32,43,32,48,46,53,44,32,48,46,48,41,59,10,9,114,101,116,117,114,110,32,118,101,99,52,40,118,101,99,50,40,97,116,97,110,40,110,111,114,109,97,108,46,121,44,32,110,111,114,109,97,108,46,120,41,47,107,80,73,44,32,110,111,114,109,97,108,46,122,41,44,32,48,46,48,44,32,48,46,48,41,59,10,125,10,10,102,108,111,97,116,32,86,101,99,116,111,114,84,111,68,101,112,116,104,86,97,108,117,101,40,118,101,99,51,32,118,101,99,44,32,102,108,111,97,116,32,122,78,101,97,114,44,32,102,108,111,97,116,32,122,70,97,114,41,10,123,10,9,118,101,99,51,32,97,98,115,86,101,99,32,61,32,97,98,115,40,118,101,99,41,59,10,9,102,108,111,97,116,32,108,111,99,97,108,90,32,61,32,109,97,120,40,97,98,115,86,101,99,46,120,44,32,109,97,120,40,97,98,115,86,101,99,46,121,44,32,97,98,115,86,101,99,46,122,41,41,59,10,10,9,102,108,111,97,116,32,110,111,114,109,90,32,61,32,40,40,122,70,97,114,32,43,32,122,78,101,97,114,41,32,42,32,108,111,99,97,108,90,32,45,32,40,50,46,48,42,122,70,97,114,42,122,78,101,97,114,41,41,32,47,32,40,40,122,70,97,114,32,45,32,122,78,101,97,114,41,42,108,111,99,97,108,90,41,59,10,9,114,101,116,117,114,110,32,40,110,111,114,109,90,32,43,32,49,46,48,41,32,42,32,48,46,53,59,10,125,10,10,35,105,102,32,83,72,65,68,79,87,95,77,65,80,80,73,78,71,10,102,108,111,97,116,32,67,97,108,99,117,108,97,116,101,68,105,114,101,99,116,105,111,110,97,108,83,104,97,100,111,119,70,97,99,116,111,114,40,105,110,116,32,108,105,103,104,116,73,110,100,101,120,41,10,123,10,9,118,101,99,52,32,108,105,103,104,116,83,112,97,99,101,80,111,115,32,61,32,118,76,105,103,104,116,83,112,97,99,101,80,111,115,91,108,105,103,104,116,73,110,100,101,120,93,59,10,9,114,101,116,117,114,110,32,40,116,101,120,116,117,114,101,40,68,105,114,101,99,116,105,111,110,97,108,83,112,111,116,76,105,103,104,116,83,104,97,100,111,119,77,97,112,91,108,105,103,104,116,73,110,100,101,120,93,44,32,108,105,103,104,116,83,112,97,99,101,80,111,115,46,120,121,41,46,120,32,62,61,32,40,108,105,103,104,116,83,112,97,99,101,80,111,115,46,122,32,45,32,48,46,48,48,48,53,41,41,32,63,32,49,46,48,32,58,32,48,46,48,59,10,125,10,10,102,108,111,97,116,32,67,97,108,99,117,108,97,116,101,80,111,105,110,116,83,104,97,100,111,119,70,97,99,116,111,114,40,105,110,116,32,108,105,103,104,116,73,110,100,101,120,44,32,118,101,99,51,32,108,105,103,104,116,84,111,87,111,114,108,100,44,32,102,108,111,97,116,32,122,78,101,97,114,44,32,102,108,111,97,116,32,122,70,97,114,41,10,123,10,9,114,101,116,117,114,110,32,40,116,101,120,116,117,114,101,40,80,111,105,110,116,76,105,103,104,116,83,104,97,100,111,119,77,97,112,91,108,105,103,104,116,73,110,100,101,120,93,44,32,118,101,99,51,40,108,105,103,104,116,84,111,87,111,114,108,100,46,120,44,32,45,108,105,103,104,116,84,111,87,111,114,108,100,46,121,44,32,45,108,105,103,104,116,84,111,87,111,114,108,100,46,122,41,41,46,120,32,62,61,32,86,101,99,116,111,114,84,111,68,101,112,116,104,86,97,108,117,101,40,108,105,103,104,116,84,111,87,111,114,108,100,44,32,122,78,101,97,114,44,32,122,70,97,114,41,41,32,63,32,49,46,48,32,58,32,48,46,48,59,10,125,10,10,102,108,111,97,116,32,67,97,108,99,117,108,97,116,101,83,112,111,116,83,104,97,100,111,119,70,97,99,116,111,114,40,105,110,116,32,108,105,103,104,116,73,110,100,101,120,41,10,123,10,9,118,101,99,52,32,108,105,103,104,116,83,112,97,99,101,80,111,115,32,61,32,118,76,105,103,104,116,83,112,97,99,101,80,111,115,91,108,105,103,104,116,73,110,100,101,120,93,59,10,10,9,102,108,111,97,116,32,118,105,115,105,98,105,108,105,116,121,32,61,32,49,46,48,59,10,9,102,108,111,97,116,32,120,44,121,59,10,9,102,111,114,32,40,121,32,61,32,45,51,46,53,59,32,121,32,60,61,32,51,46,53,59,32,121,43,61,32,49,46,48,41,10,9,9,102,111,114,32,40,120,32,61,32,45,51,46,53,59,32,120,32,60,61,32,51,46,53,59,32,120,43,61,32,49,46,48,41,10,9,9,9,118,105,115,105,98,105,108,105,116,121,32,43,61,32,40,116,101,120,116,117,114,101,80,114,111,106,40,68,105,114,101,99,116,105,111,110,97,108,83,112,111,116,76,105,103,104,116,83,104,97,100,111,119,77,97,112,91,108,105,103,104,116,73,110,100,101,120,93,44,32,108,105,103,104,116,83,112,97,99,101,80,111,115,46,120,121,119,32,43,32,118,101,99,51,40,120,47,49,48,50,52,46,48,32,42,32,108,105,103,104,116,83,112,97,99,101,80,111,115,46,119,44,32,121,47,49,48,50,52,46,48,32,42,32,108,105,103,104,116,83,112,97,99,101,80,111,115,46,119,44,32,48,46,48,41,41,46,120,32,62,61,32,40,108,105,103,104,116,83,112,97,99,101,80,111,115,46,122,32,45,32,48,46,48,48,48,53,41,47,108,105,103,104,116,83,112,97,99,101,80,111,115,46,119,41,32,63,32,49,46,48,32,58,32,48,46,48,59,10,10,9,118,105,115,105,98,105,108,105,116,121,32,47,61,32,54,52,46,48,59,10,9,10,9,114,101,116,117,114,110,32,118,105,115,105,98,105,108,105,116,121,59,10,125,10,35,101,110,100,105,102,10,10,118,111,105,100,32,109,97,105,110,40,41,10,123,10,9,118,101,99,52,32,100,105,102,102,117,115,101,67,111,108,111,114,32,61,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,32,42,32,118,67,111,108,111,114,59,10,10,35,105,102,32,65,85,84,79,95,84,69,88,67,79,79,82,68,83,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,103,108,95,70,114,97,103,67,111,111,114,100,46,120,121,32,42,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,10,35,101,108,115,101,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,118,84,101,120,67,111,111,114,100,59,10,35,101,110,100,105,102,10,10,35,105,102,32,80,65,82,65,76,76,65,88,95,77,65,80,80,73,78,71,10,9,102,108,111,97,116,32,104,101,105,103,104,116,32,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,72,101,105,103,104,116,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,59,10,9,102,108,111,97,116,32,118,32,61,32,104,101,105,103,104,116,42,80,97,114,97,108,108,97,120,83,99,97,108,101,32,43,32,80,97,114,97,108,108,97,120,66,105,97,115,59,10,10,9,118,101,99,51,32,118,105,101,119,68,105,114,32,61,32,110,111,114,109,97,108,105,122,101,40,118,86,105,101,119,68,105,114,41,59,10,9,116,101,120,67,111,111,114,100,32,43,61,32,118,32,42,32,118,105,101,119,68,105,114,46,120,121,59,10,35,101,110,100,105,102,10,10,35,105,102,32,68,73,70,70,85,83,69,95,77,65,80,80,73,78,71,10,9,100,105,102,102,117,115,101,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,44,32,116,101,120,67,111,111,114,100,41,59,10,35,101,110,100,105,102,10,10,35,105,102,32,70,76,65,71,95,84,69,88,84,85,82,69,79,86,69,82,76,65,89,10,9,100,105,102,102,117,115,101,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,84,101,120,116,117,114,101,79,118,101,114,108,97,121,44,32,116,101,120,67,111,111,114,100,41,59,10,35,101,110,100,105,102,10,10,35,105,102,32,70,76,65,71,95,68,69,70,69,82,82,69,68,10,9,35,105,102,32,65,76,80,72,65,95,84,69,83,84,10,9,9,47,47,32,73,110,117,116,105,108,101,32,100,101,32,102,97,105,114,101,32,100,101,32,108,39,97,108,112,104,97,45,109,97,112,112,105,110,103,32,115,97,110,115,32,97,108,112,104,97,45,116,101,115,116,32,101,110,32,68,101,102,101,114,114,101,100,32,40,108,39,97,108,112,104,97,32,110,39,101,115,116,32,112,97,115,32,115,97,117,118,101,103,97,114,100,195,169,32,100,97,110,115,32,108,101,32,71,45,66,117,102,102,101,114,41,10,9,9,35,105,102,32,65,76,80,72,65,95,77,65,80,80,73,78,71,10,9,100,105,102,102,117,115,101,67,111,108,111,114,46,97,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,59,10,9,9,35,101,110,100,105,102,10,9,9,10,9,105,102,32,40,100,105,102,102,117,115,101,67,111,108,111,114,46,97,32,60,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,41,10,9,9,100,105,115,99,97,114,100,59,10,9,35,101,110,100,105,102,32,47,47,32,65,76,80,72,65,95,84,69,83,84,10,10,9,35,105,102,32,78,79,82,77,65,76,95,77,65,80,80,73,78,71,10,9,118,101,99,51,32,110,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,118,76,105,103,104,116,84,111,87,111,114,108,100,32,42,32,40,50,46,48,32,42,32,118,101,99,51,40,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,78,111,114,109,97,108,77,97,112,44,32,116,101,120,67,111,111,114,100,41,41,32,45,32,49,46,48,41,41,59,10,9,35,101,108,115,101,10,9,118,101,99,51,32,110,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,118,78,111,114,109,97,108,41,59,10,9,35,101,110,100,105,102,32,47,47,32,78,79,82,77,65,76,95,77,65,80,80,73,78,71,10,10,9,118,101,99,51,32,115,112,101,99,117,108,97,114,67,111,108,111,114,32,61,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,46,114,103,98,59,10,9,35,105,102,32,83,80,69,67,85,76,65,82,95,77,65,80,80,73,78,71,10,9,115,112,101,99,117,108,97,114,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,103,98,59,10,9,35,101,110,100,105,102,10,10,9,47,42,10,9,84,101,120,116,117,114,101,48,58,32,68,105,102,102,117,115,101,32,67,111,108,111,114,32,43,32,83,112,101,99,117,108,97,114,10,9,84,101,120,116,117,114,101,49,58,32,78,111,114,109,97,108,32,43,32,83,112,101,99,117,108,97,114,10,9,84,101,120,116,117,114,101,50,58,32,69,110,99,111,100,101,100,32,100,101,112,116,104,32,43,32,83,104,105,110,105,110,101,115,115,10,9,42,47,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,118,101,99,52,40,100,105,102,102,117,115,101,67,111,108,111,114,46,114,103,98,44,32,100,111,116,40,115,112,101,99,117,108,97,114,67,111,108,111,114,44,32,118,101,99,51,40,48,46,51,44,32,48,46,53,57,44,32,48,46,49,49,41,41,41,59,10,9,82,101,110,100,101,114,84,97,114,103,101,116,49,32,61,32,118,101,99,52,40,69,110,99,111,100,101,78,111,114,109,97,108,40,110,111,114,109,97,108,41,41,59,10,9,82,101,110,100,101,114,84,97,114,103,101,116,50,32,61,32,118,101,99,52,40,70,108,111,97,116,84,111,67,111,108,111,114,40,103,108,95,70,114,97,103,67,111,111,114,100,46,122,41,44,32,40,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,32,61,61,32,48,46,48,41,32,63,32,48,46,48,32,58,32,109,97,120,40,108,111,103,50,40,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,41,44,32,48,46,49,41,47,49,48,46,53,41,59,32,47,47,32,104,116,116,112,58,47,47,119,119,119,46,103,117,101,114,114,105,108,108,97,45,103,97,109,101,115,46,99,111,109,47,112,117,98,108,105,99,97,116,105,111,110,115,47,100,114,95,107,122,50,95,114,115,120,95,100,101,118,48,55,46,112,100,102,10,35,101,108,115,101,32,47,47,32,70,76,65,71,95,68,69,70,69,82,82,69,68,10,9,35,105,102,32,65,76,80,72,65,95,77,65,80,80,73,78,71,10,9,100,105,102,102,117,115,101,67,111,108,111,114,46,97,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,59,10,9,35,101,110,100,105,102,10,10,9,35,105,102,32,65,76,80,72,65,95,84,69,83,84,10,9,105,102,32,40,100,105,102,102,117,115,101,67,111,108,111,114,46,97,32,60,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,41,10,9,9,100,105,115,99,97,114,100,59,10,9,35,101,110,100,105,102,10,10,9,118,101,99,51,32,108,105,103,104,116,65,109,98,105,101,110,116,32,61,32,118,101,99,51,40,48,46,48,41,59,10,9,118,101,99,51,32,108,105,103,104,116,68,105,102,102,117,115,101,32,61,32,118,101,99,51,40,48,46,48,41,59,10,9,118,101,99,51,32,108,105,103,104,116,83,112,101,99,117,108,97,114,32,61,32,118,101,99,51,40,48,46,48,41,59,10,10,9,35,105,102,32,78,79,82,77,65,76,95,77,65,80,80,73,78,71,10,9,118,101,99,51,32,110,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,118,76,105,103,104,116,84,111,87,111,114,108,100,32,42,32,40,50,46,48,32,42,32,118,101,99,51,40,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,78,111,114,109,97,108,77,97,112,44,32,116,101,120,67,111,111,114,100,41,41,32,45,32,49,46,48,41,41,59,10,9,35,101,108,115,101,10,9,118,101,99,51,32,110,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,118,78,111,114,109,97,108,41,59,10,9,35,101,110,100,105,102,10,10,9,105,102,32,40,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,32,62,32,48,46,48,41,10,9,123,10,9,9,118,101,99,51,32,101,121,101,86,101,99,32,61,32,110,111,114,109,97,108,105,122,101,40,69,121,101,80,111,115,105,116,105,111,110,32,45,32,118,87,111,114,108,100,80,111,115,41,59,10,10,9,9,102,111,114,32,40,105,110,116,32,105,32,61,32,48,59,32,105,32,60,32,51,59,32,43,43,105,41,10,9,9,123,10,9,9,9,118,101,99,52,32,108,105,103,104,116,67,111,108,111,114,32,61,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,59,10,9,9,9,102,108,111,97,116,32,108,105,103,104,116,65,109,98,105,101,110,116,70,97,99,116,111,114,32,61,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,120,59,10,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,102,102,117,115,101,70,97,99,116,111,114,32,61,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,121,59,10,10,9,9,9,115,119,105,116,99,104,32,40,76,105,103,104,116,115,91,105,93,46,116,121,112,101,41,10,9,9,9,123,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,68,73,82,69,67,84,73,79,78,65,76,58,10,9,9,9,9,123,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,45,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,59,10,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,65,109,98,105,101,110,116,70,97,99,116,111,114,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,10,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,49,46,48,59,10,10,9,9,9,9,9,35,105,102,32,83,72,65,68,79,87,95,77,65,80,80,73,78,71,10,9,9,9,9,9,105,102,32,40,76,105,103,104,116,115,91,105,93,46,115,104,97,100,111,119,77,97,112,112,105,110,103,41,10,9,9,9,9,9,123,10,9,9,9,9,9,9,102,108,111,97,116,32,115,104,97,100,111,119,70,97,99,116,111,114,32,61,32,67,97,108,99,117,108,97,116,101,68,105,114,101,99,116,105,111,110,97,108,83,104,97,100,111,119,70,97,99,116,111,114,40,105,41,59,10,9,9,9,9,9,9,105,102,32,40,115,104,97,100,111,119,70,97,99,116,111,114,32,61,61,32,48,46,48,41,10,9,9,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,9,9,9,10,9,9,9,9,9,9,97,116,116,32,42,61,32,115,104,97,100,111,119,70,97,99,116,111,114,59,10,9,9,9,9,9,125,10,9,9,9,9,9,35,101,110,100,105,102,10,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,10,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,68,105,102,102,117,115,101,70,97,99,116,111,114,59,10,10,9,9,9,9,9,47,47,32,83,112,101,99,117,108,97,114,10,9,9,9,9,9,118,101,99,51,32,114,101,102,108,101,99,116,105,111,110,32,61,32,114,101,102,108,101,99,116,40,45,108,105,103,104,116,68,105,114,44,32,110,111,114,109,97,108,41,59,10,9,9,9,9,9,102,108,111,97,116,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,109,97,120,40,100,111,116,40,114,101,102,108,101,99,116,105,111,110,44,32,101,121,101,86,101,99,41,44,32,48,46,48,41,59,10,9,9,9,9,9,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,112,111,119,40,115,112,101,99,117,108,97,114,70,97,99,116,111,114,44,32,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,41,59,10,10,9,9,9,9,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,43,61,32,97,116,116,32,42,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,59,10,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,125,10,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,80,79,73,78,84,58,10,9,9,9,9,123,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,80,111,115,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,65,116,116,101,110,117,97,116,105,111,110,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,119,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,73,110,118,82,97,100,105,117,115,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,119,59,10,9,9,9,9,9,10,9,9,9,9,9,118,101,99,51,32,119,111,114,108,100,84,111,76,105,103,104,116,32,61,32,108,105,103,104,116,80,111,115,32,45,32,118,87,111,114,108,100,80,111,115,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,32,61,32,108,101,110,103,116,104,40,119,111,114,108,100,84,111,76,105,103,104,116,41,59,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,119,111,114,108,100,84,111,76,105,103,104,116,32,47,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,59,32,47,47,32,78,111,114,109,97,108,105,115,97,116,105,111,110,10,9,9,9,9,9,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,109,97,120,40,108,105,103,104,116,65,116,116,101,110,117,97,116,105,111,110,32,45,32,108,105,103,104,116,73,110,118,82,97,100,105,117,115,32,42,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,44,32,48,46,48,41,59,10,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,97,116,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,65,109,98,105,101,110,116,70,97,99,116,111,114,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,10,10,9,9,9,9,9,35,105,102,32,83,72,65,68,79,87,95,77,65,80,80,73,78,71,10,9,9,9,9,9,105,102,32,40,76,105,103,104,116,115,91,105,93,46,115,104,97,100,111,119,77,97,112,112,105,110,103,41,10,9,9,9,9,9,123,10,9,9,9,9,9,9,102,108,111,97,116,32,115,104,97,100,111,119,70,97,99,116,111,114,32,61,32,67,97,108,99,117,108,97,116,101,80,111,105,110,116,83,104,97,100,111,119,70,97,99,116,111,114,40,105,44,32,118,87,111,114,108,100,80,111,115,32,45,32,108,105,103,104,116,80,111,115,44,32,48,46,49,44,32,53,48,46,48,41,59,10,9,9,9,9,9,9,105,102,32,40,115,104,97,100,111,119,70,97,99,116,111,114,32,61,61,32,48,46,48,41,10,9,9,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,9,9,9,10,9,9,9,9,9,9,97,116,116,32,42,61,32,115,104,97,100,111,119,70,97,99,116,111,114,59,10,9,9,9,9,9,125,10,9,9,9,9,9,35,101,110,100,105,102,10,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,10,9,9,9,9,9,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,68,105,102,102,117,115,101,70,97,99,116,111,114,59,10,10,9,9,9,9,9,47,47,32,83,112,101,99,117,108,97,114,10,9,9,9,9,9,118,101,99,51,32,114,101,102,108,101,99,116,105,111,110,32,61,32,114,101,102,108,101,99,116,40,45,108,105,103,104,116,68,105,114,44,32,110,111,114,109,97,108,41,59,10,9,9,9,9,9,102,108,111,97,116,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,109,97,120,40,100,111,116,40,114,101,102,108,101,99,116,105,111,110,44,32,101,121,101,86,101,99,41,44,32,48,46,48,41,59,10,9,9,9,9,9,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,112,111,119,40,115,112,101,99,117,108,97,114,70,97,99,116,111,114,44,32,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,41,59,10,10,9,9,9,9,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,43,61,32,97,116,116,32,42,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,59,10,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,125,10,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,83,80,79,84,58,10,9,9,9,9,123,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,80,111,115,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,59,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,120,121,122,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,65,116,116,101,110,117,97,116,105,111,110,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,119,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,73,110,118,82,97,100,105,117,115,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,119,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,73,110,110,101,114,65,110,103,108,101,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,51,46,120,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,79,117,116,101,114,65,110,103,108,101,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,51,46,121,59,10,10,9,9,9,9,9,118,101,99,51,32,119,111,114,108,100,84,111,76,105,103,104,116,32,61,32,108,105,103,104,116,80,111,115,32,45,32,118,87,111,114,108,100,80,111,115,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,115,116,97,110,99,101,32,61,32,108,101,110,103,116,104,40,119,111,114,108,100,84,111,76,105,103,104,116,41,59,10,9,9,9,9,9,119,111,114,108,100,84,111,76,105,103,104,116,32,47,61,32,108,105,103,104,116,68,105,115,116,97,110,99,101,59,32,47,47,32,78,111,114,109,97,108,105,115,97,116,105,111,110,10,9,9,9,9,9,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,109,97,120,40,108,105,103,104,116,65,116,116,101,110,117,97,116,105,111,110,32,45,32,108,105,103,104,116,73,110,118,82,97,100,105,117,115,32,42,32,108,105,103,104,116,68,105,115,116,97,110,99,101,44,32,48,46,48,41,59,10,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,97,116,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,65,109,98,105,101,110,116,70,97,99,116,111,114,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,10,10,9,9,9,9,9,35,105,102,32,83,72,65,68,79,87,95,77,65,80,80,73,78,71,10,9,9,9,9,9,105,102,32,40,76,105,103,104,116,115,91,105,93,46,115,104,97,100,111,119,77,97,112,112,105,110,103,41,10,9,9,9,9,9,123,10,9,9,9,9,9,9,102,108,111,97,116,32,115,104,97,100,111,119,70,97,99,116,111,114,32,61,32,67,97,108,99,117,108,97,116,101,83,112,111,116,83,104,97,100,111,119,70,97,99,116,111,114,40,105,41,59,10,9,9,9,9,9,9,105,102,32,40,115,104,97,100,111,119,70,97,99,116,111,114,32,61,61,32,48,46,48,41,10,9,9,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,9,9,9,10,9,9,9,9,9,9,97,116,116,32,42,61,32,115,104,97,100,111,119,70,97,99,116,111,114,59,10,9,9,9,9,9,125,10,9,9,9,9,9,35,101,110,100,105,102,10,10,9,9,9,9,9,47,47,32,77,111,100,105,102,105,99,97,116,105,111,110,32,100,101,32,108,39,97,116,116,195,169,110,117,97,116,105,111,110,32,112,111,117,114,32,103,195,169,114,101,114,32,108,101,32,115,112,111,116,10,9,9,9,9,9,102,108,111,97,116,32,99,117,114,65,110,103,108,101,32,61,32,100,111,116,40,108,105,103,104,116,68,105,114,44,32,45,119,111,114,108,100,84,111,76,105,103,104,116,41,59,10,9,9,9,9,9,102,108,111,97,116,32,105,110,110,101,114,77,105,110,117,115,79,117,116,101,114,65,110,103,108,101,32,61,32,108,105,103,104,116,73,110,110,101,114,65,110,103,108,101,32,45,32,108,105,103,104,116,79,117,116,101,114,65,110,103,108,101,59,10,9,9,9,9,9,97,116,116,32,42,61,32,109,97,120,40,40,99,117,114,65,110,103,108,101,32,45,32,108,105,103,104,116,79,117,116,101,114,65,110,103,108,101,41,32,47,32,105,110,110,101,114,77,105,110,117,115,79,117,116,101,114,65,110,103,108,101,44,32,48,46,48,41,59,10,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,119,111,114,108,100,84,111,76,105,103,104,116,41,44,32,48,46,48,41,59,10,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,68,105,102,102,117,115,101,70,97,99,116,111,114,59,10,10,9,9,9,9,9,47,47,32,83,112,101,99,117,108,97,114,10,9,9,9,9,9,118,101,99,51,32,114,101,102,108,101,99,116,105,111,110,32,61,32,114,101,102,108,101,99,116,40,45,119,111,114,108,100,84,111,76,105,103,104,116,44,32,110,111,114,109,97,108,41,59,10,9,9,9,9,9,102,108,111,97,116,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,109,97,120,40,100,111,116,40,114,101,102,108,101,99,116,105,111,110,44,32,101,121,101,86,101,99,41,44,32,48,46,48,41,59,10,9,9,9,9,9,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,61,32,112,111,119,40,115,112,101,99,117,108,97,114,70,97,99,116,111,114,44,32,77,97,116,101,114,105,97,108,83,104,105,110,105,110,101,115,115,41,59,10,10,9,9,9,9,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,43,61,32,97,116,116,32,42,32,115,112,101,99,117,108,97,114,70,97,99,116,111,114,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,59,10,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,125,10,9,9,9,9,10,9,9,9,9,100,101,102,97,117,108,116,58,10,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,125,10,9,9,125,10,9,125,10,9,101,108,115,101,10,9,123,10,9,9,102,111,114,32,40,105,110,116,32,105,32,61,32,48,59,32,105,32,60,32,51,59,32,43,43,105,41,10,9,9,123,10,9,9,9,118,101,99,52,32,108,105,103,104,116,67,111,108,111,114,32,61,32,76,105,103,104,116,115,91,105,93,46,99,111,108,111,114,59,10,9,9,9,102,108,111,97,116,32,108,105,103,104,116,65,109,98,105,101,110,116,70,97,99,116,111,114,32,61,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,120,59,10,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,102,102,117,115,101,70,97,99,116,111,114,32,61,32,76,105,103,104,116,115,91,105,93,46,102,97,99,116,111,114,115,46,121,59,10,10,9,9,9,115,119,105,116,99,104,32,40,76,105,103,104,116,115,91,105,93,46,116,121,112,101,41,10,9,9,9,123,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,68,73,82,69,67,84,73,79,78,65,76,58,10,9,9,9,9,123,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,45,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,59,10,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,65,109,98,105,101,110,116,70,97,99,116,111,114,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,10,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,49,46,48,59,10,10,9,9,9,9,9,35,105,102,32,83,72,65,68,79,87,95,77,65,80,80,73,78,71,10,9,9,9,9,9,105,102,32,40,76,105,103,104,116,115,91,105,93,46,115,104,97,100,111,119,77,97,112,112,105,110,103,41,10,9,9,9,9,9,123,10,9,9,9,9,9,9,102,108,111,97,116,32,115,104,97,100,111,119,70,97,99,116,111,114,32,61,32,67,97,108,99,117,108,97,116,101,68,105,114,101,99,116,105,111,110,97,108,83,104,97,100,111,119,70,97,99,116,111,114,40,105,41,59,10,9,9,9,9,9,9,105,102,32,40,115,104,97,100,111,119,70,97,99,116,111,114,32,61,61,32,48,46,48,41,10,9,9,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,9,9,9,10,9,9,9,9,9,9,97,116,116,32,42,61,32,115,104,97,100,111,119,70,97,99,116,111,114,59,10,9,9,9,9,9,125,10,9,9,9,9,9,35,101,110,100,105,102,10,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,10,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,68,105,102,102,117,115,101,70,97,99,116,111,114,59,10,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,125,10,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,80,79,73,78,84,58,10,9,9,9,9,123,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,80,111,115,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,65,116,116,101,110,117,97,116,105,111,110,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,119,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,73,110,118,82,97,100,105,117,115,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,119,59,10,9,9,9,9,9,10,9,9,9,9,9,118,101,99,51,32,119,111,114,108,100,84,111,76,105,103,104,116,32,61,32,108,105,103,104,116,80,111,115,32,45,32,118,87,111,114,108,100,80,111,115,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,32,61,32,108,101,110,103,116,104,40,119,111,114,108,100,84,111,76,105,103,104,116,41,59,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,119,111,114,108,100,84,111,76,105,103,104,116,32,47,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,59,32,47,47,32,78,111,114,109,97,108,105,115,97,116,105,111,110,10,9,9,9,9,9,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,109,97,120,40,108,105,103,104,116,65,116,116,101,110,117,97,116,105,111,110,32,45,32,108,105,103,104,116,73,110,118,82,97,100,105,117,115,32,42,32,108,105,103,104,116,68,105,114,76,101,110,103,116,104,44,32,48,46,48,41,59,10,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,97,116,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,65,109,98,105,101,110,116,70,97,99,116,111,114,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,10,10,9,9,9,9,9,35,105,102,32,83,72,65,68,79,87,95,77,65,80,80,73,78,71,10,9,9,9,9,9,105,102,32,40,76,105,103,104,116,115,91,105,93,46,115,104,97,100,111,119,77,97,112,112,105,110,103,41,10,9,9,9,9,9,123,10,9,9,9,9,9,9,102,108,111,97,116,32,115,104,97,100,111,119,70,97,99,116,111,114,32,61,32,67,97,108,99,117,108,97,116,101,80,111,105,110,116,83,104,97,100,111,119,70,97,99,116,111,114,40,105,44,32,118,87,111,114,108,100,80,111,115,32,45,32,108,105,103,104,116,80,111,115,44,32,48,46,49,44,32,53,48,46,48,41,59,10,9,9,9,9,9,9,105,102,32,40,115,104,97,100,111,119,70,97,99,116,111,114,32,61,61,32,48,46,48,41,10,9,9,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,9,9,9,10,9,9,9,9,9,9,97,116,116,32,42,61,32,115,104,97,100,111,119,70,97,99,116,111,114,59,10,9,9,9,9,9,125,10,9,9,9,9,9,35,101,110,100,105,102,10,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,108,105,103,104,116,68,105,114,41,44,32,48,46,48,41,59,10,9,9,9,9,9,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,68,105,102,102,117,115,101,70,97,99,116,111,114,59,10,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,125,10,10,9,9,9,9,99,97,115,101,32,76,73,71,72,84,95,83,80,79,84,58,10,9,9,9,9,123,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,80,111,115,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,120,121,122,59,10,9,9,9,9,9,118,101,99,51,32,108,105,103,104,116,68,105,114,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,120,121,122,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,65,116,116,101,110,117,97,116,105,111,110,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,49,46,119,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,73,110,118,82,97,100,105,117,115,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,50,46,119,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,73,110,110,101,114,65,110,103,108,101,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,51,46,120,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,79,117,116,101,114,65,110,103,108,101,32,61,32,76,105,103,104,116,115,91,105,93,46,112,97,114,97,109,101,116,101,114,115,51,46,121,59,10,10,9,9,9,9,9,118,101,99,51,32,119,111,114,108,100,84,111,76,105,103,104,116,32,61,32,108,105,103,104,116,80,111,115,32,45,32,118,87,111,114,108,100,80,111,115,59,10,9,9,9,9,9,102,108,111,97,116,32,108,105,103,104,116,68,105,115,116,97,110,99,101,32,61,32,108,101,110,103,116,104,40,119,111,114,108,100,84,111,76,105,103,104,116,41,59,10,9,9,9,9,9,119,111,114,108,100,84,111,76,105,103,104,116,32,47,61,32,108,105,103,104,116,68,105,115,116,97,110,99,101,59,32,47,47,32,78,111,114,109,97,108,105,115,97,116,105,111,110,10,9,9,9,9,9,10,9,9,9,9,9,102,108,111,97,116,32,97,116,116,32,61,32,109,97,120,40,108,105,103,104,116,65,116,116,101,110,117,97,116,105,111,110,32,45,32,108,105,103,104,116,73,110,118,82,97,100,105,117,115,32,42,32,108,105,103,104,116,68,105,115,116,97,110,99,101,44,32,48,46,48,41,59,10,10,9,9,9,9,9,47,47,32,65,109,98,105,101,110,116,10,9,9,9,9,9,108,105,103,104,116,65,109,98,105,101,110,116,32,43,61,32,97,116,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,65,109,98,105,101,110,116,70,97,99,116,111,114,32,42,32,40,77,97,116,101,114,105,97,108,65,109,98,105,101,110,116,46,114,103,98,32,43,32,83,99,101,110,101,65,109,98,105,101,110,116,46,114,103,98,41,59,10,10,9,9,9,9,9,35,105,102,32,83,72,65,68,79,87,95,77,65,80,80,73,78,71,10,9,9,9,9,9,105,102,32,40,76,105,103,104,116,115,91,105,93,46,115,104,97,100,111,119,77,97,112,112,105,110,103,41,10,9,9,9,9,9,123,10,9,9,9,9,9,9,102,108,111,97,116,32,115,104,97,100,111,119,70,97,99,116,111,114,32,61,32,67,97,108,99,117,108,97,116,101,83,112,111,116,83,104,97,100,111,119,70,97,99,116,111,114,40,105,41,59,10,9,9,9,9,9,9,105,102,32,40,115,104,97,100,111,119,70,97,99,116,111,114,32,61,61,32,48,46,48,41,10,9,9,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,9,9,9,9,10,9,9,9,9,9,9,97,116,116,32,42,61,32,115,104,97,100,111,119,70,97,99,116,111,114,59,10,9,9,9,9,9,125,10,9,9,9,9,9,35,101,110,100,105,102,10,10,9,9,9,9,9,47,47,32,77,111,100,105,102,105,99,97,116,105,111,110,32,100,101,32,108,39,97,116,116,195,169,110,117,97,116,105,111,110,32,112,111,117,114,32,103,195,169,114,101,114,32,108,101,32,115,112,111,116,10,9,9,9,9,9,102,108,111,97,116,32,99,117,114,65,110,103,108,101,32,61,32,100,111,116,40,108,105,103,104,116,68,105,114,44,32,45,119,111,114,108,100,84,111,76,105,103,104,116,41,59,10,9,9,9,9,9,102,108,111,97,116,32,105,110,110,101,114,77,105,110,117,115,79,117,116,101,114,65,110,103,108,101,32,61,32,108,105,103,104,116,73,110,110,101,114,65,110,103,108,101,32,45,32,108,105,103,104,116,79,117,116,101,114,65,110,103,108,101,59,10,9,9,9,9,9,97,116,116,32,42,61,32,109,97,120,40,40,99,117,114,65,110,103,108,101,32,45,32,108,105,103,104,116,79,117,116,101,114,65,110,103,108,101,41,32,47,32,105,110,110,101,114,77,105,110,117,115,79,117,116,101,114,65,110,103,108,101,44,32,48,46,48,41,59,10,10,9,9,9,9,9,47,47,32,68,105,102,102,117,115,101,10,9,9,9,9,9,102,108,111,97,116,32,108,97,109,98,101,114,116,32,61,32,109,97,120,40,100,111,116,40,110,111,114,109,97,108,44,32,119,111,114,108,100,84,111,76,105,103,104,116,41,44,32,48,46,48,41,59,10,10,9,9,9,9,9,108,105,103,104,116,68,105,102,102,117,115,101,32,43,61,32,97,116,116,32,42,32,108,97,109,98,101,114,116,32,42,32,108,105,103,104,116,67,111,108,111,114,46,114,103,98,32,42,32,108,105,103,104,116,68,105,102,102,117,115,101,70,97,99,116,111,114,59,10,9,9,9,9,125,10,9,9,9,9,10,9,9,9,9,100,101,102,97,117,108,116,58,10,9,9,9,9,9,98,114,101,97,107,59,10,9,9,9,125,10,9,9,125,10,9,125,10,9,10,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,42,61,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,46,114,103,98,59,10,9,35,105,102,32,83,80,69,67,85,76,65,82,95,77,65,80,80,73,78,71,10,9,108,105,103,104,116,83,112,101,99,117,108,97,114,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,103,98,59,32,47,47,32,85,116,105,108,105,115,101,114,32,108,39,97,108,112,104,97,32,100,101,32,77,97,116,101,114,105,97,108,83,112,101,99,117,108,97,114,32,110,39,97,117,114,97,105,116,32,97,117,99,117,110,32,115,101,110,115,10,9,35,101,110,100,105,102,10,9,9,10,9,118,101,99,51,32,108,105,103,104,116,67,111,108,111,114,32,61,32,40,108,105,103,104,116,65,109,98,105,101,110,116,32,43,32,108,105,103,104,116,68,105,102,102,117,115,101,32,43,32,108,105,103,104,116,83,112,101,99,117,108,97,114,41,59,10,9,118,101,99,52,32,102,114,97,103,109,101,110,116,67,111,108,111,114,32,61,32,118,101,99,52,40,108,105,103,104,116,67,111,108,111,114,44,32,49,46,48,41,32,42,32,100,105,102,102,117,115,101,67,111,108,111,114,59,10,10,9,35,105,102,32,69,77,73,83,83,73,86,69,95,77,65,80,80,73,78,71,10,9,102,108,111,97,116,32,108,105,103,104,116,73,110,116,101,110,115,105,116,121,32,61,32,100,111,116,40,108,105,103,104,116,67,111,108,111,114,44,32,118,101,99,51,40,48,46,51,44,32,48,46,53,57,44,32,48,46,49,49,41,41,59,10,10,9,118,101,99,51,32,101,109,105,115,115,105,111,110,67,111,108,111,114,32,61,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,46,114,103,98,32,42,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,69,109,105,115,115,105,118,101,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,103,98,59,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,118,101,99,52,40,109,105,120,40,102,114,97,103,109,101,110,116,67,111,108,111,114,46,114,103,98,44,32,101,109,105,115,115,105,111,110,67,111,108,111,114,44,32,99,108,97,109,112,40,49,46,48,32,45,32,51,46,48,42,108,105,103,104,116,73,110,116,101,110,115,105,116,121,44,32,48,46,48,44,32,49,46,48,41,41,44,32,102,114,97,103,109,101,110,116,67,111,108,111,114,46,97,41,59,10,9,35,101,108,115,101,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,102,114,97,103,109,101,110,116,67,111,108,111,114,59,10,9,35,101,110,100,105,102,32,47,47,32,69,77,73,83,83,73,86,69,95,77,65,80,80,73,78,71,10,35,101,110,100,105,102,32,47,47,32,70,76,65,71,95,68,69,70,69,82,82,69,68,10,125,10,10, \ No newline at end of file diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert b/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert index 89f71e527..8943c5935 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert +++ b/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert @@ -12,9 +12,11 @@ in vec3 VertexPosition; in vec3 VertexNormal; in vec3 VertexTangent; in vec2 VertexTexCoord; +in vec4 VertexUserdata0; /********************Sortant********************/ out vec4 vColor; +out vec4 vLightSpacePos[3]; out mat3 vLightToWorld; out vec3 vNormal; out vec2 vTexCoord; @@ -23,7 +25,10 @@ out vec3 vWorldPos; /********************Uniformes********************/ uniform vec3 EyePosition; +uniform mat4 InvViewMatrix; +uniform mat4 LightViewProjMatrix[3]; uniform float VertexDepth; +uniform mat4 ViewMatrix; uniform mat4 ViewProjMatrix; uniform mat4 WorldMatrix; uniform mat4 WorldViewProjMatrix; @@ -104,33 +109,36 @@ void main() vColor = color; -#if LIGHTING - #if FLAG_INSTANCING +#if FLAG_INSTANCING mat3 rotationMatrix = mat3(InstanceData0); - #else +#else mat3 rotationMatrix = mat3(WorldMatrix); - #endif +#endif - #if COMPUTE_TBNMATRIX +#if COMPUTE_TBNMATRIX vec3 binormal = cross(VertexNormal, VertexTangent); vLightToWorld[0] = normalize(rotationMatrix * VertexTangent); vLightToWorld[1] = normalize(rotationMatrix * binormal); vLightToWorld[2] = normalize(rotationMatrix * VertexNormal); - #else +#else vNormal = normalize(rotationMatrix * VertexNormal); - #endif +#endif + +#if SHADOW_MAPPING + for (int i = 0; i < 3; ++i) + vLightSpacePos[i] = LightViewProjMatrix[i] * WorldMatrix * vec4(VertexPosition, 1.0); #endif #if TEXTURE_MAPPING vTexCoord = VertexTexCoord; #endif -#if LIGHTING && PARALLAX_MAPPING +#if PARALLAX_MAPPING vViewDir = EyePosition - VertexPosition; vViewDir *= vLightToWorld; #endif -#if LIGHTING && !FLAG_DEFERRED +#if !FLAG_DEFERRED #if FLAG_INSTANCING vWorldPos = vec3(InstanceData0 * vec4(VertexPosition, 1.0)); #else diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert.h b/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert.h index d40070111..bed6edcab 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert.h +++ b/src/Nazara/Graphics/Resources/Shaders/PhongLighting/core.vert.h @@ -1 +1 @@ -47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,69,110,116,114,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,35,105,102,32,70,76,65,71,95,66,73,76,76,66,79,65,82,68,10,105,110,32,118,101,99,51,32,73,110,115,116,97,110,99,101,68,97,116,97,48,59,32,47,47,32,99,101,110,116,101,114,10,105,110,32,118,101,99,52,32,73,110,115,116,97,110,99,101,68,97,116,97,49,59,32,47,47,32,115,105,122,101,32,124,32,115,105,110,32,99,111,115,10,105,110,32,118,101,99,52,32,73,110,115,116,97,110,99,101,68,97,116,97,50,59,32,47,47,32,99,111,108,111,114,10,35,101,108,115,101,10,105,110,32,109,97,116,52,32,73,110,115,116,97,110,99,101,68,97,116,97,48,59,10,35,101,110,100,105,102,10,10,105,110,32,118,101,99,52,32,86,101,114,116,101,120,67,111,108,111,114,59,10,105,110,32,118,101,99,51,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,59,10,105,110,32,118,101,99,51,32,86,101,114,116,101,120,78,111,114,109,97,108,59,10,105,110,32,118,101,99,51,32,86,101,114,116,101,120,84,97,110,103,101,110,116,59,10,105,110,32,118,101,99,50,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,59,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,83,111,114,116,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,111,117,116,32,118,101,99,52,32,118,67,111,108,111,114,59,10,111,117,116,32,109,97,116,51,32,118,76,105,103,104,116,84,111,87,111,114,108,100,59,10,111,117,116,32,118,101,99,51,32,118,78,111,114,109,97,108,59,10,111,117,116,32,118,101,99,50,32,118,84,101,120,67,111,111,114,100,59,10,111,117,116,32,118,101,99,51,32,118,86,105,101,119,68,105,114,59,10,111,117,116,32,118,101,99,51,32,118,87,111,114,108,100,80,111,115,59,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,85,110,105,102,111,114,109,101,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,117,110,105,102,111,114,109,32,118,101,99,51,32,69,121,101,80,111,115,105,116,105,111,110,59,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,86,101,114,116,101,120,68,101,112,116,104,59,10,117,110,105,102,111,114,109,32,109,97,116,52,32,86,105,101,119,80,114,111,106,77,97,116,114,105,120,59,10,117,110,105,102,111,114,109,32,109,97,116,52,32,87,111,114,108,100,77,97,116,114,105,120,59,10,117,110,105,102,111,114,109,32,109,97,116,52,32,87,111,114,108,100,86,105,101,119,80,114,111,106,77,97,116,114,105,120,59,10,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,70,111,110,99,116,105,111,110,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,10,118,111,105,100,32,109,97,105,110,40,41,10,123,10,35,105,102,32,70,76,65,71,95,86,69,82,84,69,88,67,79,76,79,82,10,9,118,101,99,52,32,99,111,108,111,114,32,61,32,86,101,114,116,101,120,67,111,108,111,114,59,10,35,101,108,115,101,10,9,118,101,99,52,32,99,111,108,111,114,32,61,32,118,101,99,52,40,49,46,48,41,59,10,35,101,110,100,105,102,10,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,115,59,10,10,35,105,102,32,70,76,65,71,95,66,73,76,76,66,79,65,82,68,10,9,35,105,102,32,70,76,65,71,95,73,78,83,84,65,78,67,73,78,71,10,9,118,101,99,51,32,98,105,108,108,98,111,97,114,100,67,101,110,116,101,114,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,48,59,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,83,105,122,101,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,49,46,120,121,59,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,49,46,122,119,59,10,9,118,101,99,52,32,98,105,108,108,98,111,97,114,100,67,111,108,111,114,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,50,59,10,10,9,118,101,99,50,32,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,59,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,120,32,61,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,121,32,45,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,121,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,120,59,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,121,32,61,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,121,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,121,32,43,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,120,59,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,32,42,61,32,98,105,108,108,98,111,97,114,100,83,105,122,101,59,10,10,9,118,101,99,51,32,99,97,109,101,114,97,82,105,103,104,116,32,61,32,118,101,99,51,40,86,105,101,119,77,97,116,114,105,120,91,48,93,91,48,93,44,32,86,105,101,119,77,97,116,114,105,120,91,49,93,91,48,93,44,32,86,105,101,119,77,97,116,114,105,120,91,50,93,91,48,93,41,59,10,9,118,101,99,51,32,99,97,109,101,114,97,85,112,32,61,32,118,101,99,51,40,86,105,101,119,77,97,116,114,105,120,91,48,93,91,49,93,44,32,86,105,101,119,77,97,116,114,105,120,91,49,93,91,49,93,44,32,86,105,101,119,77,97,116,114,105,120,91,50,93,91,49,93,41,59,10,9,118,101,99,51,32,118,101,114,116,101,120,80,111,115,32,61,32,98,105,108,108,98,111,97,114,100,67,101,110,116,101,114,32,43,32,99,97,109,101,114,97,82,105,103,104,116,42,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,120,32,43,32,99,97,109,101,114,97,85,112,42,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,121,59,10,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,86,105,101,119,80,114,111,106,77,97,116,114,105,120,32,42,32,118,101,99,52,40,118,101,114,116,101,120,80,111,115,44,32,49,46,48,41,59,10,9,99,111,108,111,114,32,61,32,98,105,108,108,98,111,97,114,100,67,111,108,111,114,59,10,9,116,101,120,67,111,111,114,100,115,32,61,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,121,32,43,32,48,46,53,59,10,9,35,101,108,115,101,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,32,61,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,32,45,32,48,46,53,59,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,83,105,122,101,32,61,32,86,101,114,116,101,120,85,115,101,114,100,97,116,97,48,46,120,121,59,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,32,61,32,86,101,114,116,101,120,85,115,101,114,100,97,116,97,48,46,122,119,59,10,9,10,9,118,101,99,50,32,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,59,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,120,32,61,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,46,120,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,121,32,45,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,46,121,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,120,59,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,121,32,61,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,46,121,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,121,32,43,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,46,120,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,120,59,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,32,42,61,32,98,105,108,108,98,111,97,114,100,83,105,122,101,59,10,10,9,118,101,99,51,32,99,97,109,101,114,97,82,105,103,104,116,32,61,32,118,101,99,51,40,86,105,101,119,77,97,116,114,105,120,91,48,93,91,48,93,44,32,86,105,101,119,77,97,116,114,105,120,91,49,93,91,48,93,44,32,86,105,101,119,77,97,116,114,105,120,91,50,93,91,48,93,41,59,10,9,118,101,99,51,32,99,97,109,101,114,97,85,112,32,61,32,118,101,99,51,40,86,105,101,119,77,97,116,114,105,120,91,48,93,91,49,93,44,32,86,105,101,119,77,97,116,114,105,120,91,49,93,91,49,93,44,32,86,105,101,119,77,97,116,114,105,120,91,50,93,91,49,93,41,59,10,9,118,101,99,51,32,118,101,114,116,101,120,80,111,115,32,61,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,32,43,32,99,97,109,101,114,97,82,105,103,104,116,42,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,120,32,43,32,99,97,109,101,114,97,85,112,42,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,121,59,10,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,86,105,101,119,80,114,111,106,77,97,116,114,105,120,32,42,32,118,101,99,52,40,118,101,114,116,101,120,80,111,115,44,32,49,46,48,41,59,10,9,116,101,120,67,111,111,114,100,115,32,61,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,59,10,9,35,101,110,100,105,102,10,9,116,101,120,67,111,111,114,100,115,46,121,32,61,32,49,46,48,32,45,32,116,101,120,67,111,111,114,100,115,46,121,59,10,35,101,108,115,101,10,9,35,105,102,32,70,76,65,71,95,73,78,83,84,65,78,67,73,78,71,10,9,9,35,105,102,32,84,82,65,78,83,70,79,82,77,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,86,105,101,119,80,114,111,106,77,97,116,114,105,120,32,42,32,73,110,115,116,97,110,99,101,68,97,116,97,48,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,10,9,9,35,101,108,115,101,10,9,9,9,35,105,102,32,85,78,73,70,79,82,77,95,86,69,82,84,69,88,95,68,69,80,84,72,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,48,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,121,44,32,86,101,114,116,101,120,68,101,112,116,104,44,32,49,46,48,41,59,10,9,9,9,35,101,108,115,101,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,48,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,10,9,9,9,35,101,110,100,105,102,10,9,9,35,101,110,100,105,102,10,9,35,101,108,115,101,10,9,9,35,105,102,32,84,82,65,78,83,70,79,82,77,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,87,111,114,108,100,86,105,101,119,80,114,111,106,77,97,116,114,105,120,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,10,9,9,35,101,108,115,101,10,9,9,9,35,105,102,32,85,78,73,70,79,82,77,95,86,69,82,84,69,88,95,68,69,80,84,72,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,121,44,32,86,101,114,116,101,120,68,101,112,116,104,44,32,49,46,48,41,59,10,9,9,9,35,101,108,115,101,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,10,9,9,9,35,101,110,100,105,102,10,9,9,35,101,110,100,105,102,10,9,35,101,110,100,105,102,10,10,9,116,101,120,67,111,111,114,100,115,32,61,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,59,10,35,101,110,100,105,102,10,10,9,118,67,111,108,111,114,32,61,32,99,111,108,111,114,59,10,10,35,105,102,32,76,73,71,72,84,73,78,71,10,9,35,105,102,32,70,76,65,71,95,73,78,83,84,65,78,67,73,78,71,10,9,109,97,116,51,32,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,61,32,109,97,116,51,40,73,110,115,116,97,110,99,101,68,97,116,97,48,41,59,10,9,35,101,108,115,101,10,9,109,97,116,51,32,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,61,32,109,97,116,51,40,87,111,114,108,100,77,97,116,114,105,120,41,59,10,9,35,101,110,100,105,102,10,9,10,9,35,105,102,32,67,79,77,80,85,84,69,95,84,66,78,77,65,84,82,73,88,10,9,118,101,99,51,32,98,105,110,111,114,109,97,108,32,61,32,99,114,111,115,115,40,86,101,114,116,101,120,78,111,114,109,97,108,44,32,86,101,114,116,101,120,84,97,110,103,101,110,116,41,59,10,9,118,76,105,103,104,116,84,111,87,111,114,108,100,91,48,93,32,61,32,110,111,114,109,97,108,105,122,101,40,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,42,32,86,101,114,116,101,120,84,97,110,103,101,110,116,41,59,10,9,118,76,105,103,104,116,84,111,87,111,114,108,100,91,49,93,32,61,32,110,111,114,109,97,108,105,122,101,40,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,42,32,98,105,110,111,114,109,97,108,41,59,10,9,118,76,105,103,104,116,84,111,87,111,114,108,100,91,50,93,32,61,32,110,111,114,109,97,108,105,122,101,40,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,42,32,86,101,114,116,101,120,78,111,114,109,97,108,41,59,10,9,35,101,108,115,101,10,9,118,78,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,42,32,86,101,114,116,101,120,78,111,114,109,97,108,41,59,10,9,35,101,110,100,105,102,10,35,101,110,100,105,102,10,10,35,105,102,32,84,69,88,84,85,82,69,95,77,65,80,80,73,78,71,10,9,118,84,101,120,67,111,111,114,100,32,61,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,59,10,35,101,110,100,105,102,10,10,35,105,102,32,76,73,71,72,84,73,78,71,32,38,38,32,80,65,82,65,76,76,65,88,95,77,65,80,80,73,78,71,10,9,118,86,105,101,119,68,105,114,32,61,32,69,121,101,80,111,115,105,116,105,111,110,32,45,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,59,32,10,9,118,86,105,101,119,68,105,114,32,42,61,32,118,76,105,103,104,116,84,111,87,111,114,108,100,59,10,35,101,110,100,105,102,10,10,35,105,102,32,76,73,71,72,84,73,78,71,32,38,38,32,33,70,76,65,71,95,68,69,70,69,82,82,69,68,10,9,35,105,102,32,70,76,65,71,95,73,78,83,84,65,78,67,73,78,71,10,9,118,87,111,114,108,100,80,111,115,32,61,32,118,101,99,51,40,73,110,115,116,97,110,99,101,68,97,116,97,48,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,41,59,10,9,35,101,108,115,101,10,9,118,87,111,114,108,100,80,111,115,32,61,32,118,101,99,51,40,87,111,114,108,100,77,97,116,114,105,120,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,41,59,10,9,35,101,110,100,105,102,10,35,101,110,100,105,102,10,125,10, \ No newline at end of file +47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,69,110,116,114,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,35,105,102,32,70,76,65,71,95,66,73,76,76,66,79,65,82,68,13,10,105,110,32,118,101,99,51,32,73,110,115,116,97,110,99,101,68,97,116,97,48,59,32,47,47,32,99,101,110,116,101,114,13,10,105,110,32,118,101,99,52,32,73,110,115,116,97,110,99,101,68,97,116,97,49,59,32,47,47,32,115,105,122,101,32,124,32,115,105,110,32,99,111,115,13,10,105,110,32,118,101,99,52,32,73,110,115,116,97,110,99,101,68,97,116,97,50,59,32,47,47,32,99,111,108,111,114,13,10,35,101,108,115,101,13,10,105,110,32,109,97,116,52,32,73,110,115,116,97,110,99,101,68,97,116,97,48,59,13,10,35,101,110,100,105,102,13,10,13,10,105,110,32,118,101,99,52,32,86,101,114,116,101,120,67,111,108,111,114,59,13,10,105,110,32,118,101,99,51,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,59,13,10,105,110,32,118,101,99,51,32,86,101,114,116,101,120,78,111,114,109,97,108,59,13,10,105,110,32,118,101,99,51,32,86,101,114,116,101,120,84,97,110,103,101,110,116,59,13,10,105,110,32,118,101,99,50,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,59,13,10,105,110,32,118,101,99,52,32,86,101,114,116,101,120,85,115,101,114,100,97,116,97,48,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,83,111,114,116,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,111,117,116,32,118,101,99,52,32,118,67,111,108,111,114,59,13,10,111,117,116,32,118,101,99,52,32,118,76,105,103,104,116,83,112,97,99,101,80,111,115,91,51,93,59,13,10,111,117,116,32,109,97,116,51,32,118,76,105,103,104,116,84,111,87,111,114,108,100,59,13,10,111,117,116,32,118,101,99,51,32,118,78,111,114,109,97,108,59,13,10,111,117,116,32,118,101,99,50,32,118,84,101,120,67,111,111,114,100,59,13,10,111,117,116,32,118,101,99,51,32,118,86,105,101,119,68,105,114,59,13,10,111,117,116,32,118,101,99,51,32,118,87,111,114,108,100,80,111,115,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,85,110,105,102,111,114,109,101,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,117,110,105,102,111,114,109,32,118,101,99,51,32,69,121,101,80,111,115,105,116,105,111,110,59,13,10,117,110,105,102,111,114,109,32,109,97,116,52,32,73,110,118,86,105,101,119,77,97,116,114,105,120,59,13,10,117,110,105,102,111,114,109,32,109,97,116,52,32,76,105,103,104,116,86,105,101,119,80,114,111,106,77,97,116,114,105,120,91,51,93,59,13,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,86,101,114,116,101,120,68,101,112,116,104,59,13,10,117,110,105,102,111,114,109,32,109,97,116,52,32,86,105,101,119,77,97,116,114,105,120,59,13,10,117,110,105,102,111,114,109,32,109,97,116,52,32,86,105,101,119,80,114,111,106,77,97,116,114,105,120,59,13,10,117,110,105,102,111,114,109,32,109,97,116,52,32,87,111,114,108,100,77,97,116,114,105,120,59,13,10,117,110,105,102,111,114,109,32,109,97,116,52,32,87,111,114,108,100,86,105,101,119,80,114,111,106,77,97,116,114,105,120,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,70,111,110,99,116,105,111,110,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,118,111,105,100,32,109,97,105,110,40,41,13,10,123,13,10,35,105,102,32,70,76,65,71,95,86,69,82,84,69,88,67,79,76,79,82,13,10,9,118,101,99,52,32,99,111,108,111,114,32,61,32,86,101,114,116,101,120,67,111,108,111,114,59,13,10,35,101,108,115,101,13,10,9,118,101,99,52,32,99,111,108,111,114,32,61,32,118,101,99,52,40,49,46,48,41,59,13,10,35,101,110,100,105,102,13,10,13,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,115,59,13,10,13,10,35,105,102,32,70,76,65,71,95,66,73,76,76,66,79,65,82,68,13,10,9,35,105,102,32,70,76,65,71,95,73,78,83,84,65,78,67,73,78,71,13,10,9,118,101,99,51,32,98,105,108,108,98,111,97,114,100,67,101,110,116,101,114,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,48,59,13,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,83,105,122,101,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,49,46,120,121,59,13,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,49,46,122,119,59,13,10,9,118,101,99,52,32,98,105,108,108,98,111,97,114,100,67,111,108,111,114,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,50,59,13,10,13,10,9,118,101,99,50,32,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,59,13,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,120,32,61,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,121,32,45,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,121,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,120,59,13,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,121,32,61,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,121,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,121,32,43,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,120,59,13,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,32,42,61,32,98,105,108,108,98,111,97,114,100,83,105,122,101,59,13,10,13,10,9,118,101,99,51,32,99,97,109,101,114,97,82,105,103,104,116,32,61,32,118,101,99,51,40,86,105,101,119,77,97,116,114,105,120,91,48,93,91,48,93,44,32,86,105,101,119,77,97,116,114,105,120,91,49,93,91,48,93,44,32,86,105,101,119,77,97,116,114,105,120,91,50,93,91,48,93,41,59,13,10,9,118,101,99,51,32,99,97,109,101,114,97,85,112,32,61,32,118,101,99,51,40,86,105,101,119,77,97,116,114,105,120,91,48,93,91,49,93,44,32,86,105,101,119,77,97,116,114,105,120,91,49,93,91,49,93,44,32,86,105,101,119,77,97,116,114,105,120,91,50,93,91,49,93,41,59,13,10,9,118,101,99,51,32,118,101,114,116,101,120,80,111,115,32,61,32,98,105,108,108,98,111,97,114,100,67,101,110,116,101,114,32,43,32,99,97,109,101,114,97,82,105,103,104,116,42,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,120,32,43,32,99,97,109,101,114,97,85,112,42,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,121,59,13,10,13,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,86,105,101,119,80,114,111,106,77,97,116,114,105,120,32,42,32,118,101,99,52,40,118,101,114,116,101,120,80,111,115,44,32,49,46,48,41,59,13,10,9,99,111,108,111,114,32,61,32,98,105,108,108,98,111,97,114,100,67,111,108,111,114,59,13,10,9,116,101,120,67,111,111,114,100,115,32,61,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,121,32,43,32,48,46,53,59,13,10,9,35,101,108,115,101,13,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,32,61,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,32,45,32,48,46,53,59,13,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,83,105,122,101,32,61,32,86,101,114,116,101,120,85,115,101,114,100,97,116,97,48,46,120,121,59,13,10,9,118,101,99,50,32,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,32,61,32,86,101,114,116,101,120,85,115,101,114,100,97,116,97,48,46,122,119,59,13,10,9,13,10,9,118,101,99,50,32,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,59,13,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,120,32,61,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,46,120,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,121,32,45,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,46,121,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,120,59,13,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,121,32,61,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,46,121,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,121,32,43,32,98,105,108,108,98,111,97,114,100,67,111,114,110,101,114,46,120,42,98,105,108,108,98,111,97,114,100,83,105,110,67,111,115,46,120,59,13,10,9,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,32,42,61,32,98,105,108,108,98,111,97,114,100,83,105,122,101,59,13,10,13,10,9,118,101,99,51,32,99,97,109,101,114,97,82,105,103,104,116,32,61,32,118,101,99,51,40,86,105,101,119,77,97,116,114,105,120,91,48,93,91,48,93,44,32,86,105,101,119,77,97,116,114,105,120,91,49,93,91,48,93,44,32,86,105,101,119,77,97,116,114,105,120,91,50,93,91,48,93,41,59,13,10,9,118,101,99,51,32,99,97,109,101,114,97,85,112,32,61,32,118,101,99,51,40,86,105,101,119,77,97,116,114,105,120,91,48,93,91,49,93,44,32,86,105,101,119,77,97,116,114,105,120,91,49,93,91,49,93,44,32,86,105,101,119,77,97,116,114,105,120,91,50,93,91,49,93,41,59,13,10,9,118,101,99,51,32,118,101,114,116,101,120,80,111,115,32,61,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,32,43,32,99,97,109,101,114,97,82,105,103,104,116,42,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,120,32,43,32,99,97,109,101,114,97,85,112,42,114,111,116,97,116,101,100,80,111,115,105,116,105,111,110,46,121,59,13,10,13,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,86,105,101,119,80,114,111,106,77,97,116,114,105,120,32,42,32,118,101,99,52,40,118,101,114,116,101,120,80,111,115,44,32,49,46,48,41,59,13,10,9,116,101,120,67,111,111,114,100,115,32,61,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,59,13,10,9,35,101,110,100,105,102,13,10,9,116,101,120,67,111,111,114,100,115,46,121,32,61,32,49,46,48,32,45,32,116,101,120,67,111,111,114,100,115,46,121,59,13,10,35,101,108,115,101,13,10,9,35,105,102,32,70,76,65,71,95,73,78,83,84,65,78,67,73,78,71,13,10,9,9,35,105,102,32,84,82,65,78,83,70,79,82,77,13,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,86,105,101,119,80,114,111,106,77,97,116,114,105,120,32,42,32,73,110,115,116,97,110,99,101,68,97,116,97,48,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,13,10,9,9,35,101,108,115,101,13,10,9,9,9,35,105,102,32,85,78,73,70,79,82,77,95,86,69,82,84,69,88,95,68,69,80,84,72,13,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,48,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,121,44,32,86,101,114,116,101,120,68,101,112,116,104,44,32,49,46,48,41,59,13,10,9,9,9,35,101,108,115,101,13,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,73,110,115,116,97,110,99,101,68,97,116,97,48,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,13,10,9,9,9,35,101,110,100,105,102,13,10,9,9,35,101,110,100,105,102,13,10,9,35,101,108,115,101,13,10,9,9,35,105,102,32,84,82,65,78,83,70,79,82,77,13,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,87,111,114,108,100,86,105,101,119,80,114,111,106,77,97,116,114,105,120,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,13,10,9,9,35,101,108,115,101,13,10,9,9,9,35,105,102,32,85,78,73,70,79,82,77,95,86,69,82,84,69,88,95,68,69,80,84,72,13,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,46,120,121,44,32,86,101,114,116,101,120,68,101,112,116,104,44,32,49,46,48,41,59,13,10,9,9,9,35,101,108,115,101,13,10,9,103,108,95,80,111,115,105,116,105,111,110,32,61,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,13,10,9,9,9,35,101,110,100,105,102,13,10,9,9,35,101,110,100,105,102,13,10,9,35,101,110,100,105,102,13,10,13,10,9,116,101,120,67,111,111,114,100,115,32,61,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,59,13,10,35,101,110,100,105,102,13,10,13,10,9,118,67,111,108,111,114,32,61,32,99,111,108,111,114,59,13,10,13,10,35,105,102,32,70,76,65,71,95,73,78,83,84,65,78,67,73,78,71,13,10,9,109,97,116,51,32,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,61,32,109,97,116,51,40,73,110,115,116,97,110,99,101,68,97,116,97,48,41,59,13,10,35,101,108,115,101,13,10,9,109,97,116,51,32,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,61,32,109,97,116,51,40,87,111,114,108,100,77,97,116,114,105,120,41,59,13,10,35,101,110,100,105,102,13,10,9,13,10,35,105,102,32,67,79,77,80,85,84,69,95,84,66,78,77,65,84,82,73,88,13,10,9,118,101,99,51,32,98,105,110,111,114,109,97,108,32,61,32,99,114,111,115,115,40,86,101,114,116,101,120,78,111,114,109,97,108,44,32,86,101,114,116,101,120,84,97,110,103,101,110,116,41,59,13,10,9,118,76,105,103,104,116,84,111,87,111,114,108,100,91,48,93,32,61,32,110,111,114,109,97,108,105,122,101,40,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,42,32,86,101,114,116,101,120,84,97,110,103,101,110,116,41,59,13,10,9,118,76,105,103,104,116,84,111,87,111,114,108,100,91,49,93,32,61,32,110,111,114,109,97,108,105,122,101,40,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,42,32,98,105,110,111,114,109,97,108,41,59,13,10,9,118,76,105,103,104,116,84,111,87,111,114,108,100,91,50,93,32,61,32,110,111,114,109,97,108,105,122,101,40,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,42,32,86,101,114,116,101,120,78,111,114,109,97,108,41,59,13,10,35,101,108,115,101,13,10,9,118,78,111,114,109,97,108,32,61,32,110,111,114,109,97,108,105,122,101,40,114,111,116,97,116,105,111,110,77,97,116,114,105,120,32,42,32,86,101,114,116,101,120,78,111,114,109,97,108,41,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,83,72,65,68,79,87,95,77,65,80,80,73,78,71,13,10,9,102,111,114,32,40,105,110,116,32,105,32,61,32,48,59,32,105,32,60,32,51,59,32,43,43,105,41,13,10,9,9,118,76,105,103,104,116,83,112,97,99,101,80,111,115,91,105,93,32,61,32,76,105,103,104,116,86,105,101,119,80,114,111,106,77,97,116,114,105,120,91,105,93,32,42,32,87,111,114,108,100,77,97,116,114,105,120,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,84,69,88,84,85,82,69,95,77,65,80,80,73,78,71,13,10,9,118,84,101,120,67,111,111,114,100,32,61,32,86,101,114,116,101,120,84,101,120,67,111,111,114,100,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,80,65,82,65,76,76,65,88,95,77,65,80,80,73,78,71,13,10,9,118,86,105,101,119,68,105,114,32,61,32,69,121,101,80,111,115,105,116,105,111,110,32,45,32,86,101,114,116,101,120,80,111,115,105,116,105,111,110,59,32,13,10,9,118,86,105,101,119,68,105,114,32,42,61,32,118,76,105,103,104,116,84,111,87,111,114,108,100,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,33,70,76,65,71,95,68,69,70,69,82,82,69,68,13,10,9,35,105,102,32,70,76,65,71,95,73,78,83,84,65,78,67,73,78,71,13,10,9,118,87,111,114,108,100,80,111,115,32,61,32,118,101,99,51,40,73,110,115,116,97,110,99,101,68,97,116,97,48,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,41,59,13,10,9,35,101,108,115,101,13,10,9,118,87,111,114,108,100,80,111,115,32,61,32,118,101,99,51,40,87,111,114,108,100,77,97,116,114,105,120,32,42,32,118,101,99,52,40,86,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,41,59,13,10,9,35,101,110,100,105,102,13,10,35,101,110,100,105,102,13,10,125,13,10, \ No newline at end of file diff --git a/src/Nazara/Graphics/SkeletalModel.cpp b/src/Nazara/Graphics/SkeletalModel.cpp index 12bf10b6d..8621d1fce 100644 --- a/src/Nazara/Graphics/SkeletalModel.cpp +++ b/src/Nazara/Graphics/SkeletalModel.cpp @@ -14,6 +14,17 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::SkeletalModel + * \brief Graphics class that represents a model with a skeleton + */ + + /*! + * \brief Checks whether the parameters for the skeletal mesh are correct + * \return true If parameters are valid + */ + bool SkeletalModelParameters::IsValid() const { if (!ModelParameters::IsValid()) @@ -25,12 +36,23 @@ namespace Nz return true; } + /*! + * \brief Constructs a SkeletalModel object by default + */ + SkeletalModel::SkeletalModel() : m_currentSequence(nullptr), m_animationEnabled(true) { } + /*! + * \brief Adds the skeletal mesh to the rendering queue + * + * \param renderQueue Queue to be added + * \param instanceData Data for the instance + */ + void SkeletalModel::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const { if (!m_mesh) @@ -47,10 +69,18 @@ namespace Nz meshData.primitiveMode = mesh->GetPrimitiveMode(); meshData.vertexBuffer = SkinningManager::GetBuffer(mesh, &m_skeleton); - renderQueue->AddMesh(instanceData.renderOrder, material, meshData, m_skeleton.GetAABB(), instanceData.transformMatrix); + renderQueue->AddMesh(instanceData.renderOrder, material, meshData, m_skeleton.GetAABB(), *instanceData.transformMatrix); } } + /*! + * \brief Updates the animation of the mesh + * + * \param elapsedTime Delta time between two frames + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if there is no animation + */ + void SkeletalModel::AdvanceAnimation(float elapsedTime) { #if NAZARA_GRAPHICS_SAFE @@ -67,7 +97,7 @@ namespace Nz m_interpolation -= 1.f; unsigned lastFrame = m_currentSequence->firstFrame + m_currentSequence->frameCount - 1; - if (m_nextFrame+1 > lastFrame) + if (m_nextFrame + 1 > lastFrame) { if (m_animation->IsLoopPointInterpolationEnabled()) { @@ -77,7 +107,7 @@ namespace Nz else { m_currentFrame = m_currentSequence->firstFrame; - m_nextFrame = m_currentFrame+1; + m_nextFrame = m_currentFrame + 1; } } else @@ -92,26 +122,52 @@ namespace Nz InvalidateBoundingVolume(); } + /*! + * \brief Clones this skeletal model + * \return Pointer to newly allocated SkeletalModel + */ + SkeletalModel* SkeletalModel::Clone() const { return new SkeletalModel(*this); } + /*! + * \brief Creates a default skeletal model + * \return Pointer to newly allocated SkeletalModel + */ + SkeletalModel* SkeletalModel::Create() const { return new SkeletalModel; } + /*! + * \brief Enables the animation of the model + * + * \param animation Should the model be animated + */ + void SkeletalModel::EnableAnimation(bool animation) { m_animationEnabled = animation; } + /*! + * \brief Gets the animation of the model + * \return Pointer to the animation + */ + Animation* SkeletalModel::GetAnimation() const { return m_animation; } + /*! + * \brief Gets the skeleton of the model + * \return Pointer to the skeleton + */ + Skeleton* SkeletalModel::GetSkeleton() { InvalidateBoundingVolume(); @@ -119,41 +175,96 @@ namespace Nz return &m_skeleton; } + /*! + * \brief Gets the skeleton of the model + * \return Constant pointer to the skeleton + */ + const Skeleton* SkeletalModel::GetSkeleton() const { return &m_skeleton; } + /*! + * \brief Checks whether the skeleton has an animation + * \return true If it is the case + * + * \see IsAnimated, IsAnimationEnabled + */ + bool SkeletalModel::HasAnimation() const { return m_animation != nullptr; } + /*! + * \brief Checks whether the skeleton is animated + * \return true + * + * \see HasAnimation, IsAnimationEnabled + */ + bool SkeletalModel::IsAnimated() const { return true; } + /*! + * \brief Checks whether the skeleton is currently animated + * \return true If it is the case + * + * \see HasAnimation, IsAnimated + */ + bool SkeletalModel::IsAnimationEnabled() const { return m_animationEnabled; } + /*! + * \brief Loads the skeleton model from file + * \return true if loading is successful + * + * \param filePath Path to the file + * \param params Parameters for the skeleton model + */ + bool SkeletalModel::LoadFromFile(const String& filePath, const SkeletalModelParameters& params) { return SkeletalModelLoader::LoadFromFile(this, filePath, params); } + /*! + * \brief Loads the skeleton model from memory + * \return true if loading is successful + * + * \param data Raw memory + * \param size Size of the memory + * \param params Parameters for the skeleton model + */ + bool SkeletalModel::LoadFromMemory(const void* data, std::size_t size, const SkeletalModelParameters& params) { return SkeletalModelLoader::LoadFromMemory(this, data, size, params); } + /*! + * \brief Loads the skeleton model from stream + * \return true if loading is successful + * + * \param stream Stream to the skeleton model + * \param params Parameters for the skeleton model + */ + bool SkeletalModel::LoadFromStream(Stream& stream, const SkeletalModelParameters& params) { return SkeletalModelLoader::LoadFromStream(this, stream, params); } + /*! + * \brief Resets the model + */ + void SkeletalModel::Reset() { Model::Reset(); @@ -161,6 +272,16 @@ namespace Nz m_skeleton.Destroy(); } + /*! + * \brief Sets the animation for the model + * \return true If successful + * + * \param animation Animation for the model + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no mesh + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if animation is invalid + */ + bool SkeletalModel::SetAnimation(Animation* animation) { #if NAZARA_GRAPHICS_SAFE @@ -204,6 +325,14 @@ namespace Nz return true; } + /*! + * \brief Sets the mesh for the model + * + * \param mesh Mesh for the model + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no mesh or if invalid + */ + void SkeletalModel::SetMesh(Mesh* mesh) { #if NAZARA_GRAPHICS_SAFE @@ -224,13 +353,23 @@ namespace Nz SetAnimation(nullptr); } - m_skeleton = *m_mesh->GetSkeleton(); // Copie du squelette template + m_skeleton = *m_mesh->GetSkeleton(); // Copy of skeleton template } } + /*! + * \brief Sets the sequence for the model + * \return true If successful + * + * \param sequenceName Name for the sequence animation + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no animation + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if sequence name does not exist for the current animation + */ + bool SkeletalModel::SetSequence(const String& sequenceName) { - ///TODO: Rendre cette erreur "safe" avec le nouveau système de gestions d'erreur (No-log) + ///TODO: Make this error "safe" with the new system of error handling (No-log) #if NAZARA_GRAPHICS_SAFE if (!m_animation) { @@ -240,11 +379,13 @@ namespace Nz #endif const Sequence* currentSequence = m_animation->GetSequence(sequenceName); + #if NAZARA_GRAPHICS_SAFE if (!currentSequence) { NazaraError("Sequence not found"); return false; } + #endif m_currentSequence = currentSequence; m_nextFrame = m_currentSequence->firstFrame; @@ -252,6 +393,15 @@ namespace Nz return true; } + /*! + * \brief Sets the sequence for the model + * + * \param sequenceIndex Index for the sequence animation + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if there is no animation + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE if sequence name does not exist for the current animation + */ + void SkeletalModel::SetSequence(unsigned int sequenceIndex) { #if NAZARA_GRAPHICS_SAFE @@ -275,13 +425,22 @@ namespace Nz m_nextFrame = m_currentSequence->firstFrame; } + /* + * \brief Makes the bounding volume of this text + */ + void SkeletalModel::MakeBoundingVolume() const { m_boundingVolume.Set(m_skeleton.GetAABB()); } + /*! + * \brief Updates the model + */ + void SkeletalModel::Update() { + ///TODO /*if (m_animationEnabled && m_animation) AdvanceAnimation(m_scene->GetUpdateTime());*/ } diff --git a/src/Nazara/Graphics/SkinningManager.cpp b/src/Nazara/Graphics/SkinningManager.cpp index 0f4b3140a..7391eeea6 100644 --- a/src/Nazara/Graphics/SkinningManager.cpp +++ b/src/Nazara/Graphics/SkinningManager.cpp @@ -46,6 +46,13 @@ namespace Nz SkeletonMap s_cache; std::vector s_skinningQueue; + /*! + * \brief Skins the mesh for a single thread context + * + * \param mesh Skeletal mesh to get vertex buffer from + * \param skeleton Skeleton to consider for getting data + * \param buffer Vertex buffer symbolizing the transition + */ void Skin_MonoCPU(const SkeletalMesh* mesh, const Skeleton* skeleton, VertexBuffer* buffer) { @@ -60,6 +67,14 @@ namespace Nz SkinPositionNormalTangent(skinningData, 0, mesh->GetVertexCount()); } + /*! + * \brief Skins the mesh for a multi-threaded context + * + * \param mesh Skeletal mesh to get vertex buffer from + * \param skeleton Skeleton to consider for getting data + * \param buffer Vertex buffer symbolizing the transition + */ + void Skin_MultiCPU(const SkeletalMesh* mesh, const Skeleton* skeleton, VertexBuffer* buffer) { BufferMapper inputMapper(mesh->GetVertexBuffer(), BufferAccess_ReadOnly); @@ -70,8 +85,8 @@ namespace Nz skinningData.outputVertex = static_cast(outputMapper.GetPointer()); skinningData.joints = skeleton->GetJoints(); - // Afin d'empêcher les différents threads de vouloir mettre à jour la même matrice en même temps, - // on se charge de la mettre à jour avant de les lancer + // To avoid different threads to update the same matrix at the same time + // We try to update them before launching the tasks unsigned int jointCount = skeleton->GetJointCount(); for (unsigned int i = 0; i < jointCount; ++i) skinningData.joints[i].EnsureSkinningMatrixUpdate(); @@ -87,6 +102,23 @@ namespace Nz } } + /*! + * \ingroup graphics + * \class Nz::SkinningManager + * \brief Graphics class that represents the management of skinning + */ + + /*! + * \brief Gets the vertex buffer from a skeletal mesh with its skeleton + * \return A pointer to the vertex buffer newly created + * + * \param mesh Skeletal mesh to get vertex buffer from + * \param skeleton Skeleton to consider for getting data + * + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if mesh is invalid + * \remark Produces a NazaraError with NAZARA_GRAPHICS_SAFE defined if skeleton is invalid + */ + VertexBuffer* SkinningManager::GetBuffer(const SkeletalMesh* mesh, const Skeleton* skeleton) { #if NAZARA_GRAPHICS_SAFE @@ -149,6 +181,10 @@ namespace Nz return buffer; } + /*! + * \brief Skins the skeletal mesh + */ + void SkinningManager::Skin() { for (QueueData& data : s_skinningQueue) @@ -157,6 +193,11 @@ namespace Nz s_skinningQueue.clear(); } + /*! + * \brief Initializes the skinning librairies + * \return true + */ + bool SkinningManager::Initialize() { ///TODO: GPU Skinning @@ -165,9 +206,15 @@ namespace Nz else s_skinFunc = Skin_MonoCPU; - return true; // Rien de particulier à faire + return true; // Nothing particular to do } + /*! + * \brief Handle the destruction of a skeletal mesh + * + * \param mesh SkeletalMesh being destroyed + */ + void SkinningManager::OnSkeletalMeshDestroy(const SkeletalMesh* mesh) { for (auto& pair : s_cache) @@ -177,17 +224,33 @@ namespace Nz } } + /*! + * \brief Handle the invalidation of a skeletal mesh + * + * \param mesh SkeletalMesh being invalidated + */ + void SkinningManager::OnSkeletonInvalidated(const Skeleton* skeleton) { for (auto& pair : s_cache.at(skeleton).meshMap) pair.second.updated = false; } + /*! + * \brief Handle the release of a skeletal mesh + * + * \param skeleton SkeletalMesh being released + */ + void SkinningManager::OnSkeletonRelease(const Skeleton* skeleton) { s_cache.erase(skeleton); } + /*! + * \brief Uninitializes the skinning librairies + */ + void SkinningManager::Uninitialize() { s_cache.clear(); diff --git a/src/Nazara/Graphics/SkyboxBackground.cpp b/src/Nazara/Graphics/SkyboxBackground.cpp index a70a9dd2d..fa7faefac 100644 --- a/src/Nazara/Graphics/SkyboxBackground.cpp +++ b/src/Nazara/Graphics/SkyboxBackground.cpp @@ -22,6 +22,18 @@ namespace Nz static VertexBufferRef s_vertexBuffer; } + /*! + * \ingroup graphics + * \class Nz::SkyboxBackground + * \brief Graphics class that represents a background with a cubemap texture + */ + + /*! + * \brief Constructs a SkyboxBackground object with a cubemap texture + * + * \param cubemapTexture Cubemap texture + */ + SkyboxBackground::SkyboxBackground(TextureRef cubemapTexture) : m_movementOffset(Vector3f::Zero()), m_movementScale(0.f) @@ -31,6 +43,12 @@ namespace Nz SetTexture(std::move(cubemapTexture)); } + /*! + * \brief Draws this relatively to the viewer + * + * \param viewer Viewer for the background + */ + void SkyboxBackground::Draw(const AbstractViewer* viewer) const { Matrix4f skyboxMatrix(viewer->GetViewMatrix()); @@ -65,14 +83,26 @@ namespace Nz Renderer::SetMatrix(MatrixType_View, viewer->GetViewMatrix()); } + /*! + * \brief Gets the background type + * \return Type of background + */ + BackgroundType SkyboxBackground::GetBackgroundType() const { return BackgroundType_Skybox; } + /*! + * \brief Initializes the skybox + * \return true If successful + * + * \remark Produces a NazaraError if initialization failed + */ + bool SkyboxBackground::Initialize() { - const UInt16 indices[6*6] = + const UInt16 indices[6 * 6] = { 0, 1, 2, 0, 2, 3, 3, 2, 6, 3, 6, 7, @@ -151,10 +181,10 @@ namespace Nz // Renderstates s_renderStates.depthFunc = RendererComparison_Equal; - s_renderStates.faceCulling = FaceSide_Front; - s_renderStates.parameters[RendererParameter_DepthBuffer] = true; - s_renderStates.parameters[RendererParameter_DepthWrite] = false; - s_renderStates.parameters[RendererParameter_FaceCulling] = true; + s_renderStates.cullingSide = FaceSide_Front; + s_renderStates.depthBuffer = true; + s_renderStates.depthWrite = false; + s_renderStates.faceCulling = true; // Exception-free zone s_indexBuffer = std::move(indexBuffer); @@ -170,6 +200,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the skybox + */ + void SkyboxBackground::Uninitialize() { s_indexBuffer.Reset(); diff --git a/src/Nazara/Graphics/Sprite.cpp b/src/Nazara/Graphics/Sprite.cpp index fd4ab758f..4bf076183 100644 --- a/src/Nazara/Graphics/Sprite.cpp +++ b/src/Nazara/Graphics/Sprite.cpp @@ -11,6 +11,19 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::Sprite + * \brief Graphics class that represents the rendering of a sprite + */ + + /*! + * \brief Adds the sprite to the rendering queue + * + * \param renderQueue Queue to be added + * \param instanceData Data for the instance + */ + void Sprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const { if (!m_material) @@ -20,11 +33,21 @@ namespace Nz renderQueue->AddSprites(instanceData.renderOrder, m_material, vertices, 1); } + /*! + * \brief Makes the bounding volume of this text + */ + void Sprite::MakeBoundingVolume() const { m_boundingVolume.Set(Vector3f(0.f), m_size.x*Vector3f::Right() + m_size.y*Vector3f::Down()); } + /*! + * \brief Updates the data of the sprite + * + * \param instanceData Data of the instance + */ + void Sprite::UpdateData(InstanceData* instanceData) const { instanceData->data.resize(4 * sizeof(VertexStruct_XYZ_Color_UV)); @@ -35,22 +58,29 @@ namespace Nz SparsePtr texCoordPtr(&vertices[0].uv, sizeof(VertexStruct_XYZ_Color_UV)); *colorPtr++ = m_color; - *posPtr++ = instanceData->transformMatrix.Transform(Vector3f(0.f)); + *posPtr++ = instanceData->transformMatrix->Transform(Vector3f(0.f)); *texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_LeftTop); *colorPtr++ = m_color; - *posPtr++ = instanceData->transformMatrix.Transform(m_size.x*Vector3f::Right()); + *posPtr++ = instanceData->transformMatrix->Transform(m_size.x*Vector3f::Right()); *texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_RightTop); *colorPtr++ = m_color; - *posPtr++ = instanceData->transformMatrix.Transform(m_size.y*Vector3f::Down()); + *posPtr++ = instanceData->transformMatrix->Transform(m_size.y*Vector3f::Down()); *texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_LeftBottom); *colorPtr++ = m_color; - *posPtr++ = instanceData->transformMatrix.Transform(m_size.x*Vector3f::Right() + m_size.y*Vector3f::Down()); + *posPtr++ = instanceData->transformMatrix->Transform(m_size.x*Vector3f::Right() + m_size.y*Vector3f::Down()); *texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_RightBottom); } + /*! + * \brief Initializes the sprite librairies + * \return true If successful + * + * \remark Produces a NazaraError if the sprite library failed to be initialized + */ + bool Sprite::Initialize() { if (!SpriteLibrary::Initialize()) @@ -62,6 +92,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the sprite librairies + */ + void Sprite::Uninitialize() { SpriteLibrary::Uninitialize(); diff --git a/src/Nazara/Graphics/TextSprite.cpp b/src/Nazara/Graphics/TextSprite.cpp index 6d0e41f52..2f1d733b4 100644 --- a/src/Nazara/Graphics/TextSprite.cpp +++ b/src/Nazara/Graphics/TextSprite.cpp @@ -13,6 +13,19 @@ namespace Nz { + /*! + * \ingroup graphics + * \class Nz::TextSprite + * \brief Graphics class that represents the rendering of a sprite containing text + */ + + /*! + * \brief Adds the text to the rendering queue + * + * \param renderQueue Queue to be added + * \param instanceData Data for the instance + */ + void TextSprite::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const { if (!m_material) @@ -26,11 +39,19 @@ namespace Nz if (indices.count > 0) { const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(instanceData.data.data()); - renderQueue->AddSprites(instanceData.renderOrder, m_material, &vertices[indices.first*4], indices.count, overlay); + renderQueue->AddSprites(instanceData.renderOrder, m_material, &vertices[indices.first * 4], indices.count, overlay); } } } + /*! + * \brief Updates the text + * + * \param drawer Drawer used to compose the text + * + * \remark Produces a NazaraAssert if atlas does not use a hardware storage + */ + void TextSprite::Update(const AbstractTextDrawer& drawer) { CallOnExit clearOnFail([this]() @@ -43,8 +64,8 @@ namespace Nz pair.second.used = false; // ... until they are marked as used by the drawer - unsigned int fontCount = drawer.GetFontCount(); - for (unsigned int i = 0; i < fontCount; ++i) + std::size_t fontCount = drawer.GetFontCount(); + for (std::size_t i = 0; i < fontCount; ++i) { Font* font = drawer.GetFont(i); const AbstractAtlas* atlas = font->GetAtlas().get(); @@ -74,7 +95,7 @@ namespace Nz ++atlasIt; } - unsigned int glyphCount = drawer.GetGlyphCount(); + std::size_t glyphCount = drawer.GetGlyphCount(); m_localVertices.resize(glyphCount * 4); // Reset glyph count for every texture to zero @@ -84,7 +105,7 @@ namespace Nz // Count glyph count for each texture Texture* lastTexture = nullptr; unsigned int* count = nullptr; - for (unsigned int i = 0; i < glyphCount; ++i) + for (std::size_t i = 0; i < glyphCount; ++i) { const AbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i); @@ -138,8 +159,8 @@ namespace Nz // First, compute the uv coordinates from our atlas rect Vector2ui size(texture->GetSize()); - float invWidth = 1.f/size.x; - float invHeight = 1.f/size.y; + float invWidth = 1.f / size.x; + float invHeight = 1.f / size.y; Rectf uvRect(glyph.atlasRect); uvRect.x *= invWidth; @@ -155,9 +176,9 @@ namespace Nz for (unsigned int j = 0; j < 4; ++j) { // Remember that indices->count is a counter here, not a count value - m_localVertices[indices->count*4 + j].color = glyph.color; - m_localVertices[indices->count*4 + j].position.Set(glyph.corners[j]); - m_localVertices[indices->count*4 + j].uv.Set(uvRect.GetCorner((glyph.flipped) ? flippedCorners[j] : normalCorners[j])); + m_localVertices[indices->count * 4 + j].color = glyph.color; + m_localVertices[indices->count * 4 + j].position.Set(glyph.corners[j]); + m_localVertices[indices->count * 4 + j].uv.Set(uvRect.GetCorner((glyph.flipped) ? flippedCorners[j] : normalCorners[j])); } // Increment the counter, go to next glyph @@ -172,15 +193,25 @@ namespace Nz clearOnFail.Reset(); } + /* + * \brief Makes the bounding volume of this text + */ + void TextSprite::MakeBoundingVolume() const { Rectf bounds(m_localBounds); Vector2f max = bounds.GetMaximum(); Vector2f min = bounds.GetMinimum(); - m_boundingVolume.Set(min.x*Vector3f::Right() + min.y*Vector3f::Down(), max.x*Vector3f::Right() + max.y*Vector3f::Down()); + m_boundingVolume.Set(min.x * Vector3f::Right() + min.y * Vector3f::Down(), max.x * Vector3f::Right() + max.y * Vector3f::Down()); } + /*! + * \brief Handle the invalidation of an atlas + * + * \param atlas Atlas being invalidated + */ + void TextSprite::OnAtlasInvalidated(const AbstractAtlas* atlas) { #ifdef NAZARA_DEBUG @@ -195,6 +226,14 @@ namespace Nz Clear(); } + /*! + * \brief Handle the invalidation of an atlas layer + * + * \param atlas Atlas being invalidated + * \param oldLayer Pointer to the previous layer + * \param newLayer Pointer to the new layer + */ + void TextSprite::OnAtlasLayerChange(const AbstractAtlas* atlas, AbstractImage* oldLayer, AbstractImage* newLayer) { NazaraUnused(atlas); @@ -207,23 +246,23 @@ namespace Nz } #endif - // La texture d'un atlas vient d'être recréée (changement de taille) - // nous devons ajuster les coordonnées de textures et la texture du rendu + // The texture of an atlas have just been recreated (size change) + // we have to adjust the coordinates of the texture and the rendering texture Texture* oldTexture = static_cast(oldLayer); Texture* newTexture = static_cast(newLayer); - // Il est possible que nous n'utilisions pas la texture en question (l'atlas nous prévenant pour chacun de ses layers) + // It is possible that we don't use the texture (the atlas warning us for each of its layers) auto it = m_renderInfos.find(oldTexture); if (it != m_renderInfos.end()) { - // Nous utilisons bien cette texture, nous devons mettre à jour les coordonnées de texture + // We indeed use this texture, we have to update its coordinates RenderIndices indices = std::move(it->second); Vector2ui oldSize(oldTexture->GetSize()); Vector2ui newSize(newTexture->GetSize()); - Vector2f scale = Vector2f(oldSize)/Vector2f(newSize); // ratio ancienne et nouvelle taille + Vector2f scale = Vector2f(oldSize) / Vector2f(newSize); // ratio of the old one to the new one - // On va maintenant parcourir toutes les coordonnées de texture concernées pour les multiplier par ce ratio + // Now we will iterate through each coordinates of the concerned texture to multiply them by the ratio SparsePtr texCoordPtr(&m_localVertices[indices.first].uv, sizeof(VertexStruct_XYZ_Color_UV)); for (unsigned int i = 0; i < indices.count; ++i) { @@ -231,12 +270,18 @@ namespace Nz m_localVertices[i*4 + j].uv *= scale; } - // Nous enlevons l'ancienne texture et rajoutons la nouvelle à sa place (pour les mêmes indices) + // We get rid off the old texture and we set the new one at the place (same for indices) m_renderInfos.erase(it); m_renderInfos.insert(std::make_pair(newTexture, std::move(indices))); } } + /*! + * \brief Updates the data of the sprite + * + * \param instanceData Data of the instance + */ + void TextSprite::UpdateData(InstanceData* instanceData) const { instanceData->data.resize(m_localVertices.size() * sizeof(VertexStruct_XYZ_Color_UV)); @@ -246,18 +291,18 @@ namespace Nz SparsePtr posPtr(&vertices[0].position, sizeof(VertexStruct_XYZ_Color_UV)); SparsePtr texCoordPtr(&vertices[0].uv, sizeof(VertexStruct_XYZ_Color_UV)); - // Nous allons maintenant initialiser les sommets finaux (ceux envoyés à la RenderQueue) - // à l'aide du repère, de la matrice et de notre attribut de couleur + // We will not initialize the final vertices (those send to the RenderQueue) + // With the help of the coordinates axis, the matrix and our color attribute for (auto& pair : m_renderInfos) { RenderIndices& indices = pair.second; if (indices.count == 0) continue; //< Ignore empty render indices - SparsePtr color = colorPtr + indices.first*4; - SparsePtr pos = posPtr + indices.first*4; - SparsePtr uv = texCoordPtr + indices.first*4; - VertexStruct_XY_Color_UV* localVertex = &m_localVertices[indices.first*4]; + SparsePtr color = colorPtr + indices.first * 4; + SparsePtr pos = posPtr + indices.first * 4; + SparsePtr uv = texCoordPtr + indices.first * 4; + VertexStruct_XY_Color_UV* localVertex = &m_localVertices[indices.first * 4]; for (unsigned int i = 0; i < indices.count; ++i) { for (unsigned int j = 0; j < 4; ++j) @@ -265,7 +310,7 @@ namespace Nz Vector3f localPos = localVertex->position.x*Vector3f::Right() + localVertex->position.y*Vector3f::Down(); localPos *= m_scale; - *pos++ = instanceData->transformMatrix.Transform(localPos); + *pos++ = instanceData->transformMatrix->Transform(localPos); *color++ = m_color * localVertex->color; *uv++ = localVertex->uv; diff --git a/src/Nazara/Graphics/TextureBackground.cpp b/src/Nazara/Graphics/TextureBackground.cpp index 9ceb633e6..a990f4af6 100644 --- a/src/Nazara/Graphics/TextureBackground.cpp +++ b/src/Nazara/Graphics/TextureBackground.cpp @@ -11,19 +11,36 @@ namespace Nz { namespace { + /*! + * \brief Defines render states + * \return RenderStates for the color background + */ + RenderStates BuildRenderStates() { RenderStates states; states.depthFunc = RendererComparison_Equal; - states.faceCulling = FaceSide_Back; - states.parameters[RendererParameter_DepthBuffer] = true; - states.parameters[RendererParameter_DepthWrite] = false; - states.parameters[RendererParameter_FaceCulling] = true; + states.cullingSide = FaceSide_Back; + states.depthBuffer = true; + states.depthWrite = false; + states.faceCulling = true; return states; } } + /*! + * \ingroup graphics + * \class Nz::TextureBackground + * \brief Graphics class that represents a background with a texture + */ + + /*! + * \brief Constructs a TextureBackground object with a texture + * + * \param texture Texture + */ + TextureBackground::TextureBackground(TextureRef texture) { m_uberShader = UberShaderLibrary::Get("Basic"); @@ -43,6 +60,12 @@ namespace Nz SetTexture(std::move(texture)); } + /*! + * \brief Draws this relatively to the viewer + * + * \param viewer Viewer for the background + */ + void TextureBackground::Draw(const AbstractViewer* viewer) const { NazaraUnused(viewer); @@ -62,6 +85,11 @@ namespace Nz Renderer::DrawFullscreenQuad(); } + /*! + * \brief Gets the background type + * \return Type of background + */ + BackgroundType TextureBackground::GetBackgroundType() const { return BackgroundType_Texture; diff --git a/src/Nazara/Graphics/TileMap.cpp b/src/Nazara/Graphics/TileMap.cpp new file mode 100644 index 000000000..9d835cb24 --- /dev/null +++ b/src/Nazara/Graphics/TileMap.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup graphics + * \class Nz::TileMap + * \brief Graphics class that represent several tiles of the same size assembled into a grid + * This class is far more efficient than using a sprite for every tile + */ + + /*! + * \brief Adds the TileMap to the rendering queue + * + * \param renderQueue Queue to be added + * \param instanceData Data for the instance + */ + void TileMap::AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const + { + const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(instanceData.data.data()); + + std::size_t spriteCount = 0; + for (const Layer& layer : m_layers) + { + if (layer.material) + renderQueue->AddSprites(instanceData.renderOrder, layer.material, &vertices[spriteCount], layer.tiles.size()); + + spriteCount += layer.tiles.size(); + } + } + + void TileMap::MakeBoundingVolume() const + { + Nz::Vector2f size = GetSize(); + m_boundingVolume.Set(Vector3f(0.f), size.x*Vector3f::Right() + size.y*Vector3f::Down()); + } + + void TileMap::UpdateData(InstanceData* instanceData) const + { + std::size_t spriteCount = 0; + for (const Layer& layer : m_layers) + spriteCount += layer.tiles.size(); + + instanceData->data.resize(4 * spriteCount * sizeof(VertexStruct_XYZ_Color_UV)); + VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(instanceData->data.data()); + + spriteCount = 0; + for (const Layer& layer : m_layers) + { + SparsePtr colorPtr(&vertices[spriteCount].color, sizeof(VertexStruct_XYZ_Color_UV)); + SparsePtr posPtr(&vertices[spriteCount].position, sizeof(VertexStruct_XYZ_Color_UV)); + SparsePtr texCoordPtr(&vertices[spriteCount].uv, sizeof(VertexStruct_XYZ_Color_UV)); + + for (std::size_t tileIndex : layer.tiles) + { + const Tile& tile = m_tiles[tileIndex]; + NazaraAssert(tile.enabled, "Tile specified for rendering is not enabled"); + + std::size_t x = tileIndex % m_mapSize.x; + std::size_t y = tileIndex / m_mapSize.x; + Vector3f tileLeftCorner(x * m_tileSize.x, y * -m_tileSize.y, 0.f); + + *colorPtr++ = tile.color; + *posPtr++ = instanceData->transformMatrix->Transform(tileLeftCorner); + *texCoordPtr++ = tile.textureCoords.GetCorner(RectCorner_LeftTop); + + *colorPtr++ = tile.color; + *posPtr++ = instanceData->transformMatrix->Transform(tileLeftCorner + m_tileSize.x * Vector3f::Right()); + *texCoordPtr++ = tile.textureCoords.GetCorner(RectCorner_RightTop); + + *colorPtr++ = tile.color; + *posPtr++ = instanceData->transformMatrix->Transform(tileLeftCorner + m_tileSize.y * Vector3f::Down()); + *texCoordPtr++ = tile.textureCoords.GetCorner(RectCorner_LeftBottom); + + *colorPtr++ = tile.color; + *posPtr++ = instanceData->transformMatrix->Transform(tileLeftCorner + m_tileSize.x * Vector3f::Right() + m_tileSize.y * Vector3f::Down()); + *texCoordPtr++ = tile.textureCoords.GetCorner(RectCorner_RightBottom); + } + spriteCount += layer.tiles.size(); + } + } + + bool TileMap::Initialize() + { + if (!TileMapLibrary::Initialize()) + { + NazaraError("Failed to initialise library"); + return false; + } + + return true; + } + + void TileMap::Uninitialize() + { + TileMapLibrary::Uninitialize(); + } + + TileMapLibrary::LibraryMap TileMap::s_library; +} diff --git a/src/Nazara/Lua/LuaInstance.cpp b/src/Nazara/Lua/LuaInstance.cpp index 23f852921..0ecf4c063 100644 --- a/src/Nazara/Lua/LuaInstance.cpp +++ b/src/Nazara/Lua/LuaInstance.cpp @@ -401,7 +401,7 @@ namespace Nz return false; } - unsigned int length = static_cast(file.GetSize()); + std::size_t length = static_cast(file.GetSize()); String source(length, '\0'); @@ -416,7 +416,7 @@ namespace Nz return Execute(source); } - bool LuaInstance::ExecuteFromMemory(const void* data, unsigned int size) + bool LuaInstance::ExecuteFromMemory(const void* data, std::size_t size) { MemoryView stream(data, size); return ExecuteFromStream(stream); @@ -679,7 +679,7 @@ namespace Nz lua_pushstring(m_state, str); } - void LuaInstance::PushString(const char* str, unsigned int size) const + void LuaInstance::PushString(const char* str, std::size_t size) const { lua_pushlstring(m_state, str, size); } @@ -694,7 +694,7 @@ namespace Nz lua_createtable(m_state, sequenceElementCount, arrayElementCount); } - void* LuaInstance::PushUserdata(unsigned int size) const + void* LuaInstance::PushUserdata(std::size_t size) const { return lua_newuserdata(m_state, size); } @@ -749,7 +749,7 @@ namespace Nz lua_setmetatable(m_state, index); } - void LuaInstance::SetMemoryLimit(UInt32 memoryLimit) + void LuaInstance::SetMemoryLimit(std::size_t memoryLimit) { m_memoryLimit = memoryLimit; } @@ -860,8 +860,8 @@ namespace Nz void* LuaInstance::MemoryAllocator(void* ud, void* ptr, std::size_t osize, std::size_t nsize) { LuaInstance* instance = static_cast(ud); - UInt32& memoryLimit = instance->m_memoryLimit; - UInt32& memoryUsage = instance->m_memoryUsage; + std::size_t& memoryLimit = instance->m_memoryLimit; + std::size_t& memoryUsage = instance->m_memoryUsage; if (nsize == 0) { @@ -872,7 +872,7 @@ namespace Nz } else { - UInt32 usage = memoryUsage + nsize; + std::size_t usage = memoryUsage + nsize; if (ptr) usage -= osize; diff --git a/src/Nazara/Network/AbstractSocket.cpp b/src/Nazara/Network/AbstractSocket.cpp index e46a5906a..516364373 100644 --- a/src/Nazara/Network/AbstractSocket.cpp +++ b/src/Nazara/Network/AbstractSocket.cpp @@ -17,6 +17,18 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::AbstractSocket + * \brief Network class that represents the base of socket + * + * \remark This class is abstract + */ + + /*! + * \brief Constructs a AbstractSocket object with a type + */ + AbstractSocket::AbstractSocket(SocketType type) : m_lastError(SocketError_NoError), m_handle(SocketImpl::InvalidHandle), @@ -26,6 +38,12 @@ namespace Nz { } + /*! + * \brief Constructs a AbstractSocket object with another one by move semantic + * + * \param abstractSocket AbstractSocket to move into this + */ + AbstractSocket::AbstractSocket(AbstractSocket&& abstractSocket) : m_protocol(abstractSocket.m_protocol), m_lastError(abstractSocket.m_lastError), @@ -37,11 +55,21 @@ namespace Nz abstractSocket.m_handle = SocketImpl::InvalidHandle; } + /*! + * \brief Destructs the object and calls Close + * + * \see Close + */ + AbstractSocket::~AbstractSocket() { Close(); } + /*! + * \brief Closes the socket + */ + void AbstractSocket::Close() { if (m_handle != SocketImpl::InvalidHandle) @@ -53,6 +81,12 @@ namespace Nz } } + /*! + * \brief Enables blocking + * + * \param blocking Should the read block + */ + void AbstractSocket::EnableBlocking(bool blocking) { if (m_isBlockingEnabled != blocking) @@ -64,6 +98,11 @@ namespace Nz } } + /*! + * \brief Queries the available bytes + * \return Number of bytes which can be read + */ + std::size_t AbstractSocket::QueryAvailableBytes() const { if (m_handle == SocketImpl::InvalidHandle) @@ -72,11 +111,21 @@ namespace Nz return SocketImpl::QueryAvailableBytes(m_handle); } + /*! + * \brief Operation to do when closing socket + */ + void AbstractSocket::OnClose() { UpdateState(SocketState_NotConnected); } + /*! + * \brief Operation to do when opening socket + * + * \remark Produces a NazaraWarning if blocking failed + */ + void AbstractSocket::OnOpened() { SocketError errorCode; @@ -84,6 +133,11 @@ namespace Nz NazaraWarning("Failed to set socket blocking mode (0x" + String::Number(errorCode, 16) + ')'); } + /*! + * \brief Opens the socket according to a net protocol + * \return true If successful + */ + bool AbstractSocket::Open(NetProtocol protocol) { if (m_handle == SocketImpl::InvalidHandle || m_protocol != protocol) @@ -99,6 +153,13 @@ namespace Nz return true; } + /*! + * \brief Opens the socket according to a socket handle + * \return true If successful + * + * \remark Produces a NazaraAssert if handle is invalid + */ + void AbstractSocket::Open(SocketHandle handle) { NazaraAssert(handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -109,6 +170,13 @@ namespace Nz OnOpened(); } + /*! + * \brief Moves the AbstractSocket into this + * \return A reference to this + * + * \param abstractSocket AbstractSocket to move in this + */ + AbstractSocket& AbstractSocket::operator=(AbstractSocket&& abstractSocket) { Close(); diff --git a/src/Nazara/Network/AlgorithmNetwork.cpp b/src/Nazara/Network/AlgorithmNetwork.cpp index 273169005..307ac679a 100644 --- a/src/Nazara/Network/AlgorithmNetwork.cpp +++ b/src/Nazara/Network/AlgorithmNetwork.cpp @@ -11,6 +11,15 @@ namespace Nz { namespace Detail { + /*! + * \brief Parses a decimal number + * \return true If successful + * + * \param str C-string symbolizing the string to parse + * \param number Optional argument to return the number parsed + * \param endOfRead Optional argument to determine where parsing stopped + */ + bool ParseDecimal(const char* str, unsigned int* number, const char** endOfRead) { const char* ptr = str; @@ -35,6 +44,15 @@ namespace Nz return true; } + /*! + * \brief Parses a hexadecimal number + * \return true If successful + * + * \param str C-string symbolizing the string to parse + * \param number Optional argument to return the number parsed + * \param endOfRead Optional argument to determine where parsing stopped + */ + bool ParseHexadecimal(const char* str, unsigned int* number, const char** endOfRead) { const char* ptr = str; @@ -60,10 +78,26 @@ namespace Nz } } - // From http://rosettacode.org/wiki/Parse_an_IP_Address - // Parse a textual IPv4 or IPv6 address, optionally with port, into a binary - // array (for the address, in host order), and an optionally provided port. - // Also, indicate which of those forms (4 or 6) was parsed. + /*! + * \ingroup network + * \brief Parse a textual IPv4 or IPv6 address + * \return true If successful + * + * From http://rosettacode.org/wiki/Parse_an_IP_Address + * Parse a textual IPv4 or IPv6 address, optionally with port, into a binary + * array (for the address, in host order), and an optionally provided port. + * Also, indicate which of those forms (4 or 6) was parsed. + * + * \param addressPtr C-string which symbolizes the ip adress + * \param result Byte array to return the result in + * \param port Optional argument to resolve according to a specific port + * \param isIPv6 Optional argument to determine if the address is IPv6 + * \param endOfRead Optional argument to determine where parsing stopped + * + * \remark Produces a NazaraAssert if addressPtr is invalid + * \remark Produces a NazaraAssert if result is invalid + */ + bool ParseIPAddress(const char* addressPtr, UInt8 result[16], UInt16* port, bool* isIPv6, const char** endOfRead) { NazaraAssert(addressPtr, "Invalid address string"); diff --git a/src/Nazara/Network/IpAddress.cpp b/src/Nazara/Network/IpAddress.cpp index d40e2a581..29be756ea 100644 --- a/src/Nazara/Network/IpAddress.cpp +++ b/src/Nazara/Network/IpAddress.cpp @@ -22,6 +22,19 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::IpAddress + * \brief Network class that represents an IP address + */ + + /*! + * \brief Builds the IP from a hostname + * \return true If successful + * + * \remark address C-string symbolizing the IP address or hostname + */ + bool IpAddress::BuildFromAddress(const char* address) { m_isValid = false; @@ -50,6 +63,13 @@ namespace Nz return true; } + /*! + * \brief Checks whether the IP address is loopback + * \return true If it is the case + * + * \remark Produces a NazaraAssert if internal protocol is invalid (should never happen) + */ + bool IpAddress::IsLoopback() const { if (!m_isValid) @@ -73,6 +93,13 @@ namespace Nz return false; } + /*! + * \brief Gives a string representation + * \return A string representation of the object + * + * \remark Produces a NazaraAssert if internal protocol is invalid (should never happen) + */ + String IpAddress::ToString() const { StringStream stream; @@ -153,6 +180,17 @@ namespace Nz return stream; } + /*! + * \brief Resolves the address based on the IP + * \return Hostname of the address + * + * \param address IP address to resolve + * \param service Optional argument to specify the protocol used + * \param error Optional argument to get the error + * + * \remark Produces a NazaraAssert if address is invalid + */ + String IpAddress::ResolveAddress(const IpAddress& address, String* service, ResolveError* error) { NazaraAssert(address.IsValid(), "Invalid address"); @@ -163,6 +201,18 @@ namespace Nz return hostname; } + /*! + * \brief Resolves the address based on the hostname + * \return Informations about the host: IP(s) of the address, names, ... + * + * \param protocol Net protocol to use + * \param hostname Hostname to resolve + * \param service Specify the service used (http, ...) + * \param error Optional argument to get the error + * + * \remark Produces a NazaraAssert if net protocol is set to unknown + */ + std::vector IpAddress::ResolveHostname(NetProtocol protocol, const String& hostname, const String& service, ResolveError* error) { NazaraAssert(protocol != NetProtocol_Unknown, "Invalid protocol"); diff --git a/src/Nazara/Network/NetPacket.cpp b/src/Nazara/Network/NetPacket.cpp index 7a1c0d7f5..0823f5258 100644 --- a/src/Nazara/Network/NetPacket.cpp +++ b/src/Nazara/Network/NetPacket.cpp @@ -9,11 +9,36 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::NetPacket + * \brief Network class that represents a packet + */ + + /*! + * \brief Operation to do when receiving data + * + * \param netCode Packet number + * \param data Raw memory + * \param size Size of the memory + */ + void NetPacket::OnReceive(UInt16 netCode, const void* data, std::size_t size) { Reset(netCode, data, size); } + /*! + * \brief Operation to do when sending data + * \return Beggining of the raw memory + * + * \param newSize Size of the memory to send + * + * \remark Produces a NazaraAssert if newSize is invalid + * \remark Produces a NazaraAssert if net code is invalid + * \remark Produces a NazaraError if header could not be encoded + */ + const void* NetPacket::OnSend(std::size_t* newSize) const { NazaraAssert(newSize, "Invalid size pointer"); @@ -30,6 +55,15 @@ namespace Nz return m_buffer->GetBuffer(); } + /*! + * \brief Decodes the header of the packet + * \return true If successful + * + * \param data Raw memory + * \param packetSize Size of the packet + * \param netCode Packet number + */ + bool NetPacket::DecodeHeader(const void* data, UInt16* packetSize, UInt16* netCode) { MemoryView stream(data, HeaderSize); @@ -40,6 +74,15 @@ namespace Nz return Unserialize(context, packetSize) && Unserialize(context, netCode); } + /*! + * \brief Encodes the header of the packet + * \return true If successful + * + * \param data Raw memory + * \param packetSize Size of the packet + * \param netCode Packet number + */ + bool NetPacket::EncodeHeader(void* data, UInt16 packetSize, UInt16 netCode) { MemoryView stream(data, HeaderSize); @@ -50,11 +93,19 @@ namespace Nz return Serialize(context, packetSize) && Serialize(context, netCode); } + /*! + * \brief Operation to do when stream is empty + */ + void NetPacket::OnEmptyStream() { Reset(0); } + /*! + * \brief Frees the stream + */ + void NetPacket::FreeStream() { if (!m_buffer) @@ -66,6 +117,16 @@ namespace Nz s_availableBuffers.emplace_back(std::make_pair(size, std::move(m_buffer))); } + /*! + * \brief Inits the internal stream + * + * \param minCapacity Minimal capacity of the stream + * \param cursorPos Position of the cursor in the stream + * \param openMode Flag of the stream + * + * \remark Produces a NazaraAssert if cursor position is greather than the capacity + */ + void NetPacket::InitStream(std::size_t minCapacity, UInt64 cursorPos, UInt32 openMode) { NazaraAssert(minCapacity >= cursorPos, "Cannot init stream with a smaller capacity than wanted cursor pos"); @@ -92,12 +153,21 @@ namespace Nz SetStream(&m_memoryStream); } + /*! + * \brief Initializes the NetPacket class + * \return true If initialization is successful + */ + bool NetPacket::Initialize() { s_availableBuffersMutex = std::make_unique(); return true; } + /*! + * \brief Uninitializes the NetPacket class + */ + void NetPacket::Uninitialize() { s_availableBuffers.clear(); diff --git a/src/Nazara/Network/Network.cpp b/src/Nazara/Network/Network.cpp index 41868b79b..fa3813dd4 100644 --- a/src/Nazara/Network/Network.cpp +++ b/src/Nazara/Network/Network.cpp @@ -24,9 +24,23 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::Network + * \brief Network class that represents the module initializer of Network + */ + + /*! + * \brief Initializes the Network module + * \return true if initialization is successful + * + * \remark Produces a NazaraNotice + * \remark Produces a NazaraError if one submodule failed + */ + bool Network::Initialize() { - if (s_moduleReferenceCounter > 0) + if (IsInitialized()) { s_moduleReferenceCounter++; return true; // Already initialized @@ -68,11 +82,22 @@ namespace Nz return true; } + /*! + * \brief Checks whether the module is initialized + * \return true if module is initialized + */ + bool Network::IsInitialized() { return s_moduleReferenceCounter != 0; } + /*! + * \brief Uninitializes the Core module + * + * \remark Produces a NazaraNotice + */ + void Network::Uninitialize() { if (s_moduleReferenceCounter != 1) diff --git a/src/Nazara/Network/RUdpConnection.cpp b/src/Nazara/Network/RUdpConnection.cpp index 451d08f1e..1b374d604 100644 --- a/src/Nazara/Network/RUdpConnection.cpp +++ b/src/Nazara/Network/RUdpConnection.cpp @@ -10,6 +10,16 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::RUdpConnection + * \brief Network class that represents a reliable UDP connection + */ + + /*! + * \brief Constructs a RUdpConnection object by default + */ + RUdpConnection::RUdpConnection() : m_peerIterator(0), m_forceAckSendTime(10'000), //< 10ms @@ -23,9 +33,20 @@ namespace Nz { } + /*! + * \brief Connects to the IpAddress + * \return true + * + * \param remoteAddress Address to connect to + * + * \remark Produces a NazaraAssert if socket is not bound + * \remark Produces a NazaraAssert if remote is invalid + * \remark Produces a NazaraAssert if port is not specified + */ + bool RUdpConnection::Connect(const IpAddress& remoteAddress) { - NazaraAssert(m_socket.GetState() != SocketState_Bound, "Socket must be bound first"); + NazaraAssert(m_socket.GetState() == SocketState_Bound, "Socket must be bound first"); NazaraAssert(remoteAddress.IsValid(), "Invalid remote address"); NazaraAssert(remoteAddress.GetPort() != 0, "Remote address has no port"); @@ -39,6 +60,16 @@ namespace Nz return true; } + /*! + * \brief Connects to the hostname + * \return true If successful + * + * \param hostName Hostname of the remote + * \param protocol Net protocol to use + * \param service Specify the protocol used + * \param error Optional argument to get the error + */ + bool RUdpConnection::Connect(const String& hostName, NetProtocol protocol, const String& service, ResolveError* error) { std::vector results = IpAddress::ResolveHostname(protocol, hostName, service, error); @@ -64,6 +95,13 @@ namespace Nz return Connect(hostnameAddress); } + /*! + * \brief Listens to a socket + * \return true If successfully bound + * + * \param remoteAddress Address to listen to + */ + bool RUdpConnection::Listen(const IpAddress& address) { if (!InitSocket(address.GetProtocol())) @@ -72,8 +110,19 @@ namespace Nz return m_socket.Bind(address) == SocketState_Bound; } + /*! + * \brief Polls the message + * \return true If there is a message + * + * \param message Message to poll + * + * \remark Produces a NazaraAssert if message is invalid + */ + bool RUdpConnection::PollMessage(RUdpMessage* message) { + NazaraAssert(message, "Invalid message"); + if (m_receivedMessages.empty()) return false; @@ -82,6 +131,16 @@ namespace Nz return true; } + /*! + * \brief Sends the packet to a peer + * \return true If peer exists (false may result from disconnected client) + * + * \param peerIp IpAddress of the peer + * \param priority Priority of the packet + * \param reliability Policy of reliability of the packet + * \param packet Packet to send + */ + bool RUdpConnection::Send(const IpAddress& peerIp, PacketPriority priority, PacketReliability reliability, const NetPacket& packet) { auto it = m_peerByIP.find(peerIp); @@ -92,6 +151,10 @@ namespace Nz return true; } + /*! + * \brief Updates the reliable connection + */ + void RUdpConnection::Update() { m_currentTime = m_clock.GetMicroseconds(); @@ -156,6 +219,14 @@ namespace Nz //m_activeClients.Reset(); } + /*! + * \brief Disconnects a peer + * + * \param peerIndex Index of the peer + * + * \remark Produces a NazaraNotice + */ + void RUdpConnection::DisconnectPeer(std::size_t peerIndex) { PeerData& peer = m_peers[peerIndex]; @@ -193,6 +264,15 @@ namespace Nz m_peers.pop_back(); } + /*! + * \brief Enqueues a packet in the sending list + * + * \param peer Data relative to the peer + * \param priority Priority of the packet + * \param reliability Policy of reliability of the packet + * \param packet Packet to send + */ + void RUdpConnection::EnqueuePacket(PeerData& peer, PacketPriority priority, PacketReliability reliability, const NetPacket& packet) { UInt16 protocolBegin = static_cast(m_protocol & 0xFFFF); @@ -208,6 +288,15 @@ namespace Nz EnqueuePacketInternal(peer, priority, reliability, std::move(data)); } + /*! + * \brief Enqueues internally a packet in the sending list + * + * \param peer Data relative to the peer + * \param priority Priority of the packet + * \param reliability Policy of reliability of the packet + * \param packet Packet to send + */ + void RUdpConnection::EnqueuePacketInternal(PeerData& peer, PacketPriority priority, PacketReliability reliability, NetPacket&& data) { PendingPacket pendingPacket; @@ -219,6 +308,13 @@ namespace Nz m_activeClients.UnboundedSet(peer.index); } + /*! + * \brief Inits the internal socket + * \return true If successful + * + * \param protocol Net protocol to use + */ + bool RUdpConnection::InitSocket(NetProtocol protocol) { CallOnExit updateLastError([this] @@ -233,6 +329,14 @@ namespace Nz return true; } + /*! + * \brief Processes the acks + * + * \param peer Data relative to the peer + * \param lastAck Last index of the ack + * \param ackBits Bits for acking + */ + void RUdpConnection::ProcessAcks(PeerData& peer, SequenceIndex lastAck, UInt32 ackBits) { auto it = peer.pendingAckQueue.begin(); @@ -257,6 +361,14 @@ namespace Nz } } + /*! + * \brief Registers a peer + * \return Data relative to the peer + * + * \param address Address of the peer + * \param state Status of the peer + */ + RUdpConnection::PeerData& RUdpConnection::RegisterPeer(const IpAddress& address, PeerState state) { PeerData data; @@ -266,7 +378,7 @@ namespace Nz data.index = m_peers.size(); data.lastPacketTime = m_currentTime; data.lastPingTime = m_currentTime; - data.roundTripTime = 1000000; ///< Okay that's quite a lot + data.roundTripTime = 1'000'000; ///< Okay that's quite a lot data.state = state; m_activeClients.UnboundedSet(data.index); @@ -276,6 +388,14 @@ namespace Nz return m_peers.back(); } + /*! + * \brief Operation to do when client requests a connection + * + * \param address Address of the peer + * \param sequenceId Sequence index for the ack + * \param token Token for connection + */ + void RUdpConnection::OnClientRequestingConnection(const IpAddress& address, SequenceIndex sequenceId, UInt64 token) { // Call hook to check if client should be accepted or not @@ -292,6 +412,13 @@ namespace Nz EnqueuePacket(client, PacketPriority_Immediate, PacketReliability_Reliable, connectionAcceptedPacket); } + /*! + * \brief Operation to do when a packet is lost + * + * \param peer Data relative to the peer + * \param packet Pending packet + */ + void RUdpConnection::OnPacketLost(PeerData& peer, PendingAckPacket&& packet) { //NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Lost packet " + String::Number(packet.sequenceId)); @@ -300,6 +427,14 @@ namespace Nz EnqueuePacketInternal(peer, packet.priority, packet.reliability, std::move(packet.data)); } + /*! + * \brief Operation to do when receiving a packet + * + * \param peerIndex Index of the peer + * + * \remark Produces a NazaraNotice + */ + void RUdpConnection::OnPacketReceived(const IpAddress& peerIp, NetPacket&& packet) { UInt16 protocolBegin; @@ -436,6 +571,13 @@ namespace Nz } } + /*! + * \brief Sends a packet to a peer + * + * \param peer Data relative to the peer + * \param packet Pending packet + */ + void RUdpConnection::SendPacket(PeerData& peer, PendingPacket&& packet) { if (peer.state == PeerState_WillAck) @@ -448,7 +590,7 @@ namespace Nz { if (ack == remoteSequence) continue; - + unsigned int difference = ComputeSequenceDifference(remoteSequence, ack); if (difference <= 32U) previousAcks |= (1U << (difference - 1)); @@ -473,6 +615,11 @@ namespace Nz peer.pendingAckQueue.emplace_back(std::move(pendingAckPacket)); } + /*! + * \brief Initializes the RUdpConnection class + * \return true + */ + bool RUdpConnection::Initialize() { std::random_device device; @@ -481,6 +628,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the RUdpConnection class + */ + void RUdpConnection::Uninitialize() { } diff --git a/src/Nazara/Network/TcpClient.cpp b/src/Nazara/Network/TcpClient.cpp index a134cf870..cf123ff89 100644 --- a/src/Nazara/Network/TcpClient.cpp +++ b/src/Nazara/Network/TcpClient.cpp @@ -20,6 +20,22 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::TcpClient + * \brief Network class that represents a client in a TCP connection + */ + + /*! + * \brief Connects to the IpAddress + * \return State of the socket + * + * \param remoteAddress Address to connect to + * + * \remark Produces a NazaraAssert if remote is invalid + * \remark Produces a NazaraAssert if remote's port is not specified + */ + SocketState TcpClient::Connect(const IpAddress& remoteAddress) { NazaraAssert(remoteAddress.IsValid(), "Invalid remote address"); @@ -45,6 +61,17 @@ namespace Nz return state; } + + /*! + * \brief Connects to the hostname + * \return State of the socket + * + * \param hostName Hostname of the remote + * \param protocol Net protocol to use + * \param service Specify the protocol used + * \param error Optional argument to get the error + */ + SocketState TcpClient::Connect(const String& hostName, NetProtocol protocol, const String& service, ResolveError* error) { UpdateState(SocketState_Resolving); @@ -73,6 +100,14 @@ namespace Nz return Connect(hostnameAddress); } + /*! + * \brief Enables low delay in emitting + * + * \param lowDelay Should low delay be used + * + * \remark This may produce lag + */ + void TcpClient::EnableLowDelay(bool lowDelay) { if (m_isLowDelayEnabled != lowDelay) @@ -84,6 +119,14 @@ namespace Nz } } + /*! + * \brief Enables the keep alive flag + * + * \param keepAlive Should the connection be kept alive + * \param msTime Time in milliseconds before expiration + * \param msInterval Interval in milliseconds between two pings + */ + void TcpClient::EnableKeepAlive(bool keepAlive, UInt64 msTime, UInt64 msInterval) { if (m_isKeepAliveEnabled != keepAlive || m_keepAliveTime != msTime || m_keepAliveInterval != msInterval) @@ -97,22 +140,51 @@ namespace Nz } } + /*! + * \brief Checks whether the stream reached the end of the stream + * \return true if there is no more available bytes + */ + bool TcpClient::EndOfStream() const { return QueryAvailableBytes() == 0; } + /*! + * \brief Gets the position of the cursor + * \return 0 + * + * \remark Produces a NazaraError because it is a special stream + */ + UInt64 TcpClient::GetCursorPos() const { NazaraError("GetCursorPos() cannot be used on sequential streams"); return 0; } + /*! + * \brief Gets the size of the raw memory available + * \return Size of the memory available + */ + UInt64 TcpClient::GetSize() const { return QueryAvailableBytes(); } + /*! + * \brief Receives the data available + * \return true If data received + * + * \param buffer Raw memory to write + * \param size Size of the buffer + * \param received Optional argument to get the number of bytes received + * + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if buffer and its size is invalid + */ + bool TcpClient::Receive(void* buffer, std::size_t size, std::size_t* received) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -142,6 +214,17 @@ namespace Nz return true; } + /*! + * \brief Receives the packet available + * \return true If packet received + * + * \param packet Packet to receive + * + * \remark Produces a NazaraAssert if packet is invalid + * \remark Produces a NazaraAssert if packet size is inferior to the header size + * \remark Produces a NazaraWarning if packet's header is invalid + */ + bool TcpClient::ReceivePacket(NetPacket* packet) { //TODO: Every packet requires at least two Receive call, using an internal buffer of a fixed size would prevent this @@ -157,6 +240,7 @@ namespace Nz m_pendingPacket.received += received; + //TODO: Should never happen in production ! NazaraAssert(m_pendingPacket.received <= NetPacket::HeaderSize, "Received more data than header size"); if (m_pendingPacket.received >= NetPacket::HeaderSize) { @@ -185,6 +269,7 @@ namespace Nz m_pendingPacket.received += received; + //TODO: Should never happen in production ! NazaraAssert(m_pendingPacket.received <= packetSize, "Received more data than packet size"); if (m_pendingPacket.received >= packetSize) { @@ -202,6 +287,19 @@ namespace Nz return false; } + /*! + * \brief Sends the data available + * \return true If data sended + * + * \param buffer Raw memory to read + * \param size Size of the buffer + * \param sent Optional argument to get the number of bytes sent + * + * \remark Large sending are handled, you do not need to call this multiple time + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if buffer and its size is invalid + */ + bool TcpClient::Send(const void* buffer, std::size_t size, std::size_t* sent) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -244,6 +342,15 @@ namespace Nz return true; } + /*! + * \brief Sends the packet available + * \return true If packet sent + * + * \param packet Packet to send + * + * \remark Produces a NazaraError if packet could not be prepared for sending + */ + bool TcpClient::SendPacket(const NetPacket& packet) { std::size_t size = 0; @@ -258,12 +365,30 @@ namespace Nz return Send(ptr, size, nullptr); } + /*! + * \brief Sets the position of the cursor + * \return false + * + * \param offset Offset according to the beginning of the stream + * + * \remark Produces a NazaraError because it is a special stream + */ + bool TcpClient::SetCursorPos(UInt64 offset) { NazaraError("SetCursorPos() cannot be used on sequential streams"); return false; } + /*! + * \brief Waits for being connected before time out + * \return true If connection is successful + * + * \param msTimeout Time in milliseconds before time out + * + * \remark Produces a NazaraAssert if socket is invalid + */ + bool TcpClient::WaitForConnected(UInt64 msTimeout) { switch (m_state) @@ -308,10 +433,18 @@ namespace Nz return false; } + /*! + * \brief Flushes the stream + */ + void TcpClient::FlushStream() { } + /*! + * \brief Operation to do when closing socket + */ + void TcpClient::OnClose() { AbstractSocket::OnClose(); @@ -320,6 +453,12 @@ namespace Nz m_peerAddress = IpAddress::Invalid; } + /*! + * \brief Operation to do when opening socket + * + * \remark Produces a NazaraWarning if delay mode or keep alive failed + */ + void TcpClient::OnOpened() { AbstractSocket::OnOpened(); @@ -336,6 +475,16 @@ namespace Nz m_openMode = OpenMode_ReadWrite; } + /*! + * \brief Reads blocks + * \return Number of blocks read + * + * \param buffer Preallocated buffer to contain information read + * \param size Size of the read and thus of the buffer + * + * \remark Produces a NazaraAssert if socket is invalid + */ + std::size_t TcpClient::ReadBlock(void* buffer, std::size_t size) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -357,6 +506,13 @@ namespace Nz return received; } + /*! + * \brief Resets the connection with a new socket and a peer address + * + * \param handle Socket to connect + * \param peerAddress Address to connect to + */ + void TcpClient::Reset(SocketHandle handle, const IpAddress& peerAddress) { Open(handle); @@ -365,8 +521,20 @@ namespace Nz UpdateState(SocketState_Connected); } + /*! + * \brief Writes blocks + * \return Number of blocks written + * + * \param buffer Preallocated buffer containing information to write + * \param size Size of the writting and thus of the buffer + * + * \remark Produces a NazaraAssert if buffer is nullptr + * \remark Produces a NazaraAssert if socket is invalid + */ + std::size_t TcpClient::WriteBlock(const void* buffer, std::size_t size) { + NazaraAssert(buffer, "Invalid buffer"); NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); CallOnExit restoreBlocking; diff --git a/src/Nazara/Network/TcpServer.cpp b/src/Nazara/Network/TcpServer.cpp index b5995b6f5..7a33c066b 100644 --- a/src/Nazara/Network/TcpServer.cpp +++ b/src/Nazara/Network/TcpServer.cpp @@ -19,6 +19,22 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::TcpServer + * \brief Network class that represents a server in a TCP connection + */ + + /*! + * \brief Accepts a client + * \return true If client'socket is valid + * + * \param newClient Client connection + * + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if newClient is invalid + */ + bool TcpServer::AcceptClient(TcpClient* newClient) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Server isn't listening"); @@ -35,6 +51,16 @@ namespace Nz return false; } + /*! + * \brief Listens to a socket + * \return State of the socket + * + * \param address Address to listen to + * \param queueSize Size of the queue + * + * \remark Produces a NazaraAssert if address is invalid + */ + SocketState TcpServer::Listen(const IpAddress& address, unsigned int queueSize) { NazaraAssert(address.IsValid(), "Invalid address"); @@ -49,6 +75,10 @@ namespace Nz return state; } + /*! + * \brief Operation to do when closing socket + */ + void TcpServer::OnClose() { AbstractSocket::OnClose(); @@ -56,6 +86,10 @@ namespace Nz m_boundAddress = IpAddress::Invalid; } + /*! + * \brief Operation to do when opening socket + */ + void TcpServer::OnOpened() { AbstractSocket::OnOpened(); diff --git a/src/Nazara/Network/UdpSocket.cpp b/src/Nazara/Network/UdpSocket.cpp index a233881ea..b49aedcf3 100644 --- a/src/Nazara/Network/UdpSocket.cpp +++ b/src/Nazara/Network/UdpSocket.cpp @@ -17,6 +17,16 @@ namespace Nz { + /*! + * \brief Binds a specific IpAddress + * \return State of the socket + * + * \param address Address to bind + * + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if address is invalid + */ + SocketState UdpSocket::Bind(const IpAddress& address) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created"); @@ -30,6 +40,14 @@ namespace Nz return state; } + /*! + * \brief Enables broadcasting + * + * \param broadcasting Should the UDP broadcast + * + * \remark Produces a NazaraAssert if socket is invalid + */ + void UdpSocket::EnableBroadcasting(bool broadcasting) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -41,6 +59,13 @@ namespace Nz } } + /*! + * \brief Gets the maximum datagram size allowed + * \return Number of bytes + * + * \remark Produces a NazaraAssert if socket is invalid + */ + std::size_t UdpSocket::QueryMaxDatagramSize() { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created"); @@ -48,8 +73,22 @@ namespace Nz return SocketImpl::QueryMaxDatagramSize(m_handle, &m_lastError); } + /*! + * \brief Receives the data available + * \return true If data received + * + * \param buffer Raw memory to write + * \param size Size of the buffer + * \param from IpAddress of the peer + * \param received Optional argument to get the number of bytes received + * + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if buffer and its size is invalid + */ + bool UdpSocket::Receive(void* buffer, std::size_t size, IpAddress* from, std::size_t* received) { + NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created"); NazaraAssert(buffer && size > 0, "Invalid buffer"); int read; @@ -62,8 +101,21 @@ namespace Nz return true; } + /*! + * \brief Receives the packet available + * \return true If packet received + * + * \param packet Packet to receive + * \param from IpAddress of the peer + * + * \remark Produces a NazaraAssert if packet is invalid + * \remark Produces a NazaraWarning if packet's header is invalid + */ + bool UdpSocket::ReceivePacket(NetPacket* packet, IpAddress* from) { + NazaraAssert(packet, "Invalid packet"); + // I'm not sure what's the best between having a 65k bytes buffer ready for any datagram size // or querying the next datagram size every time, for now I'll leave it as is packet->Reset(NetCode_Invalid, std::numeric_limits::max()); @@ -97,6 +149,20 @@ namespace Nz return true; } + /*! + * \brief Sends the data available + * \return true If data sended + * + * \param to IpAddress of the peer + * \param buffer Raw memory to read + * \param size Size of the buffer + * \param sent Optional argument to get the number of bytes sent + * + * \remark Produces a NazaraAssert if peer address is invalid + * \remark Produces a NazaraAssert if protocol of communication is not the same than the peer + * \remark Produces a NazaraAssert if buffer and its size is invalid + */ + bool UdpSocket::Send(const IpAddress& to, const void* buffer, std::size_t size, std::size_t* sent) { NazaraAssert(to.IsValid(), "Invalid ip address"); @@ -113,6 +179,16 @@ namespace Nz return true; } + /*! + * \brief Sends the packet available + * \return true If packet sent + * + * \param to IpAddress of the peer + * \param packet Packet to send + * + * \remark Produces a NazaraError if packet could not be prepared for sending + */ + bool UdpSocket::SendPacket(const IpAddress& to, const NetPacket& packet) { std::size_t size = 0; @@ -127,6 +203,10 @@ namespace Nz return Send(to, ptr, size, nullptr); } + /*! + * \brief Operation to do when closing socket + */ + void UdpSocket::OnClose() { AbstractSocket::OnClose(); @@ -134,6 +214,10 @@ namespace Nz m_boundAddress = IpAddress::Invalid; } + /*! + * \brief Operation to do when opening socket + */ + void UdpSocket::OnOpened() { AbstractSocket::OnOpened(); diff --git a/src/Nazara/Network/Win32/IpAddressImpl.cpp b/src/Nazara/Network/Win32/IpAddressImpl.cpp index 9f3918574..cbcebb83b 100644 --- a/src/Nazara/Network/Win32/IpAddressImpl.cpp +++ b/src/Nazara/Network/Win32/IpAddressImpl.cpp @@ -56,7 +56,7 @@ namespace Nz std::array hostnameBuffer; std::array serviceBuffer; - int result = getnameinfo(socketAddress, socketLen, hostnameBuffer.data(), hostnameBuffer.size(), serviceBuffer.data(), serviceBuffer.size(), flags); + int result = getnameinfo(socketAddress, socketLen, hostnameBuffer.data(), static_cast(hostnameBuffer.size()), serviceBuffer.data(), static_cast(serviceBuffer.size()), flags); if (result == 0) { if (hostname) @@ -84,22 +84,28 @@ namespace Nz { sockaddr_in* ipv4 = reinterpret_cast(info->ai_addr); - auto& rawIpV4 = ipv4->sin_addr.S_un.S_un_b; - return IpAddress(rawIpV4.s_b1, rawIpV4.s_b2, rawIpV4.s_b3, rawIpV4.s_b4, ntohs(ipv4->sin_port)); + auto& rawIpV4 = ipv4->sin_addr; + return IpAddress(rawIpV4.s_net, rawIpV4.s_host, rawIpV4.s_lh, rawIpV4.s_impno, ntohs(ipv4->sin_port)); } case AF_INET6: { sockaddr_in6* ipv6 = reinterpret_cast(info->ai_addr); - auto& rawIpV6 = ipv6->sin6_addr.u.Word; - return IpAddress(rawIpV6[0], rawIpV6[1], rawIpV6[2], rawIpV6[3], rawIpV6[4], rawIpV6[5], rawIpV6[6], rawIpV6[7], ntohs(ipv6->sin6_port)); + auto& rawIpV6 = ipv6->sin6_addr.s6_addr; + + IpAddress::IPv6 structIpV6; + for (unsigned int i = 0; i < 8; ++i) + structIpV6[i] = UInt16(rawIpV6[i * 2]) << 8 | UInt16(rawIpV6[i * 2 + 1]); + + return IpAddress(structIpV6, ntohs(ipv6->sin6_port)); } } return IpAddress::Invalid; } + #if NAZARA_CORE_WINDOWS_VISTA IpAddress IpAddressImpl::FromAddrinfo(const addrinfoW* info) { switch (info->ai_family) @@ -121,6 +127,7 @@ namespace Nz return IpAddress::Invalid; } + #endif IpAddress IpAddressImpl::FromSockAddr(const sockaddr* address) { @@ -144,8 +151,13 @@ namespace Nz IpAddress IpAddressImpl::FromSockAddr(const sockaddr_in6* addressv6) { - auto& rawIpV6 = addressv6->sin6_addr.u.Word; - return IpAddress(rawIpV6[0], rawIpV6[1], rawIpV6[2], rawIpV6[3], rawIpV6[4], rawIpV6[5], rawIpV6[6], rawIpV6[7], ntohs(addressv6->sin6_port)); + auto& rawIpV6 = addressv6->sin6_addr.s6_addr; + + IpAddress::IPv6 ipv6; + for (unsigned int i = 0; i < 8; ++i) + ipv6[i] = rawIpV6[i*2] << 8 | rawIpV6[i*2+1]; + + return IpAddress(ipv6, ntohs(addressv6->sin6_port)); } bool IpAddressImpl::ResolveAddress(const IpAddress& ipAddress, String* hostname, String* service, ResolveError* error) @@ -221,7 +233,7 @@ namespace Nz std::memset(socketAddress, 0, sizeof(sockaddr_in)); socketAddress->sin_family = AF_INET; socketAddress->sin_port = htons(ipAddress.GetPort()); - socketAddress->sin_addr.S_un.S_addr = htonl(ipAddress.ToUInt32()); + socketAddress->sin_addr.s_addr = htonl(ipAddress.ToUInt32()); return sizeof(sockaddr_in); } @@ -236,7 +248,10 @@ namespace Nz IpAddress::IPv6 address = ipAddress.ToIPv6(); for (unsigned int i = 0; i < 8; ++i) - socketAddress->sin6_addr.u.Word[i] = htons(address[i]); + { + socketAddress->sin6_addr.s6_addr[i * 2 + 0] = htons(address[i]) >> 8; + socketAddress->sin6_addr.s6_addr[i * 2 + 1] = htons(address[i]) >> 0; + } return sizeof(sockaddr_in6); } diff --git a/src/Nazara/Network/Win32/IpAddressImpl.hpp b/src/Nazara/Network/Win32/IpAddressImpl.hpp index 4b27bef32..960092839 100644 --- a/src/Nazara/Network/Win32/IpAddressImpl.hpp +++ b/src/Nazara/Network/Win32/IpAddressImpl.hpp @@ -17,7 +17,9 @@ namespace Nz ~IpAddressImpl() = delete; static IpAddress FromAddrinfo(const addrinfo* info); + #if NAZARA_CORE_WINDOWS_VISTA static IpAddress FromAddrinfo(const addrinfoW* info); + #endif static IpAddress FromSockAddr(const sockaddr* address); static IpAddress FromSockAddr(const sockaddr_in* addressv4); static IpAddress FromSockAddr(const sockaddr_in6* addressv6); diff --git a/src/Nazara/Network/Win32/SocketImpl.cpp b/src/Nazara/Network/Win32/SocketImpl.cpp index 35d4b2371..ab7ba7fe3 100644 --- a/src/Nazara/Network/Win32/SocketImpl.cpp +++ b/src/Nazara/Network/Win32/SocketImpl.cpp @@ -6,7 +6,22 @@ #include #include #include -#include + +#include +#ifdef NAZARA_COMPILER_MINGW +// MinGW is lacking Mstcpip.h and that's too bad +struct tcp_keepalive +{ + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; +}; + +#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) +#else + #include +#endif // NAZARA_COMPILER_MINGW + #include namespace Nz @@ -93,7 +108,7 @@ namespace Nz IpAddressImpl::SockAddrBuffer nameBuffer; int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data()); - + if (error) *error = SocketError_NoError; @@ -313,7 +328,7 @@ namespace Nz return code == TRUE; } - unsigned int SocketImpl::QueryMaxDatagramSize(SocketHandle handle, SocketError* error) + std::size_t SocketImpl::QueryMaxDatagramSize(SocketHandle handle, SocketError* error) { unsigned int code; int codeLength = sizeof(code); @@ -365,7 +380,7 @@ namespace Nz return IpAddress(); } - + if (error) *error = SocketError_NoError; @@ -566,7 +581,7 @@ namespace Nz return true; } - + bool SocketImpl::SetBroadcasting(SocketHandle handle, bool broadcasting, SocketError* error) { NazaraAssert(handle != InvalidHandle, "Invalid handle"); diff --git a/src/Nazara/Noise/Abstract2DNoise.cpp b/src/Nazara/Noise/Abstract2DNoise.cpp deleted file mode 100644 index 51cfeddcc..000000000 --- a/src/Nazara/Noise/Abstract2DNoise.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include - -namespace Nz -{ - Abstract2DNoise::~Abstract2DNoise() = default; - - float Abstract2DNoise::GetBasicValue(float x, float y) - { - return this->GetValue(x,y,m_resolution); - } - - float Abstract2DNoise::GetMappedValue(float x, float y) - { - return (this->GetValue(x,y,m_resolution) + m_offset) * m_gain; - } -} diff --git a/src/Nazara/Noise/Abstract3DNoise.cpp b/src/Nazara/Noise/Abstract3DNoise.cpp deleted file mode 100644 index 6456babec..000000000 --- a/src/Nazara/Noise/Abstract3DNoise.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include - -namespace Nz -{ - Abstract3DNoise::~Abstract3DNoise() = default; - - float Abstract3DNoise::GetBasicValue(float x, float y, float z) - { - return this->GetValue(x,y,z,m_resolution); - } - - float Abstract3DNoise::GetMappedValue(float x, float y, float z) - { - return (this->GetValue(x,y,z,m_resolution) + m_offset) * m_gain ; - } -} diff --git a/src/Nazara/Noise/Abstract4DNoise.cpp b/src/Nazara/Noise/Abstract4DNoise.cpp deleted file mode 100644 index 44bde3b0f..000000000 --- a/src/Nazara/Noise/Abstract4DNoise.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include - -namespace Nz -{ - Abstract4DNoise::~Abstract4DNoise() = default; - - float Abstract4DNoise::GetBasicValue(float x, float y, float z, float w) - { - return this->GetValue(x,y,z,w,m_resolution); - } - - float Abstract4DNoise::GetMappedValue(float x, float y, float z, float w) - { - return (this->GetValue(x,y,z,w,m_resolution) + m_offset) * m_gain ; - } -} diff --git a/src/Nazara/Noise/ComplexNoiseBase.cpp b/src/Nazara/Noise/ComplexNoiseBase.cpp deleted file mode 100644 index c62ad8a8f..000000000 --- a/src/Nazara/Noise/ComplexNoiseBase.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include - -namespace Nz -{ - ComplexNoiseBase::ComplexNoiseBase() - { - m_parametersModified = true; - m_lacunarity = 5.0f; - m_hurst = 1.2f; - m_octaves = 3.0f; - - for (int i(0) ; i < m_octaves; ++i) - { - m_exponent_array[i] = 0; - } - } - - float ComplexNoiseBase::GetLacunarity() const - { - - return m_lacunarity; - } - - float ComplexNoiseBase::GetHurstParameter() const - { - return m_hurst; - } - - float ComplexNoiseBase::GetOctaveNumber() const - { - return m_octaves; - } - - void ComplexNoiseBase::SetLacunarity(float lacunarity) - { - m_lacunarity = lacunarity; - m_parametersModified = true; - - } - - void ComplexNoiseBase::SetHurstParameter(float h) - { - m_hurst = h; - m_parametersModified = true; - } - - void ComplexNoiseBase::SetOctavesNumber(float octaves) - { - if(octaves <= 30.0f) - m_octaves = octaves; - else - m_octaves = 30.0f; - - m_parametersModified = true; - } - - void ComplexNoiseBase::RecomputeExponentArray() - { - if(m_parametersModified) - { - float frequency = 1.0; - m_sum = 0.f; - for (int i(0) ; i < static_cast(m_octaves) ; ++i) - { - - m_exponent_array[i] = std::pow( frequency, -m_hurst ); - frequency *= m_lacunarity; - - m_sum += m_exponent_array[i]; - - } - m_parametersModified = false; - } - } -} diff --git a/src/Nazara/Noise/FBM.cpp b/src/Nazara/Noise/FBM.cpp new file mode 100644 index 000000000..96126ec60 --- /dev/null +++ b/src/Nazara/Noise/FBM.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + FBM::FBM(const NoiseBase & source): m_source(source) + { + } + + ///TODO: Handle with variadic templates + float FBM::Get(float x, float y, float scale) const + { + float value = 0.f; + for(int i = 0; i < m_octaves; ++i) + { + value += m_source.Get(x, y, scale) * m_exponent_array.at(i); + scale *= m_lacunarity; + } + + float remainder = m_octaves - static_cast(m_octaves); + if(std::fabs(remainder) > 0.01f) + value += remainder * m_source.Get(x, y, scale) * m_exponent_array.at(static_cast(m_octaves-1)); + + return value / m_sum; + } + + float FBM::Get(float x, float y, float z, float scale) const + { + float value = 0.f; + for(int i = 0; i < m_octaves; ++i) + { + value += m_source.Get(x, y, z, scale) * m_exponent_array.at(i); + scale *= m_lacunarity; + } + + float remainder = m_octaves - static_cast(m_octaves); + if(std::fabs(remainder) > 0.01f) + value += remainder * m_source.Get(x, y, z, scale) * m_exponent_array.at(static_cast(m_octaves-1)); + + return value / m_sum; + } + + float FBM::Get(float x, float y, float z, float w, float scale) const + { + float value = 0.f; + for(int i = 0; i < m_octaves; ++i) + { + value += m_source.Get(x, y, z, w, scale) * m_exponent_array.at(i); + scale *= m_lacunarity; + } + + float remainder = m_octaves - static_cast(m_octaves); + if(std::fabs(remainder) > 0.01f) + value += remainder * m_source.Get(x, y, z, w, scale) * m_exponent_array.at(static_cast(m_octaves-1)); + + return value / m_sum; + } +} diff --git a/src/Nazara/Noise/FBM2D.cpp b/src/Nazara/Noise/FBM2D.cpp deleted file mode 100644 index 5a4670b80..000000000 --- a/src/Nazara/Noise/FBM2D.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - FBM2D::FBM2D(NoiseType source, unsigned int seed) - { - switch(source) - { - case PERLIN: - m_source = new Perlin2D(); - break; - - default: - m_source = new Simplex2D(); - break; - } - m_source->SetNewSeed(seed); - m_source->ShufflePermutationTable(); - m_noiseType = source; - } - - float FBM2D::GetValue(float x, float y, float resolution) - { - this->RecomputeExponentArray(); - - m_value = 0.0; - - for (int i(0); i < m_octaves; ++i) - { - m_value += m_source->GetValue(x,y,resolution) * m_exponent_array[i]; - resolution *= m_lacunarity; - } - m_remainder = m_octaves - static_cast(m_octaves); - - if(!NumberEquals(m_remainder, static_cast(0.0))) - m_value += m_remainder * m_source->GetValue(x,y,resolution) * m_exponent_array[static_cast(m_octaves-1)]; - - return m_value/this->m_sum; - } - - FBM2D::~FBM2D() - { - delete m_source; - } -} diff --git a/src/Nazara/Noise/FBM3D.cpp b/src/Nazara/Noise/FBM3D.cpp deleted file mode 100644 index f0ee6143e..000000000 --- a/src/Nazara/Noise/FBM3D.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - FBM3D::FBM3D(NoiseType source, unsigned int seed) - { - switch(source) - { - case PERLIN: - m_source = new Perlin3D(); - break; - - default: - m_source = new Simplex3D(); - break; - } - m_source->SetNewSeed(seed); - m_source->ShufflePermutationTable(); - m_noiseType = source; - } - - float FBM3D::GetValue(float x, float y, float z, float resolution) - { - this->RecomputeExponentArray(); - - m_value = 0.0; - - for (int i(0); i < m_octaves; ++i) - { - m_value += m_source->GetValue(x,y,z,resolution) * m_exponent_array[i]; - resolution *= m_lacunarity; - } - m_remainder = m_octaves - static_cast(m_octaves); - - if(!NumberEquals(m_remainder, static_cast(0.0))) - m_value += m_remainder * m_source->GetValue(x,y,z,resolution) * m_exponent_array[static_cast(m_octaves-1)]; - - return m_value/this->m_sum; - } - - FBM3D::~FBM3D() - { - delete m_source; - } -} diff --git a/src/Nazara/Noise/FBM4D.cpp b/src/Nazara/Noise/FBM4D.cpp deleted file mode 100644 index dace704d7..000000000 --- a/src/Nazara/Noise/FBM4D.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - FBM4D::FBM4D(NoiseType source, unsigned int seed) - { - switch(source) - { - case PERLIN: - m_source = new Perlin4D(); - break; - - default: - m_source = new Simplex4D(); - break; - } - m_source->SetNewSeed(seed); - m_source->ShufflePermutationTable(); - m_noiseType = source; - } - - float FBM4D::GetValue(float x, float y, float z, float w, float resolution) - { - this->RecomputeExponentArray(); - - m_value = 0.0; - - for (int i(0); i < m_octaves; ++i) - { - m_value += m_source->GetValue(x,y,z,w,resolution) * m_exponent_array[i]; - resolution *= m_lacunarity; - } - m_remainder = m_octaves - static_cast(m_octaves); - - if(!NumberEquals(m_remainder, static_cast(0.0))) - m_value += m_remainder * m_source->GetValue(x,y,z,w,resolution) * m_exponent_array[static_cast(m_octaves-1)]; - - return m_value/this->m_sum; - } - - FBM4D::~FBM4D() - { - delete m_source; - } -} diff --git a/src/Nazara/Noise/HybridMultiFractal.cpp b/src/Nazara/Noise/HybridMultiFractal.cpp new file mode 100644 index 000000000..4278d23b2 --- /dev/null +++ b/src/Nazara/Noise/HybridMultiFractal.cpp @@ -0,0 +1,98 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + HybridMultiFractal::HybridMultiFractal(const NoiseBase & source) : + m_source(source) + { + } + + float HybridMultiFractal::Get(float x, float y, float scale) const + { + float offset = 1.0f; + float value = (m_source.Get(x, y, scale) + offset) * m_exponent_array.at(0); + float weight = value; + float signal = 0.f; + + scale *= m_lacunarity; + + for(int i(1) ; i < m_octaves; ++i) + { + if (weight > 1.f) + weight = 1.f; + + signal = (m_source.Get(x, y, scale) + offset) * m_exponent_array.at(i); + value += weight * signal; + weight *= signal; + scale *= m_lacunarity; + } + + float remainder = m_octaves - static_cast(m_octaves); + + if (remainder > 0.f) + value += remainder * m_source.Get(x, y, scale) * m_exponent_array.at(static_cast(m_octaves-1)); + + return value / m_sum - offset; + } + + float HybridMultiFractal::Get(float x, float y, float z, float scale) const + { + float offset = 1.0f; + float value = (m_source.Get(x, y, z, scale) + offset) * m_exponent_array.at(0); + float weight = value; + float signal = 0.f; + + scale *= m_lacunarity; + + for(int i(1) ; i < m_octaves; ++i) + { + if (weight > 1.f) + weight = 1.f; + + signal = (m_source.Get(x, y, z, scale) + offset) * m_exponent_array.at(i); + value += weight * signal; + weight *= signal; + scale *= m_lacunarity; + } + + float remainder = m_octaves - static_cast(m_octaves); + + if (remainder > 0.f) + value += remainder * m_source.Get(x, y, z, scale) * m_exponent_array.at(static_cast(m_octaves-1)); + + return value / m_sum - offset; + } + + float HybridMultiFractal::Get(float x, float y, float z, float w, float scale) const + { + float offset = 1.0f; + float value = (m_source.Get(x, y, z, w, scale) + offset) * m_exponent_array.at(0); + float weight = value; + float signal = 0.f; + + scale *= m_lacunarity; + + for(int i(1) ; i < m_octaves; ++i) + { + if (weight > 1.f) + weight = 1.f; + + signal = (m_source.Get(x, y, z, w, scale) + offset) * m_exponent_array.at(i); + value += weight * signal; + weight *= signal; + scale *= m_lacunarity; + } + + float remainder = m_octaves - static_cast(m_octaves); + + if (remainder > 0.f) + value += remainder * m_source.Get(x, y, z, w, scale) * m_exponent_array.at(static_cast(m_octaves-1)); + + return value / m_sum - offset; + } +} diff --git a/src/Nazara/Noise/HybridMultiFractal3D.cpp b/src/Nazara/Noise/HybridMultiFractal3D.cpp deleted file mode 100644 index e97ce3497..000000000 --- a/src/Nazara/Noise/HybridMultiFractal3D.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - HybridMultiFractal3D::HybridMultiFractal3D(NoiseType source, unsigned int seed) - { - switch(source) - { - case PERLIN: - m_source = new Perlin3D(); - break; - - default: - m_source = new Simplex3D(); - break; - } - m_source->SetNewSeed(seed); - m_source->ShufflePermutationTable(); - m_noiseType = source; - } - - float HybridMultiFractal3D::GetValue(float x, float y, float z, float resolution) - { - this->RecomputeExponentArray(); - - m_offset = 1.0f; - - m_value = (m_source->GetValue(x,y,z,resolution) + m_offset) * m_exponent_array[0]; - m_weight = m_value; - m_signal = 0.f; - - resolution *= m_lacunarity; - - for(int i(1) ; i < m_octaves; ++i) - { - if (m_weight > 1.f) - m_weight = 1.f; - - m_signal = (m_source->GetValue(x,y,z,resolution) + m_offset) * m_exponent_array[i]; - m_value += m_weight * m_signal; - - m_weight *= m_signal; - - resolution *= m_lacunarity; - } - - m_remainder = m_octaves - static_cast(m_octaves); - if (m_remainder > 0.f) - m_value += m_remainder * m_source->GetValue(x,y,z,resolution) * m_exponent_array[static_cast(m_octaves-1)]; - - return m_value/this->m_sum - m_offset; - } - - HybridMultiFractal3D::~HybridMultiFractal3D() - { - delete m_source; - } -} diff --git a/src/Nazara/Noise/HybridMultiFractal4D.cpp b/src/Nazara/Noise/HybridMultiFractal4D.cpp deleted file mode 100644 index 06ddf87a1..000000000 --- a/src/Nazara/Noise/HybridMultiFractal4D.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - HybridMultiFractal4D::HybridMultiFractal4D(NoiseType source, unsigned int seed) - { - switch(source) - { - case PERLIN: - m_source = new Perlin4D(); - break; - - default: - m_source = new Simplex4D(); - break; - } - m_source->SetNewSeed(seed); - m_source->ShufflePermutationTable(); - m_noiseType = source; - } - - float HybridMultiFractal4D::GetValue(float x, float y, float z, float w, float resolution) - { - this->RecomputeExponentArray(); - - m_offset = 1.0f; - - m_value = (m_source->GetValue(x,y,z,w,resolution) + m_offset) * m_exponent_array[0]; - m_weight = m_value; - m_signal = 0.f; - - resolution *= m_lacunarity; - - for(int i(1) ; i < m_octaves; ++i) - { - if (m_weight > 1.f) - m_weight = 1.f; - - m_signal = (m_source->GetValue(x,y,z,w,resolution) + m_offset) * m_exponent_array[i]; - m_value += m_weight * m_signal; - - m_weight *= m_signal; - - resolution *= m_lacunarity; - } - - m_remainder = m_octaves - static_cast(m_octaves); - if (m_remainder > 0.f) - m_value += m_remainder * m_source->GetValue(x,y,z,w,resolution) * m_exponent_array[static_cast(m_octaves-1)]; - - return m_value/this->m_sum - m_offset; - } - - HybridMultiFractal4D::~HybridMultiFractal4D() - { - delete m_source; - } -} diff --git a/src/Nazara/Noise/HybridMultifractal2D.cpp b/src/Nazara/Noise/HybridMultifractal2D.cpp deleted file mode 100644 index 007ff78d0..000000000 --- a/src/Nazara/Noise/HybridMultifractal2D.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - HybridMultiFractal2D::HybridMultiFractal2D(NoiseType source, unsigned int seed) - { - switch(source) - { - case PERLIN: - m_source = new Perlin2D(); - break; - - default: - m_source = new Simplex2D(); - break; - } - m_source->SetNewSeed(seed); - m_source->ShufflePermutationTable(); - m_noiseType = source; - } - - float HybridMultiFractal2D::GetValue(float x, float y, float resolution) - { - this->RecomputeExponentArray(); - - m_offset = 1.0f; - - m_value = (m_source->GetValue(x,y,resolution) + m_offset) * m_exponent_array[0]; - m_weight = m_value; - m_signal = 0.f; - - resolution *= m_lacunarity; - - for(int i(1) ; i < m_octaves; ++i) - { - if (m_weight > 1.f) - m_weight = 1.f; - - m_signal = (m_source->GetValue(x,y,resolution) + m_offset) * m_exponent_array[i]; - m_value += m_weight * m_signal; - - m_weight *= m_signal; - - resolution *= m_lacunarity; - } - - m_remainder = m_octaves - static_cast(m_octaves); - if (m_remainder > 0.f) - m_value += m_remainder * m_source->GetValue(x,y,resolution) * m_exponent_array[static_cast(m_octaves-1)]; - - return m_value/this->m_sum - m_offset; - } - - HybridMultiFractal2D::~HybridMultiFractal2D() - { - delete m_source; - } -} diff --git a/src/Nazara/Noise/MappedNoiseBase.cpp b/src/Nazara/Noise/MappedNoiseBase.cpp deleted file mode 100644 index 77d8e0eb3..000000000 --- a/src/Nazara/Noise/MappedNoiseBase.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine". -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -namespace Nz -{ - MappedNoiseBase::MappedNoiseBase() : m_gain(1.f), m_offset(0.f), m_resolution(30.f) - { - - } - - float MappedNoiseBase::GetGain() const - { - return m_gain; - } - - float MappedNoiseBase::GetOffset() const - { - return m_offset; - } - - float MappedNoiseBase::GetResolution() const - { - return m_resolution; - } - - void MappedNoiseBase::SetGain(float gain) - { - m_gain = gain; - } - - void MappedNoiseBase::SetOffset(float offset) - { - m_offset = offset; - } - - void MappedNoiseBase::SetResolution(float resolution) - { - if (NumberEquals(resolution, 0.f)) - { - StringStream ss; - ss << __FILE__ << ':' << __LINE__ << " : resolution cannot be 0.0f"; - - throw std::domain_error(ss.ToString()); - } - m_resolution = resolution; - } -} diff --git a/src/Nazara/Noise/MixerBase.cpp b/src/Nazara/Noise/MixerBase.cpp new file mode 100644 index 000000000..d3dd82acc --- /dev/null +++ b/src/Nazara/Noise/MixerBase.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + MixerBase::MixerBase() : + m_hurst(1.2f), + m_lacunarity(5.f), + m_octaves(3.f) + { + Recompute(); + } + + float MixerBase::GetHurstParameter() const + { + return m_hurst; + } + + float MixerBase::GetLacunarity() const + { + return m_lacunarity; + } + + float MixerBase::GetOctaveNumber() const + { + return m_octaves; + } + + void MixerBase::SetParameters(float hurst, float lacunarity, float octaves) + { + m_hurst = hurst; + m_lacunarity = lacunarity; + m_octaves = octaves; + + Recompute(); + } + + void MixerBase::Recompute() + { + float frequency = 1.0; + m_sum = 0.f; + m_exponent_array.clear(); + + for (int i(0) ; i < static_cast(m_octaves) ; ++i) + { + m_exponent_array.push_back(std::pow( frequency, -m_hurst )); + frequency *= m_lacunarity; + m_sum += m_exponent_array.at(i); + } + } +} diff --git a/src/Nazara/Noise/NoiseBase.cpp b/src/Nazara/Noise/NoiseBase.cpp index 550579227..1dc74595a 100644 --- a/src/Nazara/Noise/NoiseBase.cpp +++ b/src/Nazara/Noise/NoiseBase.cpp @@ -1,79 +1,74 @@ -// Copyright (C) 2015 Rémi Bèges +// Copyright (C) 2016 Rémi Bèges // This file is part of the "Nazara Engine - Noise module" // For conditions of distribution and use, see copyright notice in Config.hpp #include -#include -#include +#include #include namespace Nz { - NoiseBase::NoiseBase(unsigned int seed) + NoiseBase::NoiseBase(unsigned int seed) : + m_scale(0.05f) { - Ua = 16807; - Uc = 0; - Um = 2147483647; - UcurrentSeed = 0; - Uprevious = 0; - - SetNewSeed(seed); - - for(int i(0) ; i < 512 ; i++) - perm[i] = i & 255; + SetSeed(seed); + // Fill permutations with initial values + std::iota(m_permutations.begin(), m_permutations.begin() + 256, 0); } - void NoiseBase::SetNewSeed(unsigned int seed) + float NoiseBase::GetScale() { - Uprevious = seed; - UcurrentSeed = seed; + return m_scale; } - unsigned int NoiseBase::GetUniformRandomValue() + void NoiseBase::SetScale(float scale) { - Ulast = Ua*Uprevious + Uc%Um; - Uprevious = Ulast; - return Ulast; + m_scale = scale; } - void NoiseBase::ShufflePermutationTable() + void NoiseBase::SetSeed(unsigned int seed) { - int xchanger; - unsigned int ncase; - - for(unsigned int i(0) ; i < 256 ; i++) - perm[i] = i; - - for(unsigned int j(0) ; j < 20 ; ++j) - for (unsigned int i(0); i < 256 ; ++i) - { - ncase = this->GetUniformRandomValue() & 255; - xchanger = perm[i]; - perm[i] = perm[ncase]; - perm[ncase] = xchanger; - } - - for(unsigned int i(256) ; i < 512; ++i) - perm[i] = perm[i & 255]; + m_randomEngine.seed(seed); } - int NoiseBase::fastfloor(float n) + void NoiseBase::Shuffle() { - return (n >= 0) ? static_cast(n) : static_cast(n-1); + std::shuffle(m_permutations.begin(), m_permutations.begin() + 256, m_randomEngine); + + for(std::size_t i = 1; i < (m_permutations.size() / 256); ++i) + std::copy(m_permutations.begin(), m_permutations.begin() + 256, m_permutations.begin() + 256 * i); } - int NoiseBase::JenkinsHash(int a, int b, int c) + std::array NoiseBase::s_gradients2 = { - a = a-b; a = a - c; a = a^(static_cast(c) >> 13); - b = b-c; b = b - a; b = b^(a << 8); - c = c-a; c = c - b; c = c^(static_cast(b) >> 13); - a = a-b; a = a - c; a = a^(static_cast(c) >> 12); - b = b-c; b = b - a; b = b^(a << 16); - c = c-a; c = c - b; c = c^(static_cast(b) >> 5); - a = a-b; a = a - c; a = a^(static_cast(c) >> 3); - b = b-c; b = b - a; b = b^(a << 10); - c = c-a; c = c - b; c = c^(static_cast(b) >> 15); - return c; - } + { + {1.f, 1.f}, {-1.f, 1.f}, {1.f, -1.f}, {-1.f, -1.f}, + {1.f, 0.f}, {-1.f, 0.f}, {0.f, 1.f}, { 0.f, -1.f} + } + }; + + std::array NoiseBase::s_gradients3 = + { + { + {1.f,1.f,0.f}, {-1.f, 1.f, 0.f}, {1.f, -1.f, 0.f}, {-1.f, -1.f, 0.f}, + {1.f,0.f,1.f}, {-1.f, 0.f, 1.f}, {1.f, 0.f, -1.f}, {-1.f, 0.f, -1.f}, + {0.f,1.f,1.f}, { 0.f, -1.f, 1.f}, {0.f, 1.f, -1.f}, {0.f, -1.f, -1.f}, + {1.f,1.f,0.f}, {-1.f, 1.f, 0.f}, {0.f, -1.f, 1.f}, {0.f, -1.f, -1.f} + } + }; + + std::array NoiseBase::s_gradients4 = + { + { + {0,1,1,1}, {0,1,1,-1}, {0,1,-1,1}, {0,1,-1,-1}, + {0,-1,1,1},{0,-1,1,-1},{0,-1,-1,1},{0,-1,-1,-1}, + {1,0,1,1}, {1,0,1,-1}, {1,0,-1,1}, {1,0,-1,-1}, + {-1,0,1,1},{-1,0,1,-1},{-1,0,-1,1},{-1,0,-1,-1}, + {1,1,0,1}, {1,1,0,-1}, {1,-1,0,1}, {1,-1,0,-1}, + {-1,1,0,1},{-1,1,0,-1},{-1,-1,0,1},{-1,-1,0,-1}, + {1,1,1,0}, {1,1,-1,0}, {1,-1,1,0}, {1,-1,-1,0}, + {-1,1,1,0},{-1,1,-1,0},{-1,-1,1,0},{-1,-1,-1,0} + } + }; } diff --git a/src/Nazara/Noise/NoiseTools.cpp b/src/Nazara/Noise/NoiseTools.cpp new file mode 100644 index 000000000..9cf6efd39 --- /dev/null +++ b/src/Nazara/Noise/NoiseTools.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + int fastfloor(float n) + { + return (n >= 0) ? static_cast(n) : static_cast(n-1); + } + + int JenkinsHash(int a, int b, int c) + { + a = a-b; a = a - c; a = a^(static_cast(c) >> 13); + b = b-c; b = b - a; b = b^(a << 8); + c = c-a; c = c - b; c = c^(static_cast(b) >> 13); + a = a-b; a = a - c; a = a^(static_cast(c) >> 12); + b = b-c; b = b - a; b = b^(a << 16); + c = c-a; c = c - b; c = c^(static_cast(b) >> 5); + a = a-b; a = a - c; a = a^(static_cast(c) >> 3); + b = b-c; b = b - a; b = b^(a << 10); + c = c-a; c = c - b; c = c^(static_cast(b) >> 15); + return c; + } +} diff --git a/src/Nazara/Noise/Perlin.cpp b/src/Nazara/Noise/Perlin.cpp new file mode 100644 index 000000000..a400972ff --- /dev/null +++ b/src/Nazara/Noise/Perlin.cpp @@ -0,0 +1,276 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + Perlin::Perlin(unsigned int seed) : + Perlin() + { + SetSeed(seed); + Shuffle(); + } + + float Perlin::Get(float x, float y, float scale) const + { + float xc, yc; + int x0, y0; + int gi0,gi1,gi2,gi3; + int ii, jj; + + float s,t,u,v; + float Cx,Cy; + float Li1, Li2; + float tempx,tempy; + + xc = x * scale; + yc = y * scale; + + x0 = fastfloor(xc); + y0 = fastfloor(yc); + + ii = x0 & 255; + jj = y0 & 255; + + gi0 = m_permutations[ii + m_permutations[jj]] & 7; + gi1 = m_permutations[ii + 1 + m_permutations[jj]] & 7; + gi2 = m_permutations[ii + m_permutations[jj + 1]] & 7; + gi3 = m_permutations[ii + 1 + m_permutations[jj + 1]] & 7; + + tempx = xc - x0; + tempy = yc - y0; + + Cx = tempx * tempx * tempx * (tempx * (tempx * 6 - 15) + 10); + Cy = tempy * tempy * tempy * (tempy * (tempy * 6 - 15) + 10); + + s = s_gradients2[gi0][0]*tempx + s_gradients2[gi0][1]*tempy; + + tempx = xc - (x0 + 1); + t = s_gradients2[gi1][0]*tempx + s_gradients2[gi1][1]*tempy; + + tempy = yc - (y0 + 1); + v = s_gradients2[gi3][0]*tempx + s_gradients2[gi3][1]*tempy; + + tempx = xc - x0; + u = s_gradients2[gi2][0]*tempx + s_gradients2[gi2][1]*tempy; + + Li1 = s + Cx*(t-s); + Li2 = u + Cx*(v-u); + + return Li1 + Cy*(Li2-Li1); + } + + float Perlin::Get(float x, float y, float z, float scale) const + { + float xc, yc, zc; + int x0, y0, z0; + int gi0,gi1,gi2,gi3,gi4,gi5,gi6,gi7; + int ii, jj, kk; + + float Li1,Li2,Li3,Li4,Li5,Li6; + float s[2],t[2],u[2],v[2]; + float Cx,Cy,Cz; + float tempx,tempy,tempz; + + xc = x * scale; + yc = y * scale; + zc = z * scale; + + x0 = fastfloor(xc); + y0 = fastfloor(yc); + z0 = fastfloor(zc); + + ii = x0 & 255; + jj = y0 & 255; + kk = z0 & 255; + + gi0 = m_permutations[ii + m_permutations[jj + m_permutations[kk]]] & 15; + gi1 = m_permutations[ii + 1 + m_permutations[jj + m_permutations[kk]]] & 15; + gi2 = m_permutations[ii + m_permutations[jj + 1 + m_permutations[kk]]] & 15; + gi3 = m_permutations[ii + 1 + m_permutations[jj + 1 + m_permutations[kk]]] & 15; + + gi4 = m_permutations[ii + m_permutations[jj + m_permutations[kk + 1]]] & 15; + gi5 = m_permutations[ii + 1 + m_permutations[jj + m_permutations[kk + 1]]] & 15; + gi6 = m_permutations[ii + m_permutations[jj + 1 + m_permutations[kk + 1]]] & 15; + gi7 = m_permutations[ii + 1 + m_permutations[jj + 1 + m_permutations[kk + 1]]] & 15; + + tempx = xc - x0; + tempy = yc - y0; + tempz = zc - z0; + + Cx = tempx * tempx * tempx * (tempx * (tempx * 6 - 15) + 10); + Cy = tempy * tempy * tempy * (tempy * (tempy * 6 - 15) + 10); + Cz = tempz * tempz * tempz * (tempz * (tempz * 6 - 15) + 10); + + s[0] = s_gradients3[gi0][0]*tempx + s_gradients3[gi0][1]*tempy + s_gradients3[gi0][2]*tempz; + + tempx = xc - (x0 + 1); + t[0] = s_gradients3[gi1][0]*tempx + s_gradients3[gi1][1]*tempy + s_gradients3[gi1][2]*tempz; + + tempy = yc - (y0 + 1); + v[0] = s_gradients3[gi3][0]*tempx + s_gradients3[gi3][1]*tempy + s_gradients3[gi3][2]*tempz; + + tempx = xc - x0; + u[0] = s_gradients3[gi2][0]*tempx + s_gradients3[gi2][1]*tempy + s_gradients3[gi2][2]*tempz; + + tempy = yc - y0; + tempz = zc - (z0 + 1); + s[1] = s_gradients3[gi4][0]*tempx + s_gradients3[gi4][1]*tempy + s_gradients3[gi4][2]*tempz; + + tempx = xc - (x0 + 1); + t[1] = s_gradients3[gi5][0]*tempx + s_gradients3[gi5][1]*tempy + s_gradients3[gi5][2]*tempz; + + tempy = yc - (y0 + 1); + v[1] = s_gradients3[gi7][0]*tempx + s_gradients3[gi7][1]*tempy + s_gradients3[gi7][2]*tempz; + + tempx = xc - x0; + u[1] = s_gradients3[gi6][0]*tempx + s_gradients3[gi6][1]*tempy + s_gradients3[gi6][2]*tempz; + + Li1 = s[0] + Cx*(t[0]-s[0]); + Li2 = u[0] + Cx*(v[0]-u[0]); + Li3 = s[1] + Cx*(t[1]-s[1]); + Li4 = u[1] + Cx*(v[1]-u[1]); + + Li5 = Li1 + Cy * (Li2-Li1); + Li6 = Li3 + Cy * (Li4-Li3); + + return Li5 + Cz * (Li6-Li5); + } + + float Perlin::Get(float x, float y, float z, float w, float scale) const + { + float xc,yc,zc,wc; + int x0,y0,z0,w0; + int gi0,gi1,gi2,gi3,gi4,gi5,gi6,gi7,gi8,gi9,gi10,gi11,gi12,gi13,gi14,gi15; + int ii,jj,kk,ll; + + float Li1,Li2,Li3,Li4,Li5,Li6,Li7,Li8,Li9,Li10,Li11,Li12,Li13,Li14; + float s[4],t[4],u[4],v[4]; + float Cx,Cy,Cz,Cw; + + float tempx,tempy,tempz,tempw; + + xc = x * scale; + yc = y * scale; + zc = z * scale; + wc = w * scale; + + x0 = fastfloor(xc); + y0 = fastfloor(yc); + z0 = fastfloor(zc); + w0 = fastfloor(wc); + + ii = x0 & 255; + jj = y0 & 255; + kk = z0 & 255; + ll = w0 & 255; + + gi0 = m_permutations[ii + m_permutations[jj + m_permutations[kk + m_permutations[ll]]]] & 31; + gi1 = m_permutations[ii + 1 + m_permutations[jj + m_permutations[kk + m_permutations[ll]]]] & 31; + gi2 = m_permutations[ii + m_permutations[jj + 1 + m_permutations[kk + m_permutations[ll]]]] & 31; + gi3 = m_permutations[ii + 1 + m_permutations[jj + 1 + m_permutations[kk + m_permutations[ll]]]] & 31; + + gi4 = m_permutations[ii + m_permutations[jj + + m_permutations[kk + 1 + m_permutations[ll]]]] & 31; + gi5 = m_permutations[ii + 1 + m_permutations[jj + + m_permutations[kk + 1 + m_permutations[ll]]]] & 31; + gi6 = m_permutations[ii + m_permutations[jj + 1 + m_permutations[kk + 1 + m_permutations[ll]]]] & 31; + gi7 = m_permutations[ii + 1 + m_permutations[jj + 1 + m_permutations[kk + 1 + m_permutations[ll]]]] & 31; + + gi8 = m_permutations[ii + m_permutations[jj + m_permutations[kk + m_permutations[ll + 1]]]] & 31; + gi9 = m_permutations[ii + 1 + m_permutations[jj + m_permutations[kk + m_permutations[ll + 1]]]] & 31; + gi10 = m_permutations[ii + m_permutations[jj + 1 + m_permutations[kk + m_permutations[ll + 1]]]] & 31; + gi11 = m_permutations[ii + 1 + m_permutations[jj + 1 + m_permutations[kk + m_permutations[ll + 1]]]] & 31; + + gi12 = m_permutations[ii + m_permutations[jj + m_permutations[kk + 1 + m_permutations[ll + 1]]]] & 31; + gi13 = m_permutations[ii + 1 + m_permutations[jj + m_permutations[kk + 1 + m_permutations[ll + 1]]]] & 31; + gi14 = m_permutations[ii + m_permutations[jj + 1 + m_permutations[kk + 1 + m_permutations[ll + 1]]]] & 31; + gi15 = m_permutations[ii + 1 + m_permutations[jj + 1 + m_permutations[kk + 1 + m_permutations[ll + 1]]]] & 31; + + tempx = xc - x0; + tempy = yc - y0; + tempz = zc - z0; + tempw = wc - w0; + + Cx = tempx * tempx * tempx * (tempx * (tempx * 6 - 15) + 10); + Cy = tempy * tempy * tempy * (tempy * (tempy * 6 - 15) + 10); + Cz = tempz * tempz * tempz * (tempz * (tempz * 6 - 15) + 10); + Cw = tempw * tempw * tempw * (tempw * (tempw * 6 - 15) + 10); + + s[0] = s_gradients4[gi0][0]*tempx + s_gradients4[gi0][1]*tempy + s_gradients4[gi0][2]*tempz + s_gradients4[gi0][3]*tempw; + + tempx = xc - (x0+1); + t[0] = s_gradients4[gi1][0]*tempx + s_gradients4[gi1][1]*tempy + s_gradients4[gi1][2]*tempz + s_gradients4[gi1][3]*tempw; + + tempy = yc - (y0+1); + v[0] = s_gradients4[gi3][0]*tempx + s_gradients4[gi3][1]*tempy + s_gradients4[gi3][2]*tempz + s_gradients4[gi3][3]*tempw; + + tempx = xc - x0; + u[0] = s_gradients4[gi2][0]*tempx + s_gradients4[gi2][1]*tempy + s_gradients4[gi2][2]*tempz + s_gradients4[gi2][3]*tempw; + + tempy = yc - y0; + tempz = zc - (z0+1); + s[1] = s_gradients4[gi4][0]*tempx + s_gradients4[gi4][1]*tempy + s_gradients4[gi4][2]*tempz + s_gradients4[gi4][3]*tempw; + + tempx = xc - (x0+1); + t[1] = s_gradients4[gi5][0]*tempx + s_gradients4[gi5][1]*tempy + s_gradients4[gi5][2]*tempz + s_gradients4[gi5][3]*tempw; + + tempy = yc - (y0+1); + v[1] = s_gradients4[gi7][0]*tempx + s_gradients4[gi7][1]*tempy + s_gradients4[gi7][2]*tempz + s_gradients4[gi7][3]*tempw; + + tempx = xc - x0; + u[1] = s_gradients4[gi6][0]*tempx + s_gradients4[gi6][1]*tempy + s_gradients4[gi6][2]*tempz + s_gradients4[gi6][3]*tempw; + + + tempy = yc - y0; + tempz = zc - z0; + tempw = wc - (w0+1); + s[2] = s_gradients4[gi8][0]*tempx + s_gradients4[gi8][1]*tempy + s_gradients4[gi8][2]*tempz + s_gradients4[gi8][3]*tempw; + + tempx = xc - (x0+1); + t[2] = s_gradients4[gi9][0]*tempx + s_gradients4[gi9][1]*tempy + s_gradients4[gi9][2]*tempz + s_gradients4[gi9][3]*tempw; + + tempy = yc - (y0+1); + v[2] = s_gradients4[gi11][0]*tempx + s_gradients4[gi11][1]*tempy + s_gradients4[gi11][2]*tempz + s_gradients4[gi11][3]*tempw; + + tempx = xc - x0; + u[2] = s_gradients4[gi10][0]*tempx + s_gradients4[gi10][1]*tempy + s_gradients4[gi10][2]*tempz + s_gradients4[gi10][3]*tempw; + + + tempy = yc - y0; + tempz = zc - (z0+1); + s[3] = s_gradients4[gi12][0]*tempx + s_gradients4[gi12][1]*tempy + s_gradients4[gi12][2]*tempz + s_gradients4[gi12][3]*tempw; + + tempx = xc - (x0+1); + t[3] = s_gradients4[gi13][0]*tempx + s_gradients4[gi13][1]*tempy + s_gradients4[gi13][2]*tempz + s_gradients4[gi13][3]*tempw; + + tempy = yc - (y0+1); + v[3] = s_gradients4[gi15][0]*tempx + s_gradients4[gi15][1]*tempy + s_gradients4[gi15][2]*tempz + s_gradients4[gi15][3]*tempw; + + tempx = xc - x0; + u[3] = s_gradients4[gi14][0]*tempx + s_gradients4[gi14][1]*tempy + s_gradients4[gi14][2]*tempz + s_gradients4[gi14][3]*tempw; + + Li1 = s[0] + Cx*(t[0]-s[0]); + Li2 = u[0] + Cx*(v[0]-u[0]); + Li3 = s[1] + Cx*(t[1]-s[1]); + Li4 = u[1] + Cx*(v[1]-u[1]); + Li5 = s[2] + Cx*(t[2]-s[2]); + Li6 = u[2] + Cx*(v[2]-u[2]); + Li7 = s[3] + Cx*(t[3]-s[3]); + Li8 = u[3] + Cx*(v[3]-u[3]); + + Li9 = Li1 + Cy*(Li2-Li1); + Li10 = Li3 + Cy*(Li4-Li3); + Li11 = Li5 + Cy*(Li6-Li5); + Li12 = Li7 + Cy*(Li8-Li7); + + Li13 = Li9 + Cz*(Li10-Li9); + Li14 = Li11 + Cz*(Li12-Li11); + + return Li13 + Cw*(Li14-Li13); + } +} diff --git a/src/Nazara/Noise/Perlin2D.cpp b/src/Nazara/Noise/Perlin2D.cpp deleted file mode 100644 index 1d26c01e2..000000000 --- a/src/Nazara/Noise/Perlin2D.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -namespace Nz -{ - Perlin2D::Perlin2D() - { - float grad2Temp[][2] = { - {1.f,1.f}, - {-1.f,1.f}, - {1.f,-1.f}, - {-1.f,-1.f}, - - {1.f,0.f}, - {-1.f,0.f}, - {0.f,1.f}, - {0.f,-1.f} - }; - - for(int i(0) ; i < 8 ; ++i) - for(int j(0) ; j < 2 ; ++j) - gradient2[i][j] = grad2Temp[i][j]; - } - - Perlin2D::Perlin2D(unsigned int seed) : Perlin2D() - { - this->SetNewSeed(seed); - this->ShufflePermutationTable(); - } - - float Perlin2D::GetValue(float x, float y, float resolution) - { - x *= resolution; - y *= resolution; - - x0 = fastfloor(x); - y0 = fastfloor(y); - - ii = x0 & 255; - jj = y0 & 255; - - gi0 = perm[ii + perm[jj]] & 7; - gi1 = perm[ii + 1 + perm[jj]] & 7; - gi2 = perm[ii + perm[jj + 1]] & 7; - gi3 = perm[ii + 1 + perm[jj + 1]] & 7; - - temp.x = x-x0; - temp.y = y-y0; - - Cx = temp.x * temp.x * temp.x * (temp.x * (temp.x * 6 - 15) + 10); - Cy = temp.y * temp.y * temp.y * (temp.y * (temp.y * 6 - 15) + 10); - - s = gradient2[gi0][0]*temp.x + gradient2[gi0][1]*temp.y; - - temp.x = x-(x0+1); - t = gradient2[gi1][0]*temp.x + gradient2[gi1][1]*temp.y; - - temp.y = y-(y0+1); - v = gradient2[gi3][0]*temp.x + gradient2[gi3][1]*temp.y; - - temp.x = x-x0; - u = gradient2[gi2][0]*temp.x + gradient2[gi2][1]*temp.y; - - Li1 = s + Cx*(t-s); - Li2 = u + Cx*(v-u); - - return Li1 + Cy*(Li2-Li1); - } -} diff --git a/src/Nazara/Noise/Perlin3D.cpp b/src/Nazara/Noise/Perlin3D.cpp deleted file mode 100644 index 2e91154f6..000000000 --- a/src/Nazara/Noise/Perlin3D.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -namespace Nz -{ - Perlin3D::Perlin3D() - { - float grad3Temp[][3] = { - {1,1,0},{-1,1,0},{1,-1,0},{-1,-1,0}, - {1,0,1},{-1,0,1},{1,0,-1},{-1,0,-1}, - {0,1,1},{0,-1,1},{0,1,-1},{0,-1,-1}, - {1,1,0},{-1,1,0},{0,-1,1},{0,-1,-1} - }; - - for(int i(0) ; i < 16 ; ++i) - for(int j(0) ; j < 3 ; ++j) - gradient3[i][j] = grad3Temp[i][j]; - } - - Perlin3D::Perlin3D(unsigned int seed) : Perlin3D() - { - this->SetNewSeed(seed); - this->ShufflePermutationTable(); - } - - float Perlin3D::GetValue(float x, float y, float z, float resolution) - { - x /= resolution; - y /= resolution; - z /= resolution; - - x0 = fastfloor(x); - y0 = fastfloor(y); - z0 = fastfloor(z); - - ii = x0 & 255; - jj = y0 & 255; - kk = z0 & 255; - - gi0 = perm[ii + perm[jj + perm[kk]]] & 15; - gi1 = perm[ii + 1 + perm[jj + perm[kk]]] & 15; - gi2 = perm[ii + perm[jj + 1 + perm[kk]]] & 15; - gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk]]] & 15; - - gi4 = perm[ii + perm[jj + perm[kk + 1]]] & 15; - gi5 = perm[ii + 1 + perm[jj + perm[kk + 1]]] & 15; - gi6 = perm[ii + perm[jj + 1 + perm[kk + 1]]] & 15; - gi7 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]] & 15; - - temp.x = x-x0; - temp.y = y-y0; - temp.z = z-z0; - - Cx = temp.x * temp.x * temp.x * (temp.x * (temp.x * 6 - 15) + 10); - Cy = temp.y * temp.y * temp.y * (temp.y * (temp.y * 6 - 15) + 10); - Cz = temp.z * temp.z * temp.z * (temp.z * (temp.z * 6 - 15) + 10); - - s[0] = gradient3[gi0][0]*temp.x + gradient3[gi0][1]*temp.y + gradient3[gi0][2]*temp.z; - - temp.x = x-(x0+1); - t[0] = gradient3[gi1][0]*temp.x + gradient3[gi1][1]*temp.y + gradient3[gi1][2]*temp.z; - - temp.y = y-(y0+1); - v[0] = gradient3[gi3][0]*temp.x + gradient3[gi3][1]*temp.y + gradient3[gi3][2]*temp.z; - - temp.x = x-x0; - u[0] = gradient3[gi2][0]*temp.x + gradient3[gi2][1]*temp.y + gradient3[gi2][2]*temp.z; - - temp.y = y-y0; - temp.z = z-(z0+1); - s[1] = gradient3[gi4][0]*temp.x + gradient3[gi4][1]*temp.y + gradient3[gi4][2]*temp.z; - - temp.x = x-(x0+1); - t[1] = gradient3[gi5][0]*temp.x + gradient3[gi5][1]*temp.y + gradient3[gi5][2]*temp.z; - - temp.y = y-(y0+1); - v[1] = gradient3[gi7][0]*temp.x + gradient3[gi7][1]*temp.y + gradient3[gi7][2]*temp.z; - - temp.x = x-x0; - u[1] = gradient3[gi6][0]*temp.x + gradient3[gi6][1]*temp.y + gradient3[gi6][2]*temp.z; - - Li1 = s[0] + Cx*(t[0]-s[0]); - Li2 = u[0] + Cx*(v[0]-u[0]); - Li3 = s[1] + Cx*(t[1]-s[1]); - Li4 = u[1] + Cx*(v[1]-u[1]); - - Li5 = Li1 + Cy*(Li2-Li1); - Li6 = Li3 + Cy*(Li4-Li3); - - return Li5 + Cz*(Li6-Li5); - } -} diff --git a/src/Nazara/Noise/Perlin4D.cpp b/src/Nazara/Noise/Perlin4D.cpp deleted file mode 100644 index b321616f6..000000000 --- a/src/Nazara/Noise/Perlin4D.cpp +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -namespace Nz -{ - Perlin4D::Perlin4D() - { - float grad4Temp[][4] = - { - {0,1,1,1}, {0,1,1,-1}, {0,1,-1,1}, {0,1,-1,-1}, - {0,-1,1,1},{0,-1,1,-1},{0,-1,-1,1},{0,-1,-1,-1}, - {1,0,1,1}, {1,0,1,-1}, {1,0,-1,1}, {1,0,-1,-1}, - {-1,0,1,1},{-1,0,1,-1},{-1,0,-1,1},{-1,0,-1,-1}, - {1,1,0,1}, {1,1,0,-1}, {1,-1,0,1}, {1,-1,0,-1}, - {-1,1,0,1},{-1,1,0,-1},{-1,-1,0,1},{-1,-1,0,-1}, - {1,1,1,0}, {1,1,-1,0}, {1,-1,1,0}, {1,-1,-1,0}, - {-1,1,1,0},{-1,1,-1,0},{-1,-1,1,0},{-1,-1,-1,0} - }; - - for(int i(0) ; i < 32 ; ++i) - for(int j(0) ; j < 4 ; ++j) - gradient4[i][j] = grad4Temp[i][j]; - } - - Perlin4D::Perlin4D(unsigned int seed) : Perlin4D() - { - this->SetNewSeed(seed); - this->ShufflePermutationTable(); - } - - float Perlin4D::GetValue(float x, float y, float z, float w, float resolution) - { - x *= resolution; - y *= resolution; - z *= resolution; - w *= resolution; - - x0 = fastfloor(x); - y0 = fastfloor(y); - z0 = fastfloor(z); - w0 = fastfloor(w); - - ii = x0 & 255; - jj = y0 & 255; - kk = z0 & 255; - ll = w0 & 255; - - gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] & 31; - gi1 = perm[ii + 1 + perm[jj + perm[kk + perm[ll]]]] & 31; - gi2 = perm[ii + perm[jj + 1 + perm[kk + perm[ll]]]] & 31; - gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk + perm[ll]]]] & 31; - - gi4 = perm[ii + perm[jj + + perm[kk + 1 + perm[ll]]]] & 31; - gi5 = perm[ii + 1 + perm[jj + + perm[kk + 1 + perm[ll]]]] & 31; - gi6 = perm[ii + perm[jj + 1 + perm[kk + 1 + perm[ll]]]] & 31; - gi7 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll]]]] & 31; - - gi8 = perm[ii + perm[jj + perm[kk + perm[ll + 1]]]] & 31; - gi9 = perm[ii + 1 + perm[jj + perm[kk + perm[ll + 1]]]] & 31; - gi10 = perm[ii + perm[jj + 1 + perm[kk + perm[ll + 1]]]] & 31; - gi11 = perm[ii + 1 + perm[jj + 1 + perm[kk + perm[ll + 1]]]] & 31; - - gi12 = perm[ii + perm[jj + perm[kk + 1 + perm[ll + 1]]]] & 31; - gi13 = perm[ii + 1 + perm[jj + perm[kk + 1 + perm[ll + 1]]]] & 31; - gi14 = perm[ii + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] & 31; - gi15 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] & 31; - - temp.x = x-x0; - temp.y = y-y0; - temp.z = z-z0; - temp.w = w-w0; - - Cx = temp.x * temp.x * temp.x * (temp.x * (temp.x * 6 - 15) + 10); - Cy = temp.y * temp.y * temp.y * (temp.y * (temp.y * 6 - 15) + 10); - Cz = temp.z * temp.z * temp.z * (temp.z * (temp.z * 6 - 15) + 10); - Cw = temp.w * temp.w * temp.w * (temp.w * (temp.w * 6 - 15) + 10); - - s[0] = gradient4[gi0][0]*temp.x + gradient4[gi0][1]*temp.y + gradient4[gi0][2]*temp.z + gradient4[gi0][3]*temp.w; - - temp.x = x-(x0+1); - t[0] = gradient4[gi1][0]*temp.x + gradient4[gi1][1]*temp.y + gradient4[gi1][2]*temp.z + gradient4[gi1][3]*temp.w; - - temp.y = y-(y0+1); - v[0] = gradient4[gi3][0]*temp.x + gradient4[gi3][1]*temp.y + gradient4[gi3][2]*temp.z + gradient4[gi3][3]*temp.w; - - temp.x = x-x0; - u[0] = gradient4[gi2][0]*temp.x + gradient4[gi2][1]*temp.y + gradient4[gi2][2]*temp.z + gradient4[gi2][3]*temp.w; - - temp.y = y-y0; - temp.z = z-(z0+1); - s[1] = gradient4[gi4][0]*temp.x + gradient4[gi4][1]*temp.y + gradient4[gi4][2]*temp.z + gradient4[gi4][3]*temp.w; - - temp.x = x-(x0+1); - t[1] = gradient4[gi5][0]*temp.x + gradient4[gi5][1]*temp.y + gradient4[gi5][2]*temp.z + gradient4[gi5][3]*temp.w; - - temp.y = y-(y0+1); - v[1] = gradient4[gi7][0]*temp.x + gradient4[gi7][1]*temp.y + gradient4[gi7][2]*temp.z + gradient4[gi7][3]*temp.w; - - temp.x = x-x0; - u[1] = gradient4[gi6][0]*temp.x + gradient4[gi6][1]*temp.y + gradient4[gi6][2]*temp.z + gradient4[gi6][3]*temp.w; - - - temp.y = y-y0; - temp.z = z-z0; - temp.w = w-(w0+1); - s[2] = gradient4[gi8][0]*temp.x + gradient4[gi8][1]*temp.y + gradient4[gi8][2]*temp.z + gradient4[gi8][3]*temp.w; - - temp.x = x-(x0+1); - t[2] = gradient4[gi9][0]*temp.x + gradient4[gi9][1]*temp.y + gradient4[gi9][2]*temp.z + gradient4[gi9][3]*temp.w; - - temp.y = y-(y0+1); - v[2] = gradient4[gi11][0]*temp.x + gradient4[gi11][1]*temp.y + gradient4[gi11][2]*temp.z + gradient4[gi11][3]*temp.w; - - temp.x = x-x0; - u[2] = gradient4[gi10][0]*temp.x + gradient4[gi10][1]*temp.y + gradient4[gi10][2]*temp.z + gradient4[gi10][3]*temp.w; - - - temp.y = y-y0; - temp.z = z-(z0+1); - s[3] = gradient4[gi12][0]*temp.x + gradient4[gi12][1]*temp.y + gradient4[gi12][2]*temp.z + gradient4[gi12][3]*temp.w; - - temp.x = x-(x0+1); - t[3] = gradient4[gi13][0]*temp.x + gradient4[gi13][1]*temp.y + gradient4[gi13][2]*temp.z + gradient4[gi13][3]*temp.w; - - temp.y = y-(y0+1); - v[3] = gradient4[gi15][0]*temp.x + gradient4[gi15][1]*temp.y + gradient4[gi15][2]*temp.z + gradient4[gi15][3]*temp.w; - - temp.x = x-x0; - u[3] = gradient4[gi14][0]*temp.x + gradient4[gi14][1]*temp.y + gradient4[gi14][2]*temp.z + gradient4[gi14][3]*temp.w; - - Li1 = s[0] + Cx*(t[0]-s[0]); - Li2 = u[0] + Cx*(v[0]-u[0]); - Li3 = s[1] + Cx*(t[1]-s[1]); - Li4 = u[1] + Cx*(v[1]-u[1]); - Li5 = s[2] + Cx*(t[2]-s[2]); - Li6 = u[2] + Cx*(v[2]-u[2]); - Li7 = s[3] + Cx*(t[3]-s[3]); - Li8 = u[3] + Cx*(v[3]-u[3]); - - Li9 = Li1 + Cy*(Li2-Li1); - Li10 = Li3 + Cy*(Li4-Li3); - Li11 = Li5 + Cy*(Li6-Li5); - Li12 = Li7 + Cy*(Li8-Li7); - - Li13 = Li9 + Cz*(Li10-Li9); - Li14 = Li11 + Cz*(Li12-Li11); - - return Li13 + Cw*(Li14-Li13); - } -} diff --git a/src/Nazara/Noise/Simplex.cpp b/src/Nazara/Noise/Simplex.cpp new file mode 100644 index 000000000..8d4748bff --- /dev/null +++ b/src/Nazara/Noise/Simplex.cpp @@ -0,0 +1,375 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace + { + constexpr float s_SkewCoeff2D = 0.5f * (float(M_SQRT3) - 1.f); + constexpr float s_UnskewCoeff2D = (3.f - float(M_SQRT3))/6.f; + constexpr float s_SkewCoeff3D = 1.f / 3.f; + constexpr float s_UnskewCoeff3D = 1.f / 6.f; + constexpr float s_SkewCoeff4D = (float(M_SQRT5) - 1.f)/4.f; + constexpr float s_UnskewCoeff4D = (5.f - float(M_SQRT5))/20.f; + } + + Simplex::Simplex(unsigned int seed) + { + SetSeed(seed); + Shuffle(); + } + + float Simplex::Get(float x, float y, float scale) const + { + float xc = x * scale; + float yc = y * scale; + + float sum = (xc + yc) * s_SkewCoeff2D; + Vector2i skewedCubeOrigin(fastfloor(xc + sum), fastfloor(yc + sum)); + + sum = (skewedCubeOrigin.x + skewedCubeOrigin.y) * s_UnskewCoeff2D; + Vector2f unskewedCubeOrigin(skewedCubeOrigin.x - sum, skewedCubeOrigin.y - sum); + + Vector2f unskewedDistToOrigin(xc - unskewedCubeOrigin.x, yc - unskewedCubeOrigin.y); + + Vector2ui off1; + if(unskewedDistToOrigin.x > unskewedDistToOrigin.y) + off1.Set(1, 0); + else + off1.Set(0, 1); + + std::array d; + d[0] = -unskewedDistToOrigin; + d[1] = d[0] + Vector2f(off1) - Vector2f(s_UnskewCoeff2D); + d[2] = d[0] + Vector2f(1.f - 2.f * s_UnskewCoeff2D); + + Vector2i offset(skewedCubeOrigin.x & 255, skewedCubeOrigin.y & 255); + std::array gi = + { + m_permutations[offset.x + m_permutations[offset.y]] & 7, + m_permutations[offset.x + off1.x + m_permutations[offset.y + off1.y]] & 7, + m_permutations[offset.x + 1 + m_permutations[offset.y + 1]] & 7 + }; + + float n = 0.f; + for (unsigned int i = 0; i < 3; ++i) + { + float c = 0.5f - d[i].x * d[i].x - d[i].y *d[i].y; + if (c > 0.f) + n += c * c * c * c * (s_gradients2[gi[i]].x * d[i].x + s_gradients2[gi[i]].y * d[i].y); + } + + return n*70.f; + } + + float Simplex::Get(float x, float y, float z, float scale) const + { + float xc, yc, zc; + int ii,jj,kk; + int gi0,gi1,gi2,gi3; + int skewedCubeOriginx,skewedCubeOriginy,skewedCubeOriginz; + + int off1x,off1y,off1z; + int off2x,off2y,off2z; + float n1,n2,n3,n4; + float c1,c2,c3,c4; + + float sum; + float unskewedCubeOriginx,unskewedCubeOriginy,unskewedCubeOriginz; + float unskewedDistToOriginx,unskewedDistToOriginy,unskewedDistToOriginz; + float d1x,d1y,d1z; + float d2x,d2y,d2z; + float d3x,d3y,d3z; + float d4x,d4y,d4z; + + xc = x * scale; + yc = y * scale; + zc = z * scale; + + sum = (xc + yc + zc) * s_SkewCoeff3D; + skewedCubeOriginx = fastfloor(xc + sum); + skewedCubeOriginy = fastfloor(yc + sum); + skewedCubeOriginz = fastfloor(zc + sum); + + sum = (skewedCubeOriginx + skewedCubeOriginy + skewedCubeOriginz) * s_UnskewCoeff3D; + unskewedCubeOriginx = skewedCubeOriginx - sum; + unskewedCubeOriginy = skewedCubeOriginy - sum; + unskewedCubeOriginz = skewedCubeOriginz - sum; + + unskewedDistToOriginx = xc - unskewedCubeOriginx; + unskewedDistToOriginy = yc - unskewedCubeOriginy; + unskewedDistToOriginz = zc - unskewedCubeOriginz; + + if(unskewedDistToOriginx >= unskewedDistToOriginy) + { + if(unskewedDistToOriginy >= unskewedDistToOriginz) + { + off1x = 1; + off1y = 0; + off1z = 0; + off2x = 1; + off2y = 1; + off2z = 0; + } + else if(unskewedDistToOriginx >= unskewedDistToOriginz) + { + off1x = 1; + off1y = 0; + off1z = 0; + off2x = 1; + off2y = 0; + off2z = 1; + } + else + { + off1x = 0; + off1y = 0; + off1z = 1; + off2x = 1; + off2y = 0; + off2z = 1; + } + } + else + { + if(unskewedDistToOriginy < unskewedDistToOriginz) + { + off1x = 0; + off1y = 0; + off1z = 1; + off2x = 0; + off2y = 1; + off2z = 1; + } + else if(unskewedDistToOriginx < unskewedDistToOriginz) + { + off1x = 0; + off1y = 1; + off1z = 0; + off2x = 0; + off2y = 1; + off2z = 1; + } + else + { + off1x = 0; + off1y = 1; + off1z = 0; + off2x = 1; + off2y = 1; + off2z = 0; + } + } + + d1x = unskewedDistToOriginx; + d1y = unskewedDistToOriginy; + d1z = unskewedDistToOriginz; + + d2x = d1x - off1x + s_UnskewCoeff3D; + d2y = d1y - off1y + s_UnskewCoeff3D; + d2z = d1z - off1z + s_UnskewCoeff3D; + + d3x = d1x - off2x + 2.f*s_UnskewCoeff3D; + d3y = d1y - off2y + 2.f*s_UnskewCoeff3D; + d3z = d1z - off2z + 2.f*s_UnskewCoeff3D; + + d4x = d1x - 1.f + 3.f*s_UnskewCoeff3D; + d4y = d1y - 1.f + 3.f*s_UnskewCoeff3D; + d4z = d1z - 1.f + 3.f*s_UnskewCoeff3D; + + ii = skewedCubeOriginx & 255; + jj = skewedCubeOriginy & 255; + kk = skewedCubeOriginz & 255; + + gi0 = m_permutations[ii + m_permutations[jj + m_permutations[kk ]]] % 12; + gi1 = m_permutations[ii + off1x + m_permutations[jj + off1y + m_permutations[kk + off1z ]]] % 12; + gi2 = m_permutations[ii + off2x + m_permutations[jj + off2y + m_permutations[kk + off2z ]]] % 12; + gi3 = m_permutations[ii + 1 + m_permutations[jj + 1 + m_permutations[kk + 1 ]]] % 12; + + c1 = 0.6f - d1x * d1x - d1y * d1y - d1z * d1z; + c2 = 0.6f - d2x * d2x - d2y * d2y - d2z * d2z; + c3 = 0.6f - d3x * d3x - d3y * d3y - d3z * d3z; + c4 = 0.6f - d4x * d4x - d4y * d4y - d4z * d4z; + + if(c1 < 0) + n1 = 0; + else + n1 = c1*c1*c1*c1*(s_gradients3[gi0][0] * d1x + s_gradients3[gi0][1] * d1y + s_gradients3[gi0][2] * d1z); + + if(c2 < 0) + n2 = 0; + else + n2 = c2*c2*c2*c2*(s_gradients3[gi1][0] * d2x + s_gradients3[gi1][1] * d2y + s_gradients3[gi1][2] * d2z); + + if(c3 < 0) + n3 = 0; + else + n3 = c3*c3*c3*c3*(s_gradients3[gi2][0] * d3x + s_gradients3[gi2][1] * d3y + s_gradients3[gi2][2] * d3z); + + if(c4 < 0) + n4 = 0; + else + n4 = c4*c4*c4*c4*(s_gradients3[gi3][0] * d4x + s_gradients3[gi3][1] * d4y + s_gradients3[gi3][2] * d4z); + + return (n1+n2+n3+n4)*32; + } + + float Simplex::Get(float x, float y, float z, float w, float scale) const + { + static std::array lookupTable = + { + { + {0,1,2,3}, {0,1,3,2}, {0,0,0,0}, {0,2,3,1}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {1,2,3,0}, + {0,2,1,3}, {0,0,0,0}, {0,3,1,2}, {0,3,2,1}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {1,3,2,0}, + {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, + {1,2,0,3}, {0,0,0,0}, {1,3,0,2}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {2,3,0,1}, {2,3,1,0}, + {1,0,2,3}, {1,0,3,2}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {2,0,3,1}, {0,0,0,0}, {2,1,3,0}, + {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, + {2,0,1,3}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {3,0,1,2}, {3,0,2,1}, {0,0,0,0}, {3,1,2,0}, + {2,1,0,3}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {3,1,0,2}, {0,0,0,0}, {3,2,0,1}, {3,2,1,0} + } + }; + + float xc,yc,zc,wc; + int ii,jj,kk,ll; + int gi0,gi1,gi2,gi3,gi4; + int skewedCubeOriginx,skewedCubeOriginy,skewedCubeOriginz,skewedCubeOriginw; + + int off1x,off1y,off1z,off1w; + int off2x,off2y,off2z,off2w; + int off3x,off3y,off3z,off3w; + + int c; + float n1,n2,n3,n4,n5; + float c1,c2,c3,c4,c5; + + float sum; + float unskewedCubeOriginx,unskewedCubeOriginy,unskewedCubeOriginz,unskewedCubeOriginw; + float unskewedDistToOriginx,unskewedDistToOriginy,unskewedDistToOriginz,unskewedDistToOriginw; + float d1x,d2x,d3x,d4x,d5x; + float d1y,d2y,d3y,d4y,d5y; + float d1z,d2z,d3z,d4z,d5z; + float d1w,d2w,d3w,d4w,d5w; + + xc = x * scale; + yc = y * scale; + zc = z * scale; + wc = w * scale; + + sum = (xc + yc + zc + wc) * s_SkewCoeff4D; + skewedCubeOriginx = fastfloor(xc + sum); + skewedCubeOriginy = fastfloor(yc + sum); + skewedCubeOriginz = fastfloor(zc + sum); + skewedCubeOriginw = fastfloor(wc + sum); + + sum = (skewedCubeOriginx + skewedCubeOriginy + skewedCubeOriginz + skewedCubeOriginw) * s_UnskewCoeff4D; + unskewedCubeOriginx = skewedCubeOriginx - sum; + unskewedCubeOriginy = skewedCubeOriginy - sum; + unskewedCubeOriginz = skewedCubeOriginz - sum; + unskewedCubeOriginw = skewedCubeOriginw - sum; + + unskewedDistToOriginx = xc - unskewedCubeOriginx; + unskewedDistToOriginy = yc - unskewedCubeOriginy; + unskewedDistToOriginz = zc - unskewedCubeOriginz; + unskewedDistToOriginw = wc - unskewedCubeOriginw; + + c = 0; + c += (unskewedDistToOriginx > unskewedDistToOriginy) ? 32 : 0; + c += (unskewedDistToOriginx > unskewedDistToOriginz) ? 16 : 0; + c += (unskewedDistToOriginy > unskewedDistToOriginz) ? 8 : 0; + c += (unskewedDistToOriginx > unskewedDistToOriginw) ? 4 : 0; + c += (unskewedDistToOriginy > unskewedDistToOriginw) ? 2 : 0; + c += (unskewedDistToOriginz > unskewedDistToOriginw) ? 1 : 0; + + off1x = lookupTable[c][0] >= 3 ? 1 : 0; + off1y = lookupTable[c][1] >= 3 ? 1 : 0; + off1z = lookupTable[c][2] >= 3 ? 1 : 0; + off1w = lookupTable[c][3] >= 3 ? 1 : 0; + + off2x = lookupTable[c][0] >= 2 ? 1 : 0; + off2y = lookupTable[c][1] >= 2 ? 1 : 0; + off2z = lookupTable[c][2] >= 2 ? 1 : 0; + off2w = lookupTable[c][3] >= 2 ? 1 : 0; + + off3x = lookupTable[c][0] >= 1 ? 1 : 0; + off3y = lookupTable[c][1] >= 1 ? 1 : 0; + off3z = lookupTable[c][2] >= 1 ? 1 : 0; + off3w = lookupTable[c][3] >= 1 ? 1 : 0; + + d1x = unskewedDistToOriginx; + d1y = unskewedDistToOriginy; + d1z = unskewedDistToOriginz; + d1w = unskewedDistToOriginw; + + d2x = d1x - off1x + s_UnskewCoeff4D; + d2y = d1y - off1y + s_UnskewCoeff4D; + d2z = d1z - off1z + s_UnskewCoeff4D; + d2w = d1w - off1w + s_UnskewCoeff4D; + + d3x = d1x - off2x + 2.f*s_UnskewCoeff4D; + d3y = d1y - off2y + 2.f*s_UnskewCoeff4D; + d3z = d1z - off2z + 2.f*s_UnskewCoeff4D; + d3w = d1w - off2w + 2.f*s_UnskewCoeff4D; + + d4x = d1x - off3x + 3.f*s_UnskewCoeff4D; + d4y = d1y - off3y + 3.f*s_UnskewCoeff4D; + d4z = d1z - off3z + 3.f*s_UnskewCoeff4D; + d4w = d1w - off3w + 3.f*s_UnskewCoeff4D; + + d5x = d1x - 1.f + 4*s_UnskewCoeff4D; + d5y = d1y - 1.f + 4*s_UnskewCoeff4D; + d5z = d1z - 1.f + 4*s_UnskewCoeff4D; + d5w = d1w - 1.f + 4*s_UnskewCoeff4D; + + ii = skewedCubeOriginx & 255; + jj = skewedCubeOriginy & 255; + kk = skewedCubeOriginz & 255; + ll = skewedCubeOriginw & 255; + + gi0 = m_permutations[ii + m_permutations[jj + m_permutations[kk + m_permutations[ll ]]]] & 31; + gi1 = m_permutations[ii + off1x + m_permutations[jj + off1y + m_permutations[kk + off1z + m_permutations[ll + off1w]]]] & 31; + gi2 = m_permutations[ii + off2x + m_permutations[jj + off2y + m_permutations[kk + off2z + m_permutations[ll + off2w]]]] & 31; + gi3 = m_permutations[ii + off3x + m_permutations[jj + off3y + m_permutations[kk + off3z + m_permutations[ll + off3w]]]] & 31; + gi4 = m_permutations[ii + 1 + m_permutations[jj + 1 + m_permutations[kk + 1 + m_permutations[ll + 1 ]]]] % 32; + + c1 = 0.6f - d1x*d1x - d1y*d1y - d1z*d1z - d1w*d1w; + c2 = 0.6f - d2x*d2x - d2y*d2y - d2z*d2z - d2w*d2w; + c3 = 0.6f - d3x*d3x - d3y*d3y - d3z*d3z - d3w*d3w; + c4 = 0.6f - d4x*d4x - d4y*d4y - d4z*d4z - d4w*d4w; + c5 = 0.6f - d5x*d5x - d5y*d5y - d5z*d5z - d5w*d5w; + + if(c1 < 0) + n1 = 0; + else + n1 = c1*c1*c1*c1*(s_gradients4[gi0][0]*d1x + s_gradients4[gi0][1]*d1y + s_gradients4[gi0][2]*d1z + s_gradients4[gi0][3]*d1w); + + if(c2 < 0) + n2 = 0; + else + n2 = c2*c2*c2*c2*(s_gradients4[gi1][0]*d2x + s_gradients4[gi1][1]*d2y + s_gradients4[gi1][2]*d2z + s_gradients4[gi1][3]*d2w); + + if(c3 < 0) + n3 = 0; + else + n3 = c3*c3*c3*c3*(s_gradients4[gi2][0]*d3x + s_gradients4[gi2][1]*d3y + s_gradients4[gi2][2]*d3z + s_gradients4[gi2][3]*d3w); + + if(c4 < 0) + n4 = 0; + else + n4 = c4*c4*c4*c4*(s_gradients4[gi3][0]*d4x + s_gradients4[gi3][1]*d4y + s_gradients4[gi3][2]*d4z + s_gradients4[gi3][3]*d4w); + + if(c5 < 0) + n5 = 0; + else + n5 = c5*c5*c5*c5*(s_gradients4[gi4][0]*d5x + s_gradients4[gi4][1]*d5y + s_gradients4[gi4][2]*d5z + s_gradients4[gi4][3]*d5w); + + return (n1+n2+n3+n4+n5)*27.f; + } +} diff --git a/src/Nazara/Noise/Simplex2D.cpp b/src/Nazara/Noise/Simplex2D.cpp deleted file mode 100644 index b5e50c996..000000000 --- a/src/Nazara/Noise/Simplex2D.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -namespace Nz -{ - Simplex2D::Simplex2D() - { - float grad2Temp[][2] = {{1,1},{-1,1},{1,-1},{-1,-1}, - {1,0},{-1,0},{0,1},{0,-1}}; - - for(int i(0) ; i < 8 ; ++i) - for(int j(0) ; j < 2 ; ++j) - gradient2[i][j] = grad2Temp[i][j]; - - SkewCoeff2D = 0.5f*(std::sqrt(3.f) - 1.f); - UnskewCoeff2D = (3.f-std::sqrt(3.f))/6.f; - } - - Simplex2D::Simplex2D(unsigned int seed) : Simplex2D() - { - this->SetNewSeed(seed); - this->ShufflePermutationTable(); - } - - float Simplex2D::GetValue(float x, float y, float resolution) - { - x *= resolution; - y *= resolution; - - sum = (x + y) * SkewCoeff2D; - skewedCubeOrigin.x = fastfloor(x + sum); - skewedCubeOrigin.y = fastfloor(y + sum); - - sum = (skewedCubeOrigin.x + skewedCubeOrigin.y) * UnskewCoeff2D; - unskewedCubeOrigin.x = skewedCubeOrigin.x - sum; - unskewedCubeOrigin.y = skewedCubeOrigin.y - sum; - - unskewedDistToOrigin.x = x - unskewedCubeOrigin.x; - unskewedDistToOrigin.y = y - unskewedCubeOrigin.y; - - if(unskewedDistToOrigin.x > unskewedDistToOrigin.y) - { - off1.x = 1; - off1.y = 0; - } - else - { - off1.x = 0; - off1.y = 1; - } - - d1 = - unskewedDistToOrigin; - - d2.x = d1.x + off1.x - UnskewCoeff2D; - d2.y = d1.y + off1.y - UnskewCoeff2D; - - d3.x = d1.x + 1.f - 2.f * UnskewCoeff2D; - d3.y = d1.y + 1.f - 2.f * UnskewCoeff2D; - - ii = skewedCubeOrigin.x & 255; - jj = skewedCubeOrigin.y & 255; - - gi0 = perm[ii + perm[jj ]] & 7; - gi1 = perm[ii + off1.x + perm[jj + off1.y]] & 7; - gi2 = perm[ii + 1 + perm[jj + 1 ]] & 7; - - c1 = 0.5f - d1.x * d1.x - d1.y * d1.y; - c2 = 0.5f - d2.x * d2.x - d2.y * d2.y; - c3 = 0.5f - d3.x * d3.x - d3.y * d3.y; - - if(c1 < 0) - n1 = 0; - else - n1 = c1*c1*c1*c1*(gradient2[gi0][0] * d1.x + gradient2[gi0][1] * d1.y); - - if(c2 < 0) - n2 = 0; - else - n2 = c2*c2*c2*c2*(gradient2[gi1][0] * d2.x + gradient2[gi1][1] * d2.y); - - if(c3 < 0) - n3 = 0; - else - n3 = c3*c3*c3*c3*(gradient2[gi2][0] * d3.x + gradient2[gi2][1] * d3.y); - - return (n1+n2+n3)*70.f; - } -} diff --git a/src/Nazara/Noise/Simplex3D.cpp b/src/Nazara/Noise/Simplex3D.cpp deleted file mode 100644 index 5848f38ce..000000000 --- a/src/Nazara/Noise/Simplex3D.cpp +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -namespace Nz -{ - Simplex3D::Simplex3D() - { - SkewCoeff3D = 1/3.f; - UnskewCoeff3D = 1/6.f; - - float grad3Temp[][3] = { - {1.f,1.f,0.f}, - {-1.f,1.f,0.f}, - {1.f,-1.f,0.f}, - {-1.f,-1.f,0.f}, - - {1.f,0.f,1.f}, - {-1.f,0.f,1.f}, - {1.f,0.f,-1.f}, - {-1.f,0.f,-1.f}, - - {0.f,1.f,1.f}, - {0.f,-1.f,1.f}, - {0.f,1.f,-1.f}, - {0.f,-1.f,-1.f} - }; - - for(int i(0) ; i < 12 ; ++i) - for(int j(0) ; j < 3 ; ++j) - gradient3[i][j] = grad3Temp[i][j]; - } - - Simplex3D::Simplex3D(unsigned int seed) : Simplex3D() - { - this->SetNewSeed(seed); - this->ShufflePermutationTable(); - } - - float Simplex3D::GetValue(float x, float y, float z, float resolution) - { - x *= resolution; - y *= resolution; - z *= resolution; - - sum = (x + y + z) * SkewCoeff3D; - skewedCubeOrigin.x = fastfloor(x + sum); - skewedCubeOrigin.y = fastfloor(y + sum); - skewedCubeOrigin.z = fastfloor(z + sum); - - sum = (skewedCubeOrigin.x + skewedCubeOrigin.y + skewedCubeOrigin.z) * UnskewCoeff3D; - unskewedCubeOrigin.x = skewedCubeOrigin.x - sum; - unskewedCubeOrigin.y = skewedCubeOrigin.y - sum; - unskewedCubeOrigin.z = skewedCubeOrigin.z - sum; - - unskewedDistToOrigin.x = x - unskewedCubeOrigin.x; - unskewedDistToOrigin.y = y - unskewedCubeOrigin.y; - unskewedDistToOrigin.z = z - unskewedCubeOrigin.z; - - if(unskewedDistToOrigin.x >= unskewedDistToOrigin.y) - { - if(unskewedDistToOrigin.y >= unskewedDistToOrigin.z) - { - off1.x = 1; - off1.y = 0; - off1.z = 0; - off2.x = 1; - off2.y = 1; - off2.z = 0; - } - else if(unskewedDistToOrigin.x >= unskewedDistToOrigin.z) - { - off1.x = 1; - off1.y = 0; - off1.z = 0; - off2.x = 1; - off2.y = 0; - off2.z = 1; - } - else - { - off1.x = 0; - off1.y = 0; - off1.z = 1; - off2.x = 1; - off2.y = 0; - off2.z = 1; - } - } - else - { - if(unskewedDistToOrigin.y < unskewedDistToOrigin.z) - { - off1.x = 0; - off1.y = 0; - off1.z = 1; - off2.x = 0; - off2.y = 1; - off2.z = 1; - } - else if(unskewedDistToOrigin.x < unskewedDistToOrigin.z) - { - off1.x = 0; - off1.y = 1; - off1.z = 0; - off2.x = 0; - off2.y = 1; - off2.z = 1; - } - else - { - off1.x = 0; - off1.y = 1; - off1.z = 0; - off2.x = 1; - off2.y = 1; - off2.z = 0; - } - } - - d1 = unskewedDistToOrigin; - - d2.x = d1.x - off1.x + UnskewCoeff3D; - d2.y = d1.y - off1.y + UnskewCoeff3D; - d2.z = d1.z - off1.z + UnskewCoeff3D; - - d3.x = d1.x - off2.x + 2.f*UnskewCoeff3D; - d3.y = d1.y - off2.y + 2.f*UnskewCoeff3D; - d3.z = d1.z - off2.z + 2.f*UnskewCoeff3D; - - d4.x = d1.x - 1.f + 3.f*UnskewCoeff3D; - d4.y = d1.y - 1.f + 3.f*UnskewCoeff3D; - d4.z = d1.z - 1.f + 3.f*UnskewCoeff3D; - - ii = skewedCubeOrigin.x & 255; - jj = skewedCubeOrigin.y & 255; - kk = skewedCubeOrigin.z & 255; - - gi0 = perm[ii + perm[jj + perm[kk ]]] % 12; - gi1 = perm[ii + off1.x + perm[jj + off1.y + perm[kk + off1.z]]] % 12; - gi2 = perm[ii + off2.x + perm[jj + off2.y + perm[kk + off2.z]]] % 12; - gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 ]]] % 12; - - c1 = 0.6f - d1.x * d1.x - d1.y * d1.y - d1.z * d1.z; - c2 = 0.6f - d2.x * d2.x - d2.y * d2.y - d2.z * d2.z; - c3 = 0.6f - d3.x * d3.x - d3.y * d3.y - d3.z * d3.z; - c4 = 0.6f - d4.x * d4.x - d4.y * d4.y - d4.z * d4.z; - - if(c1 < 0) - n1 = 0; - else - n1 = c1*c1*c1*c1*(gradient3[gi0][0] * d1.x + gradient3[gi0][1] * d1.y + gradient3[gi0][2] * d1.z); - - if(c2 < 0) - n2 = 0; - else - n2 = c2*c2*c2*c2*(gradient3[gi1][0] * d2.x + gradient3[gi1][1] * d2.y + gradient3[gi1][2] * d2.z); - - if(c3 < 0) - n3 = 0; - else - n3 = c3*c3*c3*c3*(gradient3[gi2][0] * d3.x + gradient3[gi2][1] * d3.y + gradient3[gi2][2] * d3.z); - - if(c4 < 0) - n4 = 0; - else - n4 = c4*c4*c4*c4*(gradient3[gi3][0] * d4.x + gradient3[gi3][1] * d4.y + gradient3[gi3][2] * d4.z); - - return (n1+n2+n3+n4)*32; - } -} diff --git a/src/Nazara/Noise/Simplex4D.cpp b/src/Nazara/Noise/Simplex4D.cpp deleted file mode 100644 index b515d5958..000000000 --- a/src/Nazara/Noise/Simplex4D.cpp +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (C) 2015 Rémi Bèges -// This file is part of the "Nazara Engine - Noise module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -namespace Nz -{ - Simplex4D::Simplex4D() - { - SkewCoeff4D = (std::sqrt(5.f) - 1.f)/4.f; - UnskewCoeff4D = (5.f - std::sqrt(5.f))/20.f; - - int lookupTemp4D[][4] = - { - {0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0}, - {0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, - {1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0}, - {1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0}, - {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, - {2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0}, - {2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0} - }; - - for(int i(0) ; i < 64 ; ++i) - for(int j(0) ; j < 4 ; ++j) - lookupTable4D[i][j] = lookupTemp4D[i][j]; - - float grad4Temp[][4] = - { - {0.f,1.f,1.f,1.f}, {0.f,1.f,1.f,-1.f}, {0.f,1.f,-1.f,1.f}, {0.f,1.f,-1.f,-1.f}, - {0.f,-1.f,1.f,1.f},{0.f,-1.f,1.f,-1.f},{0.f,-1.f,-1.f,1.f},{0.f,-1.f,-1.f,-1.f}, - {1.f,0.f,1.f,1.f}, {1.f,0.f,1.f,-1.f}, {1.f,0.f,-1.f,1.f}, {1.f,0.f,-1.f,-1.f}, - {-1.f,0.f,1.f,1.f},{-1.f,0.f,1.f,-1.f},{-1.f,0.f,-1.f,1.f},{-1.f,0.f,-1.f,-1.f}, - {1.f,1.f,0.f,1.f}, {1.f,1.f,0.f,-1.f}, {1.f,-1.f,0.f,1.f}, {1.f,-1.f,0.f,-1.f}, - {-1.f,1.f,0.f,1.f},{-1.f,1.f,0.f,-1.f},{-1.f,-1.f,0.f,1.f},{-1.f,-1.f,0.f,-1.f}, - {1.f,1.f,1.f,0.f}, {1.f,1.f,-1.f,0.f}, {1.f,-1.f,1.f,0.f}, {1.f,-1.f,-1.f,0.f}, - {-1.f,1.f,1.f,0.f},{-1.f,1.f,-1.f,0.f},{-1.f,-1.f,1.f,0.f},{-1.f,-1.f,-1.f,0.f} - }; - - for(int i(0) ; i < 32 ; ++i) - for(int j(0) ; j < 4 ; ++j) - gradient4[i][j] = grad4Temp[i][j]; - } - - Simplex4D::Simplex4D(unsigned int seed) : Simplex4D() - { - this->SetNewSeed(seed); - this->ShufflePermutationTable(); - } - - float Simplex4D::GetValue(float x, float y, float z, float w, float resolution) - { - x *= resolution; - y *= resolution; - z *= resolution; - w *= resolution; - - sum = (x + y + z + w) * SkewCoeff4D; - skewedCubeOrigin.x = fastfloor(x + sum); - skewedCubeOrigin.y = fastfloor(y + sum); - skewedCubeOrigin.z = fastfloor(z + sum); - skewedCubeOrigin.w = fastfloor(w + sum); - - sum = (skewedCubeOrigin.x + skewedCubeOrigin.y + skewedCubeOrigin.z + skewedCubeOrigin.w) * UnskewCoeff4D; - unskewedCubeOrigin.x = skewedCubeOrigin.x - sum; - unskewedCubeOrigin.y = skewedCubeOrigin.y - sum; - unskewedCubeOrigin.z = skewedCubeOrigin.z - sum; - unskewedCubeOrigin.w = skewedCubeOrigin.w - sum; - - unskewedDistToOrigin.x = x - unskewedCubeOrigin.x; - unskewedDistToOrigin.y = y - unskewedCubeOrigin.y; - unskewedDistToOrigin.z = z - unskewedCubeOrigin.z; - unskewedDistToOrigin.w = w - unskewedCubeOrigin.w; - - c1 = (unskewedDistToOrigin.x > unskewedDistToOrigin.y) ? 32 : 0; - c2 = (unskewedDistToOrigin.x > unskewedDistToOrigin.z) ? 16 : 0; - c3 = (unskewedDistToOrigin.y > unskewedDistToOrigin.z) ? 8 : 0; - c4 = (unskewedDistToOrigin.x > unskewedDistToOrigin.w) ? 4 : 0; - c5 = (unskewedDistToOrigin.y > unskewedDistToOrigin.w) ? 2 : 0; - c6 = (unskewedDistToOrigin.z > unskewedDistToOrigin.w) ? 1 : 0; - c = c1 + c2 + c3 + c4 + c5 + c6; - - off1.x = lookupTable4D[c][0] >= 3 ? 1 : 0; - off1.y = lookupTable4D[c][1] >= 3 ? 1 : 0; - off1.z = lookupTable4D[c][2] >= 3 ? 1 : 0; - off1.w = lookupTable4D[c][3] >= 3 ? 1 : 0; - - off2.x = lookupTable4D[c][0] >= 2 ? 1 : 0; - off2.y = lookupTable4D[c][1] >= 2 ? 1 : 0; - off2.z = lookupTable4D[c][2] >= 2 ? 1 : 0; - off2.w = lookupTable4D[c][3] >= 2 ? 1 : 0; - - off3.x = lookupTable4D[c][0] >= 1 ? 1 : 0; - off3.y = lookupTable4D[c][1] >= 1 ? 1 : 0; - off3.z = lookupTable4D[c][2] >= 1 ? 1 : 0; - off3.w = lookupTable4D[c][3] >= 1 ? 1 : 0; - - d1 = unskewedDistToOrigin; - - d2.x = d1.x - off1.x + UnskewCoeff4D; - d2.y = d1.y - off1.y + UnskewCoeff4D; - d2.z = d1.z - off1.z + UnskewCoeff4D; - d2.w = d1.w - off1.w + UnskewCoeff4D; - - d3.x = d1.x - off2.x + 2.f*UnskewCoeff4D; - d3.y = d1.y - off2.y + 2.f*UnskewCoeff4D; - d3.z = d1.z - off2.z + 2.f*UnskewCoeff4D; - d3.w = d1.w - off2.w + 2.f*UnskewCoeff4D; - - d4.x = d1.x - off3.x + 3.f*UnskewCoeff4D; - d4.y = d1.y - off3.y + 3.f*UnskewCoeff4D; - d4.z = d1.z - off3.z + 3.f*UnskewCoeff4D; - d4.w = d1.w - off3.w + 3.f*UnskewCoeff4D; - - d5.x = d1.x - 1.f + 4*UnskewCoeff4D; - d5.y = d1.y - 1.f + 4*UnskewCoeff4D; - d5.z = d1.z - 1.f + 4*UnskewCoeff4D; - d5.w = d1.w - 1.f + 4*UnskewCoeff4D; - - ii = skewedCubeOrigin.x & 255; - jj = skewedCubeOrigin.y & 255; - kk = skewedCubeOrigin.z & 255; - ll = skewedCubeOrigin.w & 255; - - gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] & 31; - gi1 = perm[ii + off1.x + perm[jj + off1.y + perm[kk + off1.z + perm[ll + off1.w]]]] & 31; - gi2 = perm[ii + off2.x + perm[jj + off2.y + perm[kk + off2.z + perm[ll + off2.w]]]] & 31; - gi3 = perm[ii + off3.x + perm[jj + off3.y + perm[kk + off3.z + perm[ll + off3.w]]]] & 31; - gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32; - - c1 = 0.6f - d1.x*d1.x - d1.y*d1.y - d1.z*d1.z - d1.w*d1.w; - c2 = 0.6f - d2.x*d2.x - d2.y*d2.y - d2.z*d2.z - d2.w*d2.w; - c3 = 0.6f - d3.x*d3.x - d3.y*d3.y - d3.z*d3.z - d3.w*d3.w; - c4 = 0.6f - d4.x*d4.x - d4.y*d4.y - d4.z*d4.z - d4.w*d4.w; - c5 = 0.6f - d5.x*d5.x - d5.y*d5.y - d5.z*d5.z - d5.w*d5.w; - - if(c1 < 0) - n1 = 0; - else - n1 = c1*c1*c1*c1*(gradient4[gi0][0]*d1.x + gradient4[gi0][1]*d1.y + gradient4[gi0][2]*d1.z + gradient4[gi0][3]*d1.w); - - if(c2 < 0) - n2 = 0; - else - n2 = c2*c2*c2*c2*(gradient4[gi1][0]*d2.x + gradient4[gi1][1]*d2.y + gradient4[gi1][2]*d2.z + gradient4[gi1][3]*d2.w); - - if(c3 < 0) - n3 = 0; - else - n3 = c3*c3*c3*c3*(gradient4[gi2][0]*d3.x + gradient4[gi2][1]*d3.y + gradient4[gi2][2]*d3.z + gradient4[gi2][3]*d3.w); - - if(c4 < 0) - n4 = 0; - else - n4 = c4*c4*c4*c4*(gradient4[gi3][0]*d4.x + gradient4[gi3][1]*d4.y + gradient4[gi3][2]*d4.z + gradient4[gi3][3]*d4.w); - - if(c5 < 0) - n5 = 0; - else - n5 = c5*c5*c5*c5*(gradient4[gi4][0]*d5.x + gradient4[gi4][1]*d5.y + gradient4[gi4][2]*d5.z + gradient4[gi4][3]*d5.w); - - return (n1+n2+n3+n4+n5)*27.f; - } -} diff --git a/src/Nazara/Noise/Worley.cpp b/src/Nazara/Noise/Worley.cpp new file mode 100644 index 000000000..cc98fa378 --- /dev/null +++ b/src/Nazara/Noise/Worley.cpp @@ -0,0 +1,155 @@ +// Copyright (C) 2016 Rémi Bèges +// This file is part of the "Nazara Engine - Noise module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace + { + static constexpr std::array m_functionScales = + { + 1.f / float(M_SQRT2), + 0.5f / float(M_SQRT2), + 0.5f / float(M_SQRT2), + 0.5f / float(M_SQRT2) + }; + } + Worley::Worley() : + m_function(WorleyFunction_F1) + { + } + + Worley::Worley(unsigned int seed) : + Worley() + { + SetSeed(seed); + Shuffle(); + } + + float Worley::Get(float x, float y, float scale) const + { + std::map featurePoints; + + float xc, yc; + int x0, y0; + float fractx, fracty; + + xc = x * scale; + yc = y * scale; + + x0 = fastfloor(xc); + y0 = fastfloor(yc); + + fractx = xc - static_cast(x0); + fracty = yc - static_cast(y0); + + featurePoints.clear(); + + SquareTest(x0,y0,xc,yc,featurePoints); + + std::size_t functionIndex = static_cast(m_function); + + auto it = featurePoints.begin(); + std::advance(it, functionIndex); + + if(fractx < it->first) + SquareTest(x0 - 1,y0,xc,yc,featurePoints); + + it = featurePoints.begin(); + std::advance(it, functionIndex); + + if(1.f - fractx < it->first) + SquareTest(x0 + 1,y0,xc,yc,featurePoints); + + it = featurePoints.begin(); + std::advance(it, functionIndex); + + if(fracty < it->first) + SquareTest(x0,y0 - 1,xc,yc,featurePoints); + + it = featurePoints.begin(); + std::advance(it, functionIndex); + + if (1.f - fracty < it->first) + SquareTest(x0,y0 + 1,xc,yc,featurePoints); + + it = featurePoints.begin(); + std::advance(it, functionIndex); + + if (fractx < it->first && fracty < it->first) + SquareTest(x0 - 1, y0 - 1,xc,yc,featurePoints); + + it = featurePoints.begin(); + std::advance(it, functionIndex); + + if (1.f - fractx < it->first && fracty < it->first) + SquareTest(x0 + 1, y0 - 1,xc,yc,featurePoints); + + it = featurePoints.begin(); + std::advance(it, functionIndex); + + if (fractx < it->first && 1.f - fracty < it->first) + SquareTest(x0 - 1, y0 + 1,xc,yc,featurePoints); + + it = featurePoints.begin(); + std::advance(it, functionIndex); + + if(1.f - fractx < it->first && 1.f - fracty < it->first) + SquareTest(x0 + 1, y0 + 1,xc,yc,featurePoints); + + it = featurePoints.begin(); + std::advance(it, functionIndex); + + return it->first * m_functionScales[functionIndex]; + } + + float Worley::Get(float x, float y, float z, float scale) const + { + throw std::runtime_error("Worley 3D not available yet."); + } + + float Worley::Get(float x, float y, float z, float w, float scale) const + { + throw std::runtime_error("Worley 4D not available yet."); + } + + void Worley::Set(WorleyFunction func) + { + m_function = func; + } + + void Worley::SquareTest(int xi, int yi, float x, float y, std::map& featurePoints) const + { + int ii = xi & 255; + int jj = yi & 255; + + std::size_t seed = m_permutations[ii + m_permutations[jj]]; + + //On initialise notre rng avec seed + std::minstd_rand0 randomNumberGenerator(static_cast(seed)); + + //On prend un nombre de points à déterminer dans le cube, compris entre 1 et 8 + std::size_t m = (seed & 7) + 1; + + //On calcule les emplacements des différents points + for(std::size_t i(0) ; i < m; ++i) + { + Nz::Vector2f featurePoint; + featurePoint.x = (randomNumberGenerator() & 1023) / 1023.f + static_cast(xi); + featurePoint.y = (randomNumberGenerator() & 1023) / 1023.f + static_cast(yi); + + // TODO : Check order is correct + float distance = std::sqrt((featurePoint.x - x) * (featurePoint.x - x) + + (featurePoint.y - y) * (featurePoint.y - y)); + + //Insertion dans la liste triée + featurePoints[distance] = featurePoint; + } + } +} diff --git a/src/Nazara/Physics/PhysObject.cpp b/src/Nazara/Physics/PhysObject.cpp index 1b4816ee6..1ee4a44d5 100644 --- a/src/Nazara/Physics/PhysObject.cpp +++ b/src/Nazara/Physics/PhysObject.cpp @@ -68,7 +68,7 @@ namespace Nz PhysObject::~PhysObject() { if (m_body) - NewtonDestroyBody(m_world->GetHandle(), m_body); + NewtonDestroyBody(m_body); } void PhysObject::AddForce(const Vector3f& force, CoordSys coordSys) @@ -304,11 +304,13 @@ namespace Nz Vector3f min, max; NewtonBodyGetAABB(m_body, min, max); - NewtonWorldForEachBodyInAABBDo(m_world->GetHandle(), min, max, [](const NewtonBody* const body, void* const userData) + NewtonWorldForEachBodyInAABBDo(m_world->GetHandle(), min, max, [](const NewtonBody* const body, void* const userData) -> int { NazaraUnused(userData); NewtonBodySetSleepState(body, 0); - }, nullptr); + return 1; + }, + nullptr); } /*for (std::set::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) (*it)->PhysObjectOnUpdate(this);*/ @@ -317,7 +319,7 @@ namespace Nz PhysObject& PhysObject::operator=(PhysObject&& object) { if (m_body) - NewtonDestroyBody(m_world->GetHandle(), m_body); + NewtonDestroyBody(m_body); m_body = object.m_body; m_forceAccumulator = std::move(object.m_forceAccumulator); diff --git a/src/Nazara/Renderer/DebugDrawer.cpp b/src/Nazara/Renderer/DebugDrawer.cpp index 5623e6e8d..9f06c880f 100644 --- a/src/Nazara/Renderer/DebugDrawer.cpp +++ b/src/Nazara/Renderer/DebugDrawer.cpp @@ -652,7 +652,7 @@ namespace Nz void DebugDrawer::EnableDepthBuffer(bool depthBuffer) { - s_renderStates.parameters[RendererParameter_DepthBuffer] = depthBuffer; + s_renderStates.depthBuffer = depthBuffer; } float DebugDrawer::GetLineWidth() @@ -698,7 +698,7 @@ namespace Nz } s_primaryColor = Color::Red; - s_renderStates.parameters[RendererParameter_DepthBuffer] = true; + s_renderStates.depthBuffer = true; s_secondaryColor = Color::Green; s_initialized = true; @@ -709,7 +709,7 @@ namespace Nz bool DebugDrawer::IsDepthBufferEnabled() { - return s_renderStates.parameters[RendererParameter_DepthBuffer]; + return s_renderStates.depthBuffer; } void DebugDrawer::SetLineWidth(float width) diff --git a/src/Nazara/Renderer/OpenGL.cpp b/src/Nazara/Renderer/OpenGL.cpp index 3e1e43404..ba17fe015 100644 --- a/src/Nazara/Renderer/OpenGL.cpp +++ b/src/Nazara/Renderer/OpenGL.cpp @@ -158,7 +158,7 @@ namespace Nz RenderStates& currentRenderStates = s_contextStates->renderStates; // Les fonctions de blend n'a aucun intérêt sans blending - if (states.parameters[RendererParameter_Blend]) + if (states.blending) { if (currentRenderStates.dstBlend != states.dstBlend || currentRenderStates.srcBlend != states.srcBlend) @@ -169,7 +169,7 @@ namespace Nz } } - if (states.parameters[RendererParameter_DepthBuffer]) + if (states.depthBuffer) { // La comparaison de profondeur n'a aucun intérêt sans depth buffer if (currentRenderStates.depthFunc != states.depthFunc) @@ -179,20 +179,20 @@ namespace Nz } // Le DepthWrite n'a aucune importance si le DepthBuffer est désactivé - if (currentRenderStates.parameters[RendererParameter_DepthWrite] != states.parameters[RendererParameter_DepthWrite]) + if (currentRenderStates.depthWrite != states.depthWrite) { - glDepthMask((states.parameters[RendererParameter_DepthWrite]) ? GL_TRUE : GL_FALSE); - currentRenderStates.parameters[RendererParameter_DepthWrite] = states.parameters[RendererParameter_DepthWrite]; + glDepthMask((states.depthWrite) ? GL_TRUE : GL_FALSE); + currentRenderStates.depthWrite = states.depthWrite; } } // Inutile de changer le mode de face culling s'il n'est pas actif - if (states.parameters[RendererParameter_FaceCulling]) + if (states.faceCulling) { - if (currentRenderStates.faceCulling != states.faceCulling) + if (currentRenderStates.cullingSide != states.cullingSide) { - glCullFace(FaceSide[states.faceCulling]); - currentRenderStates.faceCulling = states.faceCulling; + glCullFace(FaceSide[states.cullingSide]); + currentRenderStates.cullingSide = states.cullingSide; } } @@ -203,33 +203,46 @@ namespace Nz } // Ici encore, ça ne sert à rien de se soucier des fonctions de stencil sans qu'il soit activé - if (states.parameters[RendererParameter_StencilTest]) + if (states.stencilTest) { - for (unsigned int i = 0; i < 2; ++i) + if (currentRenderStates.stencilCompare.back != states.stencilCompare.back || + currentRenderStates.stencilReference.back != states.stencilReference.back || + currentRenderStates.stencilWriteMask.back != states.stencilWriteMask.back) { - GLenum face = (i == 0) ? GL_BACK : GL_FRONT; - const RenderStates::Face& srcStates = (i == 0) ? states.backFace : states.frontFace; - RenderStates::Face& dstStates = (i == 0) ? currentRenderStates.backFace : currentRenderStates.frontFace; + glStencilFuncSeparate(GL_BACK, RendererComparison[states.stencilCompare.back], states.stencilReference.back, states.stencilWriteMask.back); + currentRenderStates.stencilCompare.back = states.stencilCompare.back; + currentRenderStates.stencilReference.back = states.stencilReference.back; + currentRenderStates.stencilWriteMask.back = states.stencilWriteMask.back; + } - if (dstStates.stencilCompare != srcStates.stencilCompare || - dstStates.stencilMask != srcStates.stencilMask || - dstStates.stencilReference != srcStates.stencilReference) - { - glStencilFuncSeparate(face, RendererComparison[srcStates.stencilCompare], srcStates.stencilReference, srcStates.stencilMask); - dstStates.stencilCompare = srcStates.stencilCompare; - dstStates.stencilMask = srcStates.stencilMask; - dstStates.stencilReference = srcStates.stencilReference; - } + if (currentRenderStates.stencilDepthFail.back != states.stencilDepthFail.back || + currentRenderStates.stencilFail.back != states.stencilFail.back || + currentRenderStates.stencilPass.back != states.stencilPass.back) + { + glStencilOpSeparate(GL_BACK, StencilOperation[states.stencilFail.back], StencilOperation[states.stencilDepthFail.back], StencilOperation[states.stencilPass.back]); + currentRenderStates.stencilDepthFail.back = states.stencilDepthFail.back; + currentRenderStates.stencilFail.back = states.stencilFail.back; + currentRenderStates.stencilPass.back = states.stencilPass.back; + } - if (dstStates.stencilFail != srcStates.stencilFail || - dstStates.stencilPass != srcStates.stencilPass || - dstStates.stencilZFail != srcStates.stencilZFail) - { - glStencilOpSeparate(face, StencilOperation[srcStates.stencilFail], StencilOperation[srcStates.stencilZFail], StencilOperation[srcStates.stencilPass]); - dstStates.stencilFail = srcStates.stencilFail; - dstStates.stencilPass = srcStates.stencilPass; - dstStates.stencilZFail = srcStates.stencilZFail; - } + if (currentRenderStates.stencilCompare.front != states.stencilCompare.front || + currentRenderStates.stencilReference.front != states.stencilReference.front || + currentRenderStates.stencilWriteMask.front != states.stencilWriteMask.front) + { + glStencilFuncSeparate(GL_FRONT, RendererComparison[states.stencilCompare.front], states.stencilReference.front, states.stencilWriteMask.front); + currentRenderStates.stencilCompare.front = states.stencilCompare.front; + currentRenderStates.stencilReference.front = states.stencilReference.front; + currentRenderStates.stencilWriteMask.front = states.stencilWriteMask.front; + } + + if (currentRenderStates.stencilDepthFail.front != states.stencilDepthFail.front || + currentRenderStates.stencilFail.front != states.stencilFail.front || + currentRenderStates.stencilPass.front != states.stencilPass.front) + { + glStencilOpSeparate(GL_FRONT, StencilOperation[states.stencilFail.front], StencilOperation[states.stencilDepthFail.front], StencilOperation[states.stencilPass.front]); + currentRenderStates.stencilDepthFail.front = states.stencilDepthFail.front; + currentRenderStates.stencilFail.front = states.stencilFail.front; + currentRenderStates.stencilPass.front = states.stencilPass.front; } } @@ -246,62 +259,62 @@ namespace Nz } // Paramètres de rendu - if (currentRenderStates.parameters[RendererParameter_Blend] != states.parameters[RendererParameter_Blend]) + if (currentRenderStates.blending != states.blending) { - if (states.parameters[RendererParameter_Blend]) + if (states.blending) glEnable(GL_BLEND); else glDisable(GL_BLEND); - currentRenderStates.parameters[RendererParameter_Blend] = states.parameters[RendererParameter_Blend]; + currentRenderStates.blending = states.blending; } - if (currentRenderStates.parameters[RendererParameter_ColorWrite] != states.parameters[RendererParameter_ColorWrite]) + if (currentRenderStates.colorWrite != states.colorWrite) { - GLboolean param = (states.parameters[RendererParameter_ColorWrite]) ? GL_TRUE : GL_FALSE; + GLboolean param = (states.colorWrite) ? GL_TRUE : GL_FALSE; glColorMask(param, param, param, param); - currentRenderStates.parameters[RendererParameter_ColorWrite] = states.parameters[RendererParameter_ColorWrite]; + currentRenderStates.colorWrite = states.colorWrite; } - if (currentRenderStates.parameters[RendererParameter_DepthBuffer] != states.parameters[RendererParameter_DepthBuffer]) + if (currentRenderStates.depthBuffer != states.depthBuffer) { - if (states.parameters[RendererParameter_DepthBuffer]) + if (states.depthBuffer) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); - currentRenderStates.parameters[RendererParameter_DepthBuffer] = states.parameters[RendererParameter_DepthBuffer]; + currentRenderStates.depthBuffer = states.depthBuffer; } - if (currentRenderStates.parameters[RendererParameter_FaceCulling] != states.parameters[RendererParameter_FaceCulling]) + if (currentRenderStates.faceCulling != states.faceCulling) { - if (states.parameters[RendererParameter_FaceCulling]) + if (states.faceCulling) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); - currentRenderStates.parameters[RendererParameter_FaceCulling] = states.parameters[RendererParameter_FaceCulling]; + currentRenderStates.faceCulling = states.faceCulling; } - if (currentRenderStates.parameters[RendererParameter_ScissorTest] != states.parameters[RendererParameter_ScissorTest]) + if (currentRenderStates.scissorTest != states.scissorTest) { - if (states.parameters[RendererParameter_ScissorTest]) + if (states.scissorTest) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); - currentRenderStates.parameters[RendererParameter_ScissorTest] = states.parameters[RendererParameter_ScissorTest]; + currentRenderStates.scissorTest = states.scissorTest; } - if (currentRenderStates.parameters[RendererParameter_StencilTest] != states.parameters[RendererParameter_StencilTest]) + if (currentRenderStates.stencilTest != states.stencilTest) { - if (states.parameters[RendererParameter_StencilTest]) + if (states.stencilTest) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST); - currentRenderStates.parameters[RendererParameter_StencilTest] = states.parameters[RendererParameter_StencilTest]; + currentRenderStates.stencilTest = states.stencilTest; } } @@ -995,6 +1008,7 @@ namespace Nz glUniformMatrix4fv = reinterpret_cast(LoadEntry("glUniformMatrix4fv")); glUnmapBuffer = reinterpret_cast(LoadEntry("glUnmapBuffer")); glUseProgram = reinterpret_cast(LoadEntry("glUseProgram")); + glValidateProgram = reinterpret_cast(LoadEntry("glValidateProgram")); glVertexAttrib4f = reinterpret_cast(LoadEntry("glVertexAttrib4f")); glVertexAttribDivisor = reinterpret_cast(LoadEntry("glVertexAttribDivisor")); glVertexAttribPointer = reinterpret_cast(LoadEntry("glVertexAttribPointer")); @@ -1684,24 +1698,44 @@ namespace Nz format->dataFormat = GL_DEPTH_COMPONENT; format->dataType = GL_UNSIGNED_SHORT; format->internalFormat = GL_DEPTH_COMPONENT16; + + format->swizzle[0] = GL_RED; + format->swizzle[1] = GL_RED; + format->swizzle[2] = GL_RED; + format->swizzle[3] = GL_ONE; return true; case PixelFormatType_Depth24: format->dataFormat = GL_DEPTH_COMPONENT; format->dataType = GL_UNSIGNED_INT; format->internalFormat = GL_DEPTH_COMPONENT24; + + format->swizzle[0] = GL_RED; + format->swizzle[1] = GL_RED; + format->swizzle[2] = GL_RED; + format->swizzle[3] = GL_ONE; return true; case PixelFormatType_Depth24Stencil8: format->dataFormat = GL_DEPTH_STENCIL; format->dataType = GL_UNSIGNED_INT_24_8; format->internalFormat = GL_DEPTH24_STENCIL8; + + format->swizzle[0] = GL_RED; + format->swizzle[1] = GL_RED; + format->swizzle[2] = GL_RED; + format->swizzle[3] = GL_GREEN; return true; case PixelFormatType_Depth32: format->dataFormat = GL_DEPTH_COMPONENT; format->dataType = GL_UNSIGNED_BYTE; format->internalFormat = GL_DEPTH_COMPONENT32; + + format->swizzle[0] = GL_RED; + format->swizzle[1] = GL_RED; + format->swizzle[2] = GL_RED; + format->swizzle[3] = GL_ONE; return true; case PixelFormatType_Stencil1: @@ -2248,12 +2282,14 @@ PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv = nullptr; PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr; PFNGLUNMAPBUFFERPROC glUnmapBuffer = nullptr; PFNGLUSEPROGRAMPROC glUseProgram = nullptr; +PFNGLVALIDATEPROGRAMPROC glValidateProgram = nullptr; PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f = nullptr; PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor = nullptr; PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr; PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer = nullptr; PFNGLVERTEXATTRIBLPOINTERPROC glVertexAttribLPointer = nullptr; PFNGLVIEWPORTPROC glViewport = nullptr; + #if defined(NAZARA_PLATFORM_WINDOWS) PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat = nullptr; PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr; diff --git a/src/Nazara/Renderer/RenderBuffer.cpp b/src/Nazara/Renderer/RenderBuffer.cpp index b1316f4c3..b6b182ab8 100644 --- a/src/Nazara/Renderer/RenderBuffer.cpp +++ b/src/Nazara/Renderer/RenderBuffer.cpp @@ -44,7 +44,7 @@ namespace Nz OpenGL::Format openglFormat; if (!OpenGL::TranslateFormat(format, &openglFormat, OpenGL::FormatType_RenderBuffer)) { - NazaraError("Failed to translate pixel format \"" + PixelFormat::ToString(format) + "\" into OpenGL format"); + NazaraError("Failed to translate pixel format \"" + PixelFormat::GetName(format) + "\" into OpenGL format"); return false; } diff --git a/src/Nazara/Renderer/RenderTexture.cpp b/src/Nazara/Renderer/RenderTexture.cpp index 2f2ba81c6..c6e78df86 100644 --- a/src/Nazara/Renderer/RenderTexture.cpp +++ b/src/Nazara/Renderer/RenderTexture.cpp @@ -44,13 +44,27 @@ namespace Nz 2 // AttachmentPoint_Stencil }; - AttachmentPoint formatTypeToAttachment[PixelFormatTypeType_Max+1] = + AttachmentPoint FormatTypeToAttachment(PixelFormatType format) { - AttachmentPoint_Color, // PixelFormatTypeType_Color - AttachmentPoint_Depth, // PixelFormatTypeType_Depth - AttachmentPoint_DepthStencil, // PixelFormatTypeType_DepthStencil - AttachmentPoint_Stencil // PixelFormatTypeType_Stencil - }; + const PixelFormatInfo& info = PixelFormat::GetInfo(format); + switch (info.content) + { + case PixelFormatContent_ColorRGBA: + return AttachmentPoint_Color; + + case PixelFormatContent_DepthStencil: + return (!info.greenMask.TestAny()) ? AttachmentPoint_Depth : AttachmentPoint_DepthStencil; + + case PixelFormatContent_Stencil: + return AttachmentPoint_Stencil; + + case PixelFormatContent_Undefined: + break; + } + + NazaraInternalError("Unexpected pixel format content: 0x" + String::Number(info.content, 16)); + return AttachmentPoint_Max; + } GLuint lockedPrevious = 0; UInt8 lockedLevel = 0; @@ -118,9 +132,9 @@ namespace Nz } } - AttachmentPoint targetAttachmentPoint = formatTypeToAttachment[PixelFormat::GetType(buffer->GetFormat())]; + AttachmentPoint targetAttachmentPoint = FormatTypeToAttachment(buffer->GetFormat()); if (targetAttachmentPoint != attachmentPoint && targetAttachmentPoint != AttachmentPoint_DepthStencil && - attachmentPoint != AttachmentPoint_Depth && attachmentPoint != AttachmentPoint_Stencil) + attachmentPoint != AttachmentPoint_Depth && attachmentPoint != AttachmentPoint_Stencil) { NazaraError("Pixel format type does not match attachment point type"); return false; @@ -156,9 +170,6 @@ namespace Nz InvalidateSize(); InvalidateTargets(); - if (attachmentPoint == AttachmentPoint_Color && !m_impl->userDefinedTargets) - m_impl->colorTargets.push_back(index); - return true; } @@ -233,7 +244,7 @@ namespace Nz return false; } - AttachmentPoint targetAttachmentPoint = formatTypeToAttachment[PixelFormat::GetType(texture->GetFormat())]; + AttachmentPoint targetAttachmentPoint = FormatTypeToAttachment(texture->GetFormat()); if (targetAttachmentPoint != attachmentPoint && targetAttachmentPoint != AttachmentPoint_DepthStencil && attachmentPoint != AttachmentPoint_Depth && attachmentPoint != AttachmentPoint_Stencil) { @@ -293,9 +304,6 @@ namespace Nz InvalidateSize(); InvalidateTargets(); - if (attachmentPoint == AttachmentPoint_Color && !m_impl->userDefinedTargets) - m_impl->colorTargets.push_back(index); - return true; } @@ -819,6 +827,15 @@ namespace Nz void RenderTexture::UpdateTargets() const { + if (!m_impl->userDefinedTargets) + { + m_impl->colorTargets.clear(); + + unsigned int colorIndex = 0; + for (unsigned int index = attachmentIndex[AttachmentPoint_Color]; index < m_impl->attachments.size(); ++index) + m_impl->colorTargets.push_back(colorIndex++); + } + if (m_impl->colorTargets.empty()) { m_impl->drawBuffers.resize(1); diff --git a/src/Nazara/Renderer/Renderer.cpp b/src/Nazara/Renderer/Renderer.cpp index 2017e2924..028f7513f 100644 --- a/src/Nazara/Renderer/Renderer.cpp +++ b/src/Nazara/Renderer/Renderer.cpp @@ -227,7 +227,8 @@ namespace Nz } GLenum type; - UInt8* offset = reinterpret_cast(s_indexBuffer->GetStartOffset()); + UInt8* offset = nullptr; + offset += s_indexBuffer->GetStartOffset(); if (s_indexBuffer->HasLargeIndices()) { @@ -290,7 +291,8 @@ namespace Nz } GLenum type; - UInt8* offset = reinterpret_cast(s_indexBuffer->GetStartOffset()); + UInt8* offset = nullptr; + offset += s_indexBuffer->GetStartOffset(); if (s_indexBuffer->HasLargeIndices()) { @@ -394,7 +396,38 @@ namespace Nz } #endif - s_states.parameters[parameter] = enable; + switch (parameter) + { + case RendererParameter_Blend: + s_states.blending = enable; + return; + + case RendererParameter_ColorWrite: + s_states.colorWrite = enable; + return; + + case RendererParameter_DepthBuffer: + s_states.depthBuffer = enable; + return; + + case RendererParameter_DepthWrite: + s_states.depthWrite = enable; + return; + + case RendererParameter_FaceCulling: + s_states.faceCulling = enable; + return; + + case RendererParameter_ScissorTest: + s_states.scissorTest = enable; + return; + + case RendererParameter_StencilTest: + s_states.stencilTest = enable; + return; + } + + NazaraInternalError("Unhandled renderer parameter: 0x" + String::Number(parameter, 16)); } void Renderer::EndCondition() @@ -760,7 +793,32 @@ namespace Nz } #endif - return s_states.parameters[parameter]; + switch (parameter) + { + case RendererParameter_Blend: + return s_states.blending; + + case RendererParameter_ColorWrite: + return s_states.colorWrite; + + case RendererParameter_DepthBuffer: + return s_states.depthBuffer; + + case RendererParameter_DepthWrite: + return s_states.depthWrite; + + case RendererParameter_FaceCulling: + return s_states.faceCulling; + + case RendererParameter_ScissorTest: + return s_states.scissorTest; + + case RendererParameter_StencilTest: + return s_states.stencilTest; + } + + NazaraInternalError("Unhandled renderer parameter: 0x" + String::Number(parameter, 16)); + return false; } bool Renderer::IsInitialized() @@ -863,7 +921,7 @@ namespace Nz } #endif - s_states.faceCulling = faceSide; + s_states.cullingSide = faceSide; } void Renderer::SetFaceFilling(FaceFilling fillingMode) @@ -1040,16 +1098,16 @@ namespace Nz switch (faceSide) { case FaceSide_Back: - s_states.backFace.stencilCompare = compareFunc; + s_states.stencilCompare.back = compareFunc; break; case FaceSide_Front: - s_states.frontFace.stencilCompare = compareFunc; + s_states.stencilCompare.front = compareFunc; break; case FaceSide_FrontAndBack: - s_states.backFace.stencilCompare = compareFunc; - s_states.frontFace.stencilCompare = compareFunc; + s_states.stencilCompare.back = compareFunc; + s_states.stencilCompare.front = compareFunc; break; } } @@ -1073,16 +1131,16 @@ namespace Nz switch (faceSide) { case FaceSide_Back: - s_states.backFace.stencilFail = failOperation; + s_states.stencilFail.back = failOperation; break; case FaceSide_Front: - s_states.frontFace.stencilFail = failOperation; + s_states.stencilFail.front = failOperation; break; case FaceSide_FrontAndBack: - s_states.backFace.stencilFail = failOperation; - s_states.frontFace.stencilFail = failOperation; + s_states.stencilFail.back = failOperation; + s_states.stencilFail.front = failOperation; break; } } @@ -1100,16 +1158,16 @@ namespace Nz switch (faceSide) { case FaceSide_Back: - s_states.backFace.stencilMask = mask; + s_states.stencilWriteMask.back = mask; break; case FaceSide_Front: - s_states.frontFace.stencilMask = mask; + s_states.stencilWriteMask.front = mask; break; case FaceSide_FrontAndBack: - s_states.backFace.stencilMask = mask; - s_states.frontFace.stencilMask = mask; + s_states.stencilWriteMask.back = mask; + s_states.stencilWriteMask.front = mask; break; } } @@ -1133,16 +1191,16 @@ namespace Nz switch (faceSide) { case FaceSide_Back: - s_states.backFace.stencilPass = passOperation; + s_states.stencilPass.back = passOperation; break; case FaceSide_Front: - s_states.frontFace.stencilPass = passOperation; + s_states.stencilPass.front = passOperation; break; case FaceSide_FrontAndBack: - s_states.backFace.stencilPass = passOperation; - s_states.frontFace.stencilPass = passOperation; + s_states.stencilPass.back = passOperation; + s_states.stencilPass.front = passOperation; break; } } @@ -1160,16 +1218,16 @@ namespace Nz switch (faceSide) { case FaceSide_Back: - s_states.backFace.stencilReference = refValue; + s_states.stencilReference.back = refValue; break; case FaceSide_Front: - s_states.frontFace.stencilReference = refValue; + s_states.stencilReference.front = refValue; break; case FaceSide_FrontAndBack: - s_states.backFace.stencilReference = refValue; - s_states.frontFace.stencilReference = refValue; + s_states.stencilReference.back = refValue; + s_states.stencilReference.front = refValue; break; } } @@ -1193,16 +1251,16 @@ namespace Nz switch (faceSide) { case FaceSide_Back: - s_states.backFace.stencilZFail = zfailOperation; + s_states.stencilDepthFail.back = zfailOperation; break; case FaceSide_Front: - s_states.frontFace.stencilZFail = zfailOperation; + s_states.stencilDepthFail.front = zfailOperation; break; case FaceSide_FrontAndBack: - s_states.backFace.stencilZFail = zfailOperation; - s_states.frontFace.stencilZFail = zfailOperation; + s_states.stencilDepthFail.back = zfailOperation; + s_states.stencilDepthFail.front = zfailOperation; break; } } @@ -1713,6 +1771,14 @@ namespace Nz // Et on termine par envoyer nos états au driver OpenGL::ApplyStates(s_states); + #ifdef NAZARA_DEBUG + if (!s_shader->Validate()) + { + NazaraError(Error::GetLastError()); + return false; + } + #endif + return true; } diff --git a/src/Nazara/Renderer/Shader.cpp b/src/Nazara/Renderer/Shader.cpp index 9a4d15ca3..65deb8b3b 100644 --- a/src/Nazara/Renderer/Shader.cpp +++ b/src/Nazara/Renderer/Shader.cpp @@ -747,6 +747,31 @@ namespace Nz } } + bool Shader::Validate() const + { + #if NAZARA_RENDERER_SAFE + if (!m_program) + { + NazaraError("Shader is not initialized"); + return false; + } + #endif + + glValidateProgram(m_program); + + GLint success; + glGetProgramiv(m_program, GL_VALIDATE_STATUS, &success); + + if (success == GL_TRUE) + return true; + else + { + NazaraError("Failed to validate shader: " + GetLog()); + return false; + } + } + + unsigned int Shader::GetOpenGLID() const { return m_program; diff --git a/src/Nazara/Renderer/Texture.cpp b/src/Nazara/Renderer/Texture.cpp index 237553e9a..458fb9992 100644 --- a/src/Nazara/Renderer/Texture.cpp +++ b/src/Nazara/Renderer/Texture.cpp @@ -364,7 +364,7 @@ namespace Nz return Image::GetMaxLevel(m_impl->type, m_impl->width, m_impl->height, m_impl->depth); } - unsigned int Texture::GetMemoryUsage() const + std::size_t Texture::GetMemoryUsage() const { #if NAZARA_RENDERER_SAFE if (!m_impl) @@ -399,7 +399,7 @@ namespace Nz return size * PixelFormat::GetBytesPerPixel(m_impl->format); } - unsigned int Texture::GetMemoryUsage(UInt8 level) const + std::size_t Texture::GetMemoryUsage(UInt8 level) const { #if NAZARA_UTILITY_SAFE if (!m_impl) @@ -521,7 +521,7 @@ namespace Nz { ///TODO: Sélectionner le format le plus adapté selon les composantes présentes dans le premier format PixelFormatType newFormat = (PixelFormat::HasAlpha(format)) ? PixelFormatType_BGRA8 : PixelFormatType_BGR8; - NazaraWarning("Format " + PixelFormat::ToString(format) + " not supported, trying to convert it to " + PixelFormat::ToString(newFormat) + "..."); + NazaraWarning("Format " + PixelFormat::GetName(format) + " not supported, trying to convert it to " + PixelFormat::GetName(newFormat) + "..."); if (PixelFormat::IsConversionSupported(format, newFormat)) { @@ -582,6 +582,9 @@ namespace Nz } } + // Keep resource path info + SetFilePath(image.GetFilePath()); + destroyOnExit.Reset(); return true; @@ -1197,7 +1200,7 @@ namespace Nz OpenGL::Format openGLFormat; if (!OpenGL::TranslateFormat(m_impl->format, &openGLFormat, OpenGL::FormatType_Texture)) { - NazaraError("Format " + PixelFormat::ToString(m_impl->format) + " not supported by OpenGL"); + NazaraError("Format " + PixelFormat::GetName(m_impl->format) + " not supported by OpenGL"); return false; } @@ -1306,6 +1309,12 @@ namespace Nz glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, openGLFormat.swizzle[3]); } + if (!proxy && PixelFormat::GetContent(m_impl->format) == PixelFormatContent_DepthStencil) + { + glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + return true; } diff --git a/src/Nazara/Utility/AlgorithmUtility.cpp b/src/Nazara/Utility/AlgorithmUtility.cpp index b23542993..d82af98af 100644 --- a/src/Nazara/Utility/AlgorithmUtility.cpp +++ b/src/Nazara/Utility/AlgorithmUtility.cpp @@ -105,9 +105,9 @@ namespace Nz for (unsigned int i = 0; i < recursionLevel; ++i) { std::size_t triangleCount = triangles.size(); - for (std::size_t i = 0; i < triangleCount; ++i) + for (std::size_t j = 0; j < triangleCount; ++j) { - Vector3ui& triangle = triangles[i]; + Vector3ui& triangle = triangles[j]; unsigned int a = GetMiddleVertex(triangle.x, triangle.y); unsigned int b = GetMiddleVertex(triangle.y, triangle.z); @@ -333,21 +333,20 @@ namespace Nz } private: - float CalculateVertexScore(unsigned int vertex) const + float CalculateVertexScore(VertexCacheData& vertex) const { - const VertexCacheData* v = &m_vertices[vertex]; - if (v->remaining_valence <= 0) + if (vertex.remaining_valence <= 0) // No tri needs this vertex! return -1.0f; float ret = 0.0f; - if (v->position_in_cache < 0) + if (vertex.position_in_cache < 0) { // Vertex is not in FIFO cache - no score. } else { - if (v->position_in_cache < 3) + if (vertex.position_in_cache < 3) { // This vertex was used in the last triangle, // so it has a fixed score, whichever of the three @@ -360,14 +359,14 @@ namespace Nz { // Points for being high in the cache. const float Scaler = 1.0f / (32 - 3); - ret = 1.0f - (v->position_in_cache - 3) * Scaler; + ret = 1.0f - (vertex.position_in_cache - 3) * Scaler; ret = std::pow(ret, m_cacheDecayPower); } } // Bonus points for having a low number of tris still to // use the vert, so we get rid of lone verts quickly. - float valence_boost = std::pow(static_cast(v->remaining_valence), -m_valenceBoostPower); + float valence_boost = std::pow(static_cast(vertex.remaining_valence), -m_valenceBoostPower); ret += m_valenceBoostScale * valence_boost; return ret; @@ -378,8 +377,8 @@ namespace Nz int FullScoreRecalculation() { // calculate score for all vertices - for (unsigned int i = 0; i < m_vertices.size(); ++i) - m_vertices[i].current_score = CalculateVertexScore(i); + for (VertexCacheData& vertex : m_vertices) + vertex.current_score = CalculateVertexScore(vertex); // calculate scores for all active triangles float max_score = std::numeric_limits::lowest(); @@ -548,13 +547,13 @@ namespace Nz float sum = 0.f; for (unsigned int i = 0; i < 3; ++i) { - VertexCacheData* v = &m_vertices[t->verts[i]]; - float sc = v->current_score; - if (!v->calculated) - sc = CalculateVertexScore(t->verts[i]); + VertexCacheData& v = m_vertices[t->verts[i]]; + float sc = v.current_score; + if (!v.calculated) + sc = CalculateVertexScore(v); - v->current_score = sc; - v->calculated = true; + v.current_score = sc; + v.calculated = true; sum += sc; } diff --git a/src/Nazara/Utility/Animation.cpp b/src/Nazara/Utility/Animation.cpp index bbd57b1df..62591344b 100644 --- a/src/Nazara/Utility/Animation.cpp +++ b/src/Nazara/Utility/Animation.cpp @@ -37,6 +37,8 @@ namespace Nz Animation::~Animation() { OnAnimationRelease(this); + + Destroy(); } bool Animation::AddSequence(const Sequence& sequence) diff --git a/src/Nazara/Utility/Font.cpp b/src/Nazara/Utility/Font.cpp index a29d28671..b5914aa58 100644 --- a/src/Nazara/Utility/Font.cpp +++ b/src/Nazara/Utility/Font.cpp @@ -127,7 +127,7 @@ namespace Nz return m_atlas; } - unsigned int Font::GetCachedGlyphCount(unsigned int characterSize, UInt32 style) const + std::size_t Font::GetCachedGlyphCount(unsigned int characterSize, UInt32 style) const { UInt64 key = ComputeKey(characterSize, style); auto it = m_glyphes.find(key); @@ -137,9 +137,9 @@ namespace Nz return it->second.size(); } - unsigned int Font::GetCachedGlyphCount() const + std::size_t Font::GetCachedGlyphCount() const { - unsigned int count = 0; + std::size_t count = 0; for (auto& pair : m_glyphes) count += pair.second.size(); diff --git a/src/Nazara/Utility/Formats/DDSLoader.cpp b/src/Nazara/Utility/Formats/DDSLoader.cpp index baa2657b3..d9783115b 100644 --- a/src/Nazara/Utility/Formats/DDSLoader.cpp +++ b/src/Nazara/Utility/Formats/DDSLoader.cpp @@ -169,20 +169,20 @@ namespace Nz { if (header.format.flags & (DDPF_RGB | DDPF_ALPHA | DDPF_ALPHAPIXELS | DDPF_LUMINANCE)) { - PixelFormatInfo info(header.format.bpp, PixelFormatSubType_Unsigned); + PixelFormatInfo info(PixelFormatContent_ColorRGBA, header.format.bpp, PixelFormatSubType_Unsigned); if (header.format.flags & DDPF_RGB) { - // DDS Masks are in little endian - info.redMask = SwapBytes(header.format.redMask); - info.greenMask = SwapBytes(header.format.greenMask); - info.blueMask = SwapBytes(header.format.blueMask); + // Reverse bits for our masks + info.redMask = ReverseBits(header.format.redMask); + info.greenMask = ReverseBits(header.format.greenMask); + info.blueMask = ReverseBits(header.format.blueMask); } else if (header.format.flags & DDPF_LUMINANCE) - info.redMask = SwapBytes(header.format.redMask); + info.redMask = ReverseBits(header.format.redMask); if (header.format.flags & (DDPF_ALPHA | DDPF_ALPHAPIXELS)) - info.alphaMask = SwapBytes(header.format.alphaMask); + info.alphaMask = ReverseBits(header.format.alphaMask); *format = PixelFormat::IdentifyFormat(info); if (!PixelFormat::IsValid(*format)) diff --git a/src/Nazara/Utility/Formats/MD2Loader.cpp b/src/Nazara/Utility/Formats/MD2Loader.cpp index eef4ac63d..dde203482 100644 --- a/src/Nazara/Utility/Formats/MD2Loader.cpp +++ b/src/Nazara/Utility/Formats/MD2Loader.cpp @@ -102,7 +102,7 @@ namespace Nz stream.Read(skin, 68*sizeof(char)); ParameterList matData; - matData.SetParameter(MaterialData::FilePath, baseDir + skin); + matData.SetParameter(MaterialData::DiffuseTexturePath, baseDir + skin); mesh->SetMaterialData(i, std::move(matData)); } @@ -188,11 +188,10 @@ namespace Nz SwapBytes(&translate.z, sizeof(float)); #endif - // Un personnage de taille moyenne fait ~50 unités de haut dans Quake 2 - // Avec Nazara, 1 unité = 1 mètre, nous devons donc adapter l'échelle - Vector3f s(parameters.scale/29.f); // 50/29 = 1.72 (Soit 1.72 mètre, proche de la taille moyenne d'un individu) - scale *= s; - translate *= s; + constexpr float ScaleAdjust = 1.f / 27.8f; // Make a 50 Quake 2 units character a 1.8 unit long + + scale *= ScaleAdjust; + translate *= ScaleAdjust; BufferMapper vertexMapper(vertexBuffer, BufferAccess_DiscardAndWrite); MeshVertex* vertex = static_cast(vertexMapper.GetPointer()); @@ -215,11 +214,13 @@ namespace Nz /// Chargement des positions // Pour que le modèle soit correctement aligné, on génère un quaternion que nous appliquerons à chacune des vertices Quaternionf rotationQuat = EulerAnglesf(-90.f, 90.f, 0.f); + Nz::Matrix4f matrix = Matrix4f::Transform(translate, rotationQuat, scale); + matrix *= parameters.matrix; for (unsigned int v = 0; v < header.num_vertices; ++v) { const MD2_Vertex& vert = vertices[v]; - Vector3f position = rotationQuat * Vector3f(vert.x*scale.x + translate.x, vert.y*scale.y + translate.y, vert.z*scale.z + translate.z); + Vector3f position = matrix * Vector3f(vert.x, vert.y, vert.z); vertex->position = position; vertex->normal = rotationQuat * md2Normals[vert.n]; diff --git a/src/Nazara/Utility/Formats/MD5AnimLoader.cpp b/src/Nazara/Utility/Formats/MD5AnimLoader.cpp index d079dc426..e61f6dda2 100644 --- a/src/Nazara/Utility/Formats/MD5AnimLoader.cpp +++ b/src/Nazara/Utility/Formats/MD5AnimLoader.cpp @@ -37,10 +37,10 @@ namespace Nz } const MD5AnimParser::Frame* frames = parser.GetFrames(); - unsigned int frameCount = parser.GetFrameCount(); - unsigned int frameRate = parser.GetFrameRate(); + std::size_t frameCount = parser.GetFrameCount(); + std::size_t frameRate = parser.GetFrameRate(); const MD5AnimParser::Joint* joints = parser.GetJoints(); - unsigned int jointCount = parser.GetJointCount(); + std::size_t jointCount = parser.GetJointCount(); // À ce stade, nous sommes censés avoir assez d'informations pour créer l'animation animation->CreateSkeletal(frameCount, jointCount); @@ -59,10 +59,10 @@ namespace Nz Quaternionf rotationQuat = Quaternionf::RotationBetween(Vector3f::UnitX(), Vector3f::Forward()) * Quaternionf::RotationBetween(Vector3f::UnitZ(), Vector3f::Up()); - for (unsigned int i = 0; i < jointCount; ++i) + for (std::size_t i = 0; i < jointCount; ++i) { int parent = joints[i].parent; - for (unsigned int j = 0; j < frameCount; ++j) + for (std::size_t j = 0; j < frameCount; ++j) { SequenceJoint& sequenceJoint = sequenceJoints[j*jointCount + i]; diff --git a/src/Nazara/Utility/Formats/MD5AnimParser.cpp b/src/Nazara/Utility/Formats/MD5AnimParser.cpp index 321ee8692..105944858 100644 --- a/src/Nazara/Utility/Formats/MD5AnimParser.cpp +++ b/src/Nazara/Utility/Formats/MD5AnimParser.cpp @@ -48,7 +48,7 @@ namespace Nz return Ternary_False; } - unsigned int MD5AnimParser::GetAnimatedComponentCount() const + std::size_t MD5AnimParser::GetAnimatedComponentCount() const { return m_animatedComponents.size(); } @@ -58,12 +58,12 @@ namespace Nz return m_frames.data(); } - unsigned int MD5AnimParser::GetFrameCount() const + std::size_t MD5AnimParser::GetFrameCount() const { return m_frames.size(); } - unsigned int MD5AnimParser::GetFrameRate() const + std::size_t MD5AnimParser::GetFrameRate() const { return m_frameRate; } @@ -73,7 +73,7 @@ namespace Nz return m_joints.data(); } - unsigned int MD5AnimParser::GetJointCount() const + std::size_t MD5AnimParser::GetJointCount() const { return m_joints.size(); } @@ -285,7 +285,8 @@ namespace Nz if (!Advance()) return false; - if (std::sscanf(&m_currentLine[0], "( %f %f %f ) ( %f %f %f )", &m_joints[i].bindPos.x, &m_joints[i].bindPos.y, &m_joints[i].bindPos.z, + // Space is important for the buffer of \n + if (std::sscanf(&m_currentLine[0], " ( %f %f %f ) ( %f %f %f )", &m_joints[i].bindPos.x, &m_joints[i].bindPos.y, &m_joints[i].bindPos.z, &m_joints[i].bindOrient.x, &m_joints[i].bindOrient.y, &m_joints[i].bindOrient.z) != 6) { UnrecognizedLine(true); @@ -324,7 +325,8 @@ namespace Nz return false; Vector3f min, max; - if (std::sscanf(&m_currentLine[0], "( %f %f %f ) ( %f %f %f )", &min.x, &min.y, &min.z, &max.x, &max.y, &max.z) != 6) + // Space is important for the buffer of \n + if (std::sscanf(&m_currentLine[0], " ( %f %f %f ) ( %f %f %f )", &min.x, &min.y, &min.z, &max.x, &max.y, &max.z) != 6) { UnrecognizedLine(true); return false; diff --git a/src/Nazara/Utility/Formats/MD5MeshLoader.cpp b/src/Nazara/Utility/Formats/MD5MeshLoader.cpp index cda183313..cd8a6813a 100644 --- a/src/Nazara/Utility/Formats/MD5MeshLoader.cpp +++ b/src/Nazara/Utility/Formats/MD5MeshLoader.cpp @@ -48,19 +48,20 @@ namespace Nz // Le hellknight de Doom 3 fait ~120 unités, et il est dit qu'il fait trois mètres // Nous réduisons donc la taille générale des fichiers MD5 de 1/40 - Vector3f scale(parameters.scale/40.f); + Matrix4f matrix = Matrix4f::Transform(Nz::Vector3f::Zero(), rotationQuat, Vector3f(1.f / 40.f)); + matrix *= parameters.matrix; const MD5MeshParser::Joint* joints = parser.GetJoints(); const MD5MeshParser::Mesh* meshes = parser.GetMeshes(); - unsigned int jointCount = parser.GetJointCount(); - unsigned int meshCount = parser.GetMeshCount(); + std::size_t jointCount = parser.GetJointCount(); + std::size_t meshCount = parser.GetMeshCount(); if (parameters.animated) { mesh->CreateSkeletal(jointCount); Skeleton* skeleton = mesh->GetSkeleton(); - for (unsigned int i = 0; i < jointCount; ++i) + for (std::size_t i = 0; i < jointCount; ++i) { Joint* joint = skeleton->GetJoint(i); @@ -81,12 +82,12 @@ namespace Nz } mesh->SetMaterialCount(meshCount); - for (unsigned int i = 0; i < meshCount; ++i) + for (std::size_t i = 0; i < meshCount; ++i) { const MD5MeshParser::Mesh& md5Mesh = meshes[i]; - unsigned int indexCount = md5Mesh.triangles.size()*3; - unsigned int vertexCount = md5Mesh.vertices.size(); + std::size_t indexCount = md5Mesh.triangles.size()*3; + std::size_t vertexCount = md5Mesh.vertices.size(); bool largeIndices = (vertexCount > std::numeric_limits::max()); @@ -226,11 +227,11 @@ namespace Nz } mesh->SetMaterialCount(meshCount); - for (unsigned int i = 0; i < meshCount; ++i) + for (std::size_t i = 0; i < meshCount; ++i) { const MD5MeshParser::Mesh& md5Mesh = meshes[i]; - unsigned int indexCount = md5Mesh.triangles.size()*3; - unsigned int vertexCount = md5Mesh.vertices.size(); + std::size_t indexCount = md5Mesh.triangles.size()*3; + std::size_t vertexCount = md5Mesh.vertices.size(); // Index buffer bool largeIndices = (vertexCount > std::numeric_limits::max()); @@ -267,7 +268,7 @@ namespace Nz } // On retourne le modèle dans le bon sens - vertex->position = scale * (rotationQuat * finalPos); + vertex->position = matrix * finalPos; vertex->uv.Set(md5Vertex.uv.x, (parameters.flipUVs) ? 1.f - md5Vertex.uv.y : md5Vertex.uv.y); // Inversion des UV si demandé vertex++; } diff --git a/src/Nazara/Utility/Formats/MD5MeshParser.cpp b/src/Nazara/Utility/Formats/MD5MeshParser.cpp index 56e384bf0..b4fd04a33 100644 --- a/src/Nazara/Utility/Formats/MD5MeshParser.cpp +++ b/src/Nazara/Utility/Formats/MD5MeshParser.cpp @@ -58,7 +58,7 @@ namespace Nz return m_joints.data(); } - unsigned int MD5MeshParser::GetJointCount() const + std::size_t MD5MeshParser::GetJointCount() const { return m_joints.size(); } @@ -68,7 +68,7 @@ namespace Nz return m_meshes.data(); } - unsigned int MD5MeshParser::GetMeshCount() const + std::size_t MD5MeshParser::GetMeshCount() const { return m_meshes.size(); } diff --git a/src/Nazara/Utility/Formats/MTLParser.cpp b/src/Nazara/Utility/Formats/MTLParser.cpp index 2398ccb3f..6685bcd04 100644 --- a/src/Nazara/Utility/Formats/MTLParser.cpp +++ b/src/Nazara/Utility/Formats/MTLParser.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -12,36 +13,22 @@ namespace Nz { - MTLParser::MTLParser(Stream& stream) : - m_stream(stream), - m_streamFlags(stream.GetStreamOptions()) //< Saves stream flags + bool MTLParser::Parse(Stream& stream) { - m_stream.EnableTextMode(true); - } + m_currentStream = &stream; - MTLParser::~MTLParser() - { - // Reset stream flags - if ((m_streamFlags & StreamOption_Text) == 0) - m_stream.EnableTextMode(false); - } + // Force stream in text mode, reset it at the end + Nz::CallOnExit resetTextMode; + if ((stream.GetStreamOptions() & StreamOption_Text) == 0) + { + stream.EnableTextMode(true); - const MTLParser::Material* MTLParser::GetMaterial(const String& materialName) const - { - auto it = m_materials.find(materialName); - if (it != m_materials.end()) - return &it->second; - else - return nullptr; - } + resetTextMode.Reset([&stream] () + { + stream.EnableTextMode(false); + }); + } - const std::unordered_map& MTLParser::GetMaterials() const - { - return m_materials; - } - - bool MTLParser::Parse() - { m_keepLastLine = false; m_lineCount = 0; m_materials.clear(); @@ -57,7 +44,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->ambient = Color(static_cast(r*255.f), static_cast(g*255.f), static_cast(b*255.f)); } @@ -72,7 +59,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->diffuse = Color(static_cast(r*255.f), static_cast(g*255.f), static_cast(b*255.f)); } @@ -87,7 +74,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->specular = Color(static_cast(r*255.f), static_cast(g*255.f), static_cast(b*255.f)); } @@ -102,7 +89,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f", &density) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->refractionIndex = density; } @@ -117,7 +104,7 @@ namespace Nz if (std::sscanf(&m_currentLine[3], "%f", &coef) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->shininess = coef; } @@ -132,7 +119,7 @@ namespace Nz if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->alpha = alpha; } @@ -147,7 +134,7 @@ namespace Nz if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->alpha = 1.f - alpha; // tr vaut pour la "valeur de transparence", 0 = opaque } @@ -162,7 +149,7 @@ namespace Nz if (std::sscanf(&m_currentLine[6], "%u", &model) == 1) { if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->illumModel = model; } @@ -178,7 +165,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->ambientMap = map; } @@ -190,7 +177,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->diffuseMap = map; } @@ -202,7 +189,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->specularMap = map; } @@ -214,7 +201,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->bumpMap = map; } @@ -226,7 +213,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->alphaMap = map; } @@ -238,7 +225,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->decalMap = map; } @@ -250,7 +237,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->displacementMap = map; } @@ -262,7 +249,7 @@ namespace Nz { String map = m_currentLine.SubString(mapPos); if (!currentMaterial) - currentMaterial = &m_materials["default"]; + currentMaterial = AddMaterial("default"); currentMaterial->reflectionMap = map; } @@ -271,7 +258,7 @@ namespace Nz { String materialName = m_currentLine.SubString(m_currentLine.GetWordPosition(1)); if (!materialName.IsEmpty()) - currentMaterial = &m_materials[materialName]; + currentMaterial = AddMaterial(materialName); #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING else UnrecognizedLine(); @@ -286,13 +273,150 @@ namespace Nz return true; } + bool MTLParser::Save(Stream& stream) const + { + m_currentStream = &stream; + + // Force stream in text mode, reset it at the end + Nz::CallOnExit resetTextMode; + if ((stream.GetStreamOptions() & StreamOption_Text) == 0) + { + stream.EnableTextMode(true); + + resetTextMode.Reset([&stream] () + { + stream.EnableTextMode(false); + }); + } + + m_outputStream.Clear(); + + EmitLine("# Exported by Nazara Engine"); + EmitLine(); + + Emit("# material count: "); + Emit(m_materials.size()); + EmitLine(); + + for (auto& pair : m_materials) + { + const String& matName = pair.first; + const Material& mat = pair.second; + + Emit("newmtl "); + EmitLine(pair.first); + EmitLine(); + + Emit("Ka "); + Emit(mat.ambient.r / 255.f); + Emit(' '); + Emit(mat.ambient.g / 255.f); + Emit(' '); + Emit(mat.ambient.b / 255.f); + EmitLine(); + + Emit("Kd "); + Emit(mat.diffuse.r / 255.f); + Emit(' '); + Emit(mat.diffuse.g / 255.f); + Emit(' '); + Emit(mat.diffuse.b / 255.f); + EmitLine(); + + Emit("Ks "); + Emit(mat.specular.r / 255.f); + Emit(' '); + Emit(mat.specular.g / 255.f); + Emit(' '); + Emit(mat.specular.b / 255.f); + EmitLine(); + + if (mat.alpha != 1.f) + { + Emit("d "); + EmitLine(mat.alpha); + } + + if (mat.refractionIndex != 1.f) + { + Emit("ni "); + EmitLine(mat.refractionIndex); + } + + if (mat.shininess != 1.f) + { + Emit("ns "); + EmitLine(mat.shininess); + } + + if (mat.illumModel != 0) + { + Emit("illum "); + EmitLine(mat.illumModel); + } + + if (!mat.ambientMap.IsEmpty()) + { + Emit("map_Ka "); + EmitLine(mat.ambientMap); + } + + if (!mat.diffuseMap.IsEmpty()) + { + Emit("map_Kd "); + EmitLine(mat.diffuseMap); + } + + if (!mat.specularMap.IsEmpty()) + { + Emit("map_Ks "); + EmitLine(mat.specularMap); + } + + if (!mat.bumpMap.IsEmpty()) + { + Emit("map_bump "); + EmitLine(mat.bumpMap); + } + + if (!mat.alphaMap.IsEmpty()) + { + Emit("map_d "); + EmitLine(mat.alphaMap); + } + + if (!mat.decalMap.IsEmpty()) + { + Emit("map_decal "); + EmitLine(mat.decalMap); + } + + if (!mat.displacementMap.IsEmpty()) + { + Emit("map_disp "); + EmitLine(mat.displacementMap); + } + + if (!mat.reflectionMap.IsEmpty()) + { + Emit("map_refl "); + EmitLine(mat.reflectionMap); + } + EmitLine(); + } + + Flush(); + + return true; + } + bool MTLParser::Advance(bool required) { if (!m_keepLastLine) { do { - if (m_stream.EndOfStream()) + if (m_currentStream->EndOfStream()) { if (required) Error("Incomplete MTL file"); @@ -302,7 +426,7 @@ namespace Nz m_lineCount++; - m_currentLine = m_stream.ReadLine(); + m_currentLine = m_currentStream->ReadLine(); m_currentLine = m_currentLine.SubStringTo("#"); // On ignore les commentaires m_currentLine.Simplify(); // Pour un traitement plus simple } @@ -313,24 +437,4 @@ namespace Nz return true; } - - void MTLParser::Error(const String& message) - { - NazaraError(message + " at line #" + String::Number(m_lineCount)); - } - - void MTLParser::Warning(const String& message) - { - NazaraWarning(message + " at line #" + String::Number(m_lineCount)); - } - - void MTLParser::UnrecognizedLine(bool error) - { - String message = "Unrecognized \"" + m_currentLine + '"'; - - if (error) - Error(message); - else - Warning(message); - } } diff --git a/src/Nazara/Utility/Formats/OBJLoader.cpp b/src/Nazara/Utility/Formats/OBJLoader.cpp index 75b5ba22f..405c845ea 100644 --- a/src/Nazara/Utility/Formats/OBJLoader.cpp +++ b/src/Nazara/Utility/Formats/OBJLoader.cpp @@ -49,8 +49,8 @@ namespace Nz return false; } - MTLParser materialParser(file); - if (!materialParser.Parse()) + MTLParser materialParser; + if (!materialParser.Parse(file)) { NazaraError("MTL parser failed"); return false; @@ -73,8 +73,6 @@ namespace Nz { ParameterList data; - data.SetParameter(MaterialData::CustomDefined); - UInt8 alphaValue = static_cast(mtlMat->alpha*255.f); Color ambientColor(mtlMat->ambient); @@ -90,13 +88,31 @@ namespace Nz data.SetParameter(MaterialData::SpecularColor, specularColor); if (!mtlMat->alphaMap.IsEmpty()) - data.SetParameter(MaterialData::AlphaTexturePath, baseDir + mtlMat->alphaMap); + { + String fullPath = mtlMat->alphaMap; + if (!Nz::File::IsAbsolute(fullPath)) + fullPath.Prepend(baseDir); + + data.SetParameter(MaterialData::AlphaTexturePath, fullPath); + } if (!mtlMat->diffuseMap.IsEmpty()) - data.SetParameter(MaterialData::DiffuseTexturePath, baseDir + mtlMat->diffuseMap); + { + String fullPath = mtlMat->diffuseMap; + if (!Nz::File::IsAbsolute(fullPath)) + fullPath.Prepend(baseDir); + + data.SetParameter(MaterialData::DiffuseTexturePath, fullPath); + } if (!mtlMat->specularMap.IsEmpty()) - data.SetParameter(MaterialData::SpecularTexturePath, baseDir + mtlMat->specularMap); + { + String fullPath = mtlMat->specularMap; + if (!Nz::File::IsAbsolute(fullPath)) + fullPath.Prepend(baseDir); + + data.SetParameter(MaterialData::SpecularTexturePath, fullPath); + } // If we either have an alpha value or an alpha map, let's configure the material for transparency if (alphaValue != 255 || !mtlMat->alphaMap.IsEmpty()) @@ -123,8 +139,8 @@ namespace Nz if (!parameters.custom.GetIntegerParameter("NativeOBJLoader_VertexCount", &reservedVertexCount)) reservedVertexCount = 100; - OBJParser parser(stream); - if (!parser.Parse(reservedVertexCount)) + OBJParser parser; + if (!parser.Parse(stream, reservedVertexCount)) { NazaraError("OBJ parser failed"); return false; @@ -177,23 +193,24 @@ namespace Nz { bool operator()(const OBJParser::FaceVertex& lhs, const OBJParser::FaceVertex& rhs) const { - return lhs.normal == rhs.normal && + return lhs.normal == rhs.normal && lhs.position == rhs.position && lhs.texCoord == rhs.texCoord; } }; std::unordered_map vertices; + vertices.reserve(meshes[i].vertices.size()); unsigned int vertexCount = 0; for (unsigned int j = 0; j < faceCount; ++j) { - unsigned int faceVertexCount = meshes[i].faces[j].vertices.size(); + unsigned int faceVertexCount = meshes[i].faces[j].vertexCount; faceIndices.resize(faceVertexCount); for (unsigned int k = 0; k < faceVertexCount; ++k) { - const OBJParser::FaceVertex& vertex = meshes[i].faces[j].vertices[k]; + const OBJParser::FaceVertex& vertex = meshes[i].vertices[meshes[i].faces[j].firstVertex + k]; auto it = vertices.find(vertex); if (it == vertices.end()) @@ -202,6 +219,7 @@ namespace Nz faceIndices[k] = it->second; } + // Triangulation for (unsigned int k = 1; k < faceVertexCount-1; ++k) { indices.push_back(faceIndices[0]); @@ -233,18 +251,17 @@ namespace Nz MeshVertex& vertex = meshVertices[index]; - const Vector4f& vec = positions[vertexIndices.position]; - vertex.position.Set(vec.x, vec.y, vec.z); - vertex.position *= parameters.scale/vec.w; + const Vector4f& vec = positions[vertexIndices.position-1]; + vertex.position = Vector3f(parameters.matrix * vec); - if (vertexIndices.normal >= 0) - vertex.normal = normals[vertexIndices.normal]; + if (vertexIndices.normal > 0) + vertex.normal = normals[vertexIndices.normal-1]; else hasNormals = false; - if (vertexIndices.texCoord >= 0) + if (vertexIndices.texCoord > 0) { - const Vector3f& uvw = texCoords[vertexIndices.texCoord]; + const Vector3f& uvw = texCoords[vertexIndices.texCoord-1]; vertex.uv.Set(uvw.x, (parameters.flipUVs) ? 1.f - uvw.y : uvw.y); // Inversion des UVs si demandé } else @@ -297,12 +314,12 @@ namespace Nz namespace Loaders { - void RegisterOBJ() + void RegisterOBJLoader() { MeshLoader::RegisterLoader(IsSupported, Check, Load); } - void UnregisterOBJ() + void UnregisterOBJLoader() { MeshLoader::UnregisterLoader(IsSupported, Check, Load); } diff --git a/src/Nazara/Utility/Formats/OBJLoader.hpp b/src/Nazara/Utility/Formats/OBJLoader.hpp index 05a3efdff..46dd8d976 100644 --- a/src/Nazara/Utility/Formats/OBJLoader.hpp +++ b/src/Nazara/Utility/Formats/OBJLoader.hpp @@ -13,8 +13,8 @@ namespace Nz { namespace Loaders { - void RegisterOBJ(); - void UnregisterOBJ(); + void RegisterOBJLoader(); + void UnregisterOBJLoader(); } } diff --git a/src/Nazara/Utility/Formats/OBJParser.cpp b/src/Nazara/Utility/Formats/OBJParser.cpp index c0daf1f84..403950a97 100644 --- a/src/Nazara/Utility/Formats/OBJParser.cpp +++ b/src/Nazara/Utility/Formats/OBJParser.cpp @@ -3,7 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include -#include +#include #include #include #include @@ -13,79 +13,25 @@ namespace Nz { - OBJParser::OBJParser(Stream& stream) : - m_stream(stream), - m_streamFlags(stream.GetStreamOptions()) //< Saves stream flags + bool OBJParser::Parse(Nz::Stream& stream, std::size_t reservedVertexCount) { - m_stream.EnableTextMode(true); - } + m_currentStream = &stream; - OBJParser::~OBJParser() - { - // Reset stream flags - if ((m_streamFlags & StreamOption_Text) == 0) - m_stream.EnableTextMode(false); - } + // Force stream in text mode, reset it at the end + Nz::CallOnExit resetTextMode; + if ((stream.GetStreamOptions() & StreamOption_Text) == 0) + { + stream.EnableTextMode(true); - const String* OBJParser::GetMaterials() const - { - return m_materials.data(); - } + resetTextMode.Reset([&stream] () + { + stream.EnableTextMode(false); + }); + } - unsigned int OBJParser::GetMaterialCount() const - { - return m_materials.size(); - } - - const OBJParser::Mesh* OBJParser::GetMeshes() const - { - return m_meshes.data(); - } - - unsigned int OBJParser::GetMeshCount() const - { - return m_meshes.size(); - } - - const String& OBJParser::GetMtlLib() const - { - return m_mtlLib; - } - - const Vector3f* OBJParser::GetNormals() const - { - return m_normals.data(); - } - - unsigned int OBJParser::GetNormalCount() const - { - return m_normals.size(); - } - - const Vector4f* OBJParser::GetPositions() const - { - return m_positions.data(); - } - - unsigned int OBJParser::GetPositionCount() const - { - return m_positions.size(); - } - - const Vector3f* OBJParser::GetTexCoords() const - { - return m_texCoords.data(); - } - - unsigned int OBJParser::GetTexCoordCount() const - { - return m_texCoords.size(); - } - - bool OBJParser::Parse(std::size_t reservedVertexCount) - { String matName, meshName; matName = meshName = "default"; + m_errorCount = 0; m_keepLastLine = false; m_lineCount = 0; m_meshes.clear(); @@ -100,35 +46,60 @@ namespace Nz m_positions.reserve(reservedVertexCount); m_texCoords.reserve(reservedVertexCount); - // On va regrouper les meshs par nom et par matériau - using FaceVec = std::vector; - using MatPair = std::pair; - std::unordered_map> meshes; + // Sort meshes by material and group + using MatPair = std::pair; + std::unordered_map> meshesByName; - unsigned int matIndex = 0; - auto GetMaterial = [&meshes, &matIndex] (const String& mesh, const String& material) -> FaceVec* + std::size_t faceReserve = 0; + std::size_t vertexReserve = 0; + unsigned int matCount = 0; + auto GetMaterial = [&] (const String& meshName, const String& matName) -> Mesh* { - auto& map = meshes[mesh]; - auto it = map.find(material); + auto& map = meshesByName[meshName]; + auto it = map.find(matName); if (it == map.end()) - it = map.insert(std::make_pair(material, MatPair(FaceVec(), matIndex++))).first; + it = map.insert(std::make_pair(matName, MatPair(Mesh(), matCount++))).first; + + Mesh& mesh = it->second.first; + + mesh.faces.reserve(faceReserve); + mesh.vertices.reserve(vertexReserve); + faceReserve = 0; + vertexReserve = 0; return &(it->second.first); }; // On prépare le mesh par défaut - FaceVec* currentMesh = nullptr; + Mesh* currentMesh = nullptr; while (Advance(false)) { switch (std::tolower(m_currentLine[0])) { - case 'f': // Une face + case '#': //< Comment + // Some softwares write comments to gives the number of vertex/faces an importer can expect + std::size_t data; + if (std::sscanf(m_currentLine.GetConstBuffer(), "# position count: %zu", &data) == 1) + m_positions.reserve(data); + else if (std::sscanf(m_currentLine.GetConstBuffer(), "# normal count: %zu", &data) == 1) + m_normals.reserve(data); + else if (std::sscanf(m_currentLine.GetConstBuffer(), "# texcoords count: %zu", &data) == 1) + m_texCoords.reserve(data); + else if (std::sscanf(m_currentLine.GetConstBuffer(), "# face count: %zu", &data) == 1) + faceReserve = data; + else if (std::sscanf(m_currentLine.GetConstBuffer(), "# vertex count: %zu", &data) == 1) + vertexReserve = data; + + break; + + case 'f': //< Face { - if (m_currentLine.GetSize() < 7) // Le minimum syndical pour définir une face de trois sommets (f 1 2 3) + if (m_currentLine.GetSize() < 7) // Since we only treat triangles, this is the minimum length of a face line (f 1 2 3) { #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - UnrecognizedLine(); + if (!UnrecognizedLine()) + return false; #endif break; } @@ -137,22 +108,29 @@ namespace Nz if (vertexCount < 3) { #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - UnrecognizedLine(); + if (!UnrecognizedLine()) + return false; #endif break; } + if (!currentMesh) + currentMesh = GetMaterial(meshName, matName); + Face face; - face.vertices.resize(vertexCount); + face.firstVertex = currentMesh->vertices.size(); + face.vertexCount = vertexCount; + + currentMesh->vertices.resize(face.firstVertex + vertexCount, FaceVertex{0, 0, 0}); bool error = false; unsigned int pos = 2; for (unsigned int i = 0; i < vertexCount; ++i) { int offset; - int& n = face.vertices[i].normal; - int& p = face.vertices[i].position; - int& t = face.vertices[i].texCoord; + int n = 0; + int p = 0; + int t = 0; if (std::sscanf(&m_currentLine[pos], "%d/%d/%d%n", &p, &t, &n, &offset) != 3) { @@ -163,27 +141,19 @@ namespace Nz if (std::sscanf(&m_currentLine[pos], "%d%n", &p, &offset) != 1) { #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - UnrecognizedLine(); + if (!UnrecognizedLine()) + return false; #endif error = true; break; } - else - { - n = 0; - t = 0; - } } - else - n = 0; } - else - t = 0; } if (p < 0) { - p += m_positions.size(); + p += m_positions.size() - 1; if (p < 0) { Error("Vertex index out of range (" + String::Number(p) + " < 0"); @@ -191,84 +161,81 @@ namespace Nz break; } } - else - p--; if (n < 0) { - n += m_normals.size(); + n += m_normals.size() - 1; if (n < 0) { - Error("Vertex index out of range (" + String::Number(n) + " < 0"); + Error("Normal index out of range (" + String::Number(n) + " < 0"); error = true; break; } } - else - n--; if (t < 0) { - t += m_texCoords.size(); + t += m_texCoords.size() - 1; if (t < 0) { - Error("Vertex index out of range (" + String::Number(t) + " < 0"); + Error("Texture coordinates index out of range (" + String::Number(t) + " < 0"); error = true; break; } } - else - t--; - if (static_cast(p) >= m_positions.size()) + if (static_cast(p) > m_positions.size()) { Error("Vertex index out of range (" + String::Number(p) + " >= " + String::Number(m_positions.size()) + ')'); error = true; break; } - else if (n >= 0 && static_cast(n) >= m_normals.size()) + else if (n != 0 && static_cast(n) > m_normals.size()) { Error("Normal index out of range (" + String::Number(n) + " >= " + String::Number(m_normals.size()) + ')'); error = true; break; } - else if (t >= 0 && static_cast(t) >= m_texCoords.size()) + else if (t != 0 && static_cast(t) > m_texCoords.size()) { Error("TexCoord index out of range (" + String::Number(t) + " >= " + String::Number(m_texCoords.size()) + ')'); error = true; break; } + currentMesh->vertices[face.firstVertex + i].normal = static_cast(n); + currentMesh->vertices[face.firstVertex + i].position = static_cast(p); + currentMesh->vertices[face.firstVertex + i].texCoord = static_cast(t); + pos += offset; } if (!error) - { - if (!currentMesh) - currentMesh = GetMaterial(meshName, matName); - - currentMesh->push_back(std::move(face)); - } + currentMesh->faces.push_back(std::move(face)); + else + currentMesh->vertices.resize(face.firstVertex); //< Remove vertices break; } - case 'm': + case 'm': //< MTLLib #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING if (m_currentLine.GetWord(0).ToLower() != "mtllib") - UnrecognizedLine(); + if (!UnrecognizedLine()) + return false; #endif m_mtlLib = m_currentLine.SubString(m_currentLine.GetWordPosition(1)); break; - case 'g': - case 'o': + case 'g': //< Group (inside a mesh) + case 'o': //< Object (defines a mesh) { if (m_currentLine.GetSize() <= 2 || m_currentLine[1] != ' ') { #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - UnrecognizedLine(); + if (!UnrecognizedLine()) + return false; #endif break; } @@ -277,59 +244,63 @@ namespace Nz if (objectName.IsEmpty()) { #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - UnrecognizedLine(); + if (!UnrecognizedLine()) + return false; #endif break; } meshName = objectName; - currentMesh = GetMaterial(meshName, matName); + currentMesh = nullptr; break; } #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - case 's': + case 's': //< Smooth if (m_currentLine.GetSize() <= 2 || m_currentLine[1] == ' ') { String param = m_currentLine.SubString(2); if (param != "all" && param != "on" && param != "off" && !param.IsNumber()) - UnrecognizedLine(); + { + if (!UnrecognizedLine()) + return false; + } } - else - UnrecognizedLine(); + else if (!UnrecognizedLine()) + return false; break; - #endif + #endif - case 'u': + case 'u': //< Usemtl #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - if (m_currentLine.GetWord(0) != "usemtl") - UnrecognizedLine(); + if (m_currentLine.GetWord(0) != "usemtl" && !UnrecognizedLine()) + return false; #endif matName = m_currentLine.SubString(m_currentLine.GetWordPosition(1)); + currentMesh = nullptr; if (matName.IsEmpty()) { #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - UnrecognizedLine(); + if (!UnrecognizedLine()) + return false; #endif break; } - - currentMesh = GetMaterial(meshName, matName); break; - case 'v': + case 'v': //< Position/Normal/Texcoords { String word = m_currentLine.GetWord(0).ToLower(); if (word == 'v') { Vector4f vertex(Vector3f::Zero(), 1.f); unsigned int paramCount = std::sscanf(&m_currentLine[2], "%f %f %f %f", &vertex.x, &vertex.y, &vertex.z, &vertex.w); - if (paramCount >= 3) + if (paramCount >= 1) m_positions.push_back(vertex); #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - else - UnrecognizedLine(); + else if (!UnrecognizedLine()) + false; #endif } else if (word == "vn") @@ -339,8 +310,8 @@ namespace Nz if (paramCount == 3) m_normals.push_back(normal); #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - else - UnrecognizedLine(); + else if (!UnrecognizedLine()) + false; #endif } else if (word == "vt") @@ -350,13 +321,13 @@ namespace Nz if (paramCount >= 2) m_texCoords.push_back(uvw); #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - else - UnrecognizedLine(); + else if (!UnrecognizedLine()) + false; #endif } #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - else - UnrecognizedLine(); + else if (!UnrecognizedLine()) + false; #endif break; @@ -364,33 +335,32 @@ namespace Nz default: #if NAZARA_UTILITY_STRICT_RESOURCE_PARSING - UnrecognizedLine(); + if (!UnrecognizedLine()) + return false; #endif break; } } std::unordered_map materials; - m_materials.resize(matIndex); + m_materials.resize(matCount); - for (auto& meshIt : meshes) + for (auto& meshPair : meshesByName) { - for (auto& matIt : meshIt.second) + for (auto& matPair : meshPair.second) { - auto& faceVec = matIt.second.first; - unsigned int index = matIt.second.second; - if (!faceVec.empty()) + Mesh& mesh = matPair.second.first; + unsigned int index = matPair.second.second; + if (!mesh.faces.empty()) { - Mesh mesh; - mesh.faces = std::move(faceVec); - mesh.name = meshIt.first; + mesh.name = meshPair.first; - auto it = materials.find(matIt.first); + auto it = materials.find(matPair.first); if (it == materials.end()) { mesh.material = index; - materials[matIt.first] = index; - m_materials[index] = matIt.first; + materials[matPair.first] = index; + m_materials[index] = matPair.first; } else mesh.material = it->second; @@ -409,13 +379,154 @@ namespace Nz return true; } + bool OBJParser::Save(Stream& stream) const + { + m_currentStream = &stream; + + // Force stream in text mode, reset it at the end + Nz::CallOnExit resetTextMode; + if ((stream.GetStreamOptions() & StreamOption_Text) == 0) + { + stream.EnableTextMode(true); + + resetTextMode.Reset([&stream] () + { + stream.EnableTextMode(false); + }); + } + + m_outputStream.Clear(); + + EmitLine("# Exported by Nazara Engine"); + EmitLine(); + + if (!m_mtlLib.IsEmpty()) + { + Emit("mtllib "); + EmitLine(m_mtlLib); + EmitLine(); + } + + Emit("# position count: "); + EmitLine(m_positions.size()); + + for (const Nz::Vector4f& position : m_positions) + { + Emit("v "); + Emit(position.x); + Emit(' '); + Emit(position.y); + Emit(' '); + Emit(position.z); + + if (!NumberEquals(position.w, 1.f)) + { + Emit(' '); + Emit(position.w); + } + + EmitLine(); + } + EmitLine(); + + Emit("# normal count: "); + EmitLine(m_normals.size()); + + for (const Nz::Vector3f& normal : m_normals) + { + Emit("vn "); + Emit(normal.x); + Emit(' '); + Emit(normal.y); + Emit(' '); + Emit(normal.y); + EmitLine(); + } + EmitLine(); + + Emit("# texcoords count: "); + EmitLine(m_texCoords.size()); + + for (const Nz::Vector3f& uvw : m_texCoords) + { + Emit("vt "); + Emit(uvw.x); + Emit(' '); + Emit(uvw.y); + if (NumberEquals(uvw.z, 0.f)) + { + Emit(' '); + Emit(uvw.z); + } + EmitLine(); + } + EmitLine(); + + std::unordered_map /* meshes*/> meshesByMaterials; + std::size_t meshIndex = 0; + for (const Mesh& mesh : m_meshes) + meshesByMaterials[mesh.material].push_back(meshIndex++); + + for (auto& pair : meshesByMaterials) + { + Emit("usemtl "); + EmitLine(m_materials[pair.first]); + Emit("# groups count: "); + EmitLine(pair.second.size()); + EmitLine(); + + for (std::size_t meshIndex : pair.second) + { + const Mesh& mesh = m_meshes[meshIndex]; + + Emit("g "); + EmitLine(mesh.name); + EmitLine(); + + Emit("# face count: "); + EmitLine(mesh.faces.size()); + Emit("# vertex count: "); + EmitLine(mesh.vertices.size()); + + for (const Face& face : mesh.faces) + { + Emit('f'); + for (std::size_t i = 0; i < face.vertexCount; ++i) + { + Emit(' '); + const FaceVertex& faceVertex = mesh.vertices[face.firstVertex + i]; + Emit(faceVertex.position); + if (faceVertex.texCoord != 0 || faceVertex.normal != 0) + { + Emit('/'); + if (faceVertex.texCoord != 0) + Emit(faceVertex.texCoord); + + if (faceVertex.normal != 0) + { + Emit('/'); + Emit(faceVertex.normal); + } + } + } + EmitLine(); + } + } + EmitLine(); + } + + Flush(); + + return true; + } + bool OBJParser::Advance(bool required) { if (!m_keepLastLine) { do { - if (m_stream.EndOfStream()) + if (m_currentStream->EndOfStream()) { if (required) Error("Incomplete OBJ file"); @@ -425,9 +536,8 @@ namespace Nz m_lineCount++; - m_currentLine = m_stream.ReadLine(); - m_currentLine = m_currentLine.SubStringTo("#"); // On ignore les commentaires - m_currentLine.Simplify(); // Pour un traitement plus simple + m_currentLine = m_currentStream->ReadLine(); + m_currentLine.Simplify(); // Simplify lines (convert multiple blanks into a single space and trims) } while (m_currentLine.IsEmpty()); } @@ -436,24 +546,4 @@ namespace Nz return true; } - - void OBJParser::Error(const String& message) - { - NazaraError(message + " at line #" + String::Number(m_lineCount)); - } - - void OBJParser::Warning(const String& message) - { - NazaraWarning(message + " at line #" + String::Number(m_lineCount)); - } - - void OBJParser::UnrecognizedLine(bool error) - { - String message = "Unrecognized \"" + m_currentLine + '"'; - - if (error) - Error(message); - else - Warning(message); - } } diff --git a/src/Nazara/Utility/Formats/OBJSaver.cpp b/src/Nazara/Utility/Formats/OBJSaver.cpp new file mode 100644 index 000000000..a7a2cda0b --- /dev/null +++ b/src/Nazara/Utility/Formats/OBJSaver.cpp @@ -0,0 +1,228 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace + { + template + class VertexCache + { + public: + VertexCache(T* ptr) : + m_count(0), + m_buffer(ptr) + { + } + + std::size_t GetCount() const + { + return m_count; + } + + std::size_t Insert(const T& data) + { + auto it = m_cache.find(data); + if (it == m_cache.end()) + { + it = m_cache.insert(std::make_pair(data, m_count)).first; + m_buffer[m_count] = data; + m_count++; + } + + return it->second + 1; + } + + private: + std::size_t m_count; + std::map m_cache; + T* m_buffer; + }; + + bool IsSupported(const String& extension) + { + return (extension == "obj"); + } + + bool SaveToStream(const Mesh& mesh, const String& format, Stream& stream, const MeshParams& parameters) + { + if (!mesh.IsValid()) + { + NazaraError("Invalid mesh"); + return false; + } + + if (mesh.IsAnimable()) + { + NazaraError("An animated mesh cannot be saved to " + format + " format"); + return false; + } + + std::size_t worstCacheVertexCount = mesh.GetVertexCount(); + OBJParser objFormat; + objFormat.SetNormalCount(worstCacheVertexCount); + objFormat.SetPositionCount(worstCacheVertexCount); + objFormat.SetTexCoordCount(worstCacheVertexCount); + + String mtlPath = stream.GetPath(); + if (!mtlPath.IsEmpty()) + { + mtlPath.Replace(".obj", ".mtl"); + String fileName = mtlPath.SubStringFrom(NAZARA_DIRECTORY_SEPARATOR, -1, true); + if (!fileName.IsEmpty()) + objFormat.SetMtlLib(fileName); + } + + VertexCache normalCache(objFormat.GetNormals()); + VertexCache positionCache(objFormat.GetPositions()); + VertexCache texCoordsCache(objFormat.GetTexCoords()); + + // Materials + MTLParser mtlFormat; + std::unordered_set registredMaterials; + + std::size_t matCount = mesh.GetMaterialCount(); + String* materialNames = objFormat.SetMaterialCount(matCount); + for (std::size_t i = 0; i < matCount; ++i) + { + const ParameterList& matData = mesh.GetMaterialData(i); + + String name; + if (!matData.GetStringParameter(MaterialData::Name, &name)) + name = "material_" + String::Number(i); + + // Makes sure we only have one material of that name + while (registredMaterials.find(name) != registredMaterials.end()) + name += '_'; + + registredMaterials.insert(name); + materialNames[i] = name; + + MTLParser::Material* material = mtlFormat.AddMaterial(name); + + String strVal; + if (matData.GetStringParameter(MaterialData::FilePath, &strVal)) + material->diffuseMap = strVal; + else + { + Color colorVal; + float fValue; + + if (matData.GetColorParameter(MaterialData::AmbientColor, &colorVal)) + material->ambient = colorVal; + + if (matData.GetColorParameter(MaterialData::DiffuseColor, &colorVal)) + material->diffuse = colorVal; + + if (matData.GetColorParameter(MaterialData::SpecularColor, &colorVal)) + material->specular = colorVal; + + if (matData.GetFloatParameter(MaterialData::Shininess, &fValue)) + material->shininess = fValue; + + if (matData.GetStringParameter(MaterialData::AlphaTexturePath, &strVal)) + material->alphaMap = strVal; + + if (matData.GetStringParameter(MaterialData::DiffuseTexturePath, &strVal)) + material->diffuseMap = strVal; + + if (matData.GetStringParameter(MaterialData::SpecularTexturePath, &strVal)) + material->specularMap = strVal; + } + } + + // Meshes + std::size_t meshCount = mesh.GetSubMeshCount(); + OBJParser::Mesh* meshes = objFormat.SetMeshCount(meshCount); + for (std::size_t i = 0; i < meshCount; ++i) + { + const StaticMesh* staticMesh = static_cast(mesh.GetSubMesh(i)); + + std::size_t triangleCount = staticMesh->GetTriangleCount(); + + meshes[i].faces.resize(triangleCount); + meshes[i].material = staticMesh->GetMaterialIndex(); + meshes[i].name = "mesh_" + String::Number(i); + meshes[i].vertices.resize(triangleCount * 3); + + { + VertexMapper vertexMapper(staticMesh); + + SparsePtr normalPtr = vertexMapper.GetComponentPtr(VertexComponent_Normal); + SparsePtr positionPtr = vertexMapper.GetComponentPtr(VertexComponent_Position); + SparsePtr texCoordsPtr = vertexMapper.GetComponentPtr(VertexComponent_TexCoord); + + std::size_t faceIndex = 0; + TriangleIterator triangle(staticMesh); + do + { + OBJParser::Face& face = meshes[i].faces[faceIndex]; + face.firstVertex = faceIndex * 3; + face.vertexCount = 3; + + for (std::size_t j = 0; j < 3; ++j) + { + OBJParser::FaceVertex& vertexIndices = meshes[i].vertices[face.firstVertex + j]; + + std::size_t index = triangle[j]; + vertexIndices.normal = normalCache.Insert(normalPtr[index]); + vertexIndices.position = positionCache.Insert(positionPtr[index]); + vertexIndices.texCoord = texCoordsCache.Insert(texCoordsPtr[index]); + } + + faceIndex++; + } + while (triangle.Advance()); + } + } + + objFormat.SetNormalCount(normalCache.GetCount()); + objFormat.SetPositionCount(positionCache.GetCount()); + objFormat.SetTexCoordCount(texCoordsCache.GetCount()); + + objFormat.Save(stream); + + if (!mtlPath.IsEmpty()) + { + File mtlFile(mtlPath, OpenMode_WriteOnly | OpenMode_Truncate); + if (mtlFile.IsOpen()) + mtlFormat.Save(mtlFile); + } + + return true; + } + } + + namespace Loaders + { + void RegisterOBJSaver() + { + MeshSaver::RegisterSaver(IsSupported, SaveToStream); + } + + void UnregisterOBJSaver() + { + MeshSaver::UnregisterSaver(IsSupported, SaveToStream); + } + } +} diff --git a/src/Nazara/Utility/Formats/OBJSaver.hpp b/src/Nazara/Utility/Formats/OBJSaver.hpp new file mode 100644 index 000000000..ed909f165 --- /dev/null +++ b/src/Nazara/Utility/Formats/OBJSaver.hpp @@ -0,0 +1,21 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_FORMATS_OBJSAVER_HPP +#define NAZARA_FORMATS_OBJSAVER_HPP + +#include + +namespace Nz +{ + namespace Loaders + { + void RegisterOBJSaver(); + void UnregisterOBJSaver(); + } +} + +#endif // NAZARA_FORMATS_OBJSAVER_HPP diff --git a/src/Nazara/Utility/Formats/STBSaver.cpp b/src/Nazara/Utility/Formats/STBSaver.cpp index 4166de86f..1b65312fe 100644 --- a/src/Nazara/Utility/Formats/STBSaver.cpp +++ b/src/Nazara/Utility/Formats/STBSaver.cpp @@ -121,7 +121,7 @@ namespace Nz ImageType type = image.GetType(); if (type != ImageType_1D && type != ImageType_2D) { - NazaraError("Image type 0x" + String::Number(type, 16) + " is not "); + NazaraError("Image type 0x" + String::Number(type, 16) + " is not in a supported format"); return false; } diff --git a/src/Nazara/Utility/GuillotineImageAtlas.cpp b/src/Nazara/Utility/GuillotineImageAtlas.cpp index 35beab566..b3e630921 100644 --- a/src/Nazara/Utility/GuillotineImageAtlas.cpp +++ b/src/Nazara/Utility/GuillotineImageAtlas.cpp @@ -70,7 +70,7 @@ namespace Nz return layer.image.get(); } - unsigned int GuillotineImageAtlas::GetLayerCount() const + std::size_t GuillotineImageAtlas::GetLayerCount() const { return m_layers.size(); } diff --git a/src/Nazara/Utility/Image.cpp b/src/Nazara/Utility/Image.cpp index 6d05b493c..4b780eef1 100644 --- a/src/Nazara/Utility/Image.cpp +++ b/src/Nazara/Utility/Image.cpp @@ -89,7 +89,7 @@ namespace Nz if (!PixelFormat::IsConversionSupported(m_sharedImage->format, newFormat)) { - NazaraError("Conversion from " + PixelFormat::ToString(m_sharedImage->format) + " to " + PixelFormat::ToString(newFormat) + " is not supported"); + NazaraError("Conversion from " + PixelFormat::GetName(m_sharedImage->format) + " to " + PixelFormat::GetName(newFormat) + " is not supported"); return false; } #endif @@ -327,7 +327,7 @@ namespace Nz std::unique_ptr colorBuffer(new UInt8[bpp]); if (!PixelFormat::Convert(PixelFormatType_RGBA8, m_sharedImage->format, &color.r, colorBuffer.get())) { - NazaraError("Failed to convert RGBA8 to " + PixelFormat::ToString(m_sharedImage->format)); + NazaraError("Failed to convert RGBA8 to " + PixelFormat::GetName(m_sharedImage->format)); return false; } @@ -405,7 +405,7 @@ namespace Nz std::unique_ptr colorBuffer(new UInt8[bpp]); if (!PixelFormat::Convert(PixelFormatType_RGBA8, m_sharedImage->format, &color.r, colorBuffer.get())) { - NazaraError("Failed to convert RGBA8 to " + PixelFormat::ToString(m_sharedImage->format)); + NazaraError("Failed to convert RGBA8 to " + PixelFormat::GetName(m_sharedImage->format)); return false; } @@ -477,7 +477,7 @@ namespace Nz std::unique_ptr colorBuffer(new UInt8[bpp]); if (!PixelFormat::Convert(PixelFormatType_RGBA8, m_sharedImage->format, &color.r, colorBuffer.get())) { - NazaraError("Failed to convert RGBA8 to " + PixelFormat::ToString(m_sharedImage->format)); + NazaraError("Failed to convert RGBA8 to " + PixelFormat::GetName(m_sharedImage->format)); return false; } @@ -666,7 +666,7 @@ namespace Nz return GetMaxLevel(m_sharedImage->type, m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth); } - unsigned int Image::GetMemoryUsage() const + std::size_t Image::GetMemoryUsage() const { unsigned int width = m_sharedImage->width; unsigned int height = m_sharedImage->height; @@ -693,7 +693,7 @@ namespace Nz return size * PixelFormat::GetBytesPerPixel(m_sharedImage->format); } - unsigned int Image::GetMemoryUsage(UInt8 level) const + std::size_t Image::GetMemoryUsage(UInt8 level) const { return PixelFormat::ComputeSize(m_sharedImage->format, GetLevelSize(m_sharedImage->width, level), GetLevelSize(m_sharedImage->height, level), ((m_sharedImage->type == ImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level))); } @@ -1352,6 +1352,15 @@ namespace Nz void Image::Copy(UInt8* destination, const UInt8* source, PixelFormatType format, unsigned int width, unsigned int height, unsigned int depth, unsigned int dstWidth, unsigned int dstHeight, unsigned int srcWidth, unsigned int srcHeight) { + #if NAZARA_UTILITY_SAFE + if (width == 0) + NazaraError("Width must be greater than zero"); + if (height == 0) + NazaraError("Height must be greater than zero"); + if (depth == 0) + NazaraError("Depth must be greater than zero"); + #endif + if (dstWidth == 0) dstWidth = width; diff --git a/src/Nazara/Utility/IndexMapper.cpp b/src/Nazara/Utility/IndexMapper.cpp index adf80a764..5a664128c 100644 --- a/src/Nazara/Utility/IndexMapper.cpp +++ b/src/Nazara/Utility/IndexMapper.cpp @@ -12,6 +12,13 @@ namespace Nz { namespace { + UInt32 GetterSequential(const void* buffer, unsigned int i) + { + NazaraUnused(buffer); + + return i; + } + UInt32 Getter16(const void* buffer, unsigned int i) { const UInt16* ptr = static_cast(buffer); @@ -42,61 +49,67 @@ namespace Nz } } - IndexMapper::IndexMapper(IndexBuffer* indexBuffer, BufferAccess access) : - m_indexCount(indexBuffer->GetIndexCount()) + IndexMapper::IndexMapper(IndexBuffer* indexBuffer, BufferAccess access, std::size_t indexCount) : + m_indexCount((indexCount != 0) ? indexCount : indexBuffer->GetIndexCount()) { - #if NAZARA_UTILITY_SAFE - if (!indexBuffer) - { - NazaraError("Index buffer must be valid"); - return; - } - #endif + NazaraAssert(indexCount != 0 || indexBuffer, "Invalid index count with invalid index buffer"); - if (!m_mapper.Map(indexBuffer, access)) - NazaraError("Failed to map buffer"); ///TODO: Unexcepted - - if (indexBuffer->HasLargeIndices()) + if (indexBuffer) { - m_getter = Getter32; - if (access != BufferAccess_ReadOnly) - m_setter = Setter32; + if (!m_mapper.Map(indexBuffer, access)) + NazaraError("Failed to map buffer"); ///TODO: Unexcepted + + if (indexBuffer->HasLargeIndices()) + { + m_getter = Getter32; + if (access != BufferAccess_ReadOnly) + m_setter = Setter32; + else + m_setter = SetterError; + } else - m_setter = SetterError; + { + m_getter = Getter16; + if (access != BufferAccess_ReadOnly) + m_setter = Setter16; + else + m_setter = SetterError; + } } else { - m_getter = Getter16; - if (access != BufferAccess_ReadOnly) - m_setter = Setter16; - else - m_setter = SetterError; + m_getter = GetterSequential; + m_setter = SetterError; } } - IndexMapper::IndexMapper(const IndexBuffer* indexBuffer, BufferAccess access) : + IndexMapper::IndexMapper(SubMesh* subMesh, BufferAccess access) : + IndexMapper(subMesh->GetIndexBuffer(), access, (subMesh->GetIndexBuffer()) ? 0 : subMesh->GetVertexCount()) + { + } + + IndexMapper::IndexMapper(const IndexBuffer* indexBuffer, BufferAccess access, std::size_t indexCount) : m_setter(SetterError), - m_indexCount(indexBuffer->GetIndexCount()) + m_indexCount((indexCount != 0) ? indexCount : indexBuffer->GetIndexCount()) { - #if NAZARA_UTILITY_SAFE - if (!indexBuffer) + NazaraAssert(indexCount != 0 || indexBuffer, "Invalid index count with invalid index buffer"); + + if (indexBuffer) { - NazaraError("Index buffer must be valid"); - return; + if (!m_mapper.Map(indexBuffer, access)) + NazaraError("Failed to map buffer"); ///TODO: Unexcepted + + if (indexBuffer->HasLargeIndices()) + m_getter = Getter32; + else + m_getter = Getter16; } - #endif - - if (!m_mapper.Map(indexBuffer, access)) - NazaraError("Failed to map buffer"); ///TODO: Unexcepted - - if (indexBuffer->HasLargeIndices()) - m_getter = Getter32; else - m_getter = Getter16; + m_getter = GetterSequential; } - IndexMapper::IndexMapper(const SubMesh* subMesh) : - IndexMapper(subMesh->GetIndexBuffer()) + IndexMapper::IndexMapper(const SubMesh* subMesh, BufferAccess access) : + IndexMapper(subMesh->GetIndexBuffer(), access, (subMesh->GetIndexBuffer()) ? 0 : subMesh->GetVertexCount()) { } diff --git a/src/Nazara/Utility/Mesh.cpp b/src/Nazara/Utility/Mesh.cpp index fca38e415..84559abda 100644 --- a/src/Nazara/Utility/Mesh.cpp +++ b/src/Nazara/Utility/Mesh.cpp @@ -39,9 +39,9 @@ namespace Nz return false; } - if (scale == Vector3f::Zero()) + if (matrix == Matrix4f::Zero()) { - NazaraError("Invalid scale"); + NazaraError("Invalid matrix"); return false; } @@ -111,7 +111,7 @@ namespace Nz VertexBufferRef vertexBuffer; Matrix4f matrix(primitive.matrix); - matrix.ApplyScale(params.scale); + matrix *= params.matrix; VertexDeclaration* declaration = VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent); @@ -606,6 +606,16 @@ namespace Nz InvalidateAABB(); } + bool Mesh::SaveToFile(const String& filePath, const MeshParams& params) + { + return MeshSaver::SaveToFile(*this, filePath, params); + } + + bool Mesh::SaveToStream(Stream& stream, const String& format, const MeshParams& params) + { + return MeshSaver::SaveToStream(*this, stream, format, params); + } + void Mesh::SetAnimation(const String& animationPath) { NazaraAssert(m_impl, "Mesh should be created first"); @@ -697,4 +707,5 @@ namespace Nz MeshLoader::LoaderList Mesh::s_loaders; MeshManager::ManagerMap Mesh::s_managerMap; MeshManager::ManagerParams Mesh::s_managerParameters; + MeshSaver::SaverList Mesh::s_savers; } diff --git a/src/Nazara/Utility/PixelFormat.cpp b/src/Nazara/Utility/PixelFormat.cpp index 8c571d62e..a9b740b8f 100644 --- a/src/Nazara/Utility/PixelFormat.cpp +++ b/src/Nazara/Utility/PixelFormat.cpp @@ -48,7 +48,7 @@ namespace Nz NazaraUnused(dst); NazaraUnused(end); - NazaraInternalError("Conversion from " + PixelFormat::ToString(from) + " to " + PixelFormat::ToString(to) + " is not supported"); + NazaraInternalError("Conversion from " + PixelFormat::GetName(from) + " to " + PixelFormat::GetName(to) + " is not supported"); return nullptr; } @@ -1270,15 +1270,13 @@ namespace Nz PixelFormatType PixelFormat::IdentifyFormat(const PixelFormatInfo& info) { - switch (info.bitsPerPixel) + for (unsigned int i = 0; i <= PixelFormatType_Max; ++i) { - case 32: - if (info.redMask == Bitset<>(0xFF000000) && - info.greenMask == Bitset<>(0x00FF0000) && - info.blueMask == Bitset<>(0x0000FF00) && - info.alphaMask == Bitset<>(0x000000FF)) - return PixelFormatType_RGBA8; - break; + PixelFormatInfo& info2 = s_pixelFormatInfos[i]; + if (info.bitsPerPixel == info2.bitsPerPixel && info.content == info2.content && + info.redMask == info2.redMask && info.greenMask == info2.greenMask && info.blueMask == info2.blueMask && info.alphaMask == info2.alphaMask && + info.redType == info2.redType && info.greenType == info2.greenType && info.blueType == info2.blueType && info.alphaType == info2.alphaType) + return static_cast(i); } return PixelFormatType_Undefined; @@ -1286,7 +1284,67 @@ namespace Nz bool PixelFormat::Initialize() { - // Réinitialisation + // Setup informations about every pixel format + s_pixelFormatInfos[PixelFormatType_A8] = PixelFormatInfo("A8", PixelFormatContent_ColorRGBA, 0, 0, 0, 0xFF, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_BGR8] = PixelFormatInfo("BGR8", PixelFormatContent_ColorRGBA, 0x0000FF, 0x00FF00, 0xFF0000, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_BGRA8] = PixelFormatInfo("BGRA8", PixelFormatContent_ColorRGBA, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_DXT1] = PixelFormatInfo("DXT1", PixelFormatContent_ColorRGBA, 8, PixelFormatSubType_Compressed); + s_pixelFormatInfos[PixelFormatType_DXT3] = PixelFormatInfo("DXT3", PixelFormatContent_ColorRGBA, 16, PixelFormatSubType_Compressed); + s_pixelFormatInfos[PixelFormatType_DXT5] = PixelFormatInfo("DXT5", PixelFormatContent_ColorRGBA, 16, PixelFormatSubType_Compressed); + s_pixelFormatInfos[PixelFormatType_L8] = PixelFormatInfo("L8", PixelFormatContent_ColorRGBA, 0xFF, 0xFF, 0xFF, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_LA8] = PixelFormatInfo("LA8", PixelFormatContent_ColorRGBA, 0xFF00, 0xFF00, 0xFF00, 0x00FF, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_R8] = PixelFormatInfo("R8", PixelFormatContent_ColorRGBA, 0xFF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_R8I] = PixelFormatInfo("R8I", PixelFormatContent_ColorRGBA, 0xFF, 0, 0, 0, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_R8UI] = PixelFormatInfo("R8UI", PixelFormatContent_ColorRGBA, 0xFF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_R16] = PixelFormatInfo("R16", PixelFormatContent_ColorRGBA, 0xFFFF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_R16F] = PixelFormatInfo("R16F", PixelFormatContent_ColorRGBA, 0xFFFF, 0, 0, 0, PixelFormatSubType_Half); + s_pixelFormatInfos[PixelFormatType_R16I] = PixelFormatInfo("R16I", PixelFormatContent_ColorRGBA, 0xFFFF, 0, 0, 0, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_R16UI] = PixelFormatInfo("R16UI", PixelFormatContent_ColorRGBA, 0xFFFF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_R32F] = PixelFormatInfo("R32F", PixelFormatContent_ColorRGBA, 0xFFFFFFFF, 0, 0, 0, PixelFormatSubType_Float); + s_pixelFormatInfos[PixelFormatType_R32I] = PixelFormatInfo("R32I", PixelFormatContent_ColorRGBA, 0xFFFFFFFF, 0, 0, 0, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_R32UI] = PixelFormatInfo("R32UI", PixelFormatContent_ColorRGBA, 0xFFFFFFFF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RG8] = PixelFormatInfo("RG8", PixelFormatContent_ColorRGBA, 0xFF00, 0x00FF, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RG8I] = PixelFormatInfo("RG8I", PixelFormatContent_ColorRGBA, 0xFF00, 0x00FF, 0, 0, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_RG8UI] = PixelFormatInfo("RG8UI", PixelFormatContent_ColorRGBA, 0xFF00, 0x00FF, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RG16] = PixelFormatInfo("RG16", PixelFormatContent_ColorRGBA, 0xFFFF0000, 0x0000FFFF, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RG16F] = PixelFormatInfo("RG16F", PixelFormatContent_ColorRGBA, 0xFFFF0000, 0x0000FFFF, 0, 0, PixelFormatSubType_Half); + s_pixelFormatInfos[PixelFormatType_RG16I] = PixelFormatInfo("RG16I", PixelFormatContent_ColorRGBA, 0xFFFF0000, 0x0000FFFF, 0, 0, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_RG16UI] = PixelFormatInfo("RG16UI", PixelFormatContent_ColorRGBA, 0xFFFF0000, 0x0000FFFF, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RG32F] = PixelFormatInfo("RG32F", PixelFormatContent_ColorRGBA, 0xFFFFFFFF00000000, 0x00000000FFFFFFFF, 0, 0, PixelFormatSubType_Float); + s_pixelFormatInfos[PixelFormatType_RG32I] = PixelFormatInfo("RG32I", PixelFormatContent_ColorRGBA, 0xFFFFFFFF00000000, 0x00000000FFFFFFFF, 0, 0, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_RG32UI] = PixelFormatInfo("RG32UI", PixelFormatContent_ColorRGBA, 0xFFFFFFFF00000000, 0x00000000FFFFFFFF, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RGB8] = PixelFormatInfo("RGB8", PixelFormatContent_ColorRGBA, 0xFF000000, 0x00FF0000, 0x0000FF00, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RGB16F] = PixelFormatInfo("RGB16F", PixelFormatContent_ColorRGBA, 0xFFFF00000000, 0x0000FFFF0000, 0x00000000FFFF, 0, PixelFormatSubType_Half); + s_pixelFormatInfos[PixelFormatType_RGB16I] = PixelFormatInfo("RGB16I", PixelFormatContent_ColorRGBA, 0xFFFF00000000, 0x0000FFFF0000, 0x00000000FFFF, 0, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_RGB16UI] = PixelFormatInfo("RGB16UI", PixelFormatContent_ColorRGBA, 0xFFFF000000000000, 0x0000FFFF00000000, 0x00000000FFFF0000, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RGB32F] = PixelFormatInfo("RGB32F", PixelFormatContent_ColorRGBA, 0, 0, 0, 0, PixelFormatSubType_Half); + s_pixelFormatInfos[PixelFormatType_RGB32I] = PixelFormatInfo("RGB32I", PixelFormatContent_ColorRGBA, 0, 0, 0, 0, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_RGB32UI] = PixelFormatInfo("RGB32UI", PixelFormatContent_ColorRGBA, 0, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RGBA4] = PixelFormatInfo("RGBA4", PixelFormatContent_ColorRGBA, 0xF000, 0x0F00, 0x00F0, 0x000F, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RGB5A1] = PixelFormatInfo("RGB5A1", PixelFormatContent_ColorRGBA, 0xF800, 0x07C0, 0x003E, 0x0001, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RGBA8] = PixelFormatInfo("RGBA8", PixelFormatContent_ColorRGBA, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RGBA16F] = PixelFormatInfo("RGBA16F", PixelFormatContent_ColorRGBA, 0xFFFF000000000000, 0x0000FFFF00000000, 0x00000000FFFF0000, 0x000000000000FFFF, PixelFormatSubType_Half); + s_pixelFormatInfos[PixelFormatType_RGBA16I] = PixelFormatInfo("RGBA16I", PixelFormatContent_ColorRGBA, 0xFFFF000000000000, 0x0000FFFF00000000, 0x00000000FFFF0000, 0x000000000000FFFF, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_RGBA16UI] = PixelFormatInfo("RGBA16UI", PixelFormatContent_ColorRGBA, 0xFFFF000000000000, 0x0000FFFF00000000, 0x00000000FFFF0000, 0x000000000000FFFF, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_RGBA32F] = PixelFormatInfo("RGBA32F", PixelFormatContent_ColorRGBA, 0, 0, 0, 0, PixelFormatSubType_Half); + s_pixelFormatInfos[PixelFormatType_RGBA32I] = PixelFormatInfo("RGBA32I", PixelFormatContent_ColorRGBA, 0, 0, 0, 0, PixelFormatSubType_Int); + s_pixelFormatInfos[PixelFormatType_RGBA32UI] = PixelFormatInfo("RGBA32UI", PixelFormatContent_ColorRGBA, 0, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_Depth16] = PixelFormatInfo("Depth16", PixelFormatContent_DepthStencil, 0xFFFF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_Depth24] = PixelFormatInfo("Depth24", PixelFormatContent_DepthStencil, 0xFFFFFF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_Depth24Stencil8] = PixelFormatInfo("Depth24Stencil8", PixelFormatContent_DepthStencil, 0xFFFFFF00, 0x000000FF, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_Depth32] = PixelFormatInfo("Depth32", PixelFormatContent_DepthStencil, 0xFFFFFFFF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_Stencil1] = PixelFormatInfo("Stencil1", PixelFormatContent_Stencil, 0x1, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_Stencil4] = PixelFormatInfo("Stencil4", PixelFormatContent_Stencil, 0xF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_Stencil8] = PixelFormatInfo("Stencil8", PixelFormatContent_Stencil, 0xFF, 0, 0, 0, PixelFormatSubType_Unsigned); + s_pixelFormatInfos[PixelFormatType_Stencil16] = PixelFormatInfo("Stencil16", PixelFormatContent_Stencil, 0xFFFF, 0, 0, 0, PixelFormatSubType_Unsigned); + + for (unsigned int i = 0; i <= PixelFormatType_Max; ++i) + { + if (!s_pixelFormatInfos[i].Validate()) + NazaraWarning("Pixel format 0x" + String::Number(i, 16) + " (" + GetName(static_cast(i)) + ") failed validation tests"); + } + + // Reset functions std::memset(s_convertFunctions, 0, (PixelFormatType_Max+1)*(PixelFormatType_Max+1)*sizeof(PixelFormat::ConvertFunction)); /***********************************A8************************************/ @@ -1511,12 +1569,16 @@ namespace Nz void PixelFormat::Uninitialize() { + for (unsigned int i = 0; i <= PixelFormatType_Max; ++i) + s_pixelFormatInfos[i].Clear(); + std::memset(s_convertFunctions, 0, (PixelFormatType_Max+1)*(PixelFormatType_Max+1)*sizeof(PixelFormat::ConvertFunction)); for (unsigned int i = 0; i <= PixelFlipping_Max; ++i) s_flipFunctions[i].clear(); } + PixelFormatInfo PixelFormat::s_pixelFormatInfos[PixelFormatType_Max + 1]; PixelFormat::ConvertFunction PixelFormat::s_convertFunctions[PixelFormatType_Max+1][PixelFormatType_Max+1]; std::map PixelFormat::s_flipFunctions[PixelFlipping_Max+1]; } diff --git a/src/Nazara/Utility/SimpleTextDrawer.cpp b/src/Nazara/Utility/SimpleTextDrawer.cpp index ae2dc8762..03a95f35a 100644 --- a/src/Nazara/Utility/SimpleTextDrawer.cpp +++ b/src/Nazara/Utility/SimpleTextDrawer.cpp @@ -72,19 +72,19 @@ namespace Nz return m_font; } - Font* SimpleTextDrawer::GetFont(unsigned int index) const + Font* SimpleTextDrawer::GetFont(std::size_t index) const { NazaraAssert(index == 0, "Font index out of range"); return m_font; } - unsigned int SimpleTextDrawer::GetFontCount() const + std::size_t SimpleTextDrawer::GetFontCount() const { return 1; } - const AbstractTextDrawer::Glyph& SimpleTextDrawer::GetGlyph(unsigned int index) const + const AbstractTextDrawer::Glyph& SimpleTextDrawer::GetGlyph(std::size_t index) const { if (!m_glyphUpdated) UpdateGlyphs(); @@ -94,7 +94,7 @@ namespace Nz return m_glyphs[index]; } - unsigned int SimpleTextDrawer::GetGlyphCount() const + std::size_t SimpleTextDrawer::GetGlyphCount() const { if (!m_glyphUpdated) UpdateGlyphs(); diff --git a/src/Nazara/Utility/TriangleIterator.cpp b/src/Nazara/Utility/TriangleIterator.cpp index 0c6830c87..53086ddb8 100644 --- a/src/Nazara/Utility/TriangleIterator.cpp +++ b/src/Nazara/Utility/TriangleIterator.cpp @@ -17,12 +17,19 @@ namespace Nz m_triangleIndices[1] = m_indexMapper.Get(1); m_triangleIndices[2] = m_indexMapper.Get(2); - m_indexCount = indexBuffer->GetIndexCount(); + m_indexCount = m_indexMapper.GetIndexCount(); } - TriangleIterator::TriangleIterator(SubMesh* subMesh) : - TriangleIterator(subMesh->GetPrimitiveMode(), subMesh->GetIndexBuffer()) + TriangleIterator::TriangleIterator(const SubMesh* subMesh) : + m_primitiveMode(subMesh->GetPrimitiveMode()), + m_indexMapper(subMesh, BufferAccess_ReadOnly) { + m_currentIndex = 3; + m_triangleIndices[0] = m_indexMapper.Get(0); + m_triangleIndices[1] = m_indexMapper.Get(1); + m_triangleIndices[2] = m_indexMapper.Get(2); + + m_indexCount = m_indexMapper.GetIndexCount(); } bool TriangleIterator::Advance() diff --git a/src/Nazara/Utility/Utility.cpp b/src/Nazara/Utility/Utility.cpp index 78553ce54..6b2f66353 100644 --- a/src/Nazara/Utility/Utility.cpp +++ b/src/Nazara/Utility/Utility.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -124,11 +125,13 @@ namespace Nz Loaders::RegisterMD5Anim(); // Loader de fichiers .md5anim (v10) // Mesh (text) - Loaders::RegisterOBJ(); + Loaders::RegisterOBJLoader(); + Loaders::RegisterOBJSaver(); // Mesh Loaders::RegisterMD2(); // Loader de fichiers .md2 (v8) Loaders::RegisterMD5Mesh(); // Loader de fichiers .md5mesh (v10) + Loaders::RegisterOBJLoader(); // Loader de fichiers .md5mesh (v10) // Image Loaders::RegisterPCX(); // Loader de fichiers .pcx (1, 4, 8, 24 bits) @@ -162,7 +165,8 @@ namespace Nz Loaders::UnregisterMD2(); Loaders::UnregisterMD5Anim(); Loaders::UnregisterMD5Mesh(); - Loaders::UnregisterOBJ(); + Loaders::UnregisterOBJLoader(); + Loaders::UnregisterOBJSaver(); Loaders::UnregisterPCX(); Loaders::UnregisterSTBLoader(); Loaders::UnregisterSTBSaver(); diff --git a/src/Nazara/Utility/VertexDeclaration.cpp b/src/Nazara/Utility/VertexDeclaration.cpp index 998f12f8b..945888406 100644 --- a/src/Nazara/Utility/VertexDeclaration.cpp +++ b/src/Nazara/Utility/VertexDeclaration.cpp @@ -22,9 +22,9 @@ namespace Nz VertexDeclaration::VertexDeclaration(const VertexDeclaration& declaration) : RefCounted(), + m_components(declaration.m_components), m_stride(declaration.m_stride) { - std::memcpy(m_components, declaration.m_components, sizeof(Component)*(VertexComponent_Max+1)); } VertexDeclaration::~VertexDeclaration() @@ -133,7 +133,7 @@ namespace Nz VertexDeclaration& VertexDeclaration::operator=(const VertexDeclaration& declaration) { - std::memcpy(m_components, declaration.m_components, sizeof(Component)*(VertexComponent_Max+1)); + m_components = declaration.m_components; m_stride = declaration.m_stride; return *this; @@ -141,13 +141,7 @@ namespace Nz VertexDeclaration* VertexDeclaration::Get(VertexLayout layout) { - #ifdef NAZARA_DEBUG - if (layout > VertexLayout_Max) - { - NazaraError("Vertex layout out of enum"); - return nullptr; - } - #endif + NazaraAssert(layout <= VertexLayout_Max, "Vertex layout out of enum"); return &s_declarations[layout]; } @@ -301,6 +295,6 @@ namespace Nz VertexDeclarationLibrary::Uninitialize(); } - VertexDeclaration VertexDeclaration::s_declarations[VertexLayout_Max+1]; + std::array VertexDeclaration::s_declarations; VertexDeclarationLibrary::LibraryMap VertexDeclaration::s_library; } diff --git a/src/Nazara/Utility/VertexMapper.cpp b/src/Nazara/Utility/VertexMapper.cpp index 1edbdc7f8..158dae801 100644 --- a/src/Nazara/Utility/VertexMapper.cpp +++ b/src/Nazara/Utility/VertexMapper.cpp @@ -47,6 +47,42 @@ namespace Nz ErrorFlags flags(ErrorFlag_ThrowException, true); m_mapper.Map(vertexBuffer, access); } + + VertexMapper::VertexMapper(const SubMesh* subMesh, BufferAccess access) + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + + const VertexBuffer* buffer = nullptr; + switch (subMesh->GetAnimationType()) + { + case AnimationType_Skeletal: + { + const SkeletalMesh* skeletalMesh = static_cast(subMesh); + buffer = skeletalMesh->GetVertexBuffer(); + break; + } + + case AnimationType_Static: + { + const StaticMesh* staticMesh = static_cast(subMesh); + buffer = staticMesh->GetVertexBuffer(); + break; + } + } + + if (!buffer) + { + NazaraInternalError("Animation type not handled (0x" + String::Number(subMesh->GetAnimationType(), 16) + ')'); + } + + m_mapper.Map(buffer, access); + } + + VertexMapper::VertexMapper(const VertexBuffer* vertexBuffer, BufferAccess access) + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + m_mapper.Map(vertexBuffer, access); + } VertexMapper::~VertexMapper() = default; diff --git a/src/Nazara/Utility/Win32/WindowImpl.cpp b/src/Nazara/Utility/Win32/WindowImpl.cpp index 2bf9c9d02..b494a7789 100644 --- a/src/Nazara/Utility/Win32/WindowImpl.cpp +++ b/src/Nazara/Utility/Win32/WindowImpl.cpp @@ -394,7 +394,7 @@ namespace Nz void WindowImpl::SetMaximumSize(int width, int height) { RECT rect = {0, 0, width, height}; - AdjustWindowRect(&rect, GetWindowLongPtr(m_handle, GWL_STYLE), false); + AdjustWindowRect(&rect, static_cast(GetWindowLongPtr(m_handle, GWL_STYLE)), false); if (width != -1) m_maxSize.x = rect.right-rect.left; @@ -410,7 +410,7 @@ namespace Nz void WindowImpl::SetMinimumSize(int width, int height) { RECT rect = {0, 0, width, height}; - AdjustWindowRect(&rect, GetWindowLongPtr(m_handle, GWL_STYLE), false); + AdjustWindowRect(&rect, static_cast(GetWindowLongPtr(m_handle, GWL_STYLE)), false); if (width != -1) m_minSize.x = rect.right-rect.left; @@ -432,7 +432,7 @@ namespace Nz { // SetWindowPos demande la taille totale de la fenêtre RECT rect = {0, 0, static_cast(width), static_cast(height)}; - AdjustWindowRect(&rect, GetWindowLongPtr(m_handle, GWL_STYLE), false); + AdjustWindowRect(&rect, static_cast(GetWindowLongPtr(m_handle, GWL_STYLE)), false); SetWindowPos(m_handle, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER); } diff --git a/src/Nazara/Vulkan/Debug/NewOverload.cpp b/src/Nazara/Vulkan/Debug/NewOverload.cpp new file mode 100644 index 000000000..410a53c76 --- /dev/null +++ b/src/Nazara/Vulkan/Debug/NewOverload.cpp @@ -0,0 +1,31 @@ +// Copyright (C) 2014 AUTHORS +// This file is part of the "Nazara Engine - Module name" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#if NAZARA_VULKAN_MANAGE_MEMORY + +#include +#include // Nécessaire ? + +void* operator new(std::size_t size) +{ + return Nz::MemoryManager::Allocate(size, false); +} + +void* operator new[](std::size_t size) +{ + return Nz::MemoryManager::Allocate(size, true); +} + +void operator delete(void* pointer) noexcept +{ + Nz::MemoryManager::Free(pointer, false); +} + +void operator delete[](void* pointer) noexcept +{ + Nz::MemoryManager::Free(pointer, true); +} + +#endif // NAZARA_VULKAN_MANAGE_MEMORY diff --git a/src/Nazara/Vulkan/VkCommandPool.cpp b/src/Nazara/Vulkan/VkCommandPool.cpp new file mode 100644 index 000000000..3ee53ab6f --- /dev/null +++ b/src/Nazara/Vulkan/VkCommandPool.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + CommandBuffer CommandPool::AllocateCommandBuffer(VkCommandBufferLevel level) + { + VkCommandBufferAllocateInfo createInfo = + { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + nullptr, + m_handle, + level, + 1U + }; + + VkCommandBuffer handle = VK_NULL_HANDLE; + m_lastErrorCode = m_device.vkAllocateCommandBuffers(m_device, &createInfo, &handle); + + return CommandBuffer(*this, handle); + } + + std::vector CommandPool::AllocateCommandBuffers(UInt32 commandBufferCount, VkCommandBufferLevel level) + { + VkCommandBufferAllocateInfo createInfo = + { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + nullptr, + m_handle, + level, + 1U + }; + + std::vector handles(commandBufferCount, VK_NULL_HANDLE); + m_lastErrorCode = m_device.vkAllocateCommandBuffers(m_device, &createInfo, handles.data()); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + return std::vector(); + + std::vector commandBuffers; + for (UInt32 i = 0; i < commandBufferCount; ++i) + commandBuffers.emplace_back(CommandBuffer(*this, handles[i])); + + return commandBuffers; + } + } +} diff --git a/src/Nazara/Vulkan/VkDevice.cpp b/src/Nazara/Vulkan/VkDevice.cpp new file mode 100644 index 000000000..c2f39da76 --- /dev/null +++ b/src/Nazara/Vulkan/VkDevice.cpp @@ -0,0 +1,178 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + bool Device::Create(VkPhysicalDevice device, const VkDeviceCreateInfo& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = m_instance.vkCreateDevice(device, &createInfo, allocator, &m_device); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to create Vulkan device"); + return false; + } + + // Store the allocator to access them when needed + if (allocator) + m_allocator = *allocator; + else + m_allocator.pfnAllocation = nullptr; + + // Parse extensions and layers + for (UInt32 i = 0; i < createInfo.enabledExtensionCount; ++i) + m_loadedExtensions.insert(createInfo.ppEnabledExtensionNames[i]); + + for (UInt32 i = 0; i < createInfo.enabledLayerCount; ++i) + m_loadedLayers.insert(createInfo.ppEnabledLayerNames[i]); + + #define NAZARA_VULKAN_LOAD_DEVICE(func) func = reinterpret_cast(GetProcAddr(#func)) + + try + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + + NAZARA_VULKAN_LOAD_DEVICE(vkAllocateCommandBuffers); + NAZARA_VULKAN_LOAD_DEVICE(vkAllocateMemory); + NAZARA_VULKAN_LOAD_DEVICE(vkBeginCommandBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkBindBufferMemory); + NAZARA_VULKAN_LOAD_DEVICE(vkBindImageMemory); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdBeginQuery); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdBeginRenderPass); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdBindDescriptorSets); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdBindIndexBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdBindPipeline); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdBindVertexBuffers); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdBlitImage); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdClearAttachments); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdClearColorImage); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdClearDepthStencilImage); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdCopyBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdCopyBufferToImage); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdCopyImage); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdCopyImageToBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdCopyQueryPoolResults); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdDispatch); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdDispatchIndirect); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdDraw); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdDrawIndexed); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdDrawIndexedIndirect); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdDrawIndirect); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdEndQuery); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdEndRenderPass); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdExecuteCommands); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdFillBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdNextSubpass); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdPipelineBarrier); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdPushConstants); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdResetEvent); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdResetQueryPool); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdResolveImage); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetBlendConstants); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetDepthBias); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetDepthBounds); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetEvent); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetLineWidth); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetScissor); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetStencilCompareMask); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetStencilReference); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetStencilWriteMask); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdSetViewport); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdUpdateBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdWaitEvents); + NAZARA_VULKAN_LOAD_DEVICE(vkCmdWriteTimestamp); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateBufferView); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateCommandPool); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateComputePipelines); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateDescriptorPool); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateDescriptorSetLayout); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateEvent); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateFramebuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateGraphicsPipelines); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateImage); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateImageView); + NAZARA_VULKAN_LOAD_DEVICE(vkCreatePipelineLayout); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateRenderPass); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateSampler); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateSemaphore); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateShaderModule); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyBufferView); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyCommandPool); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyDescriptorPool); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyDescriptorSetLayout); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyDevice); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyEvent); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyFramebuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyImage); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyImageView); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyPipeline); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyPipelineLayout); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyRenderPass); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroySampler); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroySemaphore); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroyShaderModule); + NAZARA_VULKAN_LOAD_DEVICE(vkDeviceWaitIdle); + NAZARA_VULKAN_LOAD_DEVICE(vkEndCommandBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkFreeCommandBuffers); + NAZARA_VULKAN_LOAD_DEVICE(vkFreeDescriptorSets); + NAZARA_VULKAN_LOAD_DEVICE(vkFreeMemory); + NAZARA_VULKAN_LOAD_DEVICE(vkFlushMappedMemoryRanges); + NAZARA_VULKAN_LOAD_DEVICE(vkGetBufferMemoryRequirements); + NAZARA_VULKAN_LOAD_DEVICE(vkGetDeviceMemoryCommitment); + NAZARA_VULKAN_LOAD_DEVICE(vkGetDeviceQueue); + NAZARA_VULKAN_LOAD_DEVICE(vkGetEventStatus); + NAZARA_VULKAN_LOAD_DEVICE(vkGetFenceStatus); + NAZARA_VULKAN_LOAD_DEVICE(vkGetImageMemoryRequirements); + NAZARA_VULKAN_LOAD_DEVICE(vkGetImageSparseMemoryRequirements); + NAZARA_VULKAN_LOAD_DEVICE(vkGetImageSubresourceLayout); + NAZARA_VULKAN_LOAD_DEVICE(vkGetRenderAreaGranularity); + NAZARA_VULKAN_LOAD_DEVICE(vkInvalidateMappedMemoryRanges); + NAZARA_VULKAN_LOAD_DEVICE(vkMapMemory); + NAZARA_VULKAN_LOAD_DEVICE(vkMergePipelineCaches); + NAZARA_VULKAN_LOAD_DEVICE(vkQueueSubmit); + NAZARA_VULKAN_LOAD_DEVICE(vkQueueWaitIdle); + NAZARA_VULKAN_LOAD_DEVICE(vkResetCommandBuffer); + NAZARA_VULKAN_LOAD_DEVICE(vkResetCommandPool); + NAZARA_VULKAN_LOAD_DEVICE(vkResetDescriptorPool); + NAZARA_VULKAN_LOAD_DEVICE(vkResetFences); + NAZARA_VULKAN_LOAD_DEVICE(vkResetEvent); + NAZARA_VULKAN_LOAD_DEVICE(vkSetEvent); + NAZARA_VULKAN_LOAD_DEVICE(vkUnmapMemory); + NAZARA_VULKAN_LOAD_DEVICE(vkUpdateDescriptorSets); + NAZARA_VULKAN_LOAD_DEVICE(vkWaitForFences); + + // VK_KHR_display_swapchain + if (IsExtensionLoaded("VK_KHR_display_swapchain")) + NAZARA_VULKAN_LOAD_DEVICE(vkCreateSharedSwapchainsKHR); + + // VK_KHR_swapchain + if (IsExtensionLoaded("VK_KHR_swapchain")) + { + NAZARA_VULKAN_LOAD_DEVICE(vkAcquireNextImageKHR); + NAZARA_VULKAN_LOAD_DEVICE(vkCreateSwapchainKHR); + NAZARA_VULKAN_LOAD_DEVICE(vkDestroySwapchainKHR); + NAZARA_VULKAN_LOAD_DEVICE(vkGetSwapchainImagesKHR); + NAZARA_VULKAN_LOAD_DEVICE(vkQueuePresentKHR); + } + } + catch (const std::exception& e) + { + NazaraError(String("Failed to query device function: ") + e.what()); + return false; + } + + #undef NAZARA_VULKAN_LOAD_DEVICE + + return true; + } + } +} diff --git a/src/Nazara/Vulkan/VkInstance.cpp b/src/Nazara/Vulkan/VkInstance.cpp new file mode 100644 index 000000000..35378a699 --- /dev/null +++ b/src/Nazara/Vulkan/VkInstance.cpp @@ -0,0 +1,193 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + bool Instance::Create(const VkInstanceCreateInfo& createInfo, const VkAllocationCallbacks* allocator) + { + m_lastErrorCode = Loader::vkCreateInstance(&createInfo, allocator, &m_instance); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to create Vulkan instance"); + return false; + } + + // Store the allocator to access them when needed + if (allocator) + m_allocator = *allocator; + else + m_allocator.pfnAllocation = nullptr; + + // Parse extensions and layers + for (UInt32 i = 0; i < createInfo.enabledExtensionCount; ++i) + m_loadedExtensions.insert(createInfo.ppEnabledExtensionNames[i]); + + for (UInt32 i = 0; i < createInfo.enabledLayerCount; ++i) + m_loadedLayers.insert(createInfo.ppEnabledLayerNames[i]); + + // And now load everything + #define NAZARA_VULKAN_LOAD_INSTANCE(func) func = reinterpret_cast(GetProcAddr(#func)) + + try + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + + // Vulkan core + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateDevice); + NAZARA_VULKAN_LOAD_INSTANCE(vkDestroyInstance); + NAZARA_VULKAN_LOAD_INSTANCE(vkEnumeratePhysicalDevices); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetDeviceProcAddr); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceFeatures); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceFormatProperties); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceImageFormatProperties); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceMemoryProperties); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceProperties); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceQueueFamilyProperties); + + // VK_KHR_display + if (IsExtensionLoaded("VK_KHR_display")) + { + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateDisplayModeKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateDisplayPlaneSurfaceKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetDisplayModePropertiesKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetDisplayPlaneCapabilitiesKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetDisplayPlaneSupportedDisplaysKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceDisplayPlanePropertiesKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceDisplayPropertiesKHR); + } + + // VK_KHR_surface + if (IsExtensionLoaded("VK_KHR_surface")) + { + NAZARA_VULKAN_LOAD_INSTANCE(vkDestroySurfaceKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceSurfaceFormatsKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceSurfacePresentModesKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceSurfaceSupportKHR); + } + + // VK_EXT_debug_report + if (IsExtensionLoaded("VK_EXT_debug_report")) + { + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateDebugReportCallbackEXT); + NAZARA_VULKAN_LOAD_INSTANCE(vkDestroyDebugReportCallbackEXT); + NAZARA_VULKAN_LOAD_INSTANCE(vkDebugReportMessageEXT); + } + + #ifdef VK_USE_PLATFORM_ANDROID_KHR + // VK_KHR_android_surface + if (IsExtensionLoaded("VK_KHR_android_surface")) + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateAndroidSurfaceKHR); + #endif + + #ifdef VK_USE_PLATFORM_MIR_KHR + // VK_KHR_mir_surface + if (IsExtensionLoaded("VK_KHR_mir_surface")) + { + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateMirSurfaceKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceMirPresentationSupportKHR); + } + #endif + + #ifdef VK_USE_PLATFORM_XCB_KHR + // VK_KHR_xcb_surface + if (IsExtensionLoaded("VK_KHR_xcb_surface")) + { + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateXcbSurfaceKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceXcbPresentationSupportKHR); + } + #endif + + #ifdef VK_USE_PLATFORM_XLIB_KHR + // VK_KHR_xlib_surface + if (IsExtensionLoaded("VK_KHR_xlib_surface")) + { + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateXlibSurfaceKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceXlibPresentationSupportKHR); + } + #endif + + #ifdef VK_USE_PLATFORM_WAYLAND_KHR + // VK_KHR_wayland_surface + if (IsExtensionLoaded("VK_KHR_wayland_surface")) + { + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateWaylandSurfaceKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceWaylandPresentationSupportKHR); + } + #endif + + #ifdef VK_USE_PLATFORM_WIN32_KHR + // VK_KHR_win32_surface + if (IsExtensionLoaded("VK_KHR_win32_surface")) + { + NAZARA_VULKAN_LOAD_INSTANCE(vkCreateWin32SurfaceKHR); + NAZARA_VULKAN_LOAD_INSTANCE(vkGetPhysicalDeviceWin32PresentationSupportKHR); + } + #endif + } + catch (const std::exception& e) + { + NazaraError(String("Failed to query instance function: ") + e.what()); + return false; + } + + #undef NAZARA_VULKAN_LOAD_INSTANCE + + return true; + } + + bool Instance::EnumeratePhysicalDevices(std::vector* devices) + { + NazaraAssert(devices, "Invalid device vector"); + + // First, query physical device count + UInt32 deviceCount = 0; // Remember, Nz::UInt32 is a typedef on uint32_t + m_lastErrorCode = vkEnumeratePhysicalDevices(m_instance, &deviceCount, nullptr); + if (m_lastErrorCode != VkResult::VK_SUCCESS || deviceCount == 0) + { + NazaraError("Failed to query physical device count"); + return false; + } + + // Now we can get the list of the available physical device + devices->resize(deviceCount); + m_lastErrorCode = vkEnumeratePhysicalDevices(m_instance, &deviceCount, devices->data()); + if (m_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to query physical devices"); + return false; + } + + return true; + } + + bool Instance::GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice device, std::vector* queueFamilyProperties) + { + NazaraAssert(queueFamilyProperties, "Invalid device vector"); + + // First, query physical device count + UInt32 queueFamiliesCount = 0; // Remember, Nz::UInt32 is a typedef on uint32_t + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamiliesCount, nullptr); + if (queueFamiliesCount == 0) + { + NazaraError("Failed to query physical device count"); + return false; + } + + // Now we can get the list of the available physical device + queueFamilyProperties->resize(queueFamiliesCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamiliesCount, queueFamilyProperties->data()); + + return true; + } + + } +} diff --git a/src/Nazara/Vulkan/VkLoader.cpp b/src/Nazara/Vulkan/VkLoader.cpp new file mode 100644 index 000000000..c9d2a360d --- /dev/null +++ b/src/Nazara/Vulkan/VkLoader.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + namespace Vk + { + bool Loader::EnumerateInstanceExtensionProperties(std::vector* properties, const char* layerName) + { + NazaraAssert(properties, "Invalid device vector"); + + // First, query physical device count + UInt32 propertyCount = 0; // Remember, Nz::UInt32 is a typedef on uint32_t + s_lastErrorCode = vkEnumerateInstanceExtensionProperties(layerName, &propertyCount, properties->data()); + if (s_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to get instance extension properties count"); + return false; + } + + // Now we can get the list of the available physical device + properties->resize(propertyCount); + s_lastErrorCode = vkEnumerateInstanceExtensionProperties(layerName, &propertyCount, properties->data()); + if (s_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to enumerate instance extension properties"); + return false; + } + + return true; + } + + bool Loader::EnumerateInstanceLayerProperties(std::vector* properties) + { + NazaraAssert(properties, "Invalid device vector"); + + // First, query physical device count + UInt32 propertyCount = 0; // Remember, Nz::UInt32 is a typedef on uint32_t + s_lastErrorCode = vkEnumerateInstanceLayerProperties(&propertyCount, properties->data()); + if (s_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to get instance layer properties count"); + return false; + } + + // Now we can get the list of the available physical device + properties->resize(propertyCount); + s_lastErrorCode = vkEnumerateInstanceLayerProperties(&propertyCount, properties->data()); + if (s_lastErrorCode != VkResult::VK_SUCCESS) + { + NazaraError("Failed to enumerate instance layer properties"); + return false; + } + + return true; + } + + bool Loader::Initialize() + { + #ifdef NAZARA_PLATFORM_WINDOWS + s_vulkanLib.Load("vulkan-1.dll"); + #elif defined(NAZARA_PLATFORM_LINUX) + s_vulkanLib.Load("libvulkan.so"); + #else + #error Unhandled platform + #endif + + if (!s_vulkanLib.IsLoaded()) + { + NazaraError("Failed to open vulkan library: " + s_vulkanLib.GetLastError()); + return false; + } + + // vkGetInstanceProcAddr is the only function that's garantee to be exported + vkGetInstanceProcAddr = reinterpret_cast(s_vulkanLib.GetSymbol("vkGetInstanceProcAddr")); + if (!vkGetInstanceProcAddr) + { + NazaraError("Failed to get symbol \"vkGetInstanceProcAddr\": " + s_vulkanLib.GetLastError()); + return false; + } + + // all other functions should be loaded using vkGetInstanceProcAddr + #define NAZARA_VULKAN_LOAD_GLOBAL(func) func = reinterpret_cast(vkGetInstanceProcAddr(nullptr, #func)) + + NAZARA_VULKAN_LOAD_GLOBAL(vkCreateInstance); + NAZARA_VULKAN_LOAD_GLOBAL(vkEnumerateInstanceExtensionProperties); + NAZARA_VULKAN_LOAD_GLOBAL(vkEnumerateInstanceLayerProperties); + + #undef NAZARA_VULKAN_LOAD_GLOBAL + + s_lastErrorCode = VkResult::VK_SUCCESS; + + return true; + } + + #define NAZARA_VULKAN_GLOBAL_FUNCTION_IMPL(func) PFN_##func Loader::func = nullptr + + NAZARA_VULKAN_GLOBAL_FUNCTION_IMPL(vkCreateInstance); + NAZARA_VULKAN_GLOBAL_FUNCTION_IMPL(vkEnumerateInstanceExtensionProperties); + NAZARA_VULKAN_GLOBAL_FUNCTION_IMPL(vkEnumerateInstanceLayerProperties); + NAZARA_VULKAN_GLOBAL_FUNCTION_IMPL(vkGetInstanceProcAddr); + + #undef NAZARA_VULKAN_GLOBAL_FUNCTION_IMPL + + DynLib Loader::s_vulkanLib; + VkResult Loader::s_lastErrorCode; + + void Loader::Uninitialize() + { + s_vulkanLib.Unload(); + } + } +} diff --git a/src/Nazara/Vulkan/Vulkan.cpp b/src/Nazara/Vulkan/Vulkan.cpp new file mode 100644 index 000000000..00bb55337 --- /dev/null +++ b/src/Nazara/Vulkan/Vulkan.cpp @@ -0,0 +1,70 @@ +// Copyright (C) 2016 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + bool Vulkan::Initialize() + { + if (s_moduleReferenceCounter > 0) + { + s_moduleReferenceCounter++; + return true; // Already initialized + } + + // Initialize module dependencies + if (!Utility::Initialize()) + { + NazaraError("Failed to initialize utility module"); + return false; + } + + s_moduleReferenceCounter++; + + CallOnExit onExit(Vulkan::Uninitialize); + + // Initialize module here + + onExit.Reset(); + + NazaraNotice("Initialized: Vulkan module"); + return true; + } + + bool Vulkan::IsInitialized() + { + return s_moduleReferenceCounter != 0; + } + + void Vulkan::Uninitialize() + { + if (s_moduleReferenceCounter != 1) + { + // Either the module is not initialized, either it was initialized multiple times + if (s_moduleReferenceCounter > 1) + s_moduleReferenceCounter--; + + return; + } + + s_moduleReferenceCounter = 0; + + // Uninitialize module here + + NazaraNotice("Uninitialized: Vulkan module"); + + // Free module dependencies + Utility::Uninitialize(); + } + + unsigned int Vulkan::s_moduleReferenceCounter = 0; +} + diff --git a/tests/Engine/Audio/AlgorithmAudio.cpp b/tests/Engine/Audio/AlgorithmAudio.cpp new file mode 100644 index 000000000..dfcfeee56 --- /dev/null +++ b/tests/Engine/Audio/AlgorithmAudio.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include + +TEST_CASE("MixToMono", "[AUDIO][ALGORITHM]") +{ + SECTION("Mix two channels together") + { + std::array input{ 1, 3, 5, 3 }; + std::array output{ 0, 0 }; + + // Two channels and two frames ! + Nz::MixToMono(input.data(), output.data(), 2, 2); + + std::array theoric{ 2, 4 }; // It's the mean of the two channels + REQUIRE(output == theoric); + } +} diff --git a/tests/Engine/Audio/Music.cpp b/tests/Engine/Audio/Music.cpp new file mode 100644 index 000000000..404c57c5f --- /dev/null +++ b/tests/Engine/Audio/Music.cpp @@ -0,0 +1,49 @@ +#include +#include + +#include +#include + +SCENARIO("Music", "[AUDIO][MUSIC]") +{ + GIVEN("A music") + { + Nz::Music music; + + WHEN("We load our music") + { + REQUIRE(music.OpenFromFile("resources/Engine/Audio/The_Brabanconne.ogg")); + + THEN("We can ask the informations of the file") + { + REQUIRE(music.GetDuration() <= 64000); // 1 min 03 = 63s = 63000ms + REQUIRE(music.GetDuration() >= 63000); + REQUIRE(music.GetFormat() == Nz::AudioFormat_Stereo); + REQUIRE(music.GetPlayingOffset() == 0); + REQUIRE(music.GetSampleCount() <= 5644800); // 64s * 44100 Hz * 2 (stereo) + REQUIRE(music.GetSampleCount() >= 5556600); // 63s * 44100 Hz * 2 (stereo) + REQUIRE(music.GetSampleRate() == 44100 /* Hz */); + REQUIRE(music.GetStatus() == Nz::SoundStatus_Stopped); + REQUIRE(music.IsLooping() == false); + } + + THEN("We can play it and get the time offset") + { + Nz::Audio::SetGlobalVolume(0.f); + + music.Play(); + Nz::Thread::Sleep(1000); + REQUIRE(music.GetPlayingOffset() >= 950); + Nz::Thread::Sleep(200); + REQUIRE(music.GetPlayingOffset() <= 1300); + music.Pause(); + REQUIRE(music.GetStatus() == Nz::SoundStatus_Paused); + + music.SetPlayingOffset(3500); + REQUIRE(music.GetPlayingOffset() >= 3500); + + Nz::Audio::SetGlobalVolume(100.f); + } + } + } +} diff --git a/tests/Engine/Audio/Sound.cpp b/tests/Engine/Audio/Sound.cpp new file mode 100644 index 000000000..684138ccd --- /dev/null +++ b/tests/Engine/Audio/Sound.cpp @@ -0,0 +1,44 @@ +#include +#include + +#include +#include + +SCENARIO("Sound", "[AUDIO][SOUND]") +{ + GIVEN("A sound") + { + Nz::Sound sound; + + WHEN("We load our sound") + { + REQUIRE(sound.LoadFromFile("resources/Engine/Audio/Cat.flac")); + + THEN("We can ask the informations of the file") + { + REQUIRE(sound.GetDuration() <= 8500); // 8s = 8000ms + REQUIRE(sound.GetDuration() >= 8000); + REQUIRE(sound.GetStatus() == Nz::SoundStatus_Stopped); + REQUIRE(sound.IsLooping() == false); + } + + THEN("We can play it and get the time offset") + { + Nz::Audio::SetGlobalVolume(0.f); + + sound.Play(); + Nz::Thread::Sleep(1000); + REQUIRE(sound.GetPlayingOffset() >= 950); + Nz::Thread::Sleep(200); + REQUIRE(sound.GetPlayingOffset() <= 1300); + sound.Pause(); + REQUIRE(sound.GetStatus() == Nz::SoundStatus_Paused); + + sound.SetPlayingOffset(3500); + REQUIRE(sound.GetPlayingOffset() >= 3500); + + Nz::Audio::SetGlobalVolume(100.f); + } + } + } +} diff --git a/tests/Engine/Audio/SoundBuffer.cpp b/tests/Engine/Audio/SoundBuffer.cpp new file mode 100644 index 000000000..ed5f2f6c6 --- /dev/null +++ b/tests/Engine/Audio/SoundBuffer.cpp @@ -0,0 +1,21 @@ +#include +#include + +SCENARIO("SoundBuffer", "[AUDIO][SOUNDBUFFER]") +{ + GIVEN("A sound buffer") + { + Nz::SoundBuffer soundBuffer; + + WHEN("We load our sound") + { + REQUIRE(soundBuffer.LoadFromFile("resources/Engine/Audio/Cat.flac")); + + THEN("We can ask the informations of the file") + { + REQUIRE(soundBuffer.GetDuration() <= 8500); // 8s = 8000ms + REQUIRE(soundBuffer.GetDuration() >= 8000); + } + } + } +} diff --git a/tests/Engine/Audio/SoundEmitter.cpp b/tests/Engine/Audio/SoundEmitter.cpp new file mode 100644 index 000000000..baac41eb3 --- /dev/null +++ b/tests/Engine/Audio/SoundEmitter.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include + +SCENARIO("SoundEmitter", "[AUDIO][SOUNDEMITTER]") +{ + GIVEN("A sound emitter") + { + Nz::Sound sound; + + WHEN("We load our sound") + { + REQUIRE(sound.LoadFromFile("resources/Engine/Audio/Cat.flac")); + + THEN("We can ask information about position and velocity") + { + sound.EnableSpatialization(true); + sound.SetPosition(Nz::Vector3f::Zero()); + sound.SetVelocity(Nz::Vector3f::UnitX()); + + REQUIRE(sound.IsSpatialized()); + REQUIRE(sound.GetPosition() == Nz::Vector3f::Zero()); + REQUIRE(sound.GetVelocity() == Nz::Vector3f::UnitX()); + } + + THEN("We can ask information about attenuation, pitch, ...") + { + sound.SetAttenuation(0.4f); + sound.SetMinDistance(40.f); + sound.SetPitch(0.8f); + sound.SetVolume(50.f); + + REQUIRE(Approx(sound.GetAttenuation()) == 0.4f); + REQUIRE(Approx(sound.GetMinDistance()) == 40.f); + REQUIRE(Approx(sound.GetPitch()) == 0.8f); + REQUIRE(Approx(sound.GetVolume()) == 50.f); + } + } + } +} diff --git a/tests/Engine/Core/ByteArray.cpp b/tests/Engine/Core/ByteArray.cpp index 476643d5e..d085585a5 100644 --- a/tests/Engine/Core/ByteArray.cpp +++ b/tests/Engine/Core/ByteArray.cpp @@ -213,4 +213,24 @@ SCENARIO("ByteArray", "[CORE][BYTEARRAY]") } } } + + GIVEN("A default byte array") + { + Nz::ByteArray defaultByteArray; + + WHEN("We resize") + { + Nz::UInt64 oldSize = defaultByteArray.GetSize(); + REQUIRE(oldSize == 0); + defaultByteArray.Resize(10); + + THEN("Capacity has increased") + { + REQUIRE(defaultByteArray[oldSize] == 0); + + REQUIRE(defaultByteArray.GetCapacity() >= 10); + REQUIRE(defaultByteArray.GetSize() == 10); + } + } + } } diff --git a/tests/Engine/Core/File.cpp b/tests/Engine/Core/File.cpp index 2ddbfebbf..1163a8aad 100644 --- a/tests/Engine/Core/File.cpp +++ b/tests/Engine/Core/File.cpp @@ -32,12 +32,22 @@ SCENARIO("File", "[CORE][FILE]") REQUIRE(Nz::String(message) == "Test String"); } + AND_THEN("We can get its size") + { + REQUIRE(file.GetSize() == 33U); + } + AND_THEN("We close it") { file.Close(); - REQUIRE(file.GetSize() == 33U); CHECK(!file.IsOpen()); } + + AND_THEN("Change its size") + { + file.SetSize(50U); + REQUIRE(file.GetSize() == 50U); + } } WHEN("We delete this file") diff --git a/tests/Engine/Core/String.cpp b/tests/Engine/Core/String.cpp index eac5eaef6..fe9c80c30 100644 --- a/tests/Engine/Core/String.cpp +++ b/tests/Engine/Core/String.cpp @@ -122,5 +122,25 @@ SCENARIO("String", "[CORE][STRING]") } } }*/ + + GIVEN("A string") + { + Nz::String replaceAny("abapeilomuky"); + Nz::String replaceAnyWithCase("abapEilOmuky"); + + WHEN("We replace any of vowels after character 3") + { + unsigned int nbrOfChanges = replaceAny.ReplaceAny("aeiouy", '$', 3); + unsigned int nbrOfChangesWithCase = replaceAnyWithCase.ReplaceAny("AEIOUY", '$', 3); + + THEN("These results are expected") + { + REQUIRE(replaceAny == "abap$$l$m$k$"); + REQUIRE(nbrOfChanges == 5); + REQUIRE(replaceAnyWithCase == "abap$il$muky"); + REQUIRE(nbrOfChangesWithCase == 2); + } + } + } } diff --git a/tests/Engine/Graphics/Billboard.cpp b/tests/Engine/Graphics/Billboard.cpp new file mode 100644 index 000000000..5136e3af6 --- /dev/null +++ b/tests/Engine/Graphics/Billboard.cpp @@ -0,0 +1,34 @@ +#include +#include + +SCENARIO("Billboard", "[GRAPHICS][BILLBOARD]") +{ + GIVEN("A default billboard") + { + Nz::Billboard billboard; + + WHEN("We assign it to another") + { + Nz::MaterialRef materialRef = Nz::Material::New(); + materialRef->LoadFromFile("resources/Engine/Graphics/Nazara.png"); + Nz::Color materialColor = materialRef->GetDiffuseColor(); + Nz::BillboardRef otherBillboard = Nz::Billboard::New(materialRef); + + billboard = *otherBillboard; + + THEN("The old one has the same properties than the new one") + { + REQUIRE(billboard.GetColor() == materialColor); + REQUIRE(billboard.GetMaterial().Get() == materialRef.Get()); + REQUIRE(billboard.GetRotation() == Approx(0.f)); + REQUIRE(billboard.GetSize() == Nz::Vector2f(64.f, 64.f)); // Default sizes + } + + THEN("We set it with our new material and ask for its real size") + { + billboard.SetMaterial(materialRef, true); + REQUIRE(billboard.GetSize() == Nz::Vector2f(765.f, 212.f)); // Nazara.png sizes + } + } + } +} diff --git a/tests/Engine/Graphics/ColorBackground.cpp b/tests/Engine/Graphics/ColorBackground.cpp new file mode 100644 index 000000000..f4a88eb30 --- /dev/null +++ b/tests/Engine/Graphics/ColorBackground.cpp @@ -0,0 +1,20 @@ +#include +#include + +SCENARIO("ColorBackground", "[GRAPHICS][COLORBACKGROUND]") +{ + GIVEN("A default color background") + { + Nz::ColorBackground colorBackground; + + WHEN("We assign it a color") + { + colorBackground.SetColor(Nz::Color::Red); + + THEN("We can get it") + { + REQUIRE(colorBackground.GetColor() == Nz::Color::Red); + } + } + } +} diff --git a/tests/Engine/Graphics/DeferredRenderTechnique.cpp b/tests/Engine/Graphics/DeferredRenderTechnique.cpp new file mode 100644 index 000000000..f61c9800c --- /dev/null +++ b/tests/Engine/Graphics/DeferredRenderTechnique.cpp @@ -0,0 +1,29 @@ +#include +#include + +SCENARIO("DeferredRenderTechnique", "[GRAPHICS][DEFERREDRENDERTECHNIQUE]") +{ + GIVEN("A default deferred render technique") + { + Nz::DeferredRenderTechnique deferredRenderTechnique; + + WHEN("We can disable a pass") + { + REQUIRE(deferredRenderTechnique.IsPassEnabled(Nz::RenderPassType::RenderPassType_AA, 0)); + deferredRenderTechnique.EnablePass(Nz::RenderPassType::RenderPassType_AA, 0, false); + + THEN("It is disabled") + { + REQUIRE(!deferredRenderTechnique.IsPassEnabled(Nz::RenderPassType::RenderPassType_AA, 0)); + } + + AND_THEN("We reset it, it is disabled and not the same as the old one") + { + Nz::DeferredRenderPass* oldPass = deferredRenderTechnique.GetPass(Nz::RenderPassType::RenderPassType_AA, 0); + deferredRenderTechnique.ResetPass(Nz::RenderPassType::RenderPassType_AA, 0); + REQUIRE(!deferredRenderTechnique.IsPassEnabled(Nz::RenderPassType::RenderPassType_AA, 0)); + REQUIRE(deferredRenderTechnique.GetPass(Nz::RenderPassType::RenderPassType_AA, 0) != oldPass); + } + } + } +} diff --git a/tests/Engine/Graphics/Light.cpp b/tests/Engine/Graphics/Light.cpp new file mode 100644 index 000000000..bd5f1c7f9 --- /dev/null +++ b/tests/Engine/Graphics/Light.cpp @@ -0,0 +1,31 @@ +#include +#include + +SCENARIO("Light", "[GRAPHICS][LIGHT]") +{ + GIVEN("Different light") + { + Nz::Light directionalLight(Nz::LightType_Directional); + Nz::Light pointLight(Nz::LightType_Point); + Nz::Light spotLight(Nz::LightType_Spot); + + WHEN("We try to cull") + { + Nz::Frustumf frustum; + frustum.Build(90.f, 16.f / 9.f, 1.f, 1000.f, Nz::Vector3f::Zero(), Nz::Vector3f::UnitX()); + Nz::Matrix4f Unit3InX = Nz::Matrix4f::Translate(Nz::Vector3f::UnitX() * 3.f); + Nz::Matrix4f rotationTowardsY = Unit3InX * Nz::Matrix4f::Rotate(Nz::EulerAnglesf(Nz::FromDegrees(90.f), 0.f, 0.f).ToQuaternion()); + + THEN("These results are expected") + { + REQUIRE(directionalLight.Cull(frustum, Unit3InX)); + REQUIRE(pointLight.Cull(frustum, Unit3InX)); + REQUIRE(!spotLight.Cull(frustum, Unit3InX)); + + REQUIRE(directionalLight.Cull(frustum, rotationTowardsY)); + REQUIRE(pointLight.Cull(frustum, rotationTowardsY)); + REQUIRE(!spotLight.Cull(frustum, rotationTowardsY)); + } + } + } +} diff --git a/tests/Engine/Graphics/Model.cpp b/tests/Engine/Graphics/Model.cpp new file mode 100644 index 000000000..b363a0be2 --- /dev/null +++ b/tests/Engine/Graphics/Model.cpp @@ -0,0 +1,27 @@ +#include +#include + +SCENARIO("Model", "[GRAPHICS][MODEL]") +{ + GIVEN("The standford dragon model") + { + WHEN("We get general informations") + { + THEN("These results are expected") + { + Nz::ModelParameters params; + params.mesh.optimizeIndexBuffers = false; + + Nz::ModelRef model = Nz::Model::New(); + REQUIRE(model->LoadFromFile("resources/Engine/Graphics/dragon_recon/dragon_vrip_res4.obj", params)); + + REQUIRE(model->GetMaterialCount() == 2); + REQUIRE(model->GetSkin() == 0); + REQUIRE(model->GetSkinCount() == 1); + + Nz::Material* material = model->GetMaterial(0); + REQUIRE(material->GetAmbientColor() == Nz::Color(128)); + } + } + } +} diff --git a/tests/Engine/Graphics/ParticleDeclaration.cpp b/tests/Engine/Graphics/ParticleDeclaration.cpp new file mode 100644 index 000000000..1bc1739d3 --- /dev/null +++ b/tests/Engine/Graphics/ParticleDeclaration.cpp @@ -0,0 +1,29 @@ +#include +#include + +SCENARIO("ParticleDeclaration", "[GRAPHICS][PARTICLEDECLARATION]") +{ + GIVEN("A particle declaration of a model") + { + Nz::ParticleDeclaration* particleDeclaration = Nz::ParticleDeclaration::Get(Nz::ParticleLayout_Model); + + WHEN("We disable a component") + { + bool enabled; + Nz::ComponentType type; + unsigned int offset; + particleDeclaration->GetComponent(Nz::ParticleComponent_Position, &enabled, &type, &offset); + REQUIRE(enabled); + unsigned int oldStride = particleDeclaration->GetStride(); + + particleDeclaration->DisableComponent(Nz::ParticleComponent_Position); + REQUIRE(oldStride != particleDeclaration->GetStride()); + + THEN("We can enable it and the stride is back") + { + particleDeclaration->EnableComponent(Nz::ParticleComponent_Position, type, offset); + REQUIRE(oldStride == particleDeclaration->GetStride()); + } + } + } +} diff --git a/tests/Engine/Graphics/ParticleGroup.cpp b/tests/Engine/Graphics/ParticleGroup.cpp new file mode 100644 index 000000000..36ad2cb0b --- /dev/null +++ b/tests/Engine/Graphics/ParticleGroup.cpp @@ -0,0 +1,103 @@ +#include +#include + +#include +#include + +class TestParticleController : public Nz::ParticleController +{ + public: + // Be aware that the interval is [startId, endId] and NOT [startId, endId) + void Apply(Nz::ParticleGroup& system, Nz::ParticleMapper& mapper, unsigned int startId, unsigned int endId, float elapsedTime) override + { + Nz::SparsePtr positionPtr = mapper.GetComponentPtr(Nz::ParticleComponent_Position); + Nz::SparsePtr velocityPtr = mapper.GetComponentPtr(Nz::ParticleComponent_Velocity); + Nz::SparsePtr lifePtr = mapper.GetComponentPtr(Nz::ParticleComponent_Life); + + for (unsigned int i = startId; i <= endId; ++i) + { + Nz::Vector3f& particlePosition = positionPtr[i]; + Nz::Vector3f& particleVelocity = velocityPtr[i]; + float& particleLife = lifePtr[i]; + + particleLife -= elapsedTime; + if (particleLife <= 0.f) + system.KillParticle(i); + } + } +}; + +class TestParticleEmitter : public Nz::ParticleEmitter +{ + public: + ~TestParticleEmitter() override = default; + + void Emit(Nz::ParticleGroup& system, float elapsedTime) const override + { + system.GenerateParticles(GetEmissionCount()); + } + + private: + void SetupParticles(Nz::ParticleMapper& mapper, unsigned int count) const override + { + } +}; + +class TestParticleGenerator : public Nz::ParticleGenerator +{ + public: + ~TestParticleGenerator() override = default; + + // Be aware that the interval is [startId, endId] and NOT [startId, endId) + void Generate(Nz::ParticleGroup& system, Nz::ParticleMapper& mapper, unsigned int startId, unsigned int endId) override + { + Nz::SparsePtr positionPtr = mapper.GetComponentPtr(Nz::ParticleComponent_Position); + Nz::SparsePtr velocityPtr = mapper.GetComponentPtr(Nz::ParticleComponent_Velocity); + Nz::SparsePtr lifePtr = mapper.GetComponentPtr(Nz::ParticleComponent_Life); + + for (unsigned int i = startId; i <= endId; ++i) + { + Nz::Vector3f& particlePosition = positionPtr[i]; + Nz::Vector3f& particleVelocity = velocityPtr[i]; + float& particleLife = lifePtr[i]; + + particlePosition = Nz::Vector3f::Zero(); + particleVelocity = Nz::Vector3f::UnitX(); + particleLife = 1.3f; + } + } +}; + +SCENARIO("ParticleGroup", "[GRAPHICS][PARTICLEGROUP]") +{ + GIVEN("A particle system of maximum 10 billboards with its generators") + { + // These need to be alive longer than the particle system + TestParticleController particleController; + TestParticleGenerator particleGenerator; + Nz::ParticleGroup particleGroup(10, Nz::ParticleLayout_Billboard); + + particleGroup.AddController(&particleController); + TestParticleEmitter particleEmitter; + particleEmitter.SetEmissionCount(10); + particleGroup.AddEmitter(&particleEmitter); + + particleGroup.AddGenerator(&particleGenerator); + + WHEN("We update to generate 10 particles") + { + particleGroup.Update(1.f); + + THEN("There must be 10 particles") + { + REQUIRE(particleGroup.GetParticleCount() == 10); + } + + AND_THEN("We update to make them die") + { + particleGroup.Update(2.f); + REQUIRE(particleGroup.GetParticleCount() == 0); + } + } + } +} diff --git a/tests/Engine/Graphics/RenderTechniques.cpp b/tests/Engine/Graphics/RenderTechniques.cpp new file mode 100644 index 000000000..ac5849da9 --- /dev/null +++ b/tests/Engine/Graphics/RenderTechniques.cpp @@ -0,0 +1,47 @@ +#include +#include + +#include +#include + +SCENARIO("RenderTechniques", "[GRAPHICS][RENDERTECHNIQUES]") +{ + GIVEN("Nothing") + { + WHEN("We try to get a technique") + { + THEN("We should get it") + { + std::unique_ptr forwardByEnum(Nz::RenderTechniques::GetByEnum(Nz::RenderTechniqueType_BasicForward)); + REQUIRE(forwardByEnum->GetType() == Nz::RenderTechniqueType_BasicForward); + + std::unique_ptr forwardByIndex(Nz::RenderTechniques::GetByIndex(1)); + REQUIRE(forwardByIndex->GetType() == Nz::RenderTechniqueType_BasicForward); + + std::unique_ptr forwardByName(Nz::RenderTechniques::GetByName(Nz::RenderTechniques::ToString(Nz::RenderTechniqueType_BasicForward))); + REQUIRE(forwardByName->GetType() == Nz::RenderTechniqueType_BasicForward); + } + + THEN("We can register a render technique") + { + unsigned int previousCount = Nz::RenderTechniques::GetCount(); + + Nz::RenderTechniques::Register("test", 23, []() -> Nz::AbstractRenderTechnique* { + return new Nz::ForwardRenderTechnique; + }); + + REQUIRE(previousCount < Nz::RenderTechniques::GetCount()); + + std::unique_ptr forwardByRanking(Nz::RenderTechniques::GetByRanking(23)); + REQUIRE(forwardByRanking->GetType() == Nz::RenderTechniqueType_BasicForward); + + std::unique_ptr forwardByName(Nz::RenderTechniques::GetByName("test")); + REQUIRE(forwardByName->GetType() == Nz::RenderTechniqueType_BasicForward); + + Nz::RenderTechniques::Unregister("test"); + + REQUIRE(previousCount == Nz::RenderTechniques::GetCount()); + } + } + } +} \ No newline at end of file diff --git a/tests/Engine/Graphics/SkeletalModel.cpp b/tests/Engine/Graphics/SkeletalModel.cpp new file mode 100644 index 000000000..87d2e43f1 --- /dev/null +++ b/tests/Engine/Graphics/SkeletalModel.cpp @@ -0,0 +1,26 @@ +#include +#include + +SCENARIO("SkeletalModel", "[GRAPHICS][SKELETALMODEL]") +{ + GIVEN("A default skeletal model") + { + Nz::SkeletalModel skeletalModel; + Nz::AnimationRef animation = Nz::Animation::New(); + + WHEN("We can load the bob lamp") + { + REQUIRE(skeletalModel.LoadFromFile("resources/Engine/Graphics/Bob lamp/bob_lamp_update.md5mesh")); + REQUIRE(animation->LoadFromFile("resources/Engine/Graphics/Bob lamp/bob_lamp_update.md5anim")); + skeletalModel.SetAnimation(animation); + + THEN("We can enable its animation") + { + REQUIRE(skeletalModel.HasAnimation()); + skeletalModel.EnableAnimation(true); + skeletalModel.AdvanceAnimation(0.10f); + REQUIRE(skeletalModel.IsAnimationEnabled()); + } + } + } +} diff --git a/tests/Engine/Graphics/SkyboxBackground.cpp b/tests/Engine/Graphics/SkyboxBackground.cpp new file mode 100644 index 000000000..36ceb0db5 --- /dev/null +++ b/tests/Engine/Graphics/SkyboxBackground.cpp @@ -0,0 +1,25 @@ +#include +#include + +SCENARIO("SkyboxBackground", "[GRAPHICS][SKYBOXBACKGROUND]") +{ + GIVEN("A skybox background with a loaded image") + { + Nz::TextureRef textureRef = Nz::Texture::New(); + textureRef->LoadCubemapFromFile("resources/Engine/Graphics/skybox.png"); + Nz::SkyboxBackgroundRef skyboxBackgroundRef = Nz::SkyboxBackground::New(textureRef); + + WHEN("We assign it parameters") + { + skyboxBackgroundRef->SetMovementOffset(Nz::Vector3f::Unit()); + skyboxBackgroundRef->SetMovementScale(1.f); + + THEN("We can get it") + { + REQUIRE(skyboxBackgroundRef->GetMovementOffset() == Nz::Vector3f::Unit()); + REQUIRE(skyboxBackgroundRef->GetMovementScale() == Approx(1.f)); + REQUIRE(skyboxBackgroundRef->GetTexture().Get() == textureRef.Get()); + } + } + } +} diff --git a/tests/Engine/Graphics/TextureBackground.cpp b/tests/Engine/Graphics/TextureBackground.cpp new file mode 100644 index 000000000..33178d87f --- /dev/null +++ b/tests/Engine/Graphics/TextureBackground.cpp @@ -0,0 +1,20 @@ +#include +#include + +SCENARIO("TextureBackground", "[GRAPHICS][TEXTUREBACKGROUND]") +{ + GIVEN("A default texture background") + { + Nz::TextureRef textureRef = Nz::Texture::New(); + textureRef->LoadFromFile("resources/Engine/Graphics/skybox.png"); + Nz::TextureBackgroundRef textureBackgroundRef = Nz::TextureBackground::New(textureRef); + + WHEN("We assign it parameters") + { + THEN("We can get it") + { + REQUIRE(textureBackgroundRef->GetTexture().Get() == textureRef.Get()); + } + } + } +} \ No newline at end of file diff --git a/tests/Engine/Math/Sphere.cpp b/tests/Engine/Math/Sphere.cpp index 1820acd35..ef13ffb38 100644 --- a/tests/Engine/Math/Sphere.cpp +++ b/tests/Engine/Math/Sphere.cpp @@ -39,14 +39,14 @@ SCENARIO("Sphere", "[MATH][SPHERE]") WHEN("We ask for distance") { - THEN("These results are expected") + THEN("These results are expected because we don't take into account the border") { - REQUIRE(firstCenterAndUnit.Distance(Nz::Vector3f::UnitX()) == Approx(1.f)); - REQUIRE(firstCenterAndUnit.SquaredDistance(Nz::Vector3f::UnitX()) == Approx(1.f)); + REQUIRE(firstCenterAndUnit.Distance(Nz::Vector3f::UnitX() * 2.f) == Approx(1.f)); + REQUIRE(firstCenterAndUnit.SquaredDistance(Nz::Vector3f::UnitX() * 2.f) == Approx(1.f)); Nz::Spheref tmp(Nz::Vector3f::UnitX(), 1.f); - REQUIRE(tmp.Distance(Nz::Vector3f::UnitX() * 4.f) == Approx(3.f)); - REQUIRE(tmp.SquaredDistance(Nz::Vector3f::UnitX() * 4.f) == Approx(9.f)); + REQUIRE(tmp.Distance(Nz::Vector3f::UnitX() * 4.f) == Approx(2.f)); + REQUIRE(tmp.SquaredDistance(Nz::Vector3f::UnitX() * 4.f) == Approx(4.f)); } } @@ -84,7 +84,7 @@ SCENARIO("Sphere", "[MATH][SPHERE]") THEN("Sphere must contain it and distance should be good") { CHECK(firstCenterAndUnit.Contains(point)); - REQUIRE(firstCenterAndUnit.Distance(point) == 2.f); + REQUIRE(firstCenterAndUnit.Distance(point) == 1.f); } } diff --git a/tests/Engine/Network/IpAddress.cpp b/tests/Engine/Network/IpAddress.cpp new file mode 100644 index 000000000..8bb3e7d16 --- /dev/null +++ b/tests/Engine/Network/IpAddress.cpp @@ -0,0 +1,47 @@ +#include +#include + +SCENARIO("IpAddress", "[NETWORK][IPADDRESS]") +{ + GIVEN("Two default IpAddress") + { + Nz::IpAddress ipAddressV4; + Nz::IpAddress ipAddressV6; + + WHEN("We parse localhost") + { + Nz::String localhostIPv4 = "127.0.0.1"; + Nz::String localhostIPv6 = "::1"; + REQUIRE(ipAddressV4.BuildFromAddress(localhostIPv4.GetConstBuffer())); + REQUIRE(ipAddressV6.BuildFromAddress(localhostIPv6.GetConstBuffer())); + + THEN("It's the loop back") + { + REQUIRE(ipAddressV4.IsLoopback()); + REQUIRE(ipAddressV6.IsLoopback()); + } + } + } + + GIVEN("No IpAddress") + { + WHEN("We get the IP of Nazara") + { + std::vector hostnameInfos = Nz::IpAddress::ResolveHostname(Nz::NetProtocol_IPv4, "nazara.digitalpulsesoftware.net"); + + THEN("Result is not null") + { + REQUIRE(!hostnameInfos.empty()); + } + } + + WHEN("We convert IP to hostname") + { + Nz::IpAddress google(8, 8, 8, 8); + THEN("Google (DNS) is 8.8.8.8") + { + REQUIRE(Nz::IpAddress::ResolveAddress(google) == "google-public-dns-a.google.com"); + } + } + } +} diff --git a/tests/Engine/Network/RUdpConnection.cpp b/tests/Engine/Network/RUdpConnection.cpp new file mode 100644 index 000000000..a39100e94 --- /dev/null +++ b/tests/Engine/Network/RUdpConnection.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include + +SCENARIO("RUdpConnection", "[NETWORK][RUDPCONNECTION]") +{ + GIVEN("Two RUdpConnection, one client, one server") + { + Nz::UInt16 port = 64266; + Nz::RUdpConnection server; + REQUIRE(server.Listen(Nz::NetProtocol_IPv4, port)); + Nz::IpAddress serverIP = server.GetBoundAddress(); + REQUIRE(serverIP.IsValid()); + Nz::RUdpConnection client; + REQUIRE(client.Listen(Nz::NetProtocol_IPv4, port + 1)); + Nz::IpAddress clientIP = client.GetBoundAddress(); + REQUIRE(client.Connect(serverIP)); + REQUIRE(clientIP.IsValid()); + + WHEN("We send data from client") + { + Nz::NetPacket packet(1); + Nz::Vector3f vector123(1.f, 2.f, 3.f); + packet << vector123; + REQUIRE(client.Send(serverIP, Nz::PacketPriority_Immediate, Nz::PacketReliability_Reliable, packet)); + client.Update(); + + THEN("We should get it on the server") + { + Nz::RUdpMessage rudpMessage; + server.Update(); + REQUIRE(server.PollMessage(&rudpMessage)); + Nz::Vector3f result; + rudpMessage.data >> result; + REQUIRE(result == vector123); + } + } + } +} diff --git a/tests/Engine/Network/TCP.cpp b/tests/Engine/Network/TCP.cpp new file mode 100644 index 000000000..a613cf0c6 --- /dev/null +++ b/tests/Engine/Network/TCP.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +#include +#include + +#include + +SCENARIO("TCP", "[NETWORK][TCP]") +{ + GIVEN("Two TCP, one client, one server") + { + // Avoid reusing the same socket + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1025, 64245); + + Nz::UInt16 port = dis(gen); + Nz::TcpServer server; + server.EnableBlocking(false); + + REQUIRE(server.Listen(Nz::NetProtocol_IPv4, port) == Nz::SocketState_Bound); + Nz::IpAddress serverIP = server.GetBoundAddress(); + REQUIRE(serverIP.IsValid()); + Nz::TcpClient client; + REQUIRE(client.Connect(serverIP) == Nz::SocketState_Connecting); + Nz::IpAddress clientIP = client.GetRemoteAddress(); + REQUIRE(clientIP.IsValid()); + + Nz::TcpClient serverToClient; + REQUIRE(server.AcceptClient(&serverToClient)); + + WHEN("We send data from client") + { + Nz::NetPacket packet(1); + Nz::Vector3f vector123(1.f, 2.f, 3.f); + packet << vector123; + REQUIRE(client.SendPacket(packet)); + + THEN("We should get it on the server") + { + Nz::NetPacket resultPacket; + REQUIRE(serverToClient.ReceivePacket(&resultPacket)); + Nz::Vector3f result; + resultPacket >> result; + REQUIRE(result == vector123); + } + } + } +} diff --git a/tests/Engine/Network/UdpSocket.cpp b/tests/Engine/Network/UdpSocket.cpp new file mode 100644 index 000000000..3f199f09e --- /dev/null +++ b/tests/Engine/Network/UdpSocket.cpp @@ -0,0 +1,39 @@ +#include +#include + +#include +#include + +SCENARIO("UdpSocket", "[NETWORK][UDPSOCKET]") +{ + GIVEN("Two UdpSocket, one client, one server") + { + Nz::UInt16 port = 64256; + Nz::UdpSocket server(Nz::NetProtocol_IPv4); + REQUIRE(server.Bind(port) == Nz::SocketState_Bound); + Nz::IpAddress serverIP = server.GetBoundAddress(); + REQUIRE(serverIP.IsValid()); + Nz::UdpSocket client(Nz::NetProtocol_IPv4); + REQUIRE(client.Bind(port + 1) == Nz::SocketState_Bound); + Nz::IpAddress clientIP = client.GetBoundAddress(); + REQUIRE(clientIP.IsValid()); + + WHEN("We send data from client") + { + Nz::NetPacket packet(1); + Nz::Vector3f vector123(1.f, 2.f, 3.f); + packet << vector123; + REQUIRE(client.SendPacket(serverIP, packet)); + + THEN("We should get it on the server") + { + Nz::NetPacket resultPacket; + Nz::IpAddress fromIp; + REQUIRE(server.ReceivePacket(&resultPacket, &fromIp)); + Nz::Vector3f result; + resultPacket >> result; + REQUIRE(result == vector123); + } + } + } +} diff --git a/tests/main.cpp b/tests/main.cpp index 7be81a57e..5ba9e8e01 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,2 +1,16 @@ -#define CATCH_CONFIG_MAIN +#define CATCH_CONFIG_RUNNER #include + +#include +#include +#include +#include + +int main(int argc, char* const argv[]) +{ + Nz::Initializer modules; + + int result = Catch::Session().run(argc, argv); + + return result; +} diff --git a/tests/resources/Engine/Audio/Cat.flac.REMOVED.git-id b/tests/resources/Engine/Audio/Cat.flac.REMOVED.git-id new file mode 100644 index 000000000..a686bc57d --- /dev/null +++ b/tests/resources/Engine/Audio/Cat.flac.REMOVED.git-id @@ -0,0 +1 @@ +6993cbcca9ac596667135cb0f30bea4841178d3b \ No newline at end of file diff --git a/tests/resources/Engine/Audio/The_Brabanconne.ogg.REMOVED.git-id b/tests/resources/Engine/Audio/The_Brabanconne.ogg.REMOVED.git-id new file mode 100644 index 000000000..bfd8dbd70 --- /dev/null +++ b/tests/resources/Engine/Audio/The_Brabanconne.ogg.REMOVED.git-id @@ -0,0 +1 @@ +94b2c47c9143adbac0fb7e81df5cc87f969f7150 \ No newline at end of file diff --git a/tests/resources/Engine/Audio/copyrights.txt b/tests/resources/Engine/Audio/copyrights.txt new file mode 100644 index 000000000..7b01b7090 --- /dev/null +++ b/tests/resources/Engine/Audio/copyrights.txt @@ -0,0 +1,54 @@ +The_Brabanconne.ogg + +https://en.wikipedia.org/wiki/File:The_Brabanconne.ogg + +Original file: +The_Brabanconne.ogg ‎(Ogg Vorbis sound file, length 1 min 3 s, 378 kbps) + +Summary: + +Description: The Belgian national anthem (instrumental version) performed by the United States Navy Band. Direct link is at http://www.navyband.navy.mil/anthems/ANTHEMS/Belgium.mp3. +Date: 19 October 2004 +Source: http://www.navyband.navy.mil/anthems/national_anthems.htm +Author: United States Navy Band (rendition), uploaded to Wikimedia by Keith Lehwald + +Licencing: + +This file is a work of a sailor or employee of the U.S. Navy, taken or made as part of that person's official duties. As a work of the U.S. federal government, the image is in the public domain. +This file has been identified as being free of known restrictions under copyright law, including all related and neighboring rights. + +------------------------------------------------------------------------------------------------- + +Cat.flac: + +http://www.freesound.org/people/EARR/sounds/148013/ + +Original file: +148013__earr__angry-cat.flac (Flac sound file, length 8 s, 96000 Hz, 24 bit depth) + +Author: + +EARR + +Description: + +Slightly angry cat. She is a beautiful Siamese cat called Agostina. She is angry for the recording because i pressed his tail. + +Information about the recording and equipment: + +-Location: Living room. +-Type of acoustic environment: Small, diffuse, moderately reflective. +-Distance from sound source to microphones: Approx a few centimeters. +-Miking technique: Jecklin disk. + +-Microphones: 2 Brüel & Kjaer type 4190 capsules with type 2669L head amplifier. +-Microphone preamps: Modified Brüel & Kjaer type 5935L. +-ADC: Echo Audiofire 4. (line inputs 3 & 4). +-Recorder: Echo Audiofire 4 and Dell D630C running Samplitude 10. + +Eq: Compensation only for the response of the microphones (In this case for flat response at 60º. See Brüel & Kjaer type 4190 datasheet). +No reverb, no compression, no fx. + +Licencing: + +Creative commons diff --git a/tests/resources/Engine/Graphics/Bob lamp/Readme.txt b/tests/resources/Engine/Graphics/Bob lamp/Readme.txt new file mode 100644 index 000000000..bd605ec7d --- /dev/null +++ b/tests/resources/Engine/Graphics/Bob lamp/Readme.txt @@ -0,0 +1,24 @@ +############################# +MD5 sample mesh and animation +############################# + +INFORMATION & USAGE +=================== +File includes *.blend source files and TGA format textures for MD5 animated mesh testing. +For extensive information and usage instructions visit http://www.katsbits.com/smforum/index.php?topic=178.0 + + - bob_lamp_update.blend contain mesh and armature as would be prior to preparation for export. + - bob_lamp_update_export contains triangulated mesh ready for export. + +NOTES +===== +Included files are for use in **Blender 2.69** or above; opening files in older versions may result in errors dues to internal differences between Blender versions. +Files and media are provided "as is" without any warranties of functionality. + +COPYRIGHT & DISTRIBUTION +======================== +Copyright © 2014 KatsBits. All Rights Reserved. +For more information visit http://www.katsbits.com/ or email info@katsbits.com + +For NON-COMMERCIAL USE ONLY. This file and/or its contents and/or associated materials may not be reproduced, duplicated, distributed or otherwise 'monetised' without prior consent. +Contact info@katsbits.com or visit http://copyright.katsbits.com/ for further details regarding this material and/or distribution/copyright. diff --git a/tests/resources/Engine/Graphics/copyrights.txt b/tests/resources/Engine/Graphics/copyrights.txt new file mode 100644 index 000000000..7cd13413c --- /dev/null +++ b/tests/resources/Engine/Graphics/copyrights.txt @@ -0,0 +1,82 @@ +skybox.png + +https://commons.wikimedia.org/wiki/File:Skybox_example.png + +Original file: +Skybox example.ogg ‎(Ogg Vorbis sound file, length 1 min 3 s, 378 kbps) + +Summary: + +Description: English: An example of a skybox and how the faces can be aligned +Date: 8 June 2011 +Source: Own work +Author: Creator:Arieee + +Description: The Belgian national anthem (instrumental version) performed by the United States Navy Band. Direct link is at http://www.navyband.navy.mil/anthems/ANTHEMS/Belgium.mp3. +Date: 19 October 2004 +Source: http://www.navyband.navy.mil/anthems/national_anthems.htm +Author: United States Navy Band (rendition), uploaded to Wikimedia by Keith Lehwald + +Licencing: + +This file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license. + + You are free: + + to share – to copy, distribute and transmit the work + to remix – to adapt the work + + Under the following conditions: + + attribution – You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). + share alike – If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one. + +------------------------------------------------------------------------------------------------- + +Bob lamp + +http://www.katsbits.com/download/models/md5-example.php + +Original file: +bob_lamp_update.zip (Animated MD5 sample file) + +Summary: + +Description: Updated version of "Bob", a low-poly character mesh for general export and testing of MD5 mesh and animation - "*.md5mesh" and "*.md5anim". File and it's contents should be opened in Blender 2.69 or above to avoid compatibility issues with older versions of Blender and/or resulting MD5 exports (md5mesh & md5anim). + +File includes two versions of the source file, one 'working' - mesh is intact with surfaces in 'quad' form; and one 'prepped' (exported) - mesh has been tessilated (triangulated) for export. +Date: Januari 2014 +Source: http://www.katsbits.com/download/models/md5-example.php +Author: KatsBits + +Licencing: + +Please, refer to "Bob lamp/Readme.txt" file. + +------------------------------------------------------------------------------------------------- + +Standford dragon + +http://graphics.stanford.edu/data/3Dscanrep/ + +Original file: +dragon_recon.tar.gz (PLY files) + +Summary: + +Dragon +Source: Stanford University Computer Graphics Laboratory +Scanner: Cyberware 3030 MS + spacetime analysis +Number of scans: ~70 +Total size of scans: 2,748,318 points (about 5,500,000 triangles) +Reconstruction: vrip (conservatively decimated) +Size of reconstruction: 566,098 vertices, 1,132,830 triangles +Comments: contains numerous small holes +Date: 1996 +Source: http://graphics.stanford.edu/data/3Dscanrep/ +Author: Stanford University Computer Graphics Laboratory + +Licencing: + +Please be sure to acknowledge the source of the data and models you take from this repository. In each of the listings below, we have cited the source of the range data and reconstructed models. You are welcome to use the data and models for research purposes. You are also welcome to mirror or redistribute them for free. Finally, you may publish images made using these models, or the images on this web site, in a scholarly article or book - as long as credit is given to the Stanford Computer Graphics Laboratory. However, such models or images are not to be used for commercial purposes, nor should they appear in a product for sale (with the exception of scholarly journals or books), without our permission. +Please, refer to "dragon_recon/README" file. diff --git a/tests/resources/Engine/Graphics/dragon_recon/README b/tests/resources/Engine/Graphics/dragon_recon/README new file mode 100644 index 000000000..af64a50fe --- /dev/null +++ b/tests/resources/Engine/Graphics/dragon_recon/README @@ -0,0 +1,27 @@ + Surface Reconstructions + + Stanford Range Repository + Computer Graphics Laboratory + Stanford University + + August 4, 1996 + + +These files are the result of reconstructing a set of range images +using the "vrip" program. The first file is the high resolution +result, while the "_res*" files are decimated versions. Note that +these decimations were performed using a crude algorithm that does not +necessarily preserve mesh topology. While they are not beautiful, +they are suitable for interactive rendering. + +Note that this model is a decimated version of the original which was +constructed at the voxel resolution of 0.35 mm. The original model +has no holes in it, however, the decimated model has some holes that +we detected with software, but not by inspection. Apparently, the +decimation software introduced these holes. + +For more information, consult the web pages of the Stanford Graphics +Laboratory: + + http://www-graphics.stanford.edu +