diff --git a/.gitignore b/.gitignore index e029f58b2..d671b209b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # Nazara build examples/bin/*.exe +examples/bin/*.pdb +tests/*.exe +tests/*.pdb lib/* # Feature page @@ -21,6 +24,7 @@ build/**/*.workspace build/**/*.project # Visual Studio +build/**/*.pdb build/**/*.filters build/**/*.vcxproj build/**/*.tlog @@ -28,17 +32,17 @@ build/**/*.sln build/**/*.vcxprojResolveAssemblyReference.cache build/**/*.nativecodeanalysis.all.xml build/**/*.nativecodeanalysis.xml -lib/*.exp +build/**/*.VC.opendb # Compiled Object files build/**/*.slo build/**/*.lo build/**/*.o build/**/*.obj +build/**/*.obj.enc # Compiled Dynamic libraries build/**/*.so -lib/*.lib # Compiled Static libraries build/**/*.lai @@ -73,7 +77,6 @@ $RECYCLE.BIN/ *.ilk *.meta *.pch -*.pdb *.pgc *.pgd *.rsp diff --git a/Doxyfile b/Doxyfile index 6ab581011..7ac8e5f8c 100644 --- a/Doxyfile +++ b/Doxyfile @@ -319,7 +319,7 @@ AUTOLINK_SUPPORT = YES # diagrams that involve STL classes more complete and accurate. # The default value is: NO. -BUILTIN_STL_SUPPORT = NO +BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. @@ -412,7 +412,7 @@ TYPEDEF_HIDES_STRUCT = NO # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. -LOOKUP_CACHE_SIZE = 0 +LOOKUP_CACHE_SIZE = 5 #--------------------------------------------------------------------------- # Build related configuration options diff --git a/SDK/include/NDK/Algorithm.hpp b/SDK/include/NDK/Algorithm.hpp index 61582012d..da75f4ff7 100644 --- a/SDK/include/NDK/Algorithm.hpp +++ b/SDK/include/NDK/Algorithm.hpp @@ -20,6 +20,6 @@ namespace Ndk template bool IsSystem(S& system); } -#include +#include #endif // NDK_ALGORITHM_HPP diff --git a/SDK/include/NDK/Application.hpp b/SDK/include/NDK/Application.hpp index b35c6c7f7..5d918ef8e 100644 --- a/SDK/include/NDK/Application.hpp +++ b/SDK/include/NDK/Application.hpp @@ -8,14 +8,51 @@ #define NDK_APPLICATION_HPP #include +#include +#include +#include +#include +#include namespace Ndk { class NDK_API Application { public: - Application(); - ~Application(); + inline Application(); + Application(const Application&) = delete; + Application(Application&&) = delete; + inline ~Application(); + + #ifndef NDK_SERVER + template T& AddWindow(Args&&... args); + #endif + template World& AddWorld(Args&&... args); + + inline float GetUpdateTime() const; + + bool Run(); + + inline void Quit(); + + Application& operator=(const Application&) = delete; + Application& operator=(Application&&) = delete; + + inline static Application* Instance(); + + private: + #ifndef NDK_SERVER + std::vector> m_windows; + #endif + std::list m_worlds; + Nz::Clock m_updateClock; + #ifndef NDK_SERVER + bool m_exitOnClosedWindows; + #endif + bool m_shouldQuit; + float m_updateTime; + + static Application* s_application; }; } diff --git a/SDK/include/NDK/Application.inl b/SDK/include/NDK/Application.inl index dafbd54d8..f44ff9e8c 100644 --- a/SDK/include/NDK/Application.inl +++ b/SDK/include/NDK/Application.inl @@ -3,12 +3,21 @@ // For conditions of distribution and use, see copyright notice in Prerequesites.hpp #include +#include #include namespace Ndk { - inline Application::Application() + inline Application::Application() : + #ifndef NDK_SERVER + m_exitOnClosedWindows(true), + #endif + m_shouldQuit(false), + m_updateTime(0.f) { + NazaraAssert(s_application == nullptr, "You can create only one application instance per program"); + s_application = this; + Nz::ErrorFlags errFlags(Nz::ErrorFlag_ThrowException, true); // Initialisation du SDK @@ -17,9 +26,48 @@ namespace Ndk inline Application::~Application() { + m_worlds.clear(); + #ifndef NDK_SERVER + m_windows.clear(); + #endif + // Libération du SDK Sdk::Uninitialize(); // Libération automatique des modules + s_application = nullptr; + } + + #ifndef NDK_SERVER + template + T& Application::AddWindow(Args&&... args) + { + static_assert(std::is_base_of::value, "Type must inherit Window"); + + m_windows.emplace_back(new T(std::forward(args)...)); + return static_cast(*m_windows.back().get()); + } + #endif + + template + World& Application::AddWorld(Args&&... args) + { + m_worlds.emplace_back(std::forward(args)...); + return m_worlds.back(); + } + + inline float Application::GetUpdateTime() const + { + return m_updateTime; + } + + inline void Application::Quit() + { + m_shouldQuit = true; + } + + inline Application* Application::Instance() + { + return s_application; } } diff --git a/SDK/include/NDK/BaseComponent.hpp b/SDK/include/NDK/BaseComponent.hpp index be738b737..f045ae2a6 100644 --- a/SDK/include/NDK/BaseComponent.hpp +++ b/SDK/include/NDK/BaseComponent.hpp @@ -7,7 +7,7 @@ #ifndef NDK_BASECOMPONENT_HPP #define NDK_BASECOMPONENT_HPP -#include +#include #include #include #include @@ -31,6 +31,8 @@ namespace Ndk ComponentIndex GetIndex() const; + inline static ComponentIndex GetMaxComponentIndex(); + BaseComponent& operator=(const BaseComponent&) = default; BaseComponent& operator=(BaseComponent&&) = default; diff --git a/SDK/include/NDK/BaseComponent.inl b/SDK/include/NDK/BaseComponent.inl index b8637b457..71f8ee2c1 100644 --- a/SDK/include/NDK/BaseComponent.inl +++ b/SDK/include/NDK/BaseComponent.inl @@ -2,6 +2,7 @@ // 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 @@ -17,6 +18,11 @@ namespace Ndk return m_componentIndex; } + inline ComponentIndex BaseComponent::GetMaxComponentIndex() + { + return static_cast(s_entries.size()); + } + inline ComponentIndex BaseComponent::RegisterComponent(ComponentId id, Factory factoryFunc) { // Nous allons rajouter notre composant à la fin diff --git a/SDK/include/NDK/BaseSystem.hpp b/SDK/include/NDK/BaseSystem.hpp index 0cb66b3b4..8e828149d 100644 --- a/SDK/include/NDK/BaseSystem.hpp +++ b/SDK/include/NDK/BaseSystem.hpp @@ -8,7 +8,7 @@ #define NDK_BASESYSTEM_HPP #include -#include +#include #include namespace Ndk @@ -71,7 +71,7 @@ namespace Ndk inline void RemoveEntity(Entity* entity); - inline void SetWorld(World& world); + inline void SetWorld(World* world) noexcept; inline void ValidateEntity(Entity* entity, bool justAdded); diff --git a/SDK/include/NDK/BaseSystem.inl b/SDK/include/NDK/BaseSystem.inl index da1f04d15..0c2811ac0 100644 --- a/SDK/include/NDK/BaseSystem.inl +++ b/SDK/include/NDK/BaseSystem.inl @@ -60,14 +60,16 @@ namespace Ndk { if (m_updateRate > 0.f) { - m_updateCounter -= elapsedTime; - if (m_updateCounter >= 0.f) - return; + m_updateCounter += elapsedTime; - m_updateCounter += m_updateRate; + while (m_updateCounter >= m_updateRate) + { + OnUpdate(m_updateRate); + m_updateCounter -= m_updateRate; + } } - - OnUpdate(elapsedTime); + else + OnUpdate(elapsedTime); } template @@ -172,9 +174,9 @@ namespace Ndk OnEntityValidation(entity, justAdded); } - inline void BaseSystem::SetWorld(World& world) + inline void BaseSystem::SetWorld(World* world) noexcept { - m_world = &world; + m_world = world; } inline bool BaseSystem::Initialize() @@ -186,6 +188,6 @@ namespace Ndk inline void BaseSystem::Uninitialize() { - // Rien à faire + // Nothing to do } } diff --git a/SDK/include/NDK/Component.inl b/SDK/include/NDK/Component.inl index b016fd8be..e39091224 100644 --- a/SDK/include/NDK/Component.inl +++ b/SDK/include/NDK/Component.inl @@ -2,7 +2,7 @@ // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequesites.hpp -#include +#include #include namespace Ndk diff --git a/SDK/include/NDK/Components.hpp b/SDK/include/NDK/Components.hpp new file mode 100644 index 000000000..46392ab7c --- /dev/null +++ b/SDK/include/NDK/Components.hpp @@ -0,0 +1,20 @@ +// This file was automatically generated on 03 Mar 2016 at 14:09:12 + +#pragma once + +#ifndef NDK_COMPONENTS_GLOBAL_HPP +#define NDK_COMPONENTS_GLOBAL_HPP + +#include +#include +#include +#include + +#ifndef NDK_SERVER +#include +#include +#include +#include +#endif + +#endif // NDK_COMPONENTS_GLOBAL_HPP diff --git a/SDK/include/NDK/Components/CameraComponent.hpp b/SDK/include/NDK/Components/CameraComponent.hpp index 4eea340e8..d5951109b 100644 --- a/SDK/include/NDK/Components/CameraComponent.hpp +++ b/SDK/include/NDK/Components/CameraComponent.hpp @@ -33,24 +33,27 @@ namespace Ndk inline void EnsureViewMatrixUpdate() const; inline void EnsureViewportUpdate() const; - inline float GetAspectRatio() const; - inline Nz::Vector3f GetEyePosition() const; - inline Nz::Vector3f GetForward() const; + inline float GetAspectRatio() const override; + inline Nz::Vector3f GetEyePosition() const override; + inline Nz::Vector3f GetForward() const override; inline float GetFOV() const; - inline const Nz::Frustumf& GetFrustum() const; + inline const Nz::Frustumf& GetFrustum() const override; inline unsigned int GetLayer() const; - inline const Nz::Matrix4f& GetProjectionMatrix() const; + inline const Nz::Matrix4f& GetProjectionMatrix() const override; inline Nz::ProjectionType GetProjectionType() const; - inline const Nz::RenderTarget* GetTarget() const; + inline const Nz::Vector2f& GetSize() const; + inline const Nz::RenderTarget* GetTarget() const override; inline const Nz::Rectf& GetTargetRegion() const; - inline const Nz::Matrix4f& GetViewMatrix() const; - inline const Nz::Recti& GetViewport() const; - inline float GetZFar() const; - inline float GetZNear() const; + inline const Nz::Matrix4f& GetViewMatrix() const override; + inline const Nz::Recti& GetViewport() const override; + inline float GetZFar() const override; + inline float GetZNear() const override; inline void SetFOV(float fov); inline void SetLayer(unsigned int layer); inline void SetProjectionType(Nz::ProjectionType projection); + inline void SetSize(const Nz::Vector2f& size); + inline void SetSize(float width, float height); inline void SetTarget(const Nz::RenderTarget* renderTarget); inline void SetTargetRegion(const Nz::Rectf& region); inline void SetViewport(const Nz::Recti& viewport); @@ -89,6 +92,7 @@ namespace Ndk Nz::Rectf m_targetRegion; mutable Nz::Recti m_viewport; const Nz::RenderTarget* m_target; + Nz::Vector2f m_size; mutable bool m_frustumUpdated; mutable bool m_projectionMatrixUpdated; mutable bool m_viewMatrixUpdated; diff --git a/SDK/include/NDK/Components/CameraComponent.inl b/SDK/include/NDK/Components/CameraComponent.inl index 73e5b1cc3..7ff31d623 100644 --- a/SDK/include/NDK/Components/CameraComponent.inl +++ b/SDK/include/NDK/Components/CameraComponent.inl @@ -4,12 +4,14 @@ #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_frustumUpdated(false), m_projectionMatrixUpdated(false), @@ -28,6 +30,7 @@ namespace Ndk AbstractViewer(camera), m_projectionType(camera.m_projectionType), m_targetRegion(camera.m_targetRegion), + m_size(camera.m_size), m_target(nullptr), m_frustumUpdated(false), m_projectionMatrixUpdated(false), @@ -102,6 +105,11 @@ namespace Ndk return m_projectionType; } + inline const Nz::Vector2f & CameraComponent::GetSize() const + { + return m_size; + } + inline const Nz::RenderTarget* CameraComponent::GetTarget() const { return m_target; @@ -151,6 +159,18 @@ namespace Ndk InvalidateProjectionMatrix(); } + inline void CameraComponent::SetSize(const Nz::Vector2f& size) + { + m_size = size; + + InvalidateProjectionMatrix(); + } + + inline void CameraComponent::SetSize(float width, float height) + { + SetSize({width, height}); + } + inline void CameraComponent::SetTarget(const Nz::RenderTarget* renderTarget) { m_target = renderTarget; diff --git a/SDK/include/NDK/Components/GraphicsComponent.hpp b/SDK/include/NDK/Components/GraphicsComponent.hpp index 4a3ee80c9..f8fa2bf66 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.hpp +++ b/SDK/include/NDK/Components/GraphicsComponent.hpp @@ -13,7 +13,11 @@ namespace Ndk { - class NDK_API GraphicsComponent : public Component + class GraphicsComponent; + + using GraphicsComponentHandle = Nz::ObjectHandle; + + class NDK_API GraphicsComponent : public Component, public Nz::HandledObject { friend class RenderSystem; @@ -26,11 +30,15 @@ namespace Ndk inline void Attach(Nz::InstancedRenderableRef renderable, int renderOrder = 0); + inline void EnsureBoundingVolumeUpdate() const; inline void EnsureTransformMatrixUpdate() 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); inline void InvalidateRenderables(); inline void InvalidateTransformMatrix(); @@ -41,6 +49,7 @@ namespace Ndk void OnDetached() override; void OnNodeInvalidated(const Nz::Node* node); + void UpdateBoundingVolume() const; void UpdateTransformMatrix() const; NazaraSlot(Nz::Node, OnNodeInvalidation, m_nodeInvalidationSlot); @@ -61,7 +70,9 @@ namespace Ndk }; std::vector m_renderables; + mutable Nz::BoundingVolumef m_boundingVolume; mutable Nz::Matrix4f m_transformMatrix; + mutable bool m_boundingVolumeUpdated; mutable bool m_transformMatrixUpdated; }; } diff --git a/SDK/include/NDK/Components/GraphicsComponent.inl b/SDK/include/NDK/Components/GraphicsComponent.inl index b51bf94f7..205eb9462 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.inl +++ b/SDK/include/NDK/Components/GraphicsComponent.inl @@ -8,7 +8,9 @@ namespace Ndk { inline GraphicsComponent::GraphicsComponent(const GraphicsComponent& graphicsComponent) : Component(graphicsComponent), + m_boundingVolume(graphicsComponent.m_boundingVolume), m_transformMatrix(graphicsComponent.m_transformMatrix), + m_boundingVolumeUpdated(graphicsComponent.m_boundingVolumeUpdated), m_transformMatrixUpdated(graphicsComponent.m_transformMatrixUpdated) { m_renderables.reserve(graphicsComponent.m_renderables.size()); @@ -39,6 +41,14 @@ 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::EnsureBoundingVolumeUpdate() const + { + if (!m_boundingVolumeUpdated) + UpdateBoundingVolume(); } inline void GraphicsComponent::EnsureTransformMatrixUpdate() const @@ -47,6 +57,18 @@ namespace Ndk UpdateTransformMatrix(); } + inline const Nz::BoundingVolumef& GraphicsComponent::GetBoundingVolume() const + { + EnsureBoundingVolumeUpdate(); + + return m_boundingVolume; + } + + inline void GraphicsComponent::InvalidateBoundingVolume() + { + m_boundingVolumeUpdated = false; + } + inline void GraphicsComponent::InvalidateRenderables() { for (Renderable& r : m_renderables) @@ -55,6 +77,7 @@ namespace Ndk inline void GraphicsComponent::InvalidateTransformMatrix() { + m_boundingVolumeUpdated = false; m_transformMatrixUpdated = false; InvalidateRenderables(); diff --git a/SDK/include/NDK/Components/NodeComponent.hpp b/SDK/include/NDK/Components/NodeComponent.hpp index 1e6eee879..c2a4eeb83 100644 --- a/SDK/include/NDK/Components/NodeComponent.hpp +++ b/SDK/include/NDK/Components/NodeComponent.hpp @@ -13,8 +13,11 @@ namespace Ndk { class Entity; + class NodeComponent; - class NDK_API NodeComponent : public Component, public Nz::Node + using NodeComponentHandle = Nz::ObjectHandle; + + class NDK_API NodeComponent : public Component, public Nz::Node, public Nz::HandledObject { public: NodeComponent() = default; diff --git a/SDK/include/NDK/Components/VelocityComponent.hpp b/SDK/include/NDK/Components/VelocityComponent.hpp index 28d317a2c..33d97b030 100644 --- a/SDK/include/NDK/Components/VelocityComponent.hpp +++ b/SDK/include/NDK/Components/VelocityComponent.hpp @@ -13,8 +13,11 @@ namespace Ndk { class Entity; + class VelocityComponent; - class NDK_API VelocityComponent : public Component + using VelocityComponentHandle = Nz::ObjectHandle; + + class NDK_API VelocityComponent : public Component, public Nz::HandledObject { public: VelocityComponent(const Nz::Vector3f& velocity = Nz::Vector3f::Zero()); diff --git a/SDK/include/NDK/Console.hpp b/SDK/include/NDK/Console.hpp new file mode 100644 index 000000000..78caac3f2 --- /dev/null +++ b/SDK/include/NDK/Console.hpp @@ -0,0 +1,101 @@ +// 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_CONSOLE_HPP +#define NDK_CONSOLE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class LuaInstance; +} + +namespace Ndk +{ + class Console; + class Entity; + + using ConsoleHandle = Nz::ObjectHandle; + + class NDK_API Console : public Nz::Node, public Nz::HandledObject + { + public: + Console(World& world, const Nz::Vector2f& size, Nz::LuaInstance& instance); + Console(const Console& console) = delete; + Console(Console&& console) = default; + ~Console() = default; + + void AddLine(const Nz::String& text, const Nz::Color& color = Nz::Color::White); + + void Clear(); + + inline unsigned int GetCharacterSize() const; + inline const EntityHandle& GetHistory() const; + inline const EntityHandle& GetHistoryBackground() const; + inline const EntityHandle& GetInput() const; + inline const EntityHandle& GetInputBackground() const; + inline const Nz::Vector2f& GetSize() const; + inline const Nz::FontRef& GetTextFont() const; + + inline bool IsVisible() const; + + void SendCharacter(char32_t character); + void SendEvent(Nz::WindowEvent event); + + void SetCharacterSize(unsigned int size); + void SetSize(const Nz::Vector2f& size); + void SetTextFont(Nz::FontRef font); + + void Show(bool show = true); + + Console& operator=(const Console& console) = delete; + Console& operator=(Console&& console) = default; + + private: + void AddLineInternal(const Nz::String& text, const Nz::Color& color = Nz::Color::White); + void ExecuteInput(); + void Layout(); + void RefreshHistory(); + + struct Line + { + Nz::Color color; + Nz::String text; + }; + + std::size_t m_historyPosition; + std::vector m_commandHistory; + std::vector m_historyLines; + EntityOwner m_historyBackground; + EntityOwner m_history; + EntityOwner m_input; + EntityOwner m_inputBackground; + Nz::FontRef m_defaultFont; + Nz::LuaInstance& m_instance; + Nz::SpriteRef m_historyBackgroundSprite; + Nz::SpriteRef m_inputBackgroundSprite; + Nz::SimpleTextDrawer m_historyDrawer; + Nz::SimpleTextDrawer m_inputDrawer; + Nz::TextSpriteRef m_historyTextSprite; + Nz::TextSpriteRef m_inputTextSprite; + Nz::Vector2f m_size; + bool m_opened; + unsigned int m_characterSize; + unsigned int m_maxHistoryLines; + }; +} + +#include + +#endif // NDK_CONSOLE_HPP diff --git a/SDK/include/NDK/Console.inl b/SDK/include/NDK/Console.inl new file mode 100644 index 000000000..5fb16bb1b --- /dev/null +++ b/SDK/include/NDK/Console.inl @@ -0,0 +1,49 @@ +// 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 "Console.hpp" + +namespace Ndk +{ + inline unsigned int Console::GetCharacterSize() const + { + return m_characterSize; + } + + inline const EntityHandle& Console::GetHistory() const + { + return m_history; + } + + inline const EntityHandle& Console::GetHistoryBackground() const + { + return m_historyBackground; + } + + inline const EntityHandle& Console::GetInput() const + { + return m_input; + } + + inline const EntityHandle& Console::GetInputBackground() const + { + return m_inputBackground; + } + + inline const Nz::Vector2f& Console::GetSize() const + { + return m_size; + } + + inline const Nz::FontRef& Console::GetTextFont() const + { + return m_defaultFont; + } + + inline bool Console::IsVisible() const + { + return m_opened; + } +} diff --git a/SDK/include/NDK/Entity.hpp b/SDK/include/NDK/Entity.hpp index f2febee51..6069a13c0 100644 --- a/SDK/include/NDK/Entity.hpp +++ b/SDK/include/NDK/Entity.hpp @@ -8,6 +8,7 @@ #define NDK_ENTITY_HPP #include +#include #include #include #include @@ -15,13 +16,14 @@ namespace Ndk { class BaseComponent; - class EntityHandle; + class Entity; class World; - class NDK_API Entity + using EntityHandle = Nz::ObjectHandle; + + class NDK_API Entity : public Nz::HandledObject { friend class BaseSystem; - friend EntityHandle; friend World; public: @@ -32,7 +34,7 @@ namespace Ndk BaseComponent& AddComponent(std::unique_ptr&& component); template ComponentType& AddComponent(Args&&... args); - EntityHandle CreateHandle(); + inline void Enable(bool enable); inline BaseComponent& GetComponent(ComponentIndex index); template ComponentType& GetComponent(); @@ -47,32 +49,36 @@ namespace Ndk void Kill(); void Invalidate(); + inline bool IsEnabled() const; inline bool IsValid() const; void RemoveAllComponents(); void RemoveComponent(ComponentIndex index); template void RemoveComponent(); + inline Nz::String ToString() const; + Entity& operator=(const Entity&) = delete; Entity& operator=(Entity&&) = delete; private: - Entity(World& world, EntityId id); + Entity(World* world, EntityId id); void Create(); void Destroy(); - inline void RegisterHandle(EntityHandle* handle); inline void RegisterSystem(SystemIndex index); - inline void UnregisterHandle(EntityHandle* handle); + + inline void SetWorld(World* world) noexcept; + inline void UnregisterSystem(SystemIndex index); std::vector> m_components; - std::vector m_handles; Nz::Bitset<> m_componentBits; Nz::Bitset<> m_systemBits; EntityId m_id; World* m_world; + bool m_enabled; bool m_valid; }; } diff --git a/SDK/include/NDK/Entity.inl b/SDK/include/NDK/Entity.inl index d30f58530..799ffd97e 100644 --- a/SDK/include/NDK/Entity.inl +++ b/SDK/include/NDK/Entity.inl @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Prerequesites.hpp #include +#include #include #include #include @@ -19,6 +20,15 @@ namespace Ndk return static_cast(AddComponent(std::move(ptr))); } + inline void Entity::Enable(bool enable) + { + if (m_enabled != enable) + { + m_enabled = enable; + Invalidate(); + } + } + inline BaseComponent& Entity::GetComponent(ComponentIndex index) { ///DOC: Le component doit être présent @@ -74,6 +84,11 @@ namespace Ndk return HasComponent(index); } + inline bool Entity::IsEnabled() const + { + return m_enabled; + } + inline bool Entity::IsValid() const { return m_valid; @@ -88,10 +103,10 @@ namespace Ndk RemoveComponent(index); } - inline void Entity::RegisterHandle(EntityHandle* handle) + inline Nz::String Entity::ToString() const { - ///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); + Nz::StringStream ss; + return ss << "Entity(" << GetId() << ')'; } inline void Entity::RegisterSystem(SystemIndex index) @@ -99,14 +114,11 @@ namespace Ndk m_systemBits.UnboundedSet(index); } - inline void Entity::UnregisterHandle(EntityHandle* handle) + inline void Entity::SetWorld(World* world) 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(world, "An entity must be attached to a world at any time"); - // On échange cet élément avec le dernier, et on diminue la taille du vector de 1 - std::swap(*it, m_handles.back()); - m_handles.pop_back(); + m_world = world; } inline void Entity::UnregisterSystem(SystemIndex index) @@ -114,3 +126,19 @@ namespace Ndk m_systemBits.UnboundedReset(index); } } + +namespace std +{ + template<> + struct hash + { + size_t operator()(const Ndk::EntityHandle& handle) const + { + // Hasher le pointeur fonctionnerait jusqu'à ce que l'entité soit mise à jour et déplacée + // pour cette raison, nous devons hasher l'ID de l'entité (qui reste constante) + Ndk::EntityId id = (handle.IsValid()) ? handle->GetId() : std::numeric_limits::max(); + + return hash()(id); + } + }; +} \ No newline at end of file diff --git a/SDK/include/NDK/EntityHandle.hpp b/SDK/include/NDK/EntityHandle.hpp deleted file mode 100644 index c6a325b7e..000000000 --- a/SDK/include/NDK/EntityHandle.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// 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_ENTITYHANDLE_HPP -#define NDK_ENTITYHANDLE_HPP - -#include -#include - -namespace Ndk -{ - class EntityHandle - { - friend Entity; - - public: - EntityHandle(); - explicit EntityHandle(Entity* entity); - EntityHandle(const EntityHandle& handle); - EntityHandle(EntityHandle&& handle); - ~EntityHandle(); - - Entity* GetEntity() const; - - bool IsValid() const; - - void Reset(Entity* entity = nullptr); - void Reset(const EntityHandle& handle); - void Reset(EntityHandle&& handle); - - EntityHandle& Swap(EntityHandle& handle); - - Nz::String ToString() const; - - operator bool() const; - operator Entity*() const; - Entity* operator->() const; - - EntityHandle& operator=(Entity* entity); - EntityHandle& operator=(const EntityHandle& handle); - EntityHandle& operator=(EntityHandle&& handle); - - friend std::ostream& operator<<(std::ostream& out, const EntityHandle& handle); - - friend bool operator==(const EntityHandle& lhs, const EntityHandle& rhs); - friend bool operator==(const Entity& lhs, const EntityHandle& rhs); - friend bool operator==(const EntityHandle& lhs, const Entity& rhs); - - friend bool operator!=(const EntityHandle& lhs, const EntityHandle& rhs); - friend bool operator!=(const Entity& lhs, const EntityHandle& rhs); - friend bool operator!=(const EntityHandle& lhs, const Entity& rhs); - - friend bool operator<(const EntityHandle& lhs, const EntityHandle& rhs); - friend bool operator<(const Entity& lhs, const EntityHandle& rhs); - friend bool operator<(const EntityHandle& lhs, const Entity& rhs); - - friend bool operator<=(const EntityHandle& lhs, const EntityHandle& rhs); - friend bool operator<=(const Entity& lhs, const EntityHandle& rhs); - friend bool operator<=(const EntityHandle& lhs, const Entity& rhs); - - friend bool operator>(const EntityHandle& lhs, const EntityHandle& rhs); - friend bool operator>(const Entity& lhs, const EntityHandle& rhs); - friend bool operator>(const EntityHandle& lhs, const Entity& rhs); - - friend bool operator>=(const EntityHandle& lhs, const EntityHandle& rhs); - friend bool operator>=(const Entity& lhs, const EntityHandle& rhs); - friend bool operator>=(const EntityHandle& lhs, const Entity& rhs); - - static const EntityHandle InvalidHandle; - - private: - void OnEntityDestroyed(); - void OnEntityMoved(Entity* newEntity); - - Entity* m_entity; - }; -} - -namespace std -{ - void swap(Ndk::EntityHandle& lhs, Ndk::EntityHandle& rhs); -} - -#include - -#endif // NDK_ENTITYHANDLE_HPP diff --git a/SDK/include/NDK/EntityHandle.inl b/SDK/include/NDK/EntityHandle.inl deleted file mode 100644 index 636287878..000000000 --- a/SDK/include/NDK/EntityHandle.inl +++ /dev/null @@ -1,280 +0,0 @@ -// 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 -#include - -namespace Ndk -{ - inline EntityHandle::EntityHandle() : - m_entity(nullptr) - { - } - - inline EntityHandle::EntityHandle(Entity* entity) : - EntityHandle() - { - Reset(entity); - } - - inline EntityHandle::EntityHandle(const EntityHandle& handle) : - EntityHandle() - { - Reset(handle); - } - - inline EntityHandle::EntityHandle(EntityHandle&& handle) : - EntityHandle() - { - Reset(handle); - } - - inline EntityHandle::~EntityHandle() - { - Reset(nullptr); - } - - inline Entity* EntityHandle::GetEntity() const - { - return m_entity; - } - - inline bool EntityHandle::IsValid() const - { - return m_entity != nullptr; - } - - inline void EntityHandle::Reset(Entity* entity) - { - // Si nous avions déjà une entité, nous devons l'informer que nous ne pointons plus sur elle - if (m_entity) - m_entity->UnregisterHandle(this); - - m_entity = entity; - if (m_entity) - // On informe la nouvelle entité que nous pointons sur elle - m_entity->RegisterHandle(this); - } - - inline void EntityHandle::Reset(const EntityHandle& handle) - { - Reset(handle.GetEntity()); - } - - inline void EntityHandle::Reset(EntityHandle&& handle) - { - Reset(handle.GetEntity()); - } - - inline EntityHandle& EntityHandle::Swap(EntityHandle& 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). - if (m_entity) - { - m_entity->UnregisterHandle(this); - m_entity->RegisterHandle(&handle); - } - - if (handle.m_entity) - { - handle.m_entity->UnregisterHandle(&handle); - handle.m_entity->RegisterHandle(this); - } - - // On effectue l'échange - std::swap(m_entity, handle.m_entity); - return *this; - } - - inline Nz::String EntityHandle::ToString() const - { - Nz::StringStream ss; - ss << "EntityHandle("; - if (IsValid()) - ss << "Entity(" << m_entity->GetId() << ')'; - else - ss << "Null entity"; - - ss << ')'; - - return ss; - } - - inline EntityHandle::operator bool() const - { - return IsValid(); - } - - inline EntityHandle::operator Entity*() const - { - return m_entity; - } - - inline Entity* EntityHandle::operator->() const - { - return m_entity; - } - - inline EntityHandle& EntityHandle::operator=(Entity* entity) - { - Reset(entity); - - return *this; - } - - inline EntityHandle& EntityHandle::operator=(const EntityHandle& handle) - { - Reset(handle); - - return *this; - } - - inline EntityHandle& EntityHandle::operator=(EntityHandle&& handle) - { - Reset(handle); - - return *this; - } - - inline void EntityHandle::OnEntityDestroyed() - { - // Un raccourci, un appel à Reset nous enlèverait de la liste des handles que nous ne pouvons pas modifier - // maintenant car elle est actuellement parcourue - m_entity = nullptr; - } - - inline void EntityHandle::OnEntityMoved(Entity* newEntity) - { - // L'entité a été déplacée (peut arriver lors d'un changement de taille du conteneur du monde) - // nous mettons à jour notre pointeur - m_entity = newEntity; - } - - inline std::ostream& operator<<(std::ostream& out, const EntityHandle& handle) - { - out << "EntityHandle("; - if (handle.IsValid()) - out << "Entity(" << handle->GetId() << ')'; - else - out << "Null entity"; - - out << ')'; - - return out; - } - - inline bool operator==(const EntityHandle& lhs, const EntityHandle& rhs) - { - return lhs.m_entity == rhs.m_entity; - } - - inline bool operator==(const Entity& lhs, const EntityHandle& rhs) - { - return &lhs == rhs.m_entity; - } - - inline bool operator==(const EntityHandle& lhs, const Entity& rhs) - { - return lhs.m_entity == &rhs; - } - - inline bool operator!=(const EntityHandle& lhs, const EntityHandle& rhs) - { - return !(lhs == rhs); - } - - inline bool operator!=(const Entity& lhs, const EntityHandle& rhs) - { - return !(lhs == rhs); - } - - inline bool operator!=(const EntityHandle& lhs, const Entity& rhs) - { - return !(lhs == rhs); - } - - inline bool operator<(const EntityHandle& lhs, const EntityHandle& rhs) - { - return lhs.m_entity < rhs.m_entity; - } - - inline bool operator<(const Entity& lhs, const EntityHandle& rhs) - { - return &lhs < rhs.m_entity; - } - - inline bool operator<(const EntityHandle& lhs, const Entity& rhs) - { - return lhs.m_entity < &rhs; - } - - inline bool operator<=(const EntityHandle& lhs, const EntityHandle& rhs) - { - return !(lhs > rhs); - } - - inline bool operator<=(const Entity& lhs, const EntityHandle& rhs) - { - return !(lhs > rhs); - } - - inline bool operator<=(const EntityHandle& lhs, const Entity& rhs) - { - return !(lhs > rhs); - } - - inline bool operator>(const EntityHandle& lhs, const EntityHandle& rhs) - { - return rhs < lhs; - } - - inline bool operator>(const Entity& lhs, const EntityHandle& rhs) - { - return rhs < lhs; - } - - inline bool operator>(const EntityHandle& lhs, const Entity& rhs) - { - return rhs < lhs; - } - - inline bool operator>=(const EntityHandle& lhs, const EntityHandle& rhs) - { - return !(lhs < rhs); - } - - inline bool operator>=(const Entity& lhs, const EntityHandle& rhs) - { - return !(lhs < rhs); - } - - inline bool operator>=(const EntityHandle& lhs, const Entity& rhs) - { - return !(lhs < rhs); - } -} - -namespace std -{ - template<> - struct hash - { - size_t operator()(const Ndk::EntityHandle& handle) const - { - // Hasher le pointeur fonctionnerait jusqu'à ce que l'entité soit mise à jour et déplacée - // pour cette raison, nous devons hasher l'ID de l'entité (qui reste constante) - Ndk::EntityId id = (handle.IsValid()) ? handle->GetId() : std::numeric_limits::max(); - - return hash()(id); - } - }; - - inline void swap(Ndk::EntityHandle& lhs, Ndk::EntityHandle& rhs) - { - lhs.Swap(rhs); - } -} diff --git a/SDK/include/NDK/EntityList.hpp b/SDK/include/NDK/EntityList.hpp index 0edefde11..006967413 100644 --- a/SDK/include/NDK/EntityList.hpp +++ b/SDK/include/NDK/EntityList.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include namespace Ndk { diff --git a/SDK/include/NDK/EntityList.inl b/SDK/include/NDK/EntityList.inl index 57bd4a8af..94fa3472f 100644 --- a/SDK/include/NDK/EntityList.inl +++ b/SDK/include/NDK/EntityList.inl @@ -41,6 +41,7 @@ namespace Ndk std::swap(*it, m_entities.back()); m_entities.pop_back(); // On le sort du vector + m_entityBits.UnboundedSet(entity->GetId(), false); } } diff --git a/SDK/include/NDK/EntityOwner.hpp b/SDK/include/NDK/EntityOwner.hpp new file mode 100644 index 000000000..c7cab901f --- /dev/null +++ b/SDK/include/NDK/EntityOwner.hpp @@ -0,0 +1,34 @@ +// 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_ENTITYOWNER_HPP +#define NDK_ENTITYOWNER_HPP + +#include + +namespace Ndk +{ + class EntityOwner : public EntityHandle + { + public: + EntityOwner() = default; + explicit EntityOwner(Entity* entity); + EntityOwner(const EntityOwner& handle) = delete; + EntityOwner(EntityOwner&& handle); + ~EntityOwner(); + + void Reset(Entity* entity = nullptr); + void Reset(EntityOwner&& handle); + + EntityOwner& operator=(Entity* entity); + EntityOwner& operator=(const EntityOwner& handle) = delete; + EntityOwner& operator=(EntityOwner&& handle); + }; +} + +#include + +#endif // NDK_ENTITYOwner_HPP diff --git a/SDK/include/NDK/EntityOwner.inl b/SDK/include/NDK/EntityOwner.inl new file mode 100644 index 000000000..607f3229d --- /dev/null +++ b/SDK/include/NDK/EntityOwner.inl @@ -0,0 +1,62 @@ +// 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 +#include + +namespace Ndk +{ + inline EntityOwner::EntityOwner(Entity* entity) : + EntityOwner() + { + Reset(entity); + } + + inline EntityOwner::EntityOwner(EntityOwner&& handle) : + EntityHandle(std::move(handle)) + { + } + + inline EntityOwner::~EntityOwner() + { + Reset(nullptr); + } + + inline void EntityOwner::Reset(Entity* entity) + { + if (m_object) + m_object->Kill(); + + EntityHandle::Reset(entity); + } + + inline void EntityOwner::Reset(EntityOwner&& handle) + { + Reset(handle.GetObject()); + handle.m_object = nullptr; + } + + inline EntityOwner& EntityOwner::operator=(Entity* entity) + { + Reset(entity); + + return *this; + } + + inline EntityOwner& EntityOwner::operator=(EntityOwner&& handle) + { + Reset(std::move(handle)); + + return *this; + } +} + +namespace std +{ + template<> + struct hash : public hash + { + }; +} diff --git a/SDK/include/NDK/LuaAPI.hpp b/SDK/include/NDK/LuaAPI.hpp new file mode 100644 index 000000000..277948bd5 --- /dev/null +++ b/SDK/include/NDK/LuaAPI.hpp @@ -0,0 +1,40 @@ +// 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_LUAINTERFACE_HPP +#define NDK_LUAINTERFACE_HPP + +#include + +namespace Nz +{ + class LuaInstance; +} + +namespace Ndk +{ + class LuaBinding; + + class NDK_API LuaAPI + { + public: + LuaAPI() = delete; + ~LuaAPI() = delete; + + static bool Initialize(); + + static void RegisterClasses(Nz::LuaInstance& instance); + + static void Uninitialize(); + + private: + static LuaBinding* s_binding; + }; +} + +#include + +#endif // NDK_LUAINTERFACE_HPP diff --git a/SDK/include/NDK/LuaAPI.inl b/SDK/include/NDK/LuaAPI.inl new file mode 100644 index 000000000..f4d780386 --- /dev/null +++ b/SDK/include/NDK/LuaAPI.inl @@ -0,0 +1,385 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NDK_SERVER +#include +#include +#include +#include +#endif + +namespace Nz +{ + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Color* color, TypeTag) + { + instance.CheckType(index, Nz::LuaType_Table); + + color->r = instance.CheckField("r", index); + color->g = instance.CheckField("g", index); + color->b = instance.CheckField("b", index); + color->a = instance.CheckField("a", 255, index); + + return 1; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, EulerAnglesd* angles, TypeTag) + { + switch (instance.GetType(index)) + { + case Nz::LuaType_Table: + angles->Set(instance.CheckField("pitch", index), instance.CheckField("yaw", index), instance.CheckField("roll", index)); + return 1; + + default: + { + if (instance.IsOfType(index, "EulerAngles")) + angles->Set(*(*static_cast(instance.ToUserdata(index)))); + else + angles->Set(*(*static_cast(instance.CheckUserdata(index, "Quaternion")))); + + return 1; + } + } + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, EulerAnglesf* angles, TypeTag) + { + EulerAnglesd anglesDouble; + unsigned int ret = LuaImplQueryArg(instance, index, &anglesDouble, TypeTag()); + + angles->Set(anglesDouble); + return ret; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Quaterniond* quat, TypeTag) + { + switch (instance.GetType(index)) + { + case Nz::LuaType_Table: + quat->Set(instance.CheckField("w", index), instance.CheckField("x", index), instance.CheckField("y", index), instance.CheckField("z", index)); + return 1; + + default: + { + if (instance.IsOfType(index, "EulerAngles")) + quat->Set(*(*static_cast(instance.ToUserdata(index)))); + else + quat->Set(*(*static_cast(instance.CheckUserdata(index, "Quaternion")))); + + return 1; + } + } + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Quaternionf* quat, TypeTag) + { + Quaterniond quatDouble; + unsigned int ret = LuaImplQueryArg(instance, index, &quatDouble, TypeTag()); + + quat->Set(quatDouble); + return ret; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, IpAddress* address, TypeTag) + { + switch (instance.GetType(index)) + { + case Nz::LuaType_String: + address->BuildFromAddress(instance.CheckString(index)); + return 1; + + default: + *address = *(*static_cast(instance.CheckUserdata(index, "IpAddress"))); + return 1; + } + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Vector2d* vec, TypeTag) + { + switch (instance.GetType(index)) + { + case Nz::LuaType_Number: + if (index < 0 && index > -2) + instance.Error("Vector2 expected, two numbers are required to convert it"); + + vec->Set(instance.CheckNumber(index), instance.CheckNumber(index + 1)); + return 2; + + case Nz::LuaType_Table: + vec->Set(instance.CheckField("x", index), instance.CheckField("y", index)); + return 1; + + default: + vec->Set(*(*static_cast(instance.CheckUserdata(index, "Vector2")))); + return 1; + } + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Vector2f* vec, TypeTag) + { + Vector2d vecDouble; + unsigned int ret = LuaImplQueryArg(instance, index, &vecDouble, TypeTag()); + + vec->Set(vecDouble); + return ret; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Vector2ui* vec, TypeTag) + { + Vector2d vecDouble; + unsigned int ret = LuaImplQueryArg(instance, index, &vecDouble, TypeTag()); + + vec->Set(vecDouble); + return ret; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Vector3d* vec, TypeTag) + { + switch (instance.GetType(index)) + { + case Nz::LuaType_Number: + if (index < 0 && index > -3) + instance.Error("Vector3 expected, three numbers are required to convert it"); + + vec->Set(instance.CheckNumber(index), instance.CheckNumber(index + 1), instance.CheckNumber(index + 2, 0.0)); + return 3; + + case Nz::LuaType_Table: + vec->Set(instance.CheckField("x", index), instance.CheckField("y", index), instance.CheckField("z", 0.0, index)); + return 1; + + default: + vec->Set(*(*static_cast(instance.CheckUserdata(index, "Vector3")))); + return 1; + } + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Vector3f* vec, TypeTag) + { + Vector3d vecDouble; + unsigned int ret = LuaImplQueryArg(instance, index, &vecDouble, TypeTag()); + + vec->Set(vecDouble); + return ret; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, Vector3ui* vec, TypeTag) + { + Vector3d vecDouble; + unsigned int ret = LuaImplQueryArg(instance, index, &vecDouble, TypeTag()); + + vec->Set(vecDouble); + return ret; + } + +#ifndef NDK_SERVER + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, InstancedRenderableRef* renderable, TypeTag) + { + if (instance.IsOfType(index, "InstancedRenderable")) + *renderable = *(*static_cast(instance.CheckUserdata(index, "InstancedRenderable"))); + else + *renderable = *(*static_cast(instance.CheckUserdata(index, "Model"))); + return 1; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, MaterialParams* params, TypeTag) + { + instance.CheckType(index, Nz::LuaType_Table); + + params->loadAlphaMap = instance.CheckField("LoadAlphaMap", params->loadAlphaMap); + params->loadDiffuseMap = instance.CheckField("LoadDiffuseMap", params->loadDiffuseMap); + params->loadEmissiveMap = instance.CheckField("LoadEmissiveMap", params->loadEmissiveMap); + params->loadHeightMap = instance.CheckField("LoadHeightMap", params->loadHeightMap); + params->loadNormalMap = instance.CheckField("LoadNormalMap", params->loadNormalMap); + params->loadSpecularMap = instance.CheckField("LoadSpecularMap", params->loadSpecularMap); + + return 1; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, MeshParams* params, TypeTag) + { + instance.CheckType(index, Nz::LuaType_Table); + + params->animated = instance.CheckField("Animated", params->animated); + params->center = instance.CheckField("Center", params->center); + params->flipUVs = instance.CheckField("FlipUVs", params->flipUVs); + params->optimizeIndexBuffers = instance.CheckField("OptimizeIndexBuffers", params->optimizeIndexBuffers); + params->scale = instance.CheckField("Scale", params->scale); + + return 1; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, ModelParameters* params, TypeTag) + { + instance.CheckType(index, Nz::LuaType_Table); + + params->loadMaterials = instance.CheckField("LoadMaterials", params->loadMaterials); + + LuaImplQueryArg(instance, -1, ¶ms->material, TypeTag()); + LuaImplQueryArg(instance, -1, ¶ms->mesh, TypeTag()); + + return 1; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, MusicParams* params, TypeTag) + { + instance.CheckType(index, Nz::LuaType_Table); + + params->forceMono = instance.CheckField("ForceMono", params->forceMono); + + return 1; + } + + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, SoundBufferParams* params, TypeTag) + { + instance.CheckType(index, Nz::LuaType_Table); + + params->forceMono = instance.CheckField("ForceMono", params->forceMono); + + return 1; + } +#endif + + + + inline int LuaImplReplyVal(const LuaInstance& instance, EulerAnglesd val, TypeTag) + { + instance.PushInstance("EulerAngles", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, EulerAnglesf val, TypeTag) + { + instance.PushInstance("EulerAngles", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Quaterniond val, TypeTag) + { + instance.PushInstance("Quaternion", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Quaternionf val, TypeTag) + { + instance.PushInstance("Quaternion", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, IpAddress val, TypeTag) + { + instance.PushInstance("IpAddress", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Vector2d val, TypeTag) + { + instance.PushInstance("Vector2", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Vector2f val, TypeTag) + { + instance.PushInstance("Vector2", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Vector2ui val, TypeTag) + { + instance.PushInstance("Vector2", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Vector3d val, TypeTag) + { + instance.PushInstance("Vector3", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Vector3f val, TypeTag) + { + instance.PushInstance("Vector3", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Vector3ui val, TypeTag) + { + instance.PushInstance("Vector3", val); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Ndk::Entity* ptr, TypeTag) + { + instance.PushInstance("Entity", ptr); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Ndk::Application* ptr, TypeTag) + { + instance.PushInstance("Application", ptr); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Ndk::EntityHandle handle, TypeTag) + { + instance.PushInstance("Entity", handle); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Ndk::NodeComponentHandle handle, TypeTag) + { + instance.PushInstance("NodeComponent", handle); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Ndk::VelocityComponentHandle handle, TypeTag) + { + instance.PushInstance("VelocityComponent", handle); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Ndk::World* ptr, TypeTag) + { + instance.PushInstance("World", ptr); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Ndk::WorldHandle handle, TypeTag) + { + instance.PushInstance("World", handle); + return 1; + } + +#ifndef NDK_SERVER + inline int LuaImplReplyVal(const LuaInstance& instance, Ndk::ConsoleHandle handle, TypeTag) + { + instance.PushInstance("Console", handle); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, Ndk::GraphicsComponentHandle handle, TypeTag) + { + instance.PushInstance("GraphicsComponent", handle); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, const SoundBuffer* val, TypeTag) + { + instance.PushInstance("SoundBuffer", val); + return 1; + } +#endif +} diff --git a/SDK/include/NDK/State.hpp b/SDK/include/NDK/State.hpp new file mode 100644 index 000000000..0a113def3 --- /dev/null +++ b/SDK/include/NDK/State.hpp @@ -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 + +#pragma once + +#ifndef NDK_STATE_HPP +#define NDK_STATE_HPP + +#include + +namespace Ndk +{ + class StateMachine; + + class State + { + public: + State(); + ~State(); + + virtual void Enter(StateMachine& fsm) = 0; + virtual void Leave(StateMachine& fsm) = 0; + virtual bool Update(StateMachine& fsm, float elapsedTime) = 0; + }; +} + +#endif // NDK_STATE_HPP \ No newline at end of file diff --git a/SDK/include/NDK/StateMachine.hpp b/SDK/include/NDK/StateMachine.hpp new file mode 100644 index 000000000..8e7ad2f64 --- /dev/null +++ b/SDK/include/NDK/StateMachine.hpp @@ -0,0 +1,39 @@ +// 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_STATEMACHINE_HPP +#define NDK_STATEMACHINE_HPP + +#include +#include +#include + +namespace Ndk +{ + class StateMachine + { + public: + inline StateMachine(std::shared_ptr originalState); + StateMachine(const StateMachine&) = delete; + inline StateMachine(StateMachine&& fsm) = default; + inline ~StateMachine(); + + inline void ChangeState(std::shared_ptr state); + + inline bool Update(float elapsedTime); + + inline StateMachine& operator=(StateMachine&& fsm) = default; + StateMachine& operator=(const StateMachine&) = delete; + + private: + std::shared_ptr m_currentState; + std::shared_ptr m_nextState; + }; +} + +#include + +#endif // NDK_STATEMACHINE_HPP \ No newline at end of file diff --git a/SDK/include/NDK/StateMachine.inl b/SDK/include/NDK/StateMachine.inl new file mode 100644 index 000000000..fc4c7f787 --- /dev/null +++ b/SDK/include/NDK/StateMachine.inl @@ -0,0 +1,40 @@ +// 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 +#include + +namespace Ndk +{ + inline StateMachine::StateMachine(std::shared_ptr originalState) : + m_currentState(std::move(originalState)) + { + NazaraAssert(m_currentState, "StateMachine must have a state to begin with"); + m_currentState->Enter(*this); + } + + inline StateMachine::~StateMachine() + { + m_currentState->Leave(*this); + } + + + inline void StateMachine::ChangeState(std::shared_ptr state) + { + m_nextState = std::move(state); + } + + inline bool StateMachine::Update(float elapsedTime) + { + if (m_nextState) + { + m_currentState->Leave(*this); + m_currentState = std::move(m_nextState); + m_currentState->Enter(*this); + } + + return m_currentState->Update(*this, elapsedTime); + } +} diff --git a/SDK/include/NDK/System.inl b/SDK/include/NDK/System.inl index c7bfdf401..9b382d924 100644 --- a/SDK/include/NDK/System.inl +++ b/SDK/include/NDK/System.inl @@ -2,7 +2,7 @@ // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequesites.hpp -#include +#include #include namespace Ndk diff --git a/SDK/include/NDK/Systems.hpp b/SDK/include/NDK/Systems.hpp new file mode 100644 index 000000000..193438b5e --- /dev/null +++ b/SDK/include/NDK/Systems.hpp @@ -0,0 +1,16 @@ +// This file was automatically generated on 03 Mar 2016 at 14:09:12 + +#pragma once + +#ifndef NDK_SYSTEMS_GLOBAL_HPP +#define NDK_SYSTEMS_GLOBAL_HPP + +#include +#include + +#ifndef NDK_SERVER +#include +#include +#endif + +#endif // NDK_SYSTEMS_GLOBAL_HPP diff --git a/SDK/include/NDK/Systems/RenderSystem.hpp b/SDK/include/NDK/Systems/RenderSystem.hpp index a27db2e7a..72aa1efb1 100644 --- a/SDK/include/NDK/Systems/RenderSystem.hpp +++ b/SDK/include/NDK/Systems/RenderSystem.hpp @@ -27,11 +27,15 @@ namespace Ndk inline RenderSystem(const RenderSystem& renderSystem); ~RenderSystem() = default; + template void ChangeRenderTechnique(); + inline void ChangeRenderTechnique(std::unique_ptr&& renderTechnique); + inline const Nz::BackgroundRef& GetDefaultBackground() const; inline const Nz::Matrix4f& GetCoordinateSystemMatrix() const; inline Nz::Vector3f GetGlobalForward() const; inline Nz::Vector3f GetGlobalRight() const; inline Nz::Vector3f GetGlobalUp() const; + inline Nz::AbstractRenderTechnique& GetRenderTechnique() const; inline void SetDefaultBackground(Nz::BackgroundRef background); inline void SetGlobalForward(const Nz::Vector3f& direction); @@ -49,6 +53,7 @@ namespace Ndk void UpdateDirectionalShadowMaps(const Nz::AbstractViewer& viewer); void UpdatePointSpotShadowMaps(); + std::unique_ptr m_renderTechnique; EntityList m_cameras; EntityList m_drawables; EntityList m_directionalLights; @@ -56,7 +61,6 @@ namespace Ndk EntityList m_pointSpotLights; Nz::BackgroundRef m_background; Nz::DepthRenderTechnique m_shadowTechnique; - Nz::ForwardRenderTechnique m_renderTechnique; Nz::Matrix4f m_coordinateSystemMatrix; Nz::RenderTexture m_shadowRT; bool m_coordinateSystemInvalidated; diff --git a/SDK/include/NDK/Systems/RenderSystem.inl b/SDK/include/NDK/Systems/RenderSystem.inl index d0cb0af47..b85ace59f 100644 --- a/SDK/include/NDK/Systems/RenderSystem.inl +++ b/SDK/include/NDK/Systems/RenderSystem.inl @@ -9,6 +9,17 @@ namespace Ndk { } + template + inline void RenderSystem::ChangeRenderTechnique() + { + ChangeRenderTechnique(std::make_unique()); + } + + inline void RenderSystem::ChangeRenderTechnique(std::unique_ptr&& renderTechnique) + { + m_renderTechnique = std::move(renderTechnique); + } + inline const Nz::BackgroundRef& RenderSystem::GetDefaultBackground() const { return m_background; @@ -34,6 +45,11 @@ namespace Ndk return Nz::Vector3f(m_coordinateSystemMatrix.m12, m_coordinateSystemMatrix.m22, m_coordinateSystemMatrix.m32); } + inline Nz::AbstractRenderTechnique& RenderSystem::GetRenderTechnique() const + { + return *m_renderTechnique.get(); + } + inline void RenderSystem::SetDefaultBackground(Nz::BackgroundRef background) { m_background = std::move(background); diff --git a/SDK/include/NDK/World.hpp b/SDK/include/NDK/World.hpp index 3b7e16684..40508e8e4 100644 --- a/SDK/include/NDK/World.hpp +++ b/SDK/include/NDK/World.hpp @@ -8,8 +8,8 @@ #define NDK_WORLD_HPP #include +#include #include -#include #include #include #include @@ -18,7 +18,11 @@ namespace Ndk { - class NDK_API World + class World; + + using WorldHandle = Nz::ObjectHandle; + + class NDK_API World : public Nz::HandledObject { friend Entity; @@ -27,8 +31,8 @@ namespace Ndk inline World(bool addDefaultSystems = true); World(const World&) = delete; - World(World&&) = delete; ///TODO - ~World(); + inline World(World&& world) noexcept; + ~World() noexcept; void AddDefaultSystems(); @@ -38,7 +42,7 @@ namespace Ndk const EntityHandle& CreateEntity(); inline EntityList CreateEntities(unsigned int count); - void Clear(); + void Clear() noexcept; const EntityHandle& GetEntity(EntityId id); inline const EntityList& GetEntities(); @@ -62,7 +66,7 @@ namespace Ndk inline void Update(float elapsedTime); World& operator=(const World&) = delete; - World& operator=(World&&) = delete; ///TODO + inline World& operator=(World&& world) noexcept; private: inline void Invalidate(); diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index a9e22774b..91ebc3922 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -13,6 +13,12 @@ namespace Ndk AddDefaultSystems(); } + inline World::World(World&& world) noexcept : + HandledObject(std::move(world)) + { + operator=(std::move(world)); + } + inline BaseSystem& World::AddSystem(std::unique_ptr&& system) { NazaraAssert(system, "System must be valid"); @@ -25,7 +31,7 @@ namespace Ndk // Affectation et retour du système m_systems[index] = std::move(system); - m_systems[index]->SetWorld(*this); + m_systems[index]->SetWorld(this); Invalidate(); // On force une mise à jour de toutes les entités @@ -152,4 +158,22 @@ namespace Ndk { m_dirtyEntities.UnboundedSet(id, true); } + + inline World& World::operator=(World&& world) noexcept + { + m_aliveEntities = std::move(world.m_aliveEntities); + m_dirtyEntities = std::move(world.m_dirtyEntities); + m_freeIdList = std::move(world.m_freeIdList); + m_killedEntities = std::move(world.m_killedEntities); + + m_entities = std::move(world.m_entities); + for (EntityBlock& block : m_entities) + block.entity.SetWorld(this); + + m_systems = std::move(world.m_systems); + for (const auto& systemPtr : m_systems) + systemPtr->SetWorld(this); + + return *this; + } } diff --git a/SDK/src/NDK/Application.cpp b/SDK/src/NDK/Application.cpp index a2141b0f8..df2dfa12c 100644 --- a/SDK/src/NDK/Application.cpp +++ b/SDK/src/NDK/Application.cpp @@ -3,3 +3,47 @@ // For conditions of distribution and use, see copyright notice in Prerequesites.hpp #include + +namespace Ndk +{ + bool Application::Run() + { + #ifndef NDK_SERVER + bool hasAtLeastOneActiveWindow = false; + + auto it = m_windows.begin(); + while (it != m_windows.end()) + { + Nz::Window& window = **it; + + if (!window.IsOpen(true)) + { + it = m_windows.erase(it); + continue; + } + + hasAtLeastOneActiveWindow = true; + + ++it; + } + #endif + + #ifndef NDK_SERVER + if (m_exitOnClosedWindows && !hasAtLeastOneActiveWindow) + return false; + #endif + + if (m_shouldQuit) + return false; + + m_updateTime = m_updateClock.GetSeconds(); + m_updateClock.Restart(); + + for (World& world : m_worlds) + world.Update(m_updateTime); + + return true; + } + + Application* Application::s_application = nullptr; +} \ No newline at end of file diff --git a/SDK/src/NDK/Components/CameraComponent.cpp b/SDK/src/NDK/Components/CameraComponent.cpp index ac369e566..1b7a3e06d 100644 --- a/SDK/src/NDK/Components/CameraComponent.cpp +++ b/SDK/src/NDK/Components/CameraComponent.cpp @@ -119,9 +119,14 @@ namespace Ndk switch (m_projectionType) { case Nz::ProjectionType_Orthogonal: - EnsureViewportUpdate(); + if (m_size.x <= 0.f || m_size.y <= 0.f) + { + EnsureViewportUpdate(); - m_projectionMatrix.MakeOrtho(0.f, static_cast(m_viewport.width), 0.f, static_cast(m_viewport.height), m_zNear, m_zFar); + m_projectionMatrix.MakeOrtho(0.f, static_cast(m_viewport.width), 0.f, static_cast(m_viewport.height), m_zNear, m_zFar); + } + else + m_projectionMatrix.MakeOrtho(0.f, m_size.x, 0.f, m_size.y, m_zNear, m_zFar); break; case Nz::ProjectionType_Perspective: diff --git a/SDK/src/NDK/Components/GraphicsComponent.cpp b/SDK/src/NDK/Components/GraphicsComponent.cpp index 566724350..e345bdb85 100644 --- a/SDK/src/NDK/Components/GraphicsComponent.cpp +++ b/SDK/src/NDK/Components/GraphicsComponent.cpp @@ -63,6 +63,18 @@ namespace Ndk InvalidateTransformMatrix(); } + void GraphicsComponent::UpdateBoundingVolume() const + { + EnsureTransformMatrixUpdate(); + + m_boundingVolume.MakeNull(); + for (const Renderable& r : m_renderables) + m_boundingVolume.ExtendTo(r.renderable->GetBoundingVolume()); + + m_boundingVolume.Update(m_transformMatrix); + m_boundingVolumeUpdated = true; + } + void GraphicsComponent::UpdateTransformMatrix() const { NazaraAssert(m_entity && m_entity->HasComponent(), "GraphicsComponent requires NodeComponent"); diff --git a/SDK/src/NDK/Console.cpp b/SDK/src/NDK/Console.cpp new file mode 100644 index 000000000..31586d346 --- /dev/null +++ b/SDK/src/NDK/Console.cpp @@ -0,0 +1,272 @@ +// 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 +#include +#include +#include +#include + +///TODO: For now is unable to display different color in the history, it needs a RichTextDrawer to do so + +namespace Ndk +{ + namespace + { + const char s_inputPrefix[] = "> "; + constexpr std::size_t s_inputPrefixSize = Nz::CountOf(s_inputPrefix) - 1; + } + + Console::Console(World& world, const Nz::Vector2f& size, Nz::LuaInstance& instance) : + m_historyPosition(0), + m_defaultFont(Nz::Font::GetDefault()), + m_instance(instance), + m_size(size), + m_opened(false), + m_characterSize(24) + { + Nz::MaterialRef backgroundMaterial = Nz::Material::New(); + backgroundMaterial->Enable(Nz::RendererParameter_Blend, true); + backgroundMaterial->Enable(Nz::RendererParameter_DepthBuffer, false); + backgroundMaterial->SetDstBlend(Nz::BlendFunc_InvSrcAlpha); + backgroundMaterial->SetSrcBlend(Nz::BlendFunc_SrcAlpha); + + // History bakckground + m_historyBackgroundSprite = Nz::Sprite::New(); + m_historyBackgroundSprite->SetColor(Nz::Color(80, 80, 160, 128)); + m_historyBackgroundSprite->SetMaterial(backgroundMaterial); + + m_historyBackground = world.CreateEntity(); + m_historyBackground->Enable(m_opened); + m_historyBackground->AddComponent().Attach(m_historyBackgroundSprite, -1); + m_historyBackground->AddComponent().SetParent(this); + + // History + m_historyDrawer.SetCharacterSize(m_characterSize); + m_historyDrawer.SetColor(Nz::Color(200, 200, 200)); + m_historyDrawer.SetFont(m_defaultFont); + + m_historyTextSprite = Nz::TextSprite::New(); + + m_history = world.CreateEntity(); + m_history->Enable(m_opened); + m_history->AddComponent().Attach(m_historyTextSprite); + + Ndk::NodeComponent& historyNode = m_history->AddComponent(); + historyNode.SetParent(this); + + // Input background + m_inputBackgroundSprite = Nz::Sprite::New(); + m_inputBackgroundSprite->SetColor(Nz::Color(255, 255, 255, 200)); + m_inputBackgroundSprite->SetMaterial(backgroundMaterial); + + m_inputBackground = world.CreateEntity(); + m_inputBackground->Enable(m_opened); + m_inputBackground->AddComponent().Attach(m_inputBackgroundSprite, -1); + m_inputBackground->AddComponent().SetParent(this); + + // Input + m_inputDrawer.SetColor(Nz::Color::Black); + m_inputDrawer.SetCharacterSize(m_characterSize); + m_inputDrawer.SetFont(m_defaultFont); + m_inputDrawer.SetText(s_inputPrefix); + + m_inputTextSprite = Nz::TextSprite::New(); + m_inputTextSprite->Update(m_inputDrawer); + + m_input = world.CreateEntity(); + m_input->Enable(m_opened); + m_input->AddComponent().Attach(m_inputTextSprite); + + Ndk::NodeComponent& inputNode = m_input->AddComponent(); + inputNode.SetParent(this); + + Layout(); + } + + void Console::AddLine(const Nz::String& text, const Nz::Color& color) + { + AddLineInternal(text, color); + RefreshHistory(); + } + + void Console::Clear() + { + m_historyLines.clear(); + RefreshHistory(); + } + + void Console::SendCharacter(char32_t character) + { + switch (character) + { + case '\b': + { + Nz::String input = m_inputDrawer.GetText(); + if (input.GetLength() <= s_inputPrefixSize) // Prevent removal of the input prefix + return; // Ignore if no user character is there + + input.Resize(-1, Nz::String::HandleUtf8); + m_inputDrawer.SetText(input); + break; + } + + case '\r': + case '\n': + ExecuteInput(); + break; + + default: + { + if (Nz::Unicode::GetCategory(character) == Nz::Unicode::Category_Other_Control) + return; + + m_inputDrawer.AppendText(Nz::String::Unicode(character)); + break; + } + } + + m_inputTextSprite->Update(m_inputDrawer); + } + + void Console::SendEvent(Nz::WindowEvent event) + { + switch (event.type) + { + case Nz::WindowEventType_TextEntered: + SendCharacter(event.text.character); + break; + + case Nz::WindowEventType_KeyPressed: + { + switch (event.key.code) + { + 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 + { + if (m_historyPosition > 1) + m_historyPosition--; + else if (m_historyPosition == 0) + m_historyPosition = 1; + } + + Nz::String text = m_commandHistory[m_commandHistory.size() - m_historyPosition]; + m_inputDrawer.SetText(s_inputPrefix + text); + m_inputTextSprite->Update(m_inputDrawer); + break; + } + break; + } + } + } + + void Console::SetCharacterSize(unsigned int size) + { + m_characterSize = size; + + m_historyDrawer.SetCharacterSize(m_characterSize); + m_historyTextSprite->Update(m_historyDrawer); + m_inputDrawer.SetCharacterSize(m_characterSize); + m_inputTextSprite->Update(m_inputDrawer); + + Layout(); + } + + void Console::SetSize(const Nz::Vector2f& size) + { + m_size = size; + m_historyBackgroundSprite->SetSize(m_size); + Layout(); + } + + void Console::SetTextFont(Nz::FontRef font) + { + Layout(); + } + + void Console::Show(bool show) + { + if (m_opened != show) + { + m_historyBackground->Enable(show); + m_history->Enable(show); + m_input->Enable(show); + m_inputBackground->Enable(show); + + m_opened = show; + } + } + + void Console::AddLineInternal(const Nz::String& text, const Nz::Color& color) + { + m_historyLines.emplace_back(Line{color, text}); + } + + void Console::ExecuteInput() + { + Nz::String input = m_inputDrawer.GetText(); + Nz::String inputCmd = input.SubString(s_inputPrefixSize);; + m_inputDrawer.SetText(s_inputPrefix); + + if (m_commandHistory.empty() || m_commandHistory.back() != inputCmd) + m_commandHistory.push_back(inputCmd); + + m_historyPosition = 0; + + AddLineInternal(input); //< With the input prefix + + if (!m_instance.Execute(inputCmd)) + AddLineInternal(m_instance.GetLastError(), Nz::Color::Red); + + RefreshHistory(); + } + + void Console::Layout() + { + unsigned int lineHeight = m_defaultFont->GetSizeInfo(m_characterSize).lineHeight; + + Ndk::NodeComponent& inputNode = m_input->GetComponent(); + inputNode.SetPosition(0.f, m_size.y - lineHeight - 5.f); + + float historyHeight = m_size.y - lineHeight - 5.f - 2.f; + m_historyBackgroundSprite->SetSize(m_size.x, historyHeight); + + m_maxHistoryLines = static_cast(std::ceil(historyHeight / lineHeight)); + + Ndk::NodeComponent& historyNode = m_history->GetComponent(); + historyNode.SetPosition(0.f, historyHeight - m_maxHistoryLines * lineHeight); + + Ndk::NodeComponent& inputBackgroundNode = m_inputBackground->GetComponent(); + inputBackgroundNode.SetPosition(0.f, historyHeight + 2.f); + + m_inputBackgroundSprite->SetSize(m_size.x, m_size.y - historyHeight); + } + + void Console::RefreshHistory() + { + m_historyDrawer.Clear(); + auto it = m_historyLines.end(); + if (m_historyLines.size() > m_maxHistoryLines) + it -= m_maxHistoryLines; + else + it = m_historyLines.begin(); + + for (unsigned int i = 0; i < m_maxHistoryLines; ++i) + { + if (m_maxHistoryLines - i <= m_historyLines.size() && it != m_historyLines.end()) + { + m_historyDrawer.AppendText(it->text); + ++it; + } + + m_historyDrawer.AppendText(Nz::String('\n')); + } + + m_historyTextSprite->Update(m_historyDrawer); + } +} diff --git a/SDK/src/NDK/Entity.cpp b/SDK/src/NDK/Entity.cpp index 42ecf36b1..17730a234 100644 --- a/SDK/src/NDK/Entity.cpp +++ b/SDK/src/NDK/Entity.cpp @@ -4,27 +4,25 @@ #include #include -#include #include namespace Ndk { Entity::Entity(Entity&& entity) : + HandledObject(std::move(entity)), m_components(std::move(entity.m_components)), - m_handles(std::move(entity.m_handles)), m_componentBits(std::move(entity.m_componentBits)), m_systemBits(std::move(entity.m_systemBits)), m_id(entity.m_id), m_world(entity.m_world), + m_enabled(entity.m_enabled), m_valid(entity.m_valid) { - for (EntityHandle* handle : m_handles) - handle->OnEntityMoved(this); } - Entity::Entity(World& world, EntityId id) : + Entity::Entity(World* world, EntityId id) : m_id(id), - m_world(&world) + m_world(world) { } @@ -62,11 +60,6 @@ namespace Ndk return component; } - EntityHandle Entity::CreateHandle() - { - return EntityHandle(this); - } - void Entity::Kill() { m_world->KillEntity(this); @@ -114,6 +107,7 @@ namespace Ndk void Entity::Create() { + m_enabled = true; m_valid = true; } @@ -130,11 +124,7 @@ namespace Ndk } m_systemBits.Clear(); - // On informe chaque handle de notre destruction pour éviter qu'il ne continue de pointer sur nous - for (EntityHandle* handle : m_handles) - handle->OnEntityDestroyed(); - - m_handles.clear(); + UnregisterAllHandles(); m_valid = false; } diff --git a/SDK/src/NDK/EntityHandle.cpp b/SDK/src/NDK/EntityHandle.cpp deleted file mode 100644 index 7b9d035e3..000000000 --- a/SDK/src/NDK/EntityHandle.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// 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 -{ - const EntityHandle EntityHandle::InvalidHandle; -} diff --git a/SDK/src/NDK/LuaAPI.cpp b/SDK/src/NDK/LuaAPI.cpp new file mode 100644 index 000000000..a1b84bf5d --- /dev/null +++ b/SDK/src/NDK/LuaAPI.cpp @@ -0,0 +1,31 @@ +// This file was automatically generated on 26 May 2014 at 01:05:31 + +#include +#include + +namespace Ndk +{ + bool LuaAPI::Initialize() + { + s_binding = new LuaBinding; + return true; + } + + void LuaAPI::RegisterClasses(Nz::LuaInstance& instance) + { + if (!s_binding && !Initialize()) + { + NazaraError("Failed to initialize binding"); + return; + } + + s_binding->RegisterClasses(instance); + } + + void LuaAPI::Uninitialize() + { + delete s_binding; + } + + LuaBinding* LuaAPI::s_binding = nullptr; +} diff --git a/SDK/src/NDK/LuaBinding.cpp b/SDK/src/NDK/LuaBinding.cpp new file mode 100644 index 000000000..ab99eb101 --- /dev/null +++ b/SDK/src/NDK/LuaBinding.cpp @@ -0,0 +1,80 @@ +// This file was automatically generated on 26 May 2014 at 01:05:31 + +#include + +namespace Ndk +{ + LuaBinding::LuaBinding() : + // Core + clockClass("Clock"), + directoryClass("Directory"), + fileClass("File"), + streamClass("Stream"), + + // Math + eulerAnglesClass("EulerAngles"), + quaternionClass("Quaternion"), + vector2dClass("Vector2"), + vector3dClass("Vector3"), + + // Network + abstractSocketClass("AbstractSocket"), + ipAddressClass("IpAddress"), + + // Utility + abstractImage("AbstractImage"), + nodeClass("Node"), + + // SDK + application("Application"), + nodeComponent("NodeComponent"), + entityClass("Entity"), + velocityComponent("VelocityComponent"), + worldClass("World") + + #ifndef NDK_SERVER + , + + // Audio + musicClass("Music"), + soundBuffer("SoundBuffer"), + soundEmitter("SoundEmitter"), + soundClass("Sound"), + + // Graphics + instancedRenderable("InstancedRenderable"), + modelClass("Model"), + + // SDK + consoleClass("Console"), + graphicsComponent("GraphicsComponent") + #endif + { + BindCore(); + BindMath(); + BindNetwork(); + BindSDK(); + BindUtility(); + + #ifndef NDK_SERVER + BindAudio(); + BindGraphics(); + BindRenderer(); + #endif + } + + void LuaBinding::RegisterClasses(Nz::LuaInstance& instance) + { + RegisterCore(instance); + RegisterMath(instance); + RegisterNetwork(instance); + RegisterSDK(instance); + RegisterUtility(instance); + + #ifndef NDK_SERVER + RegisterAudio(instance); + RegisterGraphics(instance); + RegisterRenderer(instance); + #endif + } +} diff --git a/SDK/src/NDK/LuaBinding.hpp b/SDK/src/NDK/LuaBinding.hpp new file mode 100644 index 000000000..93363f7fb --- /dev/null +++ b/SDK/src/NDK/LuaBinding.hpp @@ -0,0 +1,120 @@ +// 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_LUABINDING_HPP +#define NDK_LUABINDING_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NDK_SERVER +#include +#include +#include +#include +#endif + +namespace Ndk +{ + class LuaBinding + { + public: + LuaBinding(); + ~LuaBinding() = default; + + void RegisterClasses(Nz::LuaInstance& instance); + + private: + void BindCore(); + void BindMath(); + void BindNetwork(); + void BindSDK(); + void BindUtility(); + + template void EnableComponentBinding(); + + void RegisterCore(Nz::LuaInstance& instance); + void RegisterMath(Nz::LuaInstance& instance); + void RegisterNetwork(Nz::LuaInstance& instance); + void RegisterSDK(Nz::LuaInstance& instance); + void RegisterUtility(Nz::LuaInstance& instance); + + #ifndef NDK_SERVER + void BindAudio(); + void BindGraphics(); + void BindRenderer(); + + void RegisterAudio(Nz::LuaInstance& instance); + void RegisterGraphics(Nz::LuaInstance& instance); + void RegisterRenderer(Nz::LuaInstance& instance); + #endif + + // Core + Nz::LuaClass clockClass; + Nz::LuaClass directoryClass; + Nz::LuaClass fileClass; + Nz::LuaClass streamClass; + + // Math + Nz::LuaClass eulerAnglesClass; + Nz::LuaClass quaternionClass; + Nz::LuaClass vector2dClass; + Nz::LuaClass vector3dClass; + + // Network + Nz::LuaClass abstractSocketClass; + Nz::LuaClass ipAddressClass; + + // Utility + Nz::LuaClass abstractImage; + Nz::LuaClass nodeClass; + + // SDK + Nz::LuaClass application; + Nz::LuaClass entityClass; + Nz::LuaClass nodeComponent; + Nz::LuaClass velocityComponent; + Nz::LuaClass worldClass; + + using AddComponentFunc = int(*)(Nz::LuaInstance&, EntityHandle&); + using GetComponentFunc = int(*)(Nz::LuaInstance&, BaseComponent&); + + struct ComponentBinding + { + AddComponentFunc adder; + GetComponentFunc getter; + bool valid = false; + }; + + std::vector m_componentBinding; + + #ifndef NDK_SERVER + // Audio + Nz::LuaClass musicClass; + Nz::LuaClass soundClass; + Nz::LuaClass soundBuffer; + Nz::LuaClass soundEmitter; + + // Graphics + Nz::LuaClass instancedRenderable; + Nz::LuaClass modelClass; + + // SDK + Nz::LuaClass consoleClass; + Nz::LuaClass graphicsComponent; + #endif + }; +} + +#endif // NDK_LUABINDING_HPP diff --git a/SDK/src/NDK/LuaBinding_Audio.cpp b/SDK/src/NDK/LuaBinding_Audio.cpp new file mode 100644 index 000000000..7c204b833 --- /dev/null +++ b/SDK/src/NDK/LuaBinding_Audio.cpp @@ -0,0 +1,176 @@ +// This file was automatically generated on 26 May 2014 at 01:05:31 + +#include +#include + +namespace Ndk +{ + void LuaBinding::BindAudio() + { + /*********************************** Nz::Music **********************************/ + musicClass.Inherit(soundEmitter); + + musicClass.SetConstructor([] (Nz::LuaInstance& lua) -> Nz::Music* + { + return new Nz::Music; + }); + + //musicClass.SetMethod("Create", &Nz::Music::Create); + //musicClass.SetMethod("Destroy", &Nz::Music::Destroy); + + musicClass.BindMethod("EnableLooping", &Nz::Music::EnableLooping); + + musicClass.BindMethod("GetDuration", &Nz::Music::GetDuration); + musicClass.BindMethod("GetFormat", &Nz::Music::GetFormat); + musicClass.BindMethod("GetPlayingOffset", &Nz::Music::GetPlayingOffset); + musicClass.BindMethod("GetSampleCount", &Nz::Music::GetSampleCount); + musicClass.BindMethod("GetSampleRate", &Nz::Music::GetSampleRate); + musicClass.BindMethod("GetStatus", &Nz::Music::GetStatus); + + musicClass.BindMethod("IsLooping", &Nz::Music::IsLooping); + + musicClass.BindMethod("OpenFromFile", &Nz::Music::OpenFromFile, Nz::MusicParams()); + + musicClass.BindMethod("Pause", &Nz::Music::Pause); + musicClass.BindMethod("Play", &Nz::Music::Play); + + musicClass.BindMethod("SetPlayingOffset", &Nz::Music::SetPlayingOffset); + + musicClass.BindMethod("Stop", &Nz::Music::Stop); + + // Manual + musicClass.BindMethod("__tostring", [] (Nz::LuaInstance& lua, Nz::Music& music) -> int + { + Nz::StringStream stream("Music("); + stream << music.GetFilePath() << ')'; + + lua.PushString(stream); + return 1; + }); + + /*********************************** Nz::Sound **********************************/ + soundClass.Inherit(soundEmitter); + + soundClass.SetConstructor([] (Nz::LuaInstance& lua) -> Nz::Sound* + { + return new Nz::Sound; + }); + + soundClass.BindMethod("GetBuffer", &Nz::Sound::GetBuffer); + + soundClass.BindMethod("IsPlayable", &Nz::Sound::IsPlayable); + soundClass.BindMethod("IsPlaying", &Nz::Sound::IsPlaying); + + soundClass.BindMethod("LoadFromFile", &Nz::Sound::LoadFromFile, Nz::SoundBufferParams()); + + soundClass.BindMethod("SetPlayingOffset", &Nz::Sound::SetPlayingOffset); + + // Manual + soundClass.BindMethod("__tostring", [] (Nz::LuaInstance& lua, Nz::Sound& sound) -> int + { + Nz::StringStream stream("Sound("); + if (const Nz::SoundBuffer* buffer = sound.GetBuffer()) + stream << buffer; + + stream << ')'; + + lua.PushString(stream); + return 1; + }); + + /*********************************** Nz::SoundBuffer **********************************/ + soundBuffer.SetConstructor([] (Nz::LuaInstance& lua) -> Nz::SoundBufferRef* + { + return new Nz::SoundBufferRef(new Nz::SoundBuffer); + }); + + soundBuffer.BindMethod("Destroy", &Nz::SoundBuffer::Destroy); + + soundBuffer.BindMethod("GetDuration", &Nz::SoundBuffer::GetDuration); + soundBuffer.BindMethod("GetFormat", &Nz::SoundBuffer::GetFormat); + soundBuffer.BindMethod("GetSampleCount", &Nz::SoundBuffer::GetSampleCount); + soundBuffer.BindMethod("GetSampleRate", &Nz::SoundBuffer::GetSampleRate); + + soundBuffer.BindMethod("IsValid", &Nz::SoundBuffer::IsValid); + + soundBuffer.BindMethod("LoadFromFile", &Nz::SoundBuffer::LoadFromFile, Nz::SoundBufferParams()); + + soundBuffer.BindStaticMethod("IsFormatSupported", &Nz::SoundBuffer::IsFormatSupported); + + // Manual + soundBuffer.BindMethod("Create", [] (Nz::LuaInstance& lua, Nz::SoundBufferRef& instance) -> int + { + int index = 1; + Nz::AudioFormat format = lua.Check(&index); + unsigned int sampleCount = lua.Check(&index); + unsigned int sampleRate = lua.Check(&index); + + std::size_t bufferSize = 0; + const char* buffer = lua.CheckString(index, &bufferSize); + lua.ArgCheck(buffer && bufferSize >= sampleCount * sizeof(Nz::Int16), index, "Invalid buffer"); + + lua.PushBoolean(instance->Create(format, sampleCount, sampleRate, reinterpret_cast(buffer))); + return 1; + }); + + soundBuffer.BindMethod("GetSamples", [] (Nz::LuaInstance& lua, Nz::SoundBufferRef& instance) -> int + { + lua.PushString(reinterpret_cast(instance->GetSamples()), instance->GetSampleCount() * sizeof(Nz::Int16)); + return 1; + }); + + soundBuffer.BindMethod("__tostring", [] (Nz::LuaInstance& lua, Nz::SoundBufferRef& soundBuffer) -> int + { + Nz::StringStream stream("SoundBuffer("); + if (soundBuffer->IsValid()) + { + Nz::String filePath = soundBuffer->GetFilePath(); + if (!filePath.IsEmpty()) + stream << "File: " << filePath << ", "; + + stream << "Duration: " << soundBuffer->GetDuration() / 1000.f << "s"; + } + stream << ')'; + + lua.PushString(stream); + return 1; + }); + + /*********************************** Nz::SoundEmitter **********************************/ + soundEmitter.BindMethod("EnableLooping", &Nz::SoundEmitter::EnableLooping); + soundEmitter.BindMethod("EnableSpatialization", &Nz::SoundEmitter::EnableSpatialization); + + soundEmitter.BindMethod("GetAttenuation", &Nz::SoundEmitter::GetAttenuation); + soundEmitter.BindMethod("GetDuration", &Nz::SoundEmitter::GetDuration); + soundEmitter.BindMethod("GetMinDistance", &Nz::SoundEmitter::GetMinDistance); + soundEmitter.BindMethod("GetPitch", &Nz::SoundEmitter::GetPitch); + soundEmitter.BindMethod("GetPlayingOffset", &Nz::SoundEmitter::GetPlayingOffset); + soundEmitter.BindMethod("GetPosition", &Nz::Sound::GetPosition); + soundEmitter.BindMethod("GetStatus", &Nz::SoundEmitter::GetStatus); + soundEmitter.BindMethod("GetVelocity", &Nz::Sound::GetVelocity); + soundEmitter.BindMethod("GetVolume", &Nz::SoundEmitter::GetVolume); + + 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); + } + + void LuaBinding::RegisterAudio(Nz::LuaInstance& instance) + { + musicClass.Register(instance); + soundClass.Register(instance); + soundBuffer.Register(instance); + soundEmitter.Register(instance); + } +} diff --git a/SDK/src/NDK/LuaBinding_Core.cpp b/SDK/src/NDK/LuaBinding_Core.cpp new file mode 100644 index 000000000..a4676284e --- /dev/null +++ b/SDK/src/NDK/LuaBinding_Core.cpp @@ -0,0 +1,280 @@ +// This file was automatically generated on 26 May 2014 at 01:05:31 + +#include +#include + +namespace Ndk +{ + void LuaBinding::BindCore() + { + /*********************************** Nz::Clock **********************************/ + clockClass.SetConstructor([](Nz::LuaInstance& lua) -> Nz::Clock* + { + int argIndex = 1; + return new Nz::Clock(lua.Check(&argIndex, 0), lua.Check(&argIndex, false)); + }); + + clockClass.BindMethod("GetMicroseconds", &Nz::Clock::GetMicroseconds); + clockClass.BindMethod("GetMilliseconds", &Nz::Clock::GetMilliseconds); + clockClass.BindMethod("GetSeconds", &Nz::Clock::GetSeconds); + clockClass.BindMethod("IsPaused", &Nz::Clock::IsPaused); + clockClass.BindMethod("Pause", &Nz::Clock::Pause); + clockClass.BindMethod("Restart", &Nz::Clock::Restart); + clockClass.BindMethod("Unpause", &Nz::Clock::Unpause); + + // Manual + clockClass.BindMethod("__tostring", [] (Nz::LuaInstance& lua, Nz::Clock& clock) -> int { + Nz::StringStream stream("Clock(Elapsed: "); + stream << clock.GetSeconds(); + stream << "s, Paused: "; + stream << clock.IsPaused(); + stream << ')'; + + lua.PushString(stream); + return 1; + }); + + /********************************* Nz::Directory ********************************/ + directoryClass.SetConstructor([](Nz::LuaInstance& lua) -> Nz::Directory* + { + unsigned int argCount = std::min(lua.GetStackTop(), 1U); + + int argIndex = 1; + switch (argCount) + { + case 0: + return new Nz::Directory; + + case 1: + return new Nz::Directory(lua.Check(&argIndex)); + } + + return nullptr; + }); + + directoryClass.BindMethod("Close", &Nz::Directory::Close); + directoryClass.BindMethod("Exists", &Nz::Directory::Exists); + directoryClass.BindMethod("GetPath", &Nz::Directory::GetPath); + directoryClass.BindMethod("GetPattern", &Nz::Directory::GetPattern); + directoryClass.BindMethod("GetResultName", &Nz::Directory::GetResultName); + directoryClass.BindMethod("GetResultPath", &Nz::Directory::GetResultPath); + directoryClass.BindMethod("GetResultSize", &Nz::Directory::GetResultSize); + directoryClass.BindMethod("IsOpen", &Nz::Directory::IsOpen); + directoryClass.BindMethod("IsResultDirectory", &Nz::Directory::IsResultDirectory); + directoryClass.BindMethod("NextResult", &Nz::Directory::NextResult, true); + directoryClass.BindMethod("Open", &Nz::Directory::Open); + directoryClass.BindMethod("SetPath", &Nz::Directory::SetPath); + directoryClass.BindMethod("SetPattern", &Nz::Directory::SetPattern); + + directoryClass.BindStaticMethod("Copy", Nz::Directory::Copy); + directoryClass.BindStaticMethod("Create", Nz::Directory::Create); + directoryClass.BindStaticMethod("Exists", Nz::Directory::Exists); + directoryClass.BindStaticMethod("GetCurrent", Nz::Directory::GetCurrent); + directoryClass.BindStaticMethod("Remove", Nz::Directory::Remove); + directoryClass.BindStaticMethod("SetCurrent", Nz::Directory::SetCurrent); + + // Manual + directoryClass.BindMethod("__tostring", [] (Nz::LuaInstance& lua, Nz::Directory& directory) -> int { + Nz::StringStream stream("Directory("); + stream << directory.GetPath(); + stream << ')'; + + lua.PushString(stream); + return 1; + }); + + /*********************************** Nz::Stream ***********************************/ + streamClass.BindMethod("EnableTextMode", &Nz::Stream::EnableTextMode); + streamClass.BindMethod("Flush", &Nz::Stream::Flush); + streamClass.BindMethod("GetCursorPos", &Nz::Stream::GetCursorPos); + streamClass.BindMethod("GetDirectory", &Nz::Stream::GetDirectory); + streamClass.BindMethod("GetPath", &Nz::Stream::GetPath); + streamClass.BindMethod("GetOpenMode", &Nz::Stream::GetOpenMode); + streamClass.BindMethod("GetStreamOptions", &Nz::Stream::GetStreamOptions); + streamClass.BindMethod("GetSize", &Nz::Stream::GetSize); + streamClass.BindMethod("ReadLine", &Nz::Stream::ReadLine, 0U); + streamClass.BindMethod("IsReadable", &Nz::Stream::IsReadable); + streamClass.BindMethod("IsSequential", &Nz::Stream::IsSequential); + streamClass.BindMethod("IsTextModeEnabled", &Nz::Stream::IsTextModeEnabled); + streamClass.BindMethod("IsWritable", &Nz::Stream::IsWritable); + streamClass.BindMethod("SetCursorPos", &Nz::Stream::SetCursorPos); + + streamClass.BindMethod("Read", [] (Nz::LuaInstance& lua, Nz::Stream& stream) -> int { + int argIndex = 1; + + std::size_t length = lua.Check(&argIndex); + + std::unique_ptr buffer(new char[length]); + std::size_t readLength = stream.Read(buffer.get(), length); + + lua.PushString(Nz::String(buffer.get(), readLength)); + return 1; + }); + + streamClass.BindMethod("Write", [] (Nz::LuaInstance& lua, Nz::Stream& stream) -> int { + int argIndex = 1; + + std::size_t bufferSize = 0; + const char* buffer = lua.CheckString(argIndex, &bufferSize); + + if (stream.IsTextModeEnabled()) + lua.Push(stream.Write(Nz::String(buffer, bufferSize))); + else + lua.Push(stream.Write(buffer, bufferSize)); + return 1; + }); + + /*********************************** Nz::File ***********************************/ + fileClass.Inherit(streamClass); + + fileClass.SetConstructor([](Nz::LuaInstance& lua) -> Nz::File* + { + unsigned int argCount = std::min(lua.GetStackTop(), 2U); + + int argIndex = 1; + switch (argCount) + { + case 0: + return new Nz::File; + + case 1: + return new Nz::File(lua.Check(&argIndex)); + + case 2: + return new Nz::File(lua.Check(&argIndex), lua.Check(&argIndex)); + } + + return nullptr; + }); + + fileClass.BindMethod("Close", &Nz::File::Close); + fileClass.BindMethod("Copy", &Nz::File::Copy); + fileClass.BindMethod("Delete", &Nz::File::Delete); + fileClass.BindMethod("EndOfFile", &Nz::File::EndOfFile); + fileClass.BindMethod("Exists", &Nz::File::Exists); + fileClass.BindMethod("GetCreationTime", &Nz::File::GetCreationTime); + fileClass.BindMethod("GetFileName", &Nz::File::GetFileName); + fileClass.BindMethod("GetLastAccessTime", &Nz::File::GetLastAccessTime); + fileClass.BindMethod("GetLastWriteTime", &Nz::File::GetLastWriteTime); + fileClass.BindMethod("IsOpen", &Nz::File::IsOpen); + fileClass.BindMethod("Rename", &Nz::File::GetLastWriteTime); + fileClass.BindMethod("GetLastWriteTime", &Nz::File::GetLastWriteTime); + fileClass.BindMethod("SetFile", &Nz::File::GetLastWriteTime); + + fileClass.BindStaticMethod("AbsolutePath", &Nz::File::AbsolutePath); + fileClass.BindStaticMethod("ComputeHash", (Nz::ByteArray (*)(Nz::HashType, const Nz::String&)) &Nz::File::ComputeHash); + fileClass.BindStaticMethod("Copy", &Nz::File::Copy); + fileClass.BindStaticMethod("Delete", &Nz::File::Delete); + fileClass.BindStaticMethod("Exists", &Nz::File::Exists); + //fileClass.SetStaticMethod("GetCreationTime", &Nz::File::GetCreationTime); + fileClass.BindStaticMethod("GetDirectory", &Nz::File::GetDirectory); + //fileClass.SetStaticMethod("GetLastAccessTime", &Nz::File::GetLastAccessTime); + //fileClass.SetStaticMethod("GetLastWriteTime", &Nz::File::GetLastWriteTime); + fileClass.BindStaticMethod("GetSize", &Nz::File::GetSize); + fileClass.BindStaticMethod("IsAbsolute", &Nz::File::IsAbsolute); + fileClass.BindStaticMethod("NormalizePath", &Nz::File::NormalizePath); + fileClass.BindStaticMethod("NormalizeSeparators", &Nz::File::NormalizeSeparators); + fileClass.BindStaticMethod("Rename", &Nz::File::Rename); + + // Manual + fileClass.BindMethod("Open", [] (Nz::LuaInstance& lua, Nz::File& file) -> int + { + unsigned int argCount = std::min(lua.GetStackTop(), 2U); + + int argIndex = 1; + switch (argCount) + { + case 0: + case 1: + return lua.Push(file.Open(lua.Check(&argIndex, Nz::OpenMode_NotOpen))); + + case 2: + return lua.Push(file.Open(lua.Check(&argIndex), lua.Check(&argIndex, Nz::OpenMode_NotOpen))); + } + + lua.Error("No matching overload for method Open"); + return 0; + }); + + fileClass.BindMethod("SetCursorPos", [] (Nz::LuaInstance& lua, Nz::File& file) -> int + { + unsigned int argCount = std::min(lua.GetStackTop(), 2U); + + int argIndex = 1; + switch (argCount) + { + case 1: + return lua.Push(file.SetCursorPos(lua.Check(&argIndex))); + + case 2: + return lua.Push(file.SetCursorPos(lua.Check(&argIndex), lua.Check(&argIndex))); + } + + lua.Error("No matching overload for method SetCursorPos"); + return 0; + }); + + fileClass.BindMethod("__tostring", [] (Nz::LuaInstance& lua, Nz::File& file) -> int { + Nz::StringStream stream("File("); + if (file.IsOpen()) + stream << "Path: " << file.GetPath(); + + stream << ')'; + + lua.PushString(stream); + return 1; + }); + } + + void LuaBinding::RegisterCore(Nz::LuaInstance& instance) + { + // Classes + clockClass.Register(instance); + directoryClass.Register(instance); + fileClass.Register(instance); + streamClass.Register(instance); + + // Enums + + // Nz::CursorPosition + static_assert(Nz::CursorPosition_Max + 1 == 3, "Nz::CursorPosition has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 3); + { + instance.SetField("AtBegin", Nz::CursorPosition_AtBegin); + instance.SetField("AtCurrent", Nz::CursorPosition_AtCurrent); + instance.SetField("AtEnd", Nz::CursorPosition_AtEnd); + } + instance.SetGlobal("CursorPosition"); + + // Nz::HashType + static_assert(Nz::HashType_Max + 1 == 9, "Nz::HashType has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 9); + { + instance.SetField("CRC32", Nz::HashType_CRC32); + instance.SetField("Fletcher16", Nz::HashType_Fletcher16); + instance.SetField("MD5", Nz::HashType_MD5); + instance.SetField("SHA1", Nz::HashType_SHA1); + instance.SetField("SHA224", Nz::HashType_SHA224); + instance.SetField("SHA256", Nz::HashType_SHA256); + instance.SetField("SHA384", Nz::HashType_SHA384); + instance.SetField("SHA512", Nz::HashType_SHA512); + instance.SetField("Whirlpool", Nz::HashType_Whirlpool); + } + instance.SetGlobal("HashType"); + + // Nz::OpenMode + static_assert(Nz::OpenMode_Max + 1 == 2 * (64), "Nz::OpenModeFlags has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 8); + { + instance.SetField("Append", Nz::OpenMode_Append); + instance.SetField("NotOpen", Nz::OpenMode_NotOpen); + instance.SetField("Lock", Nz::OpenMode_Lock); + instance.SetField("ReadOnly", Nz::OpenMode_ReadOnly); + instance.SetField("ReadWrite", Nz::OpenMode_ReadWrite); + instance.SetField("Text", Nz::OpenMode_Text); + instance.SetField("Truncate", Nz::OpenMode_Truncate); + instance.SetField("WriteOnly", Nz::OpenMode_WriteOnly); + } + instance.SetGlobal("OpenMode"); + } +} diff --git a/SDK/src/NDK/LuaBinding_Graphics.cpp b/SDK/src/NDK/LuaBinding_Graphics.cpp new file mode 100644 index 000000000..3d7597dd5 --- /dev/null +++ b/SDK/src/NDK/LuaBinding_Graphics.cpp @@ -0,0 +1,47 @@ +// 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 LuaBinding::BindGraphics() + { + /*********************************** Nz::InstancedRenderable ***********************************/ + + /*********************************** Nz::Model ***********************************/ + modelClass.Inherit(instancedRenderable, [] (Nz::ModelRef* model) -> Nz::InstancedRenderableRef* + { + return reinterpret_cast(model); //TODO: Make a ObjectRefCast + }); + + modelClass.SetConstructor([] (Nz::LuaInstance& lua) -> Nz::ModelRef* + { + return new Nz::ModelRef(new Nz::Model); + }); + + //modelClass.SetMethod("GetMaterial", &Nz::Model::GetMaterial); + modelClass.BindMethod("GetMaterialCount", &Nz::Model::GetMaterialCount); + //modelClass.SetMethod("GetMesh", &Nz::Model::GetMesh); + modelClass.BindMethod("GetSkin", &Nz::Model::GetSkin); + modelClass.BindMethod("GetSkinCount", &Nz::Model::GetSkinCount); + + modelClass.BindMethod("IsAnimated", &Nz::Model::IsAnimated); + modelClass.BindMethod("LoadFromFile", &Nz::Model::LoadFromFile, Nz::ModelParameters()); + + modelClass.BindMethod("Reset", &Nz::Model::Reset); + + //modelClass.SetMethod("SetMaterial", &Nz::Model::SetMaterial); + //modelClass.SetMethod("SetMesh", &Nz::Model::SetMesh); + //modelClass.SetMethod("SetSequence", &Nz::Model::SetSequence); + modelClass.BindMethod("SetSkin", &Nz::Model::SetSkin); + modelClass.BindMethod("SetSkinCount", &Nz::Model::SetSkinCount); + } + + void LuaBinding::RegisterGraphics(Nz::LuaInstance& instance) + { + instancedRenderable.Register(instance); + modelClass.Register(instance); + } +} \ No newline at end of file diff --git a/SDK/src/NDK/LuaBinding_Math.cpp b/SDK/src/NDK/LuaBinding_Math.cpp new file mode 100644 index 000000000..808c93f86 --- /dev/null +++ b/SDK/src/NDK/LuaBinding_Math.cpp @@ -0,0 +1,482 @@ +// This file was automatically generated on 26 May 2014 at 01:05:31 + +#include +#include +#include + +namespace Ndk +{ + void LuaBinding::BindMath() + { + /*********************************** Nz::EulerAngles **********************************/ + eulerAnglesClass.SetConstructor([] (Nz::LuaInstance& lua) -> Nz::EulerAnglesd* + { + unsigned int argCount = std::min(lua.GetStackTop(), 3U); + switch (argCount) + { + case 0: + return new Nz::EulerAnglesd(0.0, 0.0, 0.0); + + case 1: + return new Nz::EulerAnglesd(*(*static_cast(lua.CheckUserdata(1, "EulerAngles")))); + + case 3: + return new Nz::EulerAnglesd(lua.CheckNumber(1), lua.CheckNumber(2), lua.CheckNumber(3)); + } + + lua.Error("No matching overload for EulerAngles constructor"); + return nullptr; + }); + + eulerAnglesClass.BindMethod("__tostring", &Nz::EulerAnglesd::ToString); + + eulerAnglesClass.SetGetter([] (Nz::LuaInstance& lua, Nz::EulerAnglesd& instance) + { + std::size_t length; + const char* ypr = lua.CheckString(1, &length); + + switch (length) + { + case 1: + { + switch (ypr[0]) + { + case 'p': + lua.Push(instance.pitch); + return true; + + case 'y': + lua.Push(instance.yaw); + return true; + + case 'r': + lua.Push(instance.roll); + return true; + } + break; + } + + case 3: + { + if (std::memcmp(ypr, "yaw", 3) != 0) + break; + + lua.Push(instance.yaw); + return true; + } + + case 4: + { + if (std::memcmp(ypr, "roll", 4) != 0) + break; + + lua.Push(instance.roll); + return true; + } + + case 5: + { + if (std::memcmp(ypr, "pitch", 5) != 0) + break; + + lua.Push(instance.pitch); + return true; + } + } + + return false; + }); + + eulerAnglesClass.SetSetter([] (Nz::LuaInstance& lua, Nz::EulerAnglesd& instance) + { + std::size_t length; + const char* ypr = lua.CheckString(1, &length); + double value = lua.CheckNumber(2); + + switch (length) + { + case 1: + { + switch (ypr[0]) + { + case 'p': + instance.pitch = value; + return true; + + case 'y': + instance.yaw = value; + return true; + + case 'r': + instance.roll = value; + return true; + } + break; + } + + case 3: + { + if (std::memcmp(ypr, "yaw", 3) != 0) + break; + + instance.yaw = value; + return true; + } + + case 4: + { + if (std::memcmp(ypr, "roll", 4) != 0) + break; + + instance.roll = value; + return true; + } + + case 5: + { + if (std::memcmp(ypr, "pitch", 5) != 0) + break; + + instance.pitch = value; + return true; + } + } + + return false; + }); + + /*********************************** Nz::Quaternion **********************************/ + quaternionClass.SetConstructor([] (Nz::LuaInstance& lua) -> Nz::Quaterniond* + { + unsigned int argCount = std::min(lua.GetStackTop(), 4U); + switch (argCount) + { + case 0: + return new Nz::Quaterniond(1.0, 0.0, 0.0, 0.0); + + case 1: + { + if (lua.IsOfType(1, "EulerAngles")) + return new Nz::Quaterniond(*(*static_cast(lua.ToUserdata(1)))); + else if (lua.IsOfType(1, "Quaternion")) + return new Nz::Quaterniond(*(*static_cast(lua.ToUserdata(1)))); + } + + case 2: + return new Nz::Quaterniond(lua.CheckNumber(1), *(*static_cast(lua.CheckUserdata(2, "Vector3")))); + + case 4: + return new Nz::Quaterniond(lua.CheckNumber(1), lua.CheckNumber(2), lua.CheckNumber(3), lua.CheckNumber(4)); + } + + lua.Error("No matching overload for Quaternion constructor"); + return nullptr; + }); + + quaternionClass.BindMethod("__tostring", &Nz::Quaterniond::ToString); + + quaternionClass.SetGetter([] (Nz::LuaInstance& lua, Nz::Quaterniond& instance) + { + std::size_t length; + const char* wxyz = lua.CheckString(1, &length); + + if (length != 1) + return false; + + switch (wxyz[0]) + { + case 'w': + lua.Push(instance.w); + return true; + + case 'x': + lua.Push(instance.x); + return true; + + case 'y': + lua.Push(instance.y); + return true; + + case 'z': + lua.Push(instance.z); + return true; + } + + return false; + }); + + quaternionClass.SetSetter([] (Nz::LuaInstance& lua, Nz::Quaterniond& instance) + { + std::size_t length; + const char* wxyz = lua.CheckString(1, &length); + + if (length != 1) + return false; + + double value = lua.CheckNumber(2); + + switch (wxyz[0]) + { + case 'w': + instance.w = value; + return true; + + case 'x': + instance.x = value; + return true; + + case 'y': + instance.y = value; + return true; + + case 'z': + instance.z = value; + return true; + } + + return false; + }); + + /*********************************** Nz::Vector2 **********************************/ + vector2dClass.SetConstructor([](Nz::LuaInstance& lua) -> Nz::Vector2d* + { + unsigned int argCount = std::min(lua.GetStackTop(), 2U); + switch (argCount) + { + case 0: + case 2: + return new Nz::Vector2d(lua.CheckNumber(1, 0.0), lua.CheckNumber(2, 0.0)); + + case 1: + { + if (lua.IsOfType(1, Nz::LuaType_Number)) + return new Nz::Vector2d(lua.CheckNumber(1)); + else if (lua.IsOfType(1, "Vector2")) + return new Nz::Vector2d(*(*static_cast(lua.ToUserdata(1)))); + + break; + } + } + + lua.Error("No matching overload for Vector2 constructor"); + return nullptr; + }); + + vector2dClass.BindMethod("__tostring", &Nz::Vector2d::ToString); + + vector2dClass.SetGetter([](Nz::LuaInstance& lua, Nz::Vector2d& instance) + { + switch (lua.GetType(1)) + { + case Nz::LuaType_Number: + { + long long index = lua.CheckInteger(1); + if (index < 1 || index > 2) + return false; + + lua.Push(instance[index - 1]); + return true; + } + + case Nz::LuaType_String: + { + std::size_t length; + const char* xy = lua.CheckString(1, &length); + + if (length != 1) + break; + + switch (xy[0]) + { + case 'x': + lua.Push(instance.x); + return true; + + case 'y': + lua.Push(instance.y); + return true; + } + break; + } + } + + return false; + }); + + vector2dClass.SetSetter([](Nz::LuaInstance& lua, Nz::Vector2d& instance) + { + switch (lua.GetType(1)) + { + case Nz::LuaType_Number: + { + long long index = lua.CheckInteger(1); + if (index < 1 || index > 2) + return false; + + instance[index - 1] = lua.CheckNumber(2); + return true; + } + + case Nz::LuaType_String: + { + std::size_t length; + const char* xy = lua.CheckString(1, &length); + + if (length != 1) + break; + + double value = lua.CheckNumber(2); + + switch (xy[0]) + { + case 'x': + instance.x = value; + return true; + + case 'y': + instance.y = value; + return true; + } + break; + } + } + + return false; + }); + + /*********************************** Nz::Vector3 **********************************/ + vector3dClass.SetConstructor([] (Nz::LuaInstance& lua) -> Nz::Vector3d* + { + unsigned int argCount = std::min(lua.GetStackTop(), 3U); + switch (argCount) + { + case 0: + case 3: + return new Nz::Vector3d(lua.CheckNumber(1, 0.0), lua.CheckNumber(2, 0.0), lua.CheckNumber(3, 0.0)); + + case 1: + { + if (lua.IsOfType(1, Nz::LuaType_Number)) + return new Nz::Vector3d(lua.CheckNumber(1), *(*static_cast(lua.ToUserdata(1)))); + else if (lua.IsOfType(1, "Vector2")) + return new Nz::Vector3d(*(*static_cast(lua.ToUserdata(1)))); + else if (lua.IsOfType(1, "Vector3")) + return new Nz::Vector3d(*(*static_cast(lua.ToUserdata(1)))); + + break; + } + + case 2: + { + if (lua.IsOfType(1, Nz::LuaType_Number)) + return new Nz::Vector3d(lua.CheckNumber(1), *(*static_cast(lua.CheckUserdata(1, "Vector2")))); + else if (lua.IsOfType(1, "Vector2")) + return new Nz::Vector3d(*(*static_cast(lua.ToUserdata(1))), lua.CheckNumber(2)); + + break; + } + } + + lua.Error("No matching overload for constructor"); + return nullptr; + }); + + vector3dClass.BindMethod("__tostring", &Nz::Vector3d::ToString); + + vector3dClass.SetGetter([] (Nz::LuaInstance& lua, Nz::Vector3d& instance) + { + switch (lua.GetType(1)) + { + case Nz::LuaType_Number: + { + long long index = lua.CheckInteger(1); + if (index < 1 || index > 3) + return false; + + lua.Push(instance[index - 1]); + return true; + } + + case Nz::LuaType_String: + { + std::size_t length; + const char* xyz = lua.CheckString(1, &length); + + if (length != 1) + break; + + switch (xyz[0]) + { + case 'x': + lua.Push(instance.x); + return true; + + case 'y': + lua.Push(instance.y); + return true; + + case 'z': + lua.Push(instance.z); + return true; + } + break; + } + } + + return false; + }); + + vector3dClass.SetSetter([] (Nz::LuaInstance& lua, Nz::Vector3d& instance) + { + switch (lua.GetType(1)) + { + case Nz::LuaType_Number: + { + long long index = lua.CheckInteger(1); + if (index < 1 || index > 3) + return false; + + instance[index - 1] = lua.CheckNumber(2); + return true; + } + + case Nz::LuaType_String: + { + std::size_t length; + const char* xyz = lua.CheckString(1, &length); + + if (length != 1) + break; + + double value = lua.CheckNumber(2); + + switch (xyz[0]) + { + case 'x': + instance.x = value; + return true; + + case 'y': + instance.y = value; + return true; + + case 'z': + instance.z = value; + return true; + } + break; + } + } + + return false; + }); + } + + void LuaBinding::RegisterMath(Nz::LuaInstance& instance) + { + eulerAnglesClass.Register(instance); + quaternionClass.Register(instance); + vector2dClass.Register(instance); + vector3dClass.Register(instance); + } +} diff --git a/SDK/src/NDK/LuaBinding_Network.cpp b/SDK/src/NDK/LuaBinding_Network.cpp new file mode 100644 index 000000000..4b8334017 --- /dev/null +++ b/SDK/src/NDK/LuaBinding_Network.cpp @@ -0,0 +1,214 @@ +// This file was automatically generated on 26 May 2014 at 01:05:31 + +#include +#include + +namespace Ndk +{ + void LuaBinding::BindNetwork() + { + /*********************************** Nz::AbstractSocket **********************************/ + abstractSocketClass.BindMethod("Close", &Nz::AbstractSocket::Close); + abstractSocketClass.BindMethod("EnableBlocking", &Nz::AbstractSocket::EnableBlocking); + abstractSocketClass.BindMethod("GetLastError", &Nz::AbstractSocket::GetLastError); + abstractSocketClass.BindMethod("GetState", &Nz::AbstractSocket::GetState); + abstractSocketClass.BindMethod("GetType", &Nz::AbstractSocket::GetType); + abstractSocketClass.BindMethod("IsBlockingEnabled", &Nz::AbstractSocket::IsBlockingEnabled); + abstractSocketClass.BindMethod("QueryAvailableBytes", &Nz::AbstractSocket::QueryAvailableBytes); + + /*********************************** Nz::IpAddress **********************************/ + ipAddressClass.SetConstructor([] (Nz::LuaInstance& lua) -> Nz::IpAddress* + { + unsigned int argCount = std::min(lua.GetStackTop(), 9U); + + int argIndex = 1; + switch (argCount) + { + case 0: + return new Nz::IpAddress; + + case 1: + return new Nz::IpAddress(lua.CheckString(argIndex)); + + case 4: + case 5: + return new Nz::IpAddress(lua.Check(&argIndex), lua.Check(&argIndex), lua.Check(&argIndex), lua.Check(&argIndex), lua.Check(&argIndex, 0)); + + case 8: + case 9: + return new Nz::IpAddress(lua.Check(&argIndex), lua.Check(&argIndex), lua.Check(&argIndex), lua.Check(&argIndex), + lua.Check(&argIndex), lua.Check(&argIndex), lua.Check(&argIndex), lua.Check(&argIndex), lua.Check(&argIndex, 0)); + } + + return nullptr; + }); + + ipAddressClass.BindMethod("GetPort", &Nz::IpAddress::GetPort); + ipAddressClass.BindMethod("GetProtocol", &Nz::IpAddress::GetProtocol); + ipAddressClass.BindMethod("IsLoopback", &Nz::IpAddress::IsLoopback); + ipAddressClass.BindMethod("IsValid", &Nz::IpAddress::IsValid); + ipAddressClass.BindMethod("ToUInt32", &Nz::IpAddress::ToUInt32); + ipAddressClass.BindMethod("__tostring", &Nz::IpAddress::ToString); + + ipAddressClass.BindStaticMethod("ResolveAddress", [] (Nz::LuaInstance& instance) -> int + { + Nz::String service; + Nz::ResolveError error = Nz::ResolveError_Unknown; + + int argIndex = 1; + Nz::String hostName = Nz::IpAddress::ResolveAddress(instance.Check(&argIndex), &service, &error); + + if (error == Nz::ResolveError_NoError) + { + instance.Push(hostName); + instance.Push(service); + return 2; + } + else + { + instance.PushBoolean(false); + instance.Push(error); + return 2; + } + }); + + ipAddressClass.BindStaticMethod("ResolveHostname", [] (Nz::LuaInstance& instance) -> int + { + Nz::ResolveError error = Nz::ResolveError_Unknown; + + int argIndex = 1; + Nz::NetProtocol protocol = instance.Check(&argIndex); + Nz::String hostname = instance.Check(&argIndex); + Nz::String service = instance.Check(&argIndex, "http"); + + std::vector addresses = Nz::IpAddress::ResolveHostname(protocol, hostname, service, &error); + if (error == Nz::ResolveError_NoError) + { + int index = 1; + instance.PushTable(addresses.size()); + for (Nz::HostnameInfo& info : addresses) + { + instance.PushInteger(index++); + instance.PushTable(0, 4); + instance.SetField("Address", std::move(info.address)); + instance.SetField("CanonicalName", std::move(info.canonicalName)); + instance.SetField("Protocol", std::move(info.protocol)); + instance.SetField("SocketType", std::move(info.socketType)); + instance.SetTable(); + } + + return 1; + } + else + { + instance.PushBoolean(false); + instance.Push(error); + return 2; + } + }); + } + + void LuaBinding::RegisterNetwork(Nz::LuaInstance& instance) + { + // Classes + abstractSocketClass.Register(instance); + ipAddressClass.Register(instance); + + // Enums + + // Nz::NetProtocol + static_assert(Nz::NetProtocol_Max + 1 == 4, "Nz::NetProtocol has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 4); + { + instance.SetField("Any", Nz::NetProtocol_Any); + instance.SetField("IPv4", Nz::NetProtocol_IPv4); + instance.SetField("IPv6", Nz::NetProtocol_IPv6); + instance.SetField("Unknown", Nz::NetProtocol_Unknown); + } + instance.SetGlobal("NetProtocol"); + + // Nz::PacketPriority + static_assert(Nz::PacketPriority_Max + 1 == 4, "Nz::PacketPriority has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 6); + { + instance.SetField("High", Nz::PacketPriority_High); + instance.SetField("Highest", Nz::PacketPriority_Highest); + instance.SetField("Immediate", Nz::PacketPriority_Immediate); + instance.SetField("Medium", Nz::PacketPriority_Medium); + instance.SetField("Low", Nz::PacketPriority_Low); + instance.SetField("Lowest", Nz::PacketPriority_Lowest); + } + instance.SetGlobal("PacketPriority"); + + // Nz::PacketReliability + static_assert(Nz::PacketReliability_Max + 1 == 3, "Nz::PacketReliability has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 3); + { + instance.SetField("Reliable", Nz::PacketReliability_Reliable); + instance.SetField("ReliableOrdered", Nz::PacketReliability_ReliableOrdered); + instance.SetField("Unreliable", Nz::PacketReliability_Unreliable); + } + instance.SetGlobal("PacketReliability"); + + // Nz::ResolveError + static_assert(Nz::ResolveError_Max + 1 == 9, "Nz::ResolveError has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 9); + { + instance.SetField("Internal", Nz::ResolveError_Internal); + instance.SetField("ResourceError", Nz::ResolveError_ResourceError); + instance.SetField("NoError", Nz::ResolveError_NoError); + instance.SetField("NonRecoverable", Nz::ResolveError_NonRecoverable); + instance.SetField("NotFound", Nz::ResolveError_NotFound); + instance.SetField("NotInitialized", Nz::ResolveError_NotInitialized); + instance.SetField("ProtocolNotSupported", Nz::ResolveError_ProtocolNotSupported); + instance.SetField("TemporaryFailure", Nz::ResolveError_TemporaryFailure); + instance.SetField("Unknown", Nz::ResolveError_Unknown); + } + instance.SetGlobal("ResolveError"); + + // Nz::SocketError + static_assert(Nz::SocketError_Max + 1 == 15, "Nz::ResolveError has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 15); + { + instance.SetField("AddressNotAvailable", Nz::SocketError_AddressNotAvailable); + instance.SetField("ConnectionClosed", Nz::SocketError_ConnectionClosed); + instance.SetField("ConnectionRefused", Nz::SocketError_ConnectionRefused); + instance.SetField("DatagramSize", Nz::SocketError_DatagramSize); + instance.SetField("Internal", Nz::SocketError_Internal); + instance.SetField("Packet", Nz::SocketError_Packet); + instance.SetField("NetworkError", Nz::SocketError_NetworkError); + instance.SetField("NoError", Nz::SocketError_NoError); + instance.SetField("NotInitialized", Nz::SocketError_NotInitialized); + instance.SetField("NotSupported", Nz::SocketError_NotSupported); + instance.SetField("ResolveError", Nz::SocketError_ResolveError); + instance.SetField("ResourceError", Nz::SocketError_ResourceError); + instance.SetField("TimedOut", Nz::SocketError_TimedOut); + instance.SetField("Unknown", Nz::SocketError_Unknown); + instance.SetField("UnreachableHost", Nz::SocketError_UnreachableHost); + } + instance.SetGlobal("SocketError"); + + // Nz::SocketState + static_assert(Nz::SocketState_Max + 1 == 5, "Nz::SocketState has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 5); + { + instance.SetField("Bound", Nz::SocketState_Bound); + instance.SetField("Connecting", Nz::SocketState_Connecting); + instance.SetField("Connected", Nz::SocketState_Connected); + instance.SetField("NotConnected", Nz::SocketState_NotConnected); + instance.SetField("Resolving", Nz::SocketState_Resolving); + } + instance.SetGlobal("SocketState"); + + // Nz::SocketType + static_assert(Nz::SocketType_Max + 1 == 4, "Nz::SocketState has been updated but change was not reflected to Lua binding"); + instance.PushTable(0, 4); + { + instance.SetField("Raw", Nz::SocketType_Raw); + instance.SetField("TCP", Nz::SocketType_TCP); + instance.SetField("UDP", Nz::SocketType_UDP); + instance.SetField("Unknown", Nz::SocketType_Unknown); + } + instance.SetGlobal("SocketType"); + } +} diff --git a/SDK/src/NDK/LuaBinding_Renderer.cpp b/SDK/src/NDK/LuaBinding_Renderer.cpp new file mode 100644 index 000000000..78b9fe561 --- /dev/null +++ b/SDK/src/NDK/LuaBinding_Renderer.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2016 Jérôme Leclercq, Arnaud Cadot +// 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 LuaBinding::BindRenderer() + { + } + + void LuaBinding::RegisterRenderer(Nz::LuaInstance& instance) + { + } +} \ No newline at end of file diff --git a/SDK/src/NDK/LuaBinding_SDK.cpp b/SDK/src/NDK/LuaBinding_SDK.cpp new file mode 100644 index 000000000..da456c732 --- /dev/null +++ b/SDK/src/NDK/LuaBinding_SDK.cpp @@ -0,0 +1,253 @@ +// Copyright (C) 2016 Jérôme Leclercq, Arnaud Cadot +// 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 +{ + namespace + { + int AddNilComponent(Nz::LuaInstance& lua, EntityHandle&) + { + lua.PushNil(); + return 1; + } + + template + int AddComponentOfType(Nz::LuaInstance& lua, EntityHandle& handle) + { + static_assert(std::is_base_of::value, "ComponentType must inherit BaseComponent"); + + T& component = handle->AddComponent(); + lua.Push(component.CreateHandle()); + return 1; + } + + int PushNilComponent(Nz::LuaInstance& lua, BaseComponent&) + { + lua.PushNil(); + return 1; + } + + template + int PushComponentOfType(Nz::LuaInstance& lua, BaseComponent& component) + { + static_assert(std::is_base_of::value, "ComponentType must inherit BaseComponent"); + + T& rightComponent = static_cast(component); + lua.Push(rightComponent.CreateHandle()); + return 1; + } + } + + void LuaBinding::BindSDK() + { + /*********************************** Ndk::Application **********************************/ + + #ifndef NDK_SERVER + //application.SetMethod("AddWindow", &Application::AddWindow); + #endif + application.BindMethod("AddWorld", [] (Nz::LuaInstance& instance, Application* application) -> int + { + instance.Push(application->AddWorld().CreateHandle()); + return 1; + }); + + application.BindMethod("GetUpdateTime", &Application::GetUpdateTime); + application.BindMethod("Quit", &Application::Quit); + + /*********************************** Ndk::Console **********************************/ + #ifndef NDK_SERVER + consoleClass.Inherit(nodeClass, [] (ConsoleHandle* handle) -> Nz::Node* + { + return handle->GetObject(); + }); + + consoleClass.BindMethod("AddLine", &Console::AddLine, Nz::Color::White); + consoleClass.BindMethod("Clear", &Console::Clear); + consoleClass.BindMethod("GetCharacterSize", &Console::GetCharacterSize); + consoleClass.BindMethod("GetHistory", &Console::GetHistory); + consoleClass.BindMethod("GetHistoryBackground", &Console::GetHistoryBackground); + consoleClass.BindMethod("GetInput", &Console::GetInput); + consoleClass.BindMethod("GetInputBackground", &Console::GetInputBackground); + consoleClass.BindMethod("GetSize", &Console::GetSize); + //consoleClass.SetMethod("GetTextFont", &Console::GetTextFont); + + consoleClass.BindMethod("IsVisible", &Console::IsVisible); + + consoleClass.BindMethod("SendCharacter", &Console::SendCharacter); + //consoleClass.SetMethod("SendEvent", &Console::SendEvent); + + consoleClass.BindMethod("SetCharacterSize", &Console::SetCharacterSize); + consoleClass.BindMethod("SetSize", &Console::SetSize); + //consoleClass.SetMethod("SetTextFont", &Console::SetTextFont); + + consoleClass.BindMethod("Show", &Console::Show, true); + #endif + + /*********************************** Ndk::Entity **********************************/ + entityClass.BindMethod("Enable", &Entity::Enable); + entityClass.BindMethod("GetId", &Entity::GetId); + entityClass.BindMethod("GetWorld", &Entity::GetWorld); + entityClass.BindMethod("Kill", &Entity::Kill); + entityClass.BindMethod("IsEnabled", &Entity::IsEnabled); + entityClass.BindMethod("IsValid", &Entity::IsValid); + entityClass.BindMethod("RemoveComponent", (void(Entity::*)(ComponentIndex)) &Entity::RemoveComponent); + entityClass.BindMethod("RemoveAllComponents", &Entity::RemoveAllComponents); + entityClass.BindMethod("__tostring", &EntityHandle::ToString); + + entityClass.BindMethod("AddComponent", [this] (Nz::LuaInstance& lua, EntityHandle& handle) -> int + { + int index = 1; + ComponentIndex componentIndex = lua.Check(&index); + + if (componentIndex > m_componentBinding.size()) + { + lua.Error("Invalid component index"); + return 0; + } + + ComponentBinding& binding = m_componentBinding[componentIndex]; + if (!binding.valid) + { + lua.Error("This component is not available to the LuaAPI"); + return 0; + } + + return binding.adder(lua, handle); + }); + + entityClass.BindMethod("GetComponent", [this] (Nz::LuaInstance& lua, EntityHandle& handle) -> int + { + int index = 1; + ComponentIndex componentIndex = lua.Check(&index); + + if (!handle->HasComponent(componentIndex)) + { + lua.PushNil(); + return 1; + } + + if (componentIndex > m_componentBinding.size()) + { + lua.Error("Invalid component index"); + return 0; + } + + ComponentBinding& binding = m_componentBinding[componentIndex]; + if (!binding.valid) + { + lua.Error("This component is not available to the LuaAPI"); + return 0; + } + + return binding.getter(lua, handle->GetComponent(componentIndex)); + }); + + /*********************************** Ndk::NodeComponent **********************************/ + nodeComponent.Inherit(nodeClass, [] (NodeComponentHandle* handle) -> Nz::Node* + { + return handle->GetObject(); + }); + + /*********************************** Ndk::VelocityComponent **********************************/ + velocityComponent.SetGetter([] (Nz::LuaInstance& lua, VelocityComponentHandle& instance) + { + std::size_t length; + const char* member = lua.CheckString(1, &length); + + if (std::strcmp(member, "Linear") == 0) + { + lua.Push(instance->linearVelocity); + return true; + } + + return false; + }); + + velocityComponent.SetSetter([] (Nz::LuaInstance& lua, VelocityComponentHandle& instance) + { + std::size_t length; + const char* member = lua.CheckString(1, &length); + + int argIndex = 2; + if (std::strcmp(member, "Linear") == 0) + { + instance->linearVelocity = lua.Check(&argIndex); + return true; + } + + return false; + }); + + /*********************************** Ndk::World **********************************/ + worldClass.BindMethod("CreateEntity", &World::CreateEntity); + worldClass.BindMethod("CreateEntities", &World::CreateEntities); + worldClass.BindMethod("Clear", &World::Clear); + + + #ifndef NDK_SERVER + /*********************************** Ndk::GraphicsComponent **********************************/ + graphicsComponent.BindMethod("Attach", &GraphicsComponent::Attach, 0); + #endif + + + // Components functions + m_componentBinding.resize(BaseComponent::GetMaxComponentIndex() + 1); + + EnableComponentBinding(); + EnableComponentBinding(); + + #ifndef NDK_SERVER + EnableComponentBinding(); + #endif + } + + template + void LuaBinding::EnableComponentBinding() + { + ComponentBinding binding; + binding.adder = &AddComponentOfType; + binding.getter = &PushComponentOfType; + binding.valid = true; + + NazaraAssert(T::componentIndex < m_componentBinding.size(), "Component index is over component binding size"); + + m_componentBinding[T::componentIndex] = std::move(binding); + } + + void LuaBinding::RegisterSDK(Nz::LuaInstance& instance) + { + // Classes + application.Register(instance); + entityClass.Register(instance); + nodeComponent.Register(instance); + velocityComponent.Register(instance); + worldClass.Register(instance); + + #ifndef NDK_SERVER + consoleClass.Register(instance); + graphicsComponent.Register(instance); + #endif + + // Enums + + // ComponentType (fake enumeration to expose component indexes) + instance.PushTable(); + { + #ifndef NDK_SERVER + instance.PushInteger(GraphicsComponent::componentIndex); + instance.SetField("Graphics"); + #endif + + instance.PushInteger(NodeComponent::componentIndex); + instance.SetField("Node"); + + instance.PushInteger(VelocityComponent::componentIndex); + instance.SetField("Velocity"); + } + instance.SetGlobal("ComponentType"); + } +} diff --git a/SDK/src/NDK/LuaBinding_Utility.cpp b/SDK/src/NDK/LuaBinding_Utility.cpp new file mode 100644 index 000000000..86f4a6f9a --- /dev/null +++ b/SDK/src/NDK/LuaBinding_Utility.cpp @@ -0,0 +1,255 @@ +// 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 LuaBinding::BindUtility() + { + /*********************************** Nz::AbstractImage **********************************/ + abstractImage.BindMethod("GetBytesPerPixel", &Nz::AbstractImage::GetBytesPerPixel); + abstractImage.BindMethod("GetDepth", &Nz::AbstractImage::GetDepth, static_cast(0)); + abstractImage.BindMethod("GetFormat", &Nz::AbstractImage::GetFormat); + abstractImage.BindMethod("GetHeight", &Nz::AbstractImage::GetHeight, static_cast(0)); + abstractImage.BindMethod("GetLevelCount", &Nz::AbstractImage::GetLevelCount); + abstractImage.BindMethod("GetMaxLevel", &Nz::AbstractImage::GetMaxLevel); + abstractImage.BindMethod("GetSize", &Nz::AbstractImage::GetSize, static_cast(0)); + abstractImage.BindMethod("GetType", &Nz::AbstractImage::GetType); + abstractImage.BindMethod("GetWidth", &Nz::AbstractImage::GetWidth, static_cast(0)); + abstractImage.BindMethod("IsCompressed", &Nz::AbstractImage::IsCompressed); + abstractImage.BindMethod("IsCubemap", &Nz::AbstractImage::IsCubemap); + + abstractImage.BindMethod("GetMemoryUsage", [] (Nz::LuaInstance& lua, Nz::AbstractImage* abstractImage) -> int + { + unsigned int argCount = std::min(lua.GetStackTop(), 1U); + switch (argCount) + { + case 0: + return lua.Push(abstractImage->GetMemoryUsage()); + + case 1: + { + int index = 1; + Nz::UInt8 level(lua.Check(&index)); + + return lua.Push(abstractImage->GetMemoryUsage(level)); + } + } + + lua.Error("No matching overload for method GetMemoryUsage"); + return 0; + }); + + abstractImage.BindMethod("Update", [] (Nz::LuaInstance& lua, Nz::AbstractImage* abstractImage) -> int + { + unsigned int argCount = std::min(lua.GetStackTop(), 6U); + int argIndex = 1; + + std::size_t bufferSize = 0; + const Nz::UInt8* pixels = reinterpret_cast(lua.CheckString(argIndex++, &bufferSize)); + + if (argCount < 2 || lua.IsOfType(2, Nz::LuaType_Number)) + { + // bool Update(const UInt8* pixels, unsigned int srcWidth = 0, unsigned int srcHeight = 0, UInt8 level = 0) + unsigned int srcWidth = lua.Check(&argIndex, 0); + unsigned int srcHeight = lua.Check(&argIndex, 0); + Nz::UInt8 level = lua.Check(&argIndex, 0); + + ///TODO: Buffer checks (Nz::ByteBufferView ?) + return lua.Push(abstractImage->Update(pixels, srcWidth, srcHeight, level)); + } + /* Disabled until Box and Rect have been ported + else if (lua.IsOfType(2, "Box")) + { + // bool Update(const UInt8* pixels, const Boxui& box, unsigned int srcWidth = 0, unsigned int srcHeight = 0, UInt8 level = 0) + Nz::Boxui box = lua.Check(&argIndex); + unsigned int srcWidth = lua.Check(&argIndex, 0); + unsigned int srcHeight = lua.Check(&argIndex, 0); + Nz::UInt8 level = lua.Check(&argIndex, 0); + + ///TODO: Buffer checks (Nz::ByteBufferView ?) + return lua.Push(abstractImage->Update(pixels, srcWidth, srcHeight, level)); + } + else if (lua.IsOfType(2, "Rect")) + { + // bool Update(const UInt8* pixels, const Rectui& rect, unsigned int z = 0, unsigned int srcWidth = 0, unsigned int srcHeight = 0, UInt8 level = 0) + Nz::Rectui box = lua.Check(&argIndex); + unsigned int srcWidth = lua.Check(&argIndex, 0); + unsigned int srcHeight = lua.Check(&argIndex, 0); + Nz::UInt8 level = lua.Check(&argIndex, 0); + + ///TODO: Buffer checks (Nz::ByteBufferView ?) + return lua.Push(abstractImage->Update(pixels, srcWidth, srcHeight, level)); + }*/ + + lua.Error("No matching overload for method Update"); + return 0; + }); + + /*********************************** Nz::Node **********************************/ + nodeClass.BindMethod("GetBackward", &Nz::Node::GetBackward); + //nodeClass.SetMethod("GetChilds", &Nz::Node::GetChilds); + nodeClass.BindMethod("GetDown", &Nz::Node::GetDown); + nodeClass.BindMethod("GetForward", &Nz::Node::GetForward); + nodeClass.BindMethod("GetInheritPosition", &Nz::Node::GetInheritPosition); + nodeClass.BindMethod("GetInheritRotation", &Nz::Node::GetInheritRotation); + nodeClass.BindMethod("GetInheritScale", &Nz::Node::GetInheritScale); + nodeClass.BindMethod("GetInitialPosition", &Nz::Node::GetInitialPosition); + //nodeClass.SetMethod("GetInitialRotation", &Nz::Node::GetInitialRotation); + nodeClass.BindMethod("GetInitialScale", &Nz::Node::GetInitialScale); + nodeClass.BindMethod("GetLeft", &Nz::Node::GetLeft); + nodeClass.BindMethod("GetNodeType", &Nz::Node::GetNodeType); + //nodeClass.SetMethod("GetParent", &Nz::Node::GetParent); + nodeClass.BindMethod("GetPosition", &Nz::Node::GetPosition, Nz::CoordSys_Global); + nodeClass.BindMethod("GetRight", &Nz::Node::GetRight); + //nodeClass.SetMethod("GetRotation", &Nz::Node::GetRotation, Nz::CoordSys_Global); + nodeClass.BindMethod("GetScale", &Nz::Node::GetScale, Nz::CoordSys_Global); + //nodeClass.SetMethod("GetTransformMatrix", &Nz::Node::GetTransformMatrix); + nodeClass.BindMethod("GetUp", &Nz::Node::GetUp); + + nodeClass.BindMethod("HasChilds", &Nz::Node::HasChilds); + + nodeClass.BindMethod("GetBackward", &Nz::Node::GetBackward); + nodeClass.BindMethod("GetDown", &Nz::Node::GetDown); + nodeClass.BindMethod("GetForward", &Nz::Node::GetForward); + nodeClass.BindMethod("GetInheritPosition", &Nz::Node::GetInheritPosition); + nodeClass.BindMethod("GetInheritRotation", &Nz::Node::GetInheritRotation); + nodeClass.BindMethod("GetInheritScale", &Nz::Node::GetInheritScale); + nodeClass.BindMethod("GetInitialPosition", &Nz::Node::GetInitialPosition); + nodeClass.BindMethod("GetInitialRotation", &Nz::Node::GetInitialRotation); + nodeClass.BindMethod("GetInitialScale", &Nz::Node::GetInitialScale); + nodeClass.BindMethod("GetLeft", &Nz::Node::GetLeft); + nodeClass.BindMethod("GetNodeType", &Nz::Node::GetNodeType); + nodeClass.BindMethod("GetPosition", &Nz::Node::GetPosition, Nz::CoordSys_Global); + nodeClass.BindMethod("GetRight", &Nz::Node::GetRight); + nodeClass.BindMethod("GetRotation", &Nz::Node::GetRotation, Nz::CoordSys_Global); + nodeClass.BindMethod("GetScale", &Nz::Node::GetScale, Nz::CoordSys_Global); + nodeClass.BindMethod("GetUp", &Nz::Node::GetUp); + + nodeClass.BindMethod("SetInitialPosition", (void(Nz::Node::*)(const Nz::Vector3f&)) &Nz::Node::SetInitialPosition); + nodeClass.BindMethod("SetInitialRotation", (void(Nz::Node::*)(const Nz::Quaternionf&)) &Nz::Node::SetInitialRotation); + + nodeClass.BindMethod("SetPosition", (void(Nz::Node::*)(const Nz::Vector3f&, Nz::CoordSys)) &Nz::Node::SetPosition, Nz::CoordSys_Local); + nodeClass.BindMethod("SetRotation", (void(Nz::Node::*)(const Nz::Quaternionf&, Nz::CoordSys)) &Nz::Node::SetRotation, Nz::CoordSys_Local); + + nodeClass.BindMethod("Move", [] (Nz::LuaInstance& lua, Nz::Node& node) -> int + { + int argIndex = 1; + + Nz::Vector3f offset = lua.Check(&argIndex); + Nz::CoordSys coordSys = lua.Check(&argIndex, Nz::CoordSys_Local); + node.Move(offset, coordSys); + + return 0; + }); + + nodeClass.BindMethod("Rotate", [] (Nz::LuaInstance& lua, Nz::Node& node) -> int + { + int argIndex = 1; + + Nz::Quaternionf rotation = lua.Check(&argIndex); + Nz::CoordSys coordSys = lua.Check(&argIndex, Nz::CoordSys_Local); + node.Rotate(rotation, coordSys); + + return 0; + }); + + nodeClass.BindMethod("Scale", [] (Nz::LuaInstance& lua, Nz::Node& node) -> int + { + unsigned int argCount = std::min(lua.GetStackTop(), 4U); + + int argIndex = 1; + switch (argCount) + { + case 1: + { + if (lua.IsOfType(argIndex, Nz::LuaType_Number)) + node.Scale(lua.Check(&argIndex)); + else + node.Scale(lua.Check(&argIndex)); + + return 0; + } + + case 3: + node.Scale(lua.Check(&argIndex)); + return 0; + } + + lua.Error("No matching overload for method Scale"); + return 0; + }); + + nodeClass.BindMethod("SetScale", [] (Nz::LuaInstance& lua, Nz::Node& node) -> int + { + unsigned int argCount = std::min(lua.GetStackTop(), 4U); + + int argIndex = 1; + switch (argCount) + { + case 1: + case 2: + { + if (lua.IsOfType(argIndex, Nz::LuaType_Number)) + { + float scale = lua.Check(&argIndex); + Nz::CoordSys coordSys = lua.Check(&argIndex, Nz::CoordSys_Local); + node.SetScale(scale, coordSys); + } + else + node.SetScale(lua.Check(&argIndex)); + + return 0; + } + + case 3: + case 4: + { + Nz::Vector3f scale = lua.Check(&argIndex); + Nz::CoordSys coordSys = lua.Check(&argIndex, Nz::CoordSys_Local); + + node.SetScale(scale, coordSys); + return 0; + } + } + + lua.Error("No matching overload for method SetScale"); + return 0; + }); + + nodeClass.BindMethod("SetInitialScale", [] (Nz::LuaInstance& lua, Nz::Node& node) -> int + { + unsigned int argCount = std::min(lua.GetStackTop(), 4U); + + int argIndex = 1; + switch (argCount) + { + case 1: + { + if (lua.IsOfType(argIndex, Nz::LuaType_Number)) + node.SetInitialScale(lua.Check(&argIndex)); + else + node.SetInitialScale(lua.Check(&argIndex)); + + return 0; + } + + case 2: + case 3: + node.SetInitialScale(lua.Check(&argIndex)); + return 0; + } + + lua.Error("No matching overload for method SetInitialScale"); + return 0; + }); + } + + void LuaBinding::RegisterUtility(Nz::LuaInstance& instance) + { + abstractImage.Register(instance); + nodeClass.Register(instance); + } +} \ No newline at end of file diff --git a/SDK/src/NDK/Sdk.cpp b/SDK/src/NDK/Sdk.cpp index 3a45b65b3..21bfd236f 100644 --- a/SDK/src/NDK/Sdk.cpp +++ b/SDK/src/NDK/Sdk.cpp @@ -13,64 +13,80 @@ #include #include #include -#include #include -#include -#include -#include #include #include #include -#include #include -#include #include +#ifndef NDK_SERVER +#include +#include +#include +#include +#include +#include +#endif + namespace Ndk { bool Sdk::Initialize() { if (s_referenceCounter++ > 0) - return true; // Déjà initialisé + return true; // Already initialized try { Nz::ErrorFlags errFlags(Nz::ErrorFlag_ThrowException, true); - // Initialisation du moteur + // Initialize the engine first - // Modules clients - Nz::Audio::Initialize(); - Nz::Graphics::Initialize(); - - // Modules serveurs + // Shared modules Nz::Lua::Initialize(); Nz::Noise::Initialize(); Nz::Physics::Initialize(); Nz::Utility::Initialize(); - // Initialisation du SDK + #ifndef NDK_SERVER + // Client modules + Nz::Audio::Initialize(); + Nz::Graphics::Initialize(); + #endif - // Initialisation des composants et systèmes + // SDK Initialization + + // Components BaseComponent::Initialize(); - BaseSystem::Initialize(); - // Composants - InitializeComponent("NdkCam"); + // Shared components InitializeComponent("NdkColli"); - InitializeComponent("NdkLight"); - InitializeComponent("NdkList"); - InitializeComponent("NdkGfx"); InitializeComponent("NdkNode"); InitializeComponent("NdkPhys"); InitializeComponent("NdkVeloc"); - // Systèmes - InitializeSystem(); + #ifndef NDK_SERVER + // Client components + InitializeComponent("NdkCam"); + InitializeComponent("NdkLight"); + InitializeComponent("NdkList"); + InitializeComponent("NdkGfx"); + #endif + + // Systems + + BaseSystem::Initialize(); + + // Shared systems InitializeSystem(); - InitializeSystem(); InitializeSystem(); + #ifndef NDK_SERVER + // Client systems + InitializeSystem(); + InitializeSystem(); + #endif + NazaraNotice("Initialized: SDK"); return true; } @@ -86,23 +102,25 @@ namespace Ndk { if (s_referenceCounter != 1) { - // Le module est soit encore utilisé, soit pas initialisé + // Either the module is not initialized, either it was initialized multiple times if (s_referenceCounter > 1) s_referenceCounter--; return; } - // Libération du SDK + // Uninitialize the SDK s_referenceCounter = 0; - // Libération du moteur + // Uninitialize the engine - // Modules clients + #ifndef NDK_SERVER + // Client modules Nz::Audio::Uninitialize(); Nz::Graphics::Uninitialize(); + #endif - // Modules serveurs + // Shared modules Nz::Lua::Uninitialize(); Nz::Noise::Uninitialize(); Nz::Physics::Uninitialize(); diff --git a/SDK/src/NDK/Systems/RenderSystem.cpp b/SDK/src/NDK/Systems/RenderSystem.cpp index 9f519c956..829d2b398 100644 --- a/SDK/src/NDK/Systems/RenderSystem.cpp +++ b/SDK/src/NDK/Systems/RenderSystem.cpp @@ -16,6 +16,7 @@ namespace Ndk m_coordinateSystemMatrix(Nz::Matrix4f::Identity()), m_coordinateSystemInvalidated(true) { + ChangeRenderTechnique(); SetDefaultBackground(Nz::ColorBackground::New()); SetUpdateRate(0.f); } @@ -95,7 +96,7 @@ namespace Ndk //UpdateDirectionalShadowMaps(camComponent); - Nz::AbstractRenderQueue* renderQueue = m_renderTechnique.GetRenderQueue(); + Nz::AbstractRenderQueue* renderQueue = m_renderTechnique->GetRenderQueue(); renderQueue->Clear(); //TODO: Culling @@ -123,7 +124,8 @@ namespace Ndk sceneData.background = m_background; sceneData.viewer = &camComponent; - m_renderTechnique.Draw(sceneData); + m_renderTechnique->Clear(sceneData); + m_renderTechnique->Draw(sceneData); } } diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index 7ee1fde49..fc53143dd 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -4,14 +4,17 @@ #include #include -#include #include -#include #include +#ifndef NDK_SERVER +#include +#include +#endif + namespace Ndk { - World::~World() + World::~World() noexcept { // La destruction doit se faire dans un ordre précis Clear(); @@ -19,10 +22,13 @@ namespace Ndk void World::AddDefaultSystems() { - AddSystem(); AddSystem(); - AddSystem(); AddSystem(); + + #ifndef NDK_SERVER + AddSystem(); + AddSystem(); + #endif } const EntityHandle& World::CreateEntity() @@ -40,7 +46,7 @@ namespace Ndk id = m_entities.size(); // Impossible d'utiliser emplace_back à cause de la portée - m_entities.push_back(Entity(*this, id)); + m_entities.push_back(Entity(this, id)); } // On initialise l'entité et on l'ajoute à la liste des entités vivantes @@ -53,9 +59,9 @@ namespace Ndk return m_aliveEntities.back(); } - void World::Clear() + void World::Clear() noexcept { - ///DOC: Tous les handles sont correctement invalidés + ///DOC: Tous les handles sont correctement invalidés, les entités sont immédiatement invalidées // Destruction des entités d'abord, et des handles ensuite // ceci pour éviter que les handles n'informent les entités inutilement lors de leur destruction @@ -127,33 +133,33 @@ namespace Ndk Entity* entity = &m_entities[i].entity; - // Aucun intérêt de traiter une entité n'existant plus - if (entity->IsValid()) + // Check entity validity (as it could have been reported as dirty and killed during the same iteration) + if (!entity->IsValid()) + continue; + + for (auto& system : m_systems) { - for (auto& system : m_systems) + // Ignore non-existent systems + if (!system) + continue; + + // Is our entity already part of this system? + bool partOfSystem = system->HasEntity(entity); + + // Should it be part of it? + if (entity->IsEnabled() && system->Filters(entity)) { - // Ignore non-existent systems - if (!system) - continue; + // Yes it should, add it to the system if not already done and validate it (again) + if (!partOfSystem) + system->AddEntity(entity); - // L'entité est-elle enregistrée comme faisant partie du système ? - bool partOfSystem = system->HasEntity(entity); - - // Doit-elle en faire partie ? - if (system->Filters(entity)) - { - // L'entité doit faire partie du système, revalidons-là (événement système) ou ajoutons-la au système - if (!partOfSystem) - system->AddEntity(entity); - - system->ValidateEntity(entity, !partOfSystem); - } - else - { - // Elle ne doit pas en faire partie, si elle en faisait partie nous devons la retirer - if (partOfSystem) - system->RemoveEntity(entity); - } + system->ValidateEntity(entity, !partOfSystem); + } + else + { + // No, it shouldn't, remove it if it's part of the system + if (partOfSystem) + system->RemoveEntity(entity); } } } diff --git a/build/Build CodeLite (complete).bat b/build/Build CodeLite (complete).bat index 50567ca6b..8584e6d9b 100644 --- a/build/Build CodeLite (complete).bat +++ b/build/Build CodeLite (complete).bat @@ -1 +1 @@ -premake4 --with-extlibs --with-examples codelite \ No newline at end of file +premake5 --with-extlibs --with-examples codelite \ No newline at end of file diff --git a/build/Build CodeLite (complete, united).bat b/build/Build CodeLite (complete, united).bat index d8c8c873e..52a80f504 100644 --- a/build/Build CodeLite (complete, united).bat +++ b/build/Build CodeLite (complete, united).bat @@ -1 +1 @@ -premake4 --united --with-extlibs --with-examples codelite \ No newline at end of file +premake5 --united --with-extlibs --with-examples codelite \ No newline at end of file diff --git a/build/Build Visual Studio 2013 (complete).bat b/build/Build Visual Studio 2013 (complete).bat deleted file mode 100644 index 48e93ac65..000000000 --- a/build/Build Visual Studio 2013 (complete).bat +++ /dev/null @@ -1 +0,0 @@ -premake4 --with-extlibs --with-examples vs2013 \ No newline at end of file diff --git a/build/Build Visual Studio 2013 (complete, united).bat b/build/Build Visual Studio 2013 (complete, united).bat deleted file mode 100644 index d51558a25..000000000 --- a/build/Build Visual Studio 2013 (complete, united).bat +++ /dev/null @@ -1 +0,0 @@ -premake4 --united --with-extlibs --with-examples vs2013 \ No newline at end of file diff --git a/build/Build Visual Studio 2015 (complete).bat b/build/Build Visual Studio 2015 (complete).bat new file mode 100644 index 000000000..2dd6e578a --- /dev/null +++ b/build/Build Visual Studio 2015 (complete).bat @@ -0,0 +1 @@ +premake5 --with-extlibs --with-examples vs2015 \ No newline at end of file diff --git a/build/Build Visual Studio 2015 (complete, united).bat b/build/Build Visual Studio 2015 (complete, united).bat new file mode 100644 index 000000000..aaab1938c --- /dev/null +++ b/build/Build Visual Studio 2015 (complete, united).bat @@ -0,0 +1 @@ +premake5 --united --with-extlibs --with-examples vs2015 \ No newline at end of file diff --git a/build/Encode Resources.bat b/build/Encode Resources.bat index 19afc44cf..8e4abde48 100644 --- a/build/Encode Resources.bat +++ b/build/Encode Resources.bat @@ -1,2 +1,2 @@ -premake4 encoderesources +premake5 encoderesources pause \ No newline at end of file diff --git a/build/Generate Headers.bat b/build/Generate Headers.bat index 41ac7b82a..a78069501 100644 --- a/build/Generate Headers.bat +++ b/build/Generate Headers.bat @@ -1,2 +1,2 @@ -premake4 generateheaders +premake5 generateheaders pause \ No newline at end of file diff --git a/build/Parse Unicode.bat b/build/Parse Unicode.bat index 66244ff4a..75ef2d7da 100644 --- a/build/Parse Unicode.bat +++ b/build/Parse Unicode.bat @@ -1,2 +1,2 @@ -premake4 parseunicode +premake5 parseunicode pause \ No newline at end of file diff --git a/build/scripts/actions/generateheaders.lua b/build/scripts/actions/generateheaders.lua index 01cea67e8..943919a90 100644 --- a/build/scripts/actions/generateheaders.lua +++ b/build/scripts/actions/generateheaders.lua @@ -1,11 +1,20 @@ ACTION.Name = "GenerateHeaders" ACTION.Description = "Generate a global header for each module" +ACTION.ModuleExcludes = {} +ACTION.ModuleExcludes["ConfigCheck.hpp"] = true +ACTION.ModuleExcludes["Debug.hpp"] = true +ACTION.ModuleExcludes["DebugOff.hpp"] = true +ACTION.ModuleExcludes["ThreadSafety.hpp"] = true +ACTION.ModuleExcludes["ThreadSafetyOff.hpp"] = true + +local action = ACTION ACTION.Function = function () + local paths = {} + local modules = os.matchdirs("../include/Nazara/*") for k, modulePath in pairs(modules) do local moduleName = modulePath:match(".*/(.*)") - print(moduleName) local config, err = io.open(modulePath .. "/Config.hpp", "r") local head = "" @@ -14,44 +23,72 @@ ACTION.Function = function () end for line in config:lines() do - head = head .. line .. "\n" - if (line == "#pragma once") then -- On s'arrête au #pragma once, qu'on inclut quand même + if (line == "#pragma once") then -- Stop before including the #pragma once as it's already written automatically break end + head = head .. line .. "\n" end config:close() - local header, err = io.open(modulePath .. ".hpp", "w+") + table.insert(paths, { + Excludes = action.ModuleExcludes, + Header = head, + HeaderGuard = "NAZARA_GLOBAL_" .. moduleName:upper() .. "_HPP", + Name = "Nazara" .. moduleName, + SearchDir = modulePath, + Target = modulePath .. ".hpp", + TopDir = "Nazara" + }) + end + + table.insert(paths, { + Excludes = {}, + HeaderGuard = "NDK_COMPONENTS_GLOBAL_HPP", + Name = "NDK Components", + SearchDir = "../SDK/include/NDK/Components", + TopDir = "NDK", + Target = "../SDK/include/NDK/Components.hpp" + }) + + table.insert(paths, { + Excludes = {}, + HeaderGuard = "NDK_SYSTEMS_GLOBAL_HPP", + Name = "NDK Systems", + SearchDir = "../SDK/include/NDK/Systems", + TopDir = "NDK", + Target = "../SDK/include/NDK/Systems.hpp" + }) + + for k,v in ipairs(paths) do + print(v.Name) + local header, err = io.open(v.Target, "w+") if (not header) then - error("Failed to create header file: " .. err) + error("Failed to create header file (" .. v.Target .. "): " .. err) end header:write("// This file was automatically generated on " .. os.date("%d %b %Y at %X") .. "\n\n") - header:write(head .. "\n") + if (v.Header) then + header:write(v.Header) + end - -- Protection du multi-including - local preprocessorName = "NAZARA_GLOBAL_" .. string.upper(moduleName) .. "_HPP" - header:write("#ifndef " .. preprocessorName .. "\n") - header:write("#define " .. preprocessorName .. "\n\n") + header:write("#pragma once\n\n") + header:write("#ifndef " .. v.HeaderGuard .. "\n") + header:write("#define " .. v.HeaderGuard .. "\n\n") - local files = os.matchfiles(modulePath .. "/*.hpp") + local files = os.matchfiles(v.SearchDir .. "/*.hpp") local count = 0 for k, filePath in pairs(files) do - local include, fileName = filePath:match(".*(Nazara/.*/(.*))") - if (fileName ~= "ConfigCheck.hpp" and - fileName ~= "Debug.hpp" and - fileName ~= "DebugOff.hpp" and - fileName ~= "ThreadSafety.hpp" and - fileName ~= "ThreadSafetyOff.hpp") then + local include, fileName = filePath:match(".*(" .. v.TopDir .. "/.*/(.*))") + if (not v.Excludes[fileName]) then header:write("#include <" .. include .. ">\n") count = count + 1 end end - - header:write("\n#endif // " .. preprocessorName .. "\n") + + header:write("\n#endif // " .. v.HeaderGuard .. "\n") header:close() - + print(string.format("-#include count: %d", count)) end end diff --git a/build/scripts/common.lua b/build/scripts/common.lua index 90afe4217..1018890b8 100644 --- a/build/scripts/common.lua +++ b/build/scripts/common.lua @@ -9,7 +9,7 @@ function NazaraBuild:Execute() local makeLibDir = os.is("windows") and "mingw" or "gmake" if (#self.OrderedExtLibs > 0) then - solution("NazaraExtlibs") + workspace("NazaraExtlibs") platforms({"x32", "x64"}) -- Configuration générale @@ -60,7 +60,10 @@ function NazaraBuild:Execute() flags("Symbols") configuration("Release*") - flags({"EnableSSE2", "Optimize", "OptimizeSpeed", "NoFramePointer", "NoRTTI"}) + 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 @@ -98,7 +101,7 @@ function NazaraBuild:Execute() end end - solution("NazaraEngine") + workspace("NazaraEngine") platforms({"x32", "x64"}) -- Configuration générale @@ -117,7 +120,10 @@ function NazaraBuild:Execute() flags("Symbols") configuration("Release*") - flags({"EnableSSE2", "Optimize", "OptimizeSpeed", "NoFramePointer", "NoRTTI"}) + 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 diff --git a/build/scripts/tools/ndk.lua b/build/scripts/tools/ndk.lua index ae0c99881..33c79c3fb 100644 --- a/build/scripts/tools/ndk.lua +++ b/build/scripts/tools/ndk.lua @@ -8,7 +8,8 @@ TOOL.Defines = { } TOOL.Includes = { - "../SDK/include" + "../SDK/include", + "../SDK/src" } TOOL.Files = { diff --git a/build/scripts/tools/ndk_server.lua b/build/scripts/tools/ndk_server.lua new file mode 100644 index 000000000..6b6d32941 --- /dev/null +++ b/build/scripts/tools/ndk_server.lua @@ -0,0 +1,44 @@ +TOOL.Name = "SDKServer" + +TOOL.Directory = "../SDK/lib" +TOOL.Kind = "Library" + +TOOL.Defines = { + "NDK_BUILD", + "NDK_SERVER" +} + +TOOL.Includes = { + "../SDK/include", + "../SDK/src" +} + +TOOL.Files = { + "../SDK/include/NDK/**.hpp", + "../SDK/include/NDK/**.inl", + "../SDK/src/NDK/**.hpp", + "../SDK/src/NDK/**.inl", + "../SDK/src/NDK/**.cpp" +} + +-- Exlude client-only files +TOOL.FilesExclusion = { + "../SDK/**/CameraComponent.*", + "../SDK/**/Console.*", + "../SDK/**/GraphicsComponent.*", + "../SDK/**/LightComponent.*", + "../SDK/**/ListenerComponent.*", + "../SDK/**/ListenerSystem.*", + "../SDK/**/RenderSystem.*", + "../SDK/**/LuaBinding_Audio.*", + "../SDK/**/LuaBinding_Graphics.*", + "../SDK/**/LuaBinding_Renderer.*" +} + +TOOL.Libraries = { + "NazaraCore", + "NazaraLua", + "NazaraNoise", + "NazaraPhysics", + "NazaraUtility" +} diff --git a/examples/DopplerEffect/main.cpp b/examples/DopplerEffect/main.cpp index 42d838356..b6d2d034b 100644 --- a/examples/DopplerEffect/main.cpp +++ b/examples/DopplerEffect/main.cpp @@ -13,12 +13,13 @@ #include // Thread::Sleep #include #include +#include #include int main() { - // NzKeyboard ne nécessite pas l'initialisation du module Utilitaire - Nz::Initializer audio; + // NzKeyboard nécessite l'initialisation du module Utilitaire + Nz::Initializer audio; if (!audio) { std::cout << "Failed to initialize audio module" << std::endl; diff --git a/examples/Tut01/build.lua b/examples/Tut01/build.lua new file mode 100644 index 000000000..a4f623662 --- /dev/null +++ b/examples/Tut01/build.lua @@ -0,0 +1,14 @@ +EXAMPLE.Name = "Tut01_HelloWorld" + +EXAMPLE.Console = true + +EXAMPLE.Files = { + "main.cpp" +} + +EXAMPLE.Libraries = { + "NazaraCore", + "NazaraGraphics", + "NazaraUtility", + "NazaraSDK" +} diff --git a/examples/Tut01/main.cpp b/examples/Tut01/main.cpp new file mode 100644 index 000000000..d1ebcd5d4 --- /dev/null +++ b/examples/Tut01/main.cpp @@ -0,0 +1,57 @@ +// Sources pour https://github.com/DigitalPulseSoftware/NazaraEngine/wiki/(FR)-Tutoriel-01---Hello-World + +#include +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + Ndk::Application application; + + Nz::RenderWindow& mainWindow = application.AddWindow(); + mainWindow.Create(Nz::VideoMode(800, 600, 32), "Test"); + + Ndk::World& world = application.AddWorld(); + world.GetSystem().SetGlobalUp(Nz::Vector3f::Down()); + world.GetSystem().SetDefaultBackground(Nz::ColorBackground::New(Nz::Color(192, 100, 100))); + + + Ndk::EntityHandle viewEntity = world.CreateEntity(); + viewEntity->AddComponent(); + + Ndk::CameraComponent& viewer = viewEntity->AddComponent(); + viewer.SetTarget(&mainWindow); + viewer.SetProjectionType(Nz::ProjectionType_Orthogonal); + + + Nz::TextSpriteRef textSprite = Nz::TextSprite::New(); + textSprite->Update(Nz::SimpleTextDrawer::Draw("Hello world !", 72)); + + Ndk::EntityHandle text = world.CreateEntity(); + Ndk::NodeComponent& nodeComponent = text->AddComponent(); + + Ndk::GraphicsComponent& graphicsComponent = text->AddComponent(); + graphicsComponent.Attach(textSprite); + + Nz::Boxf textBox = graphicsComponent.GetBoundingVolume().aabb; + nodeComponent.SetPosition(mainWindow.GetWidth() / 2 - textBox.width / 2, mainWindow.GetHeight() / 2 - textBox.height / 2); + + while (application.Run()) + { + Nz::WindowEvent event; + while (mainWindow.PollEvent(&event)) + { + if (event.type == Nz::WindowEventType_Quit) + application.Quit(); + } + + mainWindow.Display(); + } + + return EXIT_SUCCESS; +} diff --git a/include/Nazara/Audio/Music.hpp b/include/Nazara/Audio/Music.hpp index 50935ac79..d08732fa7 100644 --- a/include/Nazara/Audio/Music.hpp +++ b/include/Nazara/Audio/Music.hpp @@ -12,10 +12,11 @@ #include #include #include +#include namespace Nz { - struct MusicParams + struct MusicParams : ResourceParameters { bool forceMono = false; diff --git a/include/Nazara/Audio/OpenAL.hpp b/include/Nazara/Audio/OpenAL.hpp index 812b002b6..eab95e6d0 100644 --- a/include/Nazara/Audio/OpenAL.hpp +++ b/include/Nazara/Audio/OpenAL.hpp @@ -73,8 +73,8 @@ namespace Nz static bool IsInitialized(); - static unsigned int QueryInputDevices(std::vector& devices); - static unsigned int QueryOutputDevices(std::vector& devices); + static std::size_t QueryInputDevices(std::vector& devices); + static std::size_t QueryOutputDevices(std::vector& devices); static bool SetDevice(const String& deviceName); @@ -87,7 +87,6 @@ namespace Nz static bool OpenDevice(); static OpenALFunc LoadEntry(const char* name, bool throwException = false); }; -} // al NAZARA_AUDIO_API extern OpenALDetail::LPALBUFFER3F alBuffer3f; @@ -186,6 +185,8 @@ NAZARA_AUDIO_API extern OpenALDetail::LPALCOPENDEVICE alcOpenDevice; NAZARA_AUDIO_API extern OpenALDetail::LPALCPROCESSCONTEXT alcProcessContext; NAZARA_AUDIO_API extern OpenALDetail::LPALCSUSPENDCONTEXT alcSuspendContext; +} + #endif // NAZARA_AUDIO_OPENAL #endif // NAZARA_OPENAL_HPP diff --git a/include/Nazara/Audio/SoundBuffer.hpp b/include/Nazara/Audio/SoundBuffer.hpp index b144e71ad..2b02847b4 100644 --- a/include/Nazara/Audio/SoundBuffer.hpp +++ b/include/Nazara/Audio/SoundBuffer.hpp @@ -16,12 +16,13 @@ #include #include #include +#include #include #include namespace Nz { - struct SoundBufferParams + struct SoundBufferParams : ResourceParameters { bool forceMono = false; diff --git a/include/Nazara/Core.hpp b/include/Nazara/Core.hpp index 50aecb311..a9a93d45c 100644 --- a/include/Nazara/Core.hpp +++ b/include/Nazara/Core.hpp @@ -1,4 +1,4 @@ -// This file was automatically generated on 20 Nov 2015 at 14:22:32 +// This file was automatically generated on 03 Feb 2016 at 00:06:56 /* Nazara Engine - Core module @@ -30,9 +30,11 @@ #define NAZARA_GLOBAL_CORE_HPP #include +#include #include #include #include +#include #include #include #include @@ -46,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -70,16 +74,15 @@ #include #include #include -#include #include #include +#include #include #include #include #include #include #include -#include #include #endif // NAZARA_GLOBAL_CORE_HPP diff --git a/include/Nazara/Core/AbstractLogger.docx b/include/Nazara/Core/AbstractLogger.docx index cdc37f3f8..6ac06b95c 100644 --- a/include/Nazara/Core/AbstractLogger.docx +++ b/include/Nazara/Core/AbstractLogger.docx @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp /*! +* \ingroup core * \class Nz::AbstractLogger * \brief Logger interface */ diff --git a/include/Nazara/Core/Algorithm.hpp b/include/Nazara/Core/Algorithm.hpp index cbbd8af04..5f8b8aa7f 100644 --- a/include/Nazara/Core/Algorithm.hpp +++ b/include/Nazara/Core/Algorithm.hpp @@ -23,8 +23,16 @@ namespace Nz template auto Apply(O& object, F&& fn, Tuple&& t); template ByteArray ComputeHash(HashType hash, const T& v); template ByteArray ComputeHash(AbstractHash* hash, const T& v); + 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 + struct PointedType + { + using type = void; //< FIXME: I can't make SFINAE work + }; + template struct TypeTag {}; @@ -33,10 +41,10 @@ namespace Nz template std::enable_if_t::value, bool> Serialize(SerializationContext& context, T value); - inline bool Unserialize(UnserializationContext& context, bool* value); + inline bool Unserialize(SerializationContext& context, bool* value); template - std::enable_if_t::value, bool> Unserialize(UnserializationContext& context, T* value); + std::enable_if_t::value, bool> Unserialize(SerializationContext& context, T* value); } #include diff --git a/include/Nazara/Core/Algorithm.inl b/include/Nazara/Core/Algorithm.inl index 7c117e626..c45147d79 100644 --- a/include/Nazara/Core/Algorithm.inl +++ b/include/Nazara/Core/Algorithm.inl @@ -30,6 +30,16 @@ namespace Nz } } + /*! + * \ingroup core + * \brief Applies the tuple to the function (e.g. calls the function using the tuple content as arguments) + * \return The result of the function + * + * \param fn Function + * \param t Tuple of arguments for the function + * + * \see Apply + */ template auto Apply(F&& fn, Tuple&& t) { @@ -38,6 +48,17 @@ namespace Nz return Detail::ApplyImplFunc(std::forward(fn), std::forward(t), std::make_index_sequence()); } + /*! + * \ingroup core + * \brief Applies the tuple to the member function on an object (e.g. calls the member function using the tuple content as arguments) + * \return The result of the member function called + * + * \param object Object of a class + * \param fn Member function + * \param t Tuple of arguments for the member function + * + * \see Apply + */ template auto Apply(O& object, F&& fn, Tuple&& t) { @@ -46,15 +67,42 @@ namespace Nz return Detail::ApplyImplMethod(object, std::forward(fn), std::forward(t), std::make_index_sequence()); } + /*! + * \ingroup core + * \brief Computes the hash of a hashable object + * \return A bytearray which represents the hash + * + * \param hash Enumeration of type HashType + * \param v Object to hash + * + * \remark a HashAppend specialization for type T is required + * + * \see ComputeHash + */ template ByteArray ComputeHash(HashType hash, const T& v) { return ComputeHash(AbstractHash::Get(hash).get(), v); } + /*! + * \ingroup core + * \brief Computes the hash of a hashable object + * \return A bytearray which represents the hash + * + * \param hash Pointer to abstract hash + * \param v Object to hash + * + * \remark Produce a NazaraAssert if pointer to Abstracthash is invalid + * \remark a HashAppend specialization for type T is required + * + * \see ComputeHash + */ template ByteArray ComputeHash(AbstractHash* hash, const T& v) { + NazaraAssert(hash != nullptr, "Invalid abstracthash pointer"); + hash->Begin(); HashAppend(hash, v); @@ -62,7 +110,44 @@ namespace Nz return hash->End(); } - // Algorithme venant de CityHash par Google + /*! + * \ingroup core + * \brief Returns the number of elements in a C-array + * \return The number of elements + * + * \param name C-array + * + * \see CountOf + */ + template + constexpr std::size_t CountOf(T(&name)[N]) noexcept + { + return N; + } + + /*! + * \ingroup core + * \brief Returns the number of elements in a container + * \return The number of elements + * + * \param c Container with the member function "size()" + * + * \see CountOf + */ + template + std::size_t CountOf(const T& c) + { + return c.size(); + } + + /*! + * \ingroup core + * \brief Combines two hash in one + * + * \param seed First value that will be modified (expected to be 64bits) + * \param v Second value to hash + */ + // Algorithm from CityHash by Google // http://stackoverflow.com/questions/8513911/how-to-create-a-good-hash-combine-with-64-bit-output-inspired-by-boosthash-co template void HashCombine(std::size_t& seed, const T& v) @@ -79,6 +164,21 @@ namespace Nz seed = static_cast(b * kMul); } + template struct PointedType {typedef T type;}; + template struct PointedType {typedef T type;}; + template struct PointedType {typedef T type;}; + template struct PointedType {typedef T type;}; + + /*! + * \ingroup core + * \brief Serializes a boolean + * \return true if serialization succedeed + * + * \param context Context for the serialization + * \param value Boolean to serialize + * + * \see Serialize, Unserialize + */ inline bool Serialize(SerializationContext& context, bool value) { if (context.currentBitPos == 8) @@ -96,6 +196,16 @@ namespace Nz return true; } + /*! + * \ingroup core + * \brief Serializes an arithmetic type + * \return true if serialization succedeed + * + * \param context Context for the serialization + * \param value Arithmetic type to serialize + * + * \see Serialize, Unserialize + */ template std::enable_if_t::value, bool> Serialize(SerializationContext& context, T value) { @@ -114,10 +224,18 @@ namespace Nz return context.stream->Write(&value, sizeof(T)) == sizeof(T); } - inline bool Unserialize(UnserializationContext& context, bool* value) + /*! + * \ingroup core + * \brief Unserializes a boolean + * \return true if unserialization succedeed + * + * \param context Context for the unserialization + * \param value Pointer to boolean to unserialize + * + * \see Serialize, Unserialize + */ + inline bool Unserialize(SerializationContext& context, bool* value) { - NazaraAssert(value, "Invalid data pointer"); - if (context.currentBitPos == 8) { if (!Unserialize(context, &context.currentByte)) @@ -134,8 +252,20 @@ namespace Nz return true; } + /*! + * \ingroup core + * \brief Unserializes an arithmetic type + * \return true if unserialization succedeed + * + * \param context Context for the unserialization + * \param value Pointer to arithmetic type to serialize + * + * \remark Produce a NazaraAssert if pointer to value is invalid + * + * \see Serialize, Unserialize + */ template - std::enable_if_t::value, bool> Unserialize(UnserializationContext& context, T* value) + std::enable_if_t::value, bool> Unserialize(SerializationContext& context, T* value) { NazaraAssert(value, "Invalid data pointer"); diff --git a/include/Nazara/Core/Bitset.hpp b/include/Nazara/Core/Bitset.hpp index ba60ae062..630d14a40 100644 --- a/include/Nazara/Core/Bitset.hpp +++ b/include/Nazara/Core/Bitset.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -31,9 +32,9 @@ namespace Nz Bitset(const Bitset& bitset) = default; explicit Bitset(const String& bits); Bitset(Bitset&& bitset) noexcept = default; - ~Bitset() = default; + ~Bitset() noexcept = default; - void Clear(); + void Clear() noexcept; unsigned int Count() const; void Flip(); @@ -89,9 +90,9 @@ namespace Nz Bitset& operator|=(const Bitset& bitset); Bitset& operator^=(const Bitset& bitset); - static Block fullBitMask; - static unsigned int bitsPerBlock; - static unsigned int npos; + 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(); private: unsigned int FindFirstFrom(unsigned int blockIndex) const; diff --git a/include/Nazara/Core/Bitset.inl b/include/Nazara/Core/Bitset.inl index 54edf9f67..bf89628f6 100644 --- a/include/Nazara/Core/Bitset.inl +++ b/include/Nazara/Core/Bitset.inl @@ -16,12 +16,31 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Bitset + * \brief Core class that represents a set of bits + * + * This class meets the requirements of Container, AllocatorAwareContainer, SequenceContainer + */ + + /*! + * \brief Constructs a Bitset object by default + */ + template Bitset::Bitset() : m_bitCount(0) { } + /*! + * \brief Constructs a Bitset object of bitCount bits to value val + * + * \param bitCount Number of bits + * \param val Value of those bits, by default false + */ + template Bitset::Bitset(unsigned int bitCount, bool val) : Bitset() @@ -29,12 +48,29 @@ namespace Nz Resize(bitCount, val); } + /*! + * \brief Constructs a Bitset object from the contents initialized with a copy of the null-terminated character string pointed to by bits + * + * \param bits Null-terminated character string containing only '0' and '1' + * + * \remark The length of the string is determined by the first null character, if there is no null character, the behaviour is undefined + */ + template Bitset::Bitset(const char* bits) : Bitset(bits, std::strlen(bits)) { } + /*! + * \brief Constructs a Bitset object from the contents initialized with a copy of the character string pointed to by bits takings the bitCount first characters + * + * \param bits Character string containing only '0' and '1' + * \param bitCount Number of characters to take into consideration + * + * \remark If the length of the string is inferior to the bitCount, the behaviour is undefined + */ + template Bitset::Bitset(const char* bits, unsigned int bitCount) : m_blocks(ComputeBlockCount(bitCount), 0U), @@ -45,12 +81,12 @@ namespace Nz switch (*bits++) { case '1': - // On adapte l'indice (inversion par rapport à la chaîne) + // We adapt the index (inversion in comparison to the string) Set(m_bitCount - i - 1, true); break; case '0': - // Tous les blocs ont été initialisés à zéro, rien à faire ici + // Each block is zero-initialised, nothing to do break; default: @@ -60,19 +96,36 @@ namespace Nz } } + /*! + * \brief Constructs a Bitset object from a Nz::String + * + * \param bits String containing only '0' and '1' + */ + template Bitset::Bitset(const String& bits) : Bitset(bits.GetConstBuffer(), bits.GetSize()) { } + /*! + * \brief Clears the content of the bitset, GetSize() is now equals to 0 + * + * \remark The memory allocated is not released + */ + template - void Bitset::Clear() + void Bitset::Clear() noexcept { m_bitCount = 0; m_blocks.clear(); } + /*! + * \brief Counts the number of bits set to 1 + * \return Number of bits set to 1 + */ + template unsigned int Bitset::Count() const { @@ -86,6 +139,10 @@ namespace Nz return count; } + /*! + * \brief Flips each bit of the bitset + */ + template void Bitset::Flip() { @@ -95,12 +152,26 @@ namespace Nz ResetExtraBits(); } + /*! + * \brief Finds the first bit set to one in the bitset + * \return Index of the first bit + */ + template unsigned int Bitset::FindFirst() const { return FindFirstFrom(0); } + /*! + * \brief Finds the next bit set to one in the bitset + * \return Index of the next bit if exists or npos + * + * \param bit Index of the bit, the search begin with bit + 1 + * + * \remark Produce a NazaraAssert if bit is greather than number of bits in bitset + */ + template unsigned int Bitset::FindNext(unsigned int bit) const { @@ -109,23 +180,32 @@ namespace Nz if (++bit >= m_bitCount) return npos; - // Le bloc du bit, l'indice du bit + // The block of the bit and its index unsigned int blockIndex = GetBlockIndex(bit); unsigned int bitIndex = GetBitIndex(bit); - // Récupération du bloc + // We get the block Block block = m_blocks[blockIndex]; - // On ignore les X premiers bits + // We ignore the X first bits block >>= bitIndex; - // Si le bloc n'est pas nul, c'est bon, sinon on doit chercher à partir du prochain bloc + // If the block is not empty, it's good, else we must keep trying with the next block if (block) return IntegralLog2Pot(block & -block) + bit; else return FindFirstFrom(blockIndex + 1); } + /*! + * \brief Gets the ith block + * \return Block in the bitset + * + * \param i Index of the block + * + * \remark Produce a NazaraAssert if i is greather than number of blocks in bitset + */ + template Block Bitset::GetBlock(unsigned int i) const { @@ -134,41 +214,71 @@ namespace Nz return m_blocks[i]; } + /*! + * \brief Gets the number of blocks + * \return Number of blocks + */ + template unsigned int Bitset::GetBlockCount() const { return m_blocks.size(); } + /*! + * \brief Gets the capacity of the bitset + * \return Capacity of the bitset + */ + template unsigned int Bitset::GetCapacity() const { return m_blocks.capacity()*bitsPerBlock; } + /*! + * \brief Gets the number of bits + * \return Number of bits + */ + template unsigned int Bitset::GetSize() const { return m_bitCount; } + /*! + * \brief Performs the "AND" operator between two bitsets + * + * \param a First bitset + * \param b Second bitset + * + * \remark The "AND" is performed with all the bits of the smallest bitset and the capacity of this is set to the largest of the two bitsets + */ + template void Bitset::PerformsAND(const Bitset& a, const Bitset& b) { std::pair minmax = std::minmax(a.GetBlockCount(), b.GetBlockCount()); - // On réinitialise nos blocs à zéro + // We reinitialise our blocks with zero m_blocks.clear(); m_blocks.resize(minmax.second, 0U); m_bitCount = std::max(a.GetSize(), b.GetSize()); - // Dans le cas du AND, nous pouvons nous arrêter à la plus petite taille (car x & 0 = 0) + // 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) m_blocks[i] = a.GetBlock(i) & b.GetBlock(i); ResetExtraBits(); } + /*! + * \brief Performs the "NOT" operator of the bitset + * + * \param a Bitset to negate + */ + template void Bitset::PerformsNOT(const Bitset& a) { @@ -181,6 +291,15 @@ namespace Nz ResetExtraBits(); } + /*! + * \brief Performs the "OR" operator between two bitsets + * + * \param a First bitset + * \param b Second bitset + * + * \remark The "OR" is performed with all the bits of the smallest bitset and the others are copied from the largest and the capacity of this is set to the largest of the two bitsets + */ + template void Bitset::PerformsOR(const Bitset& a, const Bitset& b) { @@ -201,6 +320,15 @@ namespace Nz ResetExtraBits(); } + /*! + * \brief Performs the "XOR" operator between two bitsets + * + * \param a First bitset + * \param b Second bitset + * + * \remark The "XOR" is performed with all the bits of the smallest bitset and the others are copied from the largest and the capacity of this is set to the largest of the two bitsets + */ + template void Bitset::PerformsXOR(const Bitset& a, const Bitset& b) { @@ -221,10 +349,16 @@ namespace Nz ResetExtraBits(); } + /*! + * \brief Checks if bitsets have one block in common + * + * \param bitset Bitset to test + */ + template bool Bitset::Intersects(const Bitset& bitset) const { - // On ne testera que les blocs en commun + // We only test the blocks in common unsigned int sharedBlocks = std::min(GetBlockCount(), bitset.GetBlockCount()); for (unsigned int i = 0; i < sharedBlocks; ++i) { @@ -237,40 +371,71 @@ namespace Nz return false; } + /*! + * \brief Reserves enough blocks to contain bitCount bits + * + * \param bitCount Number of bits to reserve + */ + template void Bitset::Reserve(unsigned int bitCount) { m_blocks.reserve(ComputeBlockCount(bitCount)); } + /*! + * \brief Resizes the bitset to the size of bitCount + * + * \param bitCount Number of bits to resize + * \param defaultVal Value of the bits if new size is greather than the old one + */ + template void Bitset::Resize(unsigned int bitCount, bool defaultVal) { - // On commence par changer la taille du conteneur, avec la valeur correcte d'initialisation + // We begin with changing the size of container, with the correct value of initialisation unsigned int lastBlockIndex = m_blocks.size() - 1; m_blocks.resize(ComputeBlockCount(bitCount), (defaultVal) ? fullBitMask : 0U); unsigned int remainingBits = GetBitIndex(m_bitCount); if (bitCount > m_bitCount && remainingBits > 0 && defaultVal) - // Initialisation des bits non-utilisés du dernier bloc avant le changement de taille + // Initialisation of unused bits in the last block before the size change m_blocks[lastBlockIndex] |= fullBitMask << remainingBits; m_bitCount = bitCount; ResetExtraBits(); } + /*! + * \brief Resets the bitset to zero bits + */ + template void Bitset::Reset() { Set(false); } + /*! + * \brief Resets the bit at the index + * + * \param bit Index of the bit + * + * \see UnboundReset + */ + template void Bitset::Reset(unsigned int bit) { Set(bit, false); } + /*! + * \brief Sets the bitset to val + * + * \param val Value of the bits + */ + template void Bitset::Set(bool val) { @@ -279,6 +444,17 @@ namespace Nz ResetExtraBits(); } + /*! + * \brief Sets the bit at the index + * + * \param bit Index of the bit + * \param val Value of the bit + * + * \remark Produce a NazaraAssert if bit is greather than number of bits in bitset + * + * \see UnboundSet + */ + template void Bitset::Set(unsigned int bit, bool val) { @@ -287,11 +463,19 @@ namespace Nz Block& block = m_blocks[GetBlockIndex(bit)]; Block mask = Block(1U) << GetBitIndex(bit); - // Activation du bit sans branching + // Activation of the bit without branching // https://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching block = (block & ~mask) | (-val & mask); } + /*! + * \brief Set the ith block + * + * \param i Index of the block + * \param block Block to set + * + * \remark Produce a NazaraAssert if i is greather than number of blocks in bitset + */ template void Bitset::SetBlock(unsigned int i, Block block) { @@ -302,6 +486,12 @@ namespace Nz ResetExtraBits(); } + /*! + * \brief Swaps the two bitsets + * + * \param bitset Other bitset to swap + */ + template void Bitset::Swap(Bitset& bitset) { @@ -309,6 +499,17 @@ namespace Nz std::swap(m_blocks, bitset.m_blocks); } + /*! + * \brief Tests the ith bit + * \return true if bit is set + * + * \param bit Index of the bit + * + * \remark Produce a NazaraAssert if bit is greather than number of bits in bitset + * + * \see UnboundTest + */ + template bool Bitset::Test(unsigned int bit) const { @@ -317,22 +518,32 @@ namespace Nz return (m_blocks[GetBlockIndex(bit)] & (Block(1U) << GetBitIndex(bit))) != 0; } + /*! + * \brief Tests each block + * \return true if each block is set + */ + template bool Bitset::TestAll() const { - // Cas particulier du dernier bloc + // Special case for the last block Block lastBlockMask = GetLastBlockMask(); for (unsigned int i = 0; i < m_blocks.size(); ++i) { Block mask = (i == m_blocks.size() - 1) ? lastBlockMask : fullBitMask; - if (m_blocks[i] == mask) // Les extra bits sont à zéro, on peut donc tester sans procéder à un masquage + if (m_blocks[i] == mask) // The extra bits are set to zero, thus we can't test without proceeding with a mask return false; } return true; } + /*! + * \brief Tests if one bit is set + * \return true if one bit is set + */ + template bool Bitset::TestAny() const { @@ -348,12 +559,24 @@ namespace Nz return false; } + /*! + * \brief Tests if one bit is not set + * \return true if one bit is not set + */ + template bool Bitset::TestNone() const { return !TestAny(); } + /*! + * \brief Converts the bitset to template type + * \return The conversion of the bitset + * + * \remark Produce a NazaraAssert if the template type can not hold the number of bits + */ + template template T Bitset::To() const @@ -369,6 +592,11 @@ namespace Nz return value; } + /*! + * \brief Gives a string representation + * \return A string representation of the object with only '0' and '1' + */ + template String Bitset::ToString() const { @@ -383,12 +611,33 @@ namespace Nz return str; } + /*! + * \brief Resets the bit at the index + * + * \param bit Index of the bit + * + * \remark if bit is greather than the number of bits, the bitset is enlarged and the added bits are set to false + * + * \see Reset + */ + template void Bitset::UnboundedReset(unsigned int bit) { UnboundedSet(bit, false); } + /*! + * \brief Sets the bit at the index + * + * \param bit Index of the bit + * \param val Value of the bit + * + * \remark if bit is greather than the number of bits, the bitset is enlarged and the added bits are set to false and the one at bit is set to val + * + * \see Set + */ + template void Bitset::UnboundedSet(unsigned int bit, bool val) { @@ -402,6 +651,15 @@ namespace Nz } } + /*! + * \brief Tests the ith bit + * \return true if bit is set + * + * \param bit Index of the bit + * + * \see Test + */ + template bool Bitset::UnboundedTest(unsigned int bit) const { @@ -411,18 +669,33 @@ namespace Nz return false; } + /*! + * \brief Gets the ith bit + * \return bit in ith position + */ + template typename Bitset::Bit Bitset::operator[](int index) { return Bit(m_blocks[GetBlockIndex(index)], Block(1U) << GetBitIndex(index)); } + /*! + * \brief Gets the ith bit + * \return bit in ith position + */ + template bool Bitset::operator[](int index) const { return Test(index); } + /*! + * \brief Negates the bitset + * \return A new bitset which is the "NOT" of this bitset + */ + template Bitset Bitset::operator~() const { @@ -432,6 +705,13 @@ namespace Nz return bitset; } + /*! + * \brief Sets this bitset from a Nz::String + * \return A reference to this + * + * \param bits String containing only '0' and '1' + */ + template Bitset& Bitset::operator=(const String& bits) { @@ -441,6 +721,13 @@ namespace Nz return *this; } + /*! + * \brief Performs an "AND" with another bitset + * \return A reference to this + * + * \param bitset Other bitset + */ + template Bitset& Bitset::operator&=(const Bitset& bitset) { @@ -449,6 +736,13 @@ namespace Nz return *this; } + /*! + * \brief Performs an "OR" with another bitset + * \return A reference to this + * + * \param bitset Other bitset + */ + template Bitset& Bitset::operator|=(const Bitset& bitset) { @@ -457,6 +751,13 @@ namespace Nz return *this; } + /*! + * \brief Performs an "XOR" with another bitset + * \return A reference to this + * + * \param bitset Other bitset + */ + template Bitset& Bitset::operator^=(const Bitset& bitset) { @@ -465,13 +766,20 @@ namespace Nz return *this; } + /*! + * \brief Finds the position of the first bit set to true after the blockIndex + * \return The position of the bit + * + * \param blockIndex Index of the block + */ + template unsigned int Bitset::FindFirstFrom(unsigned int blockIndex) const { if (blockIndex >= m_blocks.size()) return npos; - // On cherche le premier bloc non-nul + // We are looking for the first non-null block unsigned int i = blockIndex; for (; i < m_blocks.size(); ++i) { @@ -479,22 +787,31 @@ namespace Nz break; } - // Est-ce qu'on a un bloc non-nul ? + // Do we have a non-null block ? if (i == m_blocks.size()) return npos; Block block = m_blocks[i]; - // Calcul de la position du LSB dans le bloc (et ajustement de la position) + // Compute the position of LSB in the block (and adjustement of the position) return IntegralLog2Pot(block & -block) + i*bitsPerBlock; } + /*! + * \brief Gets the mask associated to the last block + * \return Block which represents the mask + */ + template Block Bitset::GetLastBlockMask() const { return (Block(1U) << GetBitIndex(m_bitCount)) - 1U; } + /*! + * \brief Sets to '0' the last bits unassigned in the last block + */ + template void Bitset::ResetExtraBits() { @@ -503,33 +820,43 @@ namespace Nz m_blocks.back() &= mask; } + /*! + * \brief Computes the block count with the index of the bit + * \return Number of the blocks to contain the bit + */ + template unsigned int Bitset::ComputeBlockCount(unsigned int bitCount) { return GetBlockIndex(bitCount) + ((GetBitIndex(bitCount) != 0U) ? 1U : 0U); } + /*! + * \brief Computes the bit position in the block + * \return Index of the bit in the block + */ + template unsigned int Bitset::GetBitIndex(unsigned int bit) { return bit & (bitsPerBlock - 1U); // bit % bitsPerBlock } + /*! + * \brief Computes the block index with the index of the bit + * \return Index of the block containing the bit + */ + template unsigned int Bitset::GetBlockIndex(unsigned int bit) { return bit / bitsPerBlock; } - template - Block Bitset::fullBitMask = std::numeric_limits::max(); - - template - unsigned int Bitset::bitsPerBlock = std::numeric_limits::digits; - - template - unsigned int Bitset::npos = std::numeric_limits::max(); - + /*! + * \brief Flips the bit + * \return A reference to this + */ template typename Bitset::Bit& Bitset::Bit::Flip() @@ -539,12 +866,24 @@ namespace Nz return *this; } + /*! + * \brief Resets the bit + * \return A reference to this + */ + template typename Bitset::Bit& Bitset::Bit::Reset() { return Set(false); } + /*! + * \brief Sets the bit to a value + * \return A reference to this + * + * \param val Value of the bit + */ + template typename Bitset::Bit& Bitset::Bit::Set(bool val) { @@ -554,47 +893,85 @@ namespace Nz return *this; } + /*! + * \brief Tests the bit + * \return A reference to this + */ + template bool Bitset::Bit::Test() const { return m_block & m_mask; } + /*! + * \brief Gets the adress of the bit + * \return Nullptr + * + * \see std::addressof + */ + template template void* Bitset::Bit::operator&() const { - // Le template est nécessaire pour ne planter la compilation qu'à l'utilisation + // The template is necessary to make it fail only when used static_assert(!BadCall, "It is impossible to take the address of a bit in a bitset"); return nullptr; } + /*! + * \brief Converts this to bool + * \return true if bit set to '1' + */ + template Bitset::Bit::operator bool() const { return Test(); } + /*! + * \brief Sets the bit to a value + * \return A reference to this + * + * \param val Value of the bit + */ + template typename Bitset::Bit& Bitset::Bit::operator=(bool val) { return Set(val); } + /*! + * \brief Sets the bit to the value of another one + * \return A reference to this + * + * \param bit Other bit + */ + template typename Bitset::Bit& Bitset::Bit::operator=(const Bit& bit) { return Set(bit); } + /*! + * \brief Performs the operator "OR" on this bit with a boolean + * \return A reference to this + * + * \param val Value + */ + template typename Bitset::Bit& Bitset::Bit::operator|=(bool val) { - // Version sans branching: + // Version without branching: Set((val) ? true : Test()); - // Avec branching: + // With branching: /* if (val) Set(); @@ -603,13 +980,20 @@ namespace Nz return *this; } + /*! + * \brief Performs the operator "AND" on this bit with a boolean + * \return A reference to this + * + * \param val Value + */ + template typename Bitset::Bit& Bitset::Bit::operator&=(bool val) { - // Version sans branching: + // Version without branching: Set((val) ? Test() : false); - // Avec branching: + // With branching: /* if (!val) Reset(); @@ -618,13 +1002,20 @@ namespace Nz return *this; } + /*! + * \brief Performs the operator "XOR" on this bit with a boolean + * \return A reference to this + * + * \param val Value + */ + template typename Bitset::Bit& Bitset::Bit::operator^=(bool val) { - // Version sans branching: + // Version without branching: Set((val) ? !Test() : Test()); - // Avec branching: + // With branching: /* if (val) Flip(); @@ -633,13 +1024,20 @@ namespace Nz return *this; } + /*! + * \brief Performs the operator "RESET" on this bit with a boolean + * \return A reference to this + * + * \param val Value + */ + template typename Bitset::Bit& Bitset::Bit::operator-=(bool val) { - // Version sans branching: + // Version without branching: Set((val) ? false : Test()); - // Avec branching: + // With branching: /* if (val) Reset(); @@ -648,26 +1046,35 @@ namespace Nz return *this; } + /*! + * \brief Compares two bitsets + * \return true if the two bitsets are the same + * + * \param lhs First bitset to compare with + * \param rhs Other bitset to compare with + * + * \remark If one is bigger, they are equal only if the largest has the last bit set to '0' + */ + template bool operator==(const Bitset& lhs, const Bitset& rhs) { - // La comparaison part du principe que (uint8) 00001100 == (uint16) 00000000 00001100 - // et conserve donc cette propriété + // The comparison uses that (uint8) 00001100 == (uint16) 00000000 00001100 + // and thus conserve this property 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(); - // Nous testons les blocs en commun pour vérifier l'égalité des bits + // We test the blocks in common to check the equality of bits for (unsigned int i = 0; i < minBlockCount; ++i) { if (lhs.GetBlock(i) != rhs.GetBlock(i)) return false; } - // Nous vérifions maintenant les blocs que seul le plus grand bitset possède, pour prétendre à l'égalité - // ils doivent tous être nuls + // 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) if (greater.GetBlock(i)) return false; @@ -675,12 +1082,28 @@ namespace Nz return true; } + /*! + * \brief Compares two bitsets + * \return false if the two bitsets are the same + * + * \param lhs First bitset to compare with + * \param rhs Other bitset to compare with + */ + template bool operator!=(const Bitset& lhs, const Bitset& rhs) { return !(lhs == rhs); } + /*! + * \brief Compares two bitsets + * \return true if the binary number represented by the lhs bitset is smaller + * + * \param lhs First bitset to compare with + * \param rhs Other bitset to compare with + */ + template bool operator<(const Bitset& lhs, const Bitset& rhs) { @@ -708,24 +1131,56 @@ namespace Nz return false; // They are equal } + /*! + * \brief Compares two bitsets + * \return true if the binary number represented by the lhs bitset is smaller or equal + * + * \param lhs First bitset to compare with + * \param rhs Other bitset to compare with + */ + template bool operator<=(const Bitset& lhs, const Bitset& rhs) { return lhs < rhs || lhs == rhs; } + /*! + * \brief Compares two bitsets + * \return true if the binary number represented by the lhs bitset is greather + * + * \param lhs First bitset to compare with + * \param rhs Other bitset to compare with + */ + template bool operator>(const Bitset& lhs, const Bitset& rhs) { return rhs < lhs; } + /*! + * \brief Compares two bitsets + * \return true if the binary number represented by the lhs bitset is greather or equal + * + * \param lhs First bitset to compare with + * \param rhs Other bitset to compare with + */ + template bool operator>=(const Bitset& lhs, const Bitset& rhs) { return rhs <= lhs; } + /*! + * \brief Performs the operator "AND" between two bitsets + * \return The result of operator "AND" + * + * \param lhs First bitset + * \param rhs Second bitset + */ + template Bitset operator&(const Bitset& lhs, const Bitset& rhs) { @@ -735,6 +1190,14 @@ namespace Nz return bitset; } + /*! + * \brief Performs the operator "OR" between two bitsets + * \return The result of operator "OR" + * + * \param lhs First bitset + * \param rhs Second bitset + */ + template Bitset operator|(const Bitset& lhs, const Bitset& rhs) { @@ -744,6 +1207,14 @@ namespace Nz return bitset; } + /*! + * \brief Performs the operator "XOR" between two bitsets + * \return The result of operator "XOR" + * + * \param lhs First bitset + * \param rhs Second bitset + */ + template Bitset operator^(const Bitset& lhs, const Bitset& rhs) { @@ -757,6 +1228,13 @@ namespace Nz namespace std { + /*! + * \brief Swaps two bitsets, specialisation of std + * + * \param lhs First bitset + * \param rhs Second bitset + */ + template void swap(Nz::Bitset& lhs, Nz::Bitset& rhs) { diff --git a/include/Nazara/Core/ByteArray.inl b/include/Nazara/Core/ByteArray.inl index 1597758fd..1ff9bf185 100644 --- a/include/Nazara/Core/ByteArray.inl +++ b/include/Nazara/Core/ByteArray.inl @@ -3,62 +3,146 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include +#include namespace Nz { + /*! + * \brief Constructs a ByteArray object with a reserved size + * + * \param n Space reserved + */ + inline ByteArray::ByteArray(size_type n) : m_array() { m_array.reserve(n); } + /*! + * \brief Constructs a ByteArray object with a raw memory and a size + * + * \param ptr 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 + */ + inline ByteArray::ByteArray(const void* buffer, size_type n) : m_array(static_cast(buffer), static_cast(buffer) + n) { } + /*! + * \brief Constructs a ByteArray object with n times the same value + * + * \param n Number of repetitions + * \param value Value to repeat + */ + inline ByteArray::ByteArray(size_type n, const value_type value) : m_array(n, value) { } + /*! + * \brief Constructs a ByteArray object from two iterators + * + * \param first First iterator + * \param last Second iterator + */ + template ByteArray::ByteArray(InputIterator first, InputIterator last) : m_array(first, last) { } + /*! + * \brief Appends the content of raw memory + * + * \param ptr 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 + * + * \see Insert + */ + inline ByteArray::iterator ByteArray::Append(const void* buffer, size_type n) { return Insert(end(), static_cast(buffer), static_cast(buffer) + n); } + /*! + * \brief Appends another array of bytes + * + * \param other ByteArray to add + * + * \see Insert + */ + inline ByteArray::iterator ByteArray::Append(const ByteArray& other) { return Insert(end(), other.begin(), other.end()); } + /*! + * \brief Assigns this with n times the same value + * + * \param n Number of repetitions + * \param value Value to repeat + */ + inline void ByteArray::Assign(size_type n, value_type value) { m_array.assign(n, value); } + /*! + * \brief Assigns this with the content between two iterators + * + * \param first First iterator + * \param last Second iterator + */ + template void ByteArray::Assign(InputIterator first, InputIterator last) { m_array.assign(first, last); } + /*! + * \brief Gets last element + * \return A reference to last element + * + * \remark If ByteArray is empty, behaviour is undefined + */ + inline ByteArray::reference ByteArray::Back() { return m_array.back(); } + /*! + * \brief Gets last element + * \return A constant reference to last element + * + * \remark If ByteArray is empty, behaviour is undefined + */ + inline ByteArray::const_reference ByteArray::Back() const { return m_array.back(); } + /*! + * \brief Clears the content of the string + * + * \param keepBuffer Should the buffer be kept + */ + inline void ByteArray::Clear(bool keepBuffer) { m_array.clear(); @@ -66,142 +150,326 @@ namespace Nz m_array.shrink_to_fit(); } + /*! + * \brief Erases an element from the byte array + * + * \param pos Iterator to the element + */ + inline ByteArray::iterator ByteArray::Erase(const_iterator pos) { return m_array.erase(pos); } + /*! + * \brief Erases the elements between the two pointers from the byte array + * + * \param first First iterator + * \param last Second iterator + */ + inline ByteArray::iterator ByteArray::Erase(const_iterator first, const_iterator last) { return m_array.erase(first, last); } + /*! + * \brief Gets first element + * \return A reference to first element + * + * \remark If ByteArray is empty, behaviour is undefined + */ + inline ByteArray::reference ByteArray::Front() { return m_array.front(); } + /*! + * \brief Gets first element + * \return A constant reference to first element + * + * \remark If ByteArray is empty, behaviour is undefined + */ + inline ByteArray::const_reference ByteArray::Front() const { return m_array.front(); } + /*! + * \brief Gets the internal allocator of the byte array + * \return Allocator + */ + inline ByteArray::allocator_type ByteArray::GetAllocator() const { return m_array.get_allocator(); } + /*! + * \brief Gets the raw buffer + * \return Raw buffer + */ + inline ByteArray::pointer ByteArray::GetBuffer() { return m_array.data(); } + /*! + * \brief Gets the capacity of the byte array + * \return Capacity of the byte array + */ + inline ByteArray::size_type ByteArray::GetCapacity() const noexcept { return m_array.capacity(); } + /*! + * \brief Gets the raw buffer + * \return Raw buffer + */ + inline ByteArray::const_pointer ByteArray::GetConstBuffer() const { return m_array.data(); } + /*! + * \brief Gets the maximal size supported by the byte array + * \return Biggest size handled + */ + inline ByteArray::size_type ByteArray::GetMaxSize() const noexcept { return m_array.max_size(); } + /*! + * \brief Gets the size of the byte array + * \return Size of the byte array + */ + inline ByteArray::size_type ByteArray::GetSize() const noexcept { return m_array.size(); } + /*! + * \brief Returns a sub byte array of the byte array + * \return Sub byte array + * + * \param startPos First iterator + * \param endPos Second iterator + */ + inline ByteArray ByteArray::GetSubArray(const_iterator startPos, const_iterator endPos) const { return ByteArray(startPos, endPos); } + /*! + * \brief Inserts the content of raw memory at the iterator position + * + * \param pos Iterator to the position + * \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 + */ + inline ByteArray::iterator ByteArray::Insert(const_iterator pos, const void* buffer, size_type n) { return m_array.insert(pos, static_cast(buffer), static_cast(buffer) + n); } + /*! + * \brief Inserts the content of another byte array at the iterator position + * + * \param pos Iterator to the position + * \param other Other byte array + */ + inline ByteArray::iterator ByteArray::Insert(const_iterator pos, const ByteArray& other) { return m_array.insert(pos, other.begin(), other.end()); } + /*! + * \brief Inserts n times the same value at the iterator position + * + * \param pos Iterator to the position + * \param n Number of repetitions + * \param value Value to repeat + */ + inline ByteArray::iterator ByteArray::Insert(const_iterator pos, size_type n, value_type byte) { return m_array.insert(pos, n, byte); } + /*! + * \brief Inserts the content from two iterators at the iterator position + * + * \param pos Iterator to the position + * \param first First iterator + * \param last Second iterator + */ + template ByteArray::iterator ByteArray::Insert(const_iterator pos, InputIterator first, InputIterator last) { return m_array.insert(pos, first, last); } + /*! + * \brief Checks whether the byte array is empty + * \return true if byte array is empty + */ + inline bool ByteArray::IsEmpty() const noexcept { return m_array.empty(); } + /*! + * \brief Erases the last element + * + * \remark If byte array is empty, the behaviour is undefined + */ + inline void ByteArray::PopBack() { Erase(end() - 1); } + /*! + * \brief Erases the first element + * + * \remark If byte array is empty, the behaviour is undefined + */ + inline void ByteArray::PopFront() { Erase(begin()); } + /*! + * \brief Prepends the content of raw memory + * + * \param ptr 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 + * + * \see Insert + */ + inline ByteArray::iterator ByteArray::Prepend(const void* buffer, size_type n) { return Insert(begin(), buffer, n); } + /*! + * \brief Prepends another array of bytes + * + * \param other ByteArray to add + * + * \see Insert + */ + inline ByteArray::iterator ByteArray::Prepend(const ByteArray& other) { return Insert(begin(), other); } + /*! + * \brief Pushes the byte at the end + * + * \param byte Byte to add + */ + inline void ByteArray::PushBack(const value_type byte) { m_array.push_back(byte); } + /*! + * \brief Pushes the byte at the beginning + * + * \param byte Byte to add + */ + inline void ByteArray::PushFront(const value_type byte) { m_array.insert(begin(), 1, byte); } + /*! + * \brief Reserves enough memory for the buffer size + * + * \param bufferSize Size of the buffer to allocate + * + * \remark If bufferSize is smaller than the old one, nothing is done + */ + inline void ByteArray::Reserve(size_type bufferSize) { m_array.reserve(bufferSize); } + /*! + * \brief Resizes the string + * \return A reference to this + * + * \param newSize Target size + */ + inline void ByteArray::Resize(size_type newSize) { m_array.resize(newSize); } + /*! + * \brief Resizes the string + * \return A reference to this + * + * \param newSize Target size + * \param byte Byte to add if newSize is greather than actual size + */ + inline void ByteArray::Resize(size_type newSize, const value_type byte) { m_array.resize(newSize, byte); } + /*! + * \brief Releases the excedent memory + */ + inline void ByteArray::ShrinkToFit() { m_array.shrink_to_fit(); } + /*! + * \brief Swaps the content with the other byte array + * + * \param other Other byte array to swap with + */ + inline void ByteArray::Swap(ByteArray& other) { m_array.swap(other.m_array); } + /*! + * \brief Gives a string representation in base 16 + * \return String in base 16 + */ + inline String ByteArray::ToHex() const { std::size_t length = m_array.size() * 2; @@ -213,76 +481,153 @@ namespace Nz return hexOutput; } + /*! + * \brief Gives a string representation + * \return String where each byte is converted to char + */ + inline String ByteArray::ToString() const { return String(reinterpret_cast(GetConstBuffer()), GetSize()); } + /*! + * \brief Returns an iterator pointing to the beggining of the string + * \return Beggining of the string + */ + inline ByteArray::iterator ByteArray::begin() noexcept { return m_array.begin(); } + /*! + * \brief Returns an iterator pointing to the beggining of the string + * \return Beggining of the string + */ + inline ByteArray::const_iterator ByteArray::begin() const noexcept { return m_array.begin(); } + /*! + * \brief Returns a constant iterator pointing to the beggining of the string + * \return Beggining of the string + */ + inline ByteArray::const_iterator ByteArray::cbegin() const noexcept { return m_array.cbegin(); } + /*! + * \brief Returns a constant iterator pointing to the end of the string + * \return End of the string + */ + inline ByteArray::const_iterator ByteArray::cend() const noexcept { return m_array.cend(); } + /*! + * \brief Returns a constant reversed iterator pointing to the beggining of the string + * \return Beggining of the string + */ + inline ByteArray::const_reverse_iterator ByteArray::crbegin() const noexcept { return m_array.crbegin(); } + /*! + * \brief Returns a constant reversed iterator pointing to the end of the string + * \return End of the string + */ + inline ByteArray::const_reverse_iterator ByteArray::crend() const noexcept { return m_array.crend(); } + /*! + * \brief Checks whether the byte array is empty + * \return true if byte array is empty + */ + inline bool ByteArray::empty() const noexcept { return m_array.empty(); } + /*! + * \brief Returns an iterator pointing to the end of the string + * \return End of the string + */ + inline ByteArray::iterator ByteArray::end() noexcept { return m_array.end(); } + /*! + * \brief Returns an iterator pointing to the end of the string + * \return End of the string + */ + inline ByteArray::const_iterator ByteArray::end() const noexcept { return m_array.end(); } + /*! + * \brief Returns a reversed iterator pointing to the beggining of the string + * \return Beggining of the string + */ + inline ByteArray::reverse_iterator ByteArray::rbegin() noexcept { return m_array.rbegin(); } + /*! + * \brief Returns a reversed iterator pointing to the beggining of the string + * \return Beggining of the string + */ + inline ByteArray::const_reverse_iterator ByteArray::rbegin() const noexcept { return m_array.rbegin(); } + /*! + * \brief Returns a reversed iterator pointing to the end of the string + * \return End of the string + */ + inline ByteArray::reverse_iterator ByteArray::rend() noexcept { return m_array.rend(); } + /*! + * \brief Gets the size of the byte array + * \return Size of the byte array + */ + inline ByteArray::size_type ByteArray::size() const noexcept { return GetSize(); } + /*! + * \brief Gets the ith byte + * \return A reference to the byte at the ith position + * + * \remark Produces a NazaraAssert if pos is greather than the size + */ + inline ByteArray::reference ByteArray::operator[](size_type pos) { NazaraAssert(pos < GetSize(), "Index out of range"); @@ -290,6 +635,13 @@ namespace Nz return m_array[pos]; } + /*! + * \brief Gets the ith byte + * \return A constant reference to the byte at the ith position + * + * \remark Produces a NazaraAssert if pos is greather than the size + */ + inline ByteArray::const_reference ByteArray::operator[](size_type pos) const { NazaraAssert(pos < GetSize(), "Index out of range"); @@ -297,6 +649,13 @@ namespace Nz return m_array[pos]; } + /*! + * \brief Concatenates the byte array to another + * \return ByteArray which is the result of the concatenation + * + * \param other ByteArray to add + */ + inline ByteArray ByteArray::operator+(const ByteArray& other) const { ByteArray tmp(*this); @@ -305,6 +664,13 @@ namespace Nz return tmp; } + /*! + * \brief Concatenates the byte array to this byte array + * \return A reference to this + * + * \param other ByteArray to add + */ + inline ByteArray& ByteArray::operator+=(const ByteArray& other) { Append(other); @@ -312,31 +678,79 @@ namespace Nz return *this; } + /*! + * \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 + */ + inline bool ByteArray::operator==(const ByteArray& rhs) const { return m_array == rhs.m_array; } + /*! + * \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 + */ + inline bool ByteArray::operator!=(const ByteArray& rhs) const { return !operator==(rhs); } + /*! + * \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 + */ + inline bool ByteArray::operator<(const ByteArray& rhs) const { return m_array < rhs.m_array; } + /*! + * \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 + */ + inline bool ByteArray::operator<=(const ByteArray& rhs) const { return m_array <= rhs.m_array; } + /*! + * \brief Checks whether the first byte array is greather 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 + */ + inline bool ByteArray::operator>(const ByteArray& rhs) const { return m_array > rhs.m_array; } + /*! + * \brief Checks whether the first byte array is greather 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 + */ + inline bool ByteArray::operator>=(const ByteArray& rhs) const { return m_array >= rhs.m_array; @@ -351,8 +765,17 @@ namespace Nz namespace std { + /*! + * \brief Swaps two byte arrays, specialisation of std + * + * \param lhs First byte array + * \param rhs Second byte array + */ + inline void swap(Nz::ByteArray& lhs, Nz::ByteArray& rhs) { lhs.Swap(rhs); } } + +#include diff --git a/include/Nazara/Core/ByteStream.hpp b/include/Nazara/Core/ByteStream.hpp new file mode 100644 index 000000000..e26382cf4 --- /dev/null +++ b/include/Nazara/Core/ByteStream.hpp @@ -0,0 +1,64 @@ +// Copyright (C) 2015 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 + +#pragma once + +#ifndef NAZARA_BYTESTREAM_HPP +#define NAZARA_BYTESTREAM_HPP + +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_CORE_API ByteStream + { + public: + inline ByteStream(Stream* stream = nullptr); + ByteStream(ByteArray* byteArray, UInt32 openMode = OpenMode_ReadWrite); + ByteStream(void* ptr, Nz::UInt64 size); + ByteStream(const void* ptr, Nz::UInt64 size); + ByteStream(const ByteStream&) = delete; + inline ByteStream(ByteStream&& stream); + ~ByteStream(); + + inline Endianness GetDataEndianness() const; + inline Nz::UInt64 GetSize() const; + inline Stream* GetStream() const; + + inline bool FlushBits(); + + inline std::size_t Read(void* ptr, std::size_t size); + + inline void SetDataEndianness(Endianness endiannes); + inline void SetStream(Stream* stream); + void SetStream(ByteArray* byteArray, UInt32 openMode = OpenMode_ReadWrite); + void SetStream(void* ptr, Nz::UInt64 size); + void SetStream(const void* ptr, Nz::UInt64 size); + + inline void Write(const void* data, std::size_t size); + + template + ByteStream& operator>>(T& value); + + template + ByteStream& operator<<(const T& value); + + ByteStream& operator=(const ByteStream&) = delete; + inline ByteStream& operator=(ByteStream&&); + + private: + virtual void OnEmptyStream(); + + std::unique_ptr m_ownedStream; + SerializationContext m_context; + }; +} + +#include + +#endif // NAZARA_BYTESTREAM_HPP diff --git a/include/Nazara/Core/ByteStream.inl b/include/Nazara/Core/ByteStream.inl new file mode 100644 index 000000000..dadc056ca --- /dev/null +++ b/include/Nazara/Core/ByteStream.inl @@ -0,0 +1,226 @@ +// Copyright (C) 2015 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 +{ + /*! + * \brief Constructs a ByteStream object with a stream + */ + + inline ByteStream::ByteStream(Stream* stream) + { + m_context.stream = stream; + } + + /*! + * \brief Constructs a ByteStream object by move semantic + * + * \param stream ByteStream to move into this + */ + + inline ByteStream::ByteStream(ByteStream&& stream) : + m_ownedStream(std::move(stream.m_ownedStream)), + m_context(stream.m_context) + { + stream.m_context.stream = nullptr; + } + + /*! + * \brief Destructs the object and calls FlushBits + * + * \remark Produces a NazaraWarning if flush did not work + * + * \see FlushBits + */ + + inline ByteStream::~ByteStream() + { + if (!FlushBits()) + NazaraWarning("Failed to flush bits at serializer destruction"); + } + + /*! + * \brief Gets the stream endianness + * \return Type of the endianness + */ + + inline Endianness ByteStream::GetDataEndianness() const + { + return m_context.endianness; + } + + /*! + * \brief Gets the size of the byte stream + * \return Size of the stream + */ + + inline Nz::UInt64 ByteStream::GetSize() const + { + if (m_context.stream) + return m_context.stream->GetSize(); + else + return 0; + } + + /*! + * \brief Gets the internal stream + * \return Internal stream + */ + + inline Stream* ByteStream::GetStream() const + { + return m_context.stream; + } + + /*! + * \brief Flushes the stream + * \return true if flushing is successful + */ + + inline bool ByteStream::FlushBits() + { + if (!m_context.stream) + return true; + + if (m_context.currentBitPos != 8) + { + m_context.currentBitPos = 8; //< To prevent Serialize to flush bits itself + + if (!Serialize(m_context, m_context.currentByte)) + return false; + } + + return true; + } + + /*! + * \brief Reads data + * \return Number of data read + * + * \param buffer Preallocated buffer to contain information read + * \param size Size of the read and thus of the buffer + */ + + inline std::size_t ByteStream::Read(void* ptr, std::size_t size) + { + if (!m_context.stream) + OnEmptyStream(); + + FlushBits(); + return m_context.stream->Read(ptr, size); + } + + /*! + * \brief Sets the stream endianness + * + * \param Type of the endianness + */ + + inline void ByteStream::SetDataEndianness(Endianness endiannes) + { + m_context.endianness = endiannes; + } + + /*! + * \brief Sets this with a stream + * + * \param stream Stream existing + * + * \remark Produces a NazaraAssert if stream is invalid + */ + + inline void ByteStream::SetStream(Stream* stream) + { + NazaraAssert(stream, "Invalid stream"); + + // We don't want to lose some bits.. + FlushBits(); + + m_context.stream = stream; + m_ownedStream.reset(); + } + + /*! + * \brief Writes data + * \return Number of data 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 + */ + + inline void ByteStream::Write(const void* data, std::size_t size) + { + if (!m_context.stream) + OnEmptyStream(); + + FlushBits(); + m_context.stream->Write(data, size); + } + + /*! + * \brief Outputs a data from the stream + * \return A reference to this + * + * \param value Value to unserialize + * + * \remark Produces a NazaraError if unserialization failed + */ + + template + ByteStream& ByteStream::operator>>(T& value) + { + if (!m_context.stream) + OnEmptyStream(); + + if (!Unserialize(m_context, &value)) + NazaraError("Failed to serialize value"); + + return *this; + } + + /*! + * \brief Adds the data to the stream + * \return A reference to this + * + * \param value Value to serialize + * + * \remark Produces a NazaraError if serialization failed + */ + + template + ByteStream& ByteStream::operator<<(const T& value) + { + if (!m_context.stream) + OnEmptyStream(); + + if (!Serialize(m_context, value)) + NazaraError("Failed to serialize value"); + + return *this; + } + + /*! + * \brief Moves the other byte stream into this + * \return A reference to this + * + * \param stream ByteStream to move in this + */ + + inline ByteStream& ByteStream::operator=(ByteStream&& stream) + { + m_context = stream.m_context; + m_ownedStream = std::move(stream.m_ownedStream); + + stream.m_context.stream = nullptr; + + return *this; + } +} + +#include diff --git a/include/Nazara/Core/CallOnExit.inl b/include/Nazara/Core/CallOnExit.inl index f89810a77..0318752cc 100644 --- a/include/Nazara/Core/CallOnExit.inl +++ b/include/Nazara/Core/CallOnExit.inl @@ -7,17 +7,39 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::CallOnExit + * \brief Core class that represents a function to call at the end of the scope + */ + + /*! + * \brief Constructs a CallOnExit object with a function + * + * \param func Function to call on exit + */ + inline CallOnExit::CallOnExit(Func func) : m_func(func) { } + /*! + * \brief Destructs the object and calls the function + */ + inline CallOnExit::~CallOnExit() { if (m_func) m_func(); } + /*! + * \brief Calls the function and sets the new callback + * + * \param func Function to call on exit + */ + inline void CallOnExit::CallAndReset(Func func) { if (m_func) @@ -26,6 +48,12 @@ namespace Nz Reset(func); } + /*! + * \brief Resets the function + * + * \param func Function to call on exit + */ + inline void CallOnExit::Reset(Func func) { m_func = func; diff --git a/include/Nazara/Core/Color.hpp b/include/Nazara/Core/Color.hpp index a7df6b9a2..91a8cc43d 100644 --- a/include/Nazara/Core/Color.hpp +++ b/include/Nazara/Core/Color.hpp @@ -13,6 +13,8 @@ namespace Nz { + struct SerializationContext; + class Color { public: @@ -62,6 +64,9 @@ namespace Nz private: static float Hue2RGB(float v1, float v2, float vH); }; + + inline bool Serialize(SerializationContext& context, const Color& color); + inline bool Unserialize(SerializationContext& context, Color* color); } std::ostream& operator<<(std::ostream& out, const Nz::Color& color); diff --git a/include/Nazara/Core/Color.inl b/include/Nazara/Core/Color.inl index ac576eee3..4ac3a2246 100644 --- a/include/Nazara/Core/Color.inl +++ b/include/Nazara/Core/Color.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -11,10 +12,29 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Color + * \brief Core class that represents a color + */ + + /*! + * \brief Constructs a Color object by default + */ + inline Color::Color() { } + /*! + * \brief Constructs a Color object with values + * + * \param red Red value + * \param green Green value + * \param blue Blue value + * \param alpha Alpha value + */ + inline Color::Color(UInt8 red, UInt8 green, UInt8 blue, UInt8 alpha) : r(red), g(green), @@ -23,6 +43,12 @@ namespace Nz { } + /*! + * \brief Constructs a Color object with a light level + * + * \param lightness Value for r, g and b + */ + inline Color::Color(UInt8 lightness) : r(lightness), g(lightness), @@ -31,6 +57,13 @@ namespace Nz { } + /*! + * \brief Constructs a Color object with values + * + * \param vec[3] vec[0] = red value, vec[1] = green value, vec[2] = blue value + * \param alpha Alpha value + */ + inline Color::Color(UInt8 vec[3], UInt8 alpha) : r(vec[0]), g(vec[1]), @@ -39,6 +72,11 @@ namespace Nz { } + /*! + * \brief Converts this to string + * \return String representation of the object "Color(r, g, b[, a])" + */ + inline String Color::ToString() const { StringStream ss; @@ -52,6 +90,13 @@ namespace Nz return ss; } + /*! + * \brief Adds two colors together + * \return Color which is the sum + * + * \param color Other color + */ + inline Color Color::operator+(const Color& color) const { ///TODO: Improve this shit @@ -64,6 +109,13 @@ namespace Nz return c; } + /*! + * \brief Multiplies two colors together + * \return Color which is the product + * + * \param color Other color + */ + inline Color Color::operator*(const Color& color) const { ///TODO: Improve this shit @@ -76,48 +128,104 @@ namespace Nz return c; } + /*! + * \brief Adds the color to this + * \return Color which is the sum + * + * \param color Other color + */ + inline Color Color::operator+=(const Color& color) { return operator=(operator+(color)); } + /*! + * \brief Multiplies the color to this + * \return Color which is the product + * + * \param color Other color + */ + inline Color Color::operator*=(const Color& color) { return operator=(operator*(color)); } + /*! + * \brief Checks whether the two colors are equal + * \return true if it is the case + * + * \param color Color to compare + */ + inline bool Color::operator==(const Color& color) const { return r == color.r && g == color.g && b == color.b && a == color.a; } + /*! + * \brief Checks whether the two colors are equal + * \return false if it is the case + * + * \param color Color to compare + */ + inline bool Color::operator!=(const Color& color) const { return !operator==(color); } - // Algorithmes venant de http://www.easyrgb.com/index.php?X=MATH + // Algorithm coming from http://www.easyrgb.com/index.php?X=MATH + + /*! + * \brief Converts CMY representation to RGB + * \return Color resulting + * + * \param cyan Cyan component + * \param magenta Magenta component + * \param yellow Yellow component + */ inline Color Color::FromCMY(float cyan, float magenta, float yellow) { return Color(static_cast((1.f-cyan)*255.f), static_cast((1.f-magenta)*255.f), static_cast((1.f-yellow)*255.f)); } + /*! + * \brief Converts CMYK representation to RGB + * \return Color resulting + * + * \param cyan Cyan component + * \param magenta Magenta component + * \param yellow Yellow component + * \param black Black component + */ + inline Color Color::FromCMYK(float cyan, float magenta, float yellow, float black) { return FromCMY(cyan * (1.f - black) + black, - magenta * (1.f - black) + black, - yellow * (1.f - black) + black); + magenta * (1.f - black) + black, + yellow * (1.f - black) + black); } + /*! + * \brief Converts HSL representation to RGB + * \return Color resulting + * + * \param hue Hue component + * \param saturation Saturation component + * \param lightness Lightness component + */ + inline Color Color::FromHSL(UInt8 hue, UInt8 saturation, UInt8 lightness) { if (saturation == 0) { // RGB results from 0 to 255 return Color(lightness * 255, - lightness * 255, - lightness * 255); + lightness * 255, + lightness * 255); } else { @@ -135,11 +243,20 @@ namespace Nz float v1 = 2.f * l - v2; return Color(static_cast(255.f * Hue2RGB(v1, v2, h + (1.f/3.f))), - static_cast(255.f * Hue2RGB(v1, v2, h)), - static_cast(255.f * Hue2RGB(v1, v2, h - (1.f/3.f)))); + static_cast(255.f * Hue2RGB(v1, v2, h)), + static_cast(255.f * Hue2RGB(v1, v2, h - (1.f/3.f)))); } } + /*! + * \brief Converts HSV representation to RGB + * \return Color resulting + * + * \param hue Hue component + * \param saturation Saturation component + * \param value Value component + */ + inline Color Color::FromHSV(float hue, float saturation, float value) { if (NumberEquals(saturation, 0.f)) @@ -201,11 +318,28 @@ namespace Nz return Color(static_cast(r*255.f), static_cast(g*255.f), static_cast(b*255.f)); } } + + /*! + * \brief Converts XYZ representation to RGB + * \return Color resulting + * + * \param vec Vector3 representing the space color + */ + inline Color Color::FromXYZ(const Vector3f& vec) { return FromXYZ(vec.x, vec.y, vec.z); } + /*! + * \brief Converts XYZ representation to RGB + * \return Color resulting + * + * \param x X component + * \param y Y component + * \param z Z component + */ + inline Color Color::FromXYZ(float x, float y, float z) { x /= 100.f; // X from 0 to 95.047 @@ -234,6 +368,15 @@ namespace Nz return Color(static_cast(r * 255.f), static_cast(g * 255.f), static_cast(b * 255.f)); } + /*! + * \brief Converts RGB representation to CMYK + * + * \param color Color to transform + * \param cyan Cyan component + * \param magenta Magenta component + * \param yellow Yellow component + */ + inline void Color::ToCMY(const Color& color, float* cyan, float* magenta, float* yellow) { *cyan = 1.f - color.r/255.f; @@ -241,6 +384,15 @@ namespace Nz *yellow = 1.f - color.b/255.f; } + /*! + * \brief Converts RGB representation to CMYK + * + * \param color Color to transform + * \param cyan Cyan component + * \param magenta Magenta component + * \param yellow Yellow component + */ + inline void Color::ToCMYK(const Color& color, float* cyan, float* magenta, float* yellow, float* black) { float c, m, y; @@ -265,6 +417,15 @@ namespace Nz *black = k; } + /*! + * \brief Converts RGB representation to HSL + * + * \param color Color to transform + * \param hue Hue component + * \param saturation Saturation component + * \param lightness Lightness component + */ + inline void Color::ToHSL(const Color& color, UInt8* hue, UInt8* saturation, UInt8* lightness) { float r = color.r / 255.f; @@ -315,6 +476,15 @@ namespace Nz } } + /*! + * \brief Converts RGB representation to HSV + * + * \param color Color to transform + * \param hue Hue component + * \param saturation Saturation component + * \param value Value component + */ + inline void Color::ToHSV(const Color& color, float* hue, float* saturation, float* value) { float r = color.r / 255.f; @@ -361,11 +531,27 @@ namespace Nz } } + /*! + * \brief Converts RGB representation to XYZ + * + * \param color Color to transform + * \param vec Vector3 representing the space color + */ + inline void Color::ToXYZ(const Color& color, Vector3f* vec) { return ToXYZ(color, &vec->x, &vec->y, &vec->z); } + /*! + * \brief Converts RGB representation to XYZ + * + * \param color Color to transform + * \param x X component + * \param y Y component + * \param z Z component + */ + inline void Color::ToXYZ(const Color& color, float* x, float* y, float* z) { float r = color.r/255.f; //R from 0 to 255 @@ -397,6 +583,15 @@ namespace Nz *z = r*0.0193f + g*0.1192f + b*0.9505f; } + /*! + * \brief Converts HUE representation to RGV + * \return RGB corresponding + * + * \param v1 V1 component + * \param v2 V2 component + * \param vH VH component + */ + inline float Color::Hue2RGB(float v1, float v2, float vH) { if (vH < 0.f) @@ -415,8 +610,64 @@ namespace Nz return v1 + (v2 - v1)*(2.f/3.f - vH)*6; return v1; + } + + /*! + * \brief Serializes a Color + * \return true if successfully serialized + * + * \param context Serialization context + * \param color Input color + */ + inline bool Serialize(SerializationContext& context, const Color& color) + { + if (!Serialize(context, color.r)) + return false; + + if (!Serialize(context, color.g)) + return false; + + if (!Serialize(context, color.b)) + return false; + + if (!Serialize(context, color.a)) + return false; + + return true; + } + + /*! + * \brief Unserializes a Color + * \return true if successfully unserialized + * + * \param context Serialization context + * \param color Output color + */ + inline bool Unserialize(SerializationContext& context, Color* color) + { + if (!Unserialize(context, &color->r)) + return false; + + if (!Unserialize(context, &color->g)) + return false; + + if (!Unserialize(context, &color->b)) + return false; + + if (!Unserialize(context, &color->a)) + return false; + + return true; + } } -} + +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param color The color to output +*/ inline std::ostream& operator<<(std::ostream& out, const Nz::Color& color) { diff --git a/include/Nazara/Core/ConditionVariable.hpp b/include/Nazara/Core/ConditionVariable.hpp index 4096e00b4..f6aeafe9b 100644 --- a/include/Nazara/Core/ConditionVariable.hpp +++ b/include/Nazara/Core/ConditionVariable.hpp @@ -19,7 +19,7 @@ namespace Nz public: ConditionVariable(); ConditionVariable(const ConditionVariable&) = delete; - ConditionVariable(ConditionVariable&&) = delete; ///TODO + inline ConditionVariable(ConditionVariable&& condition) noexcept; ~ConditionVariable(); void Signal(); @@ -29,11 +29,13 @@ namespace Nz bool Wait(Mutex* mutex, UInt32 timeout); ConditionVariable& operator=(const ConditionVariable&) = delete; - ConditionVariable& operator=(ConditionVariable&&) = delete; ///TODO + inline ConditionVariable& operator=(ConditionVariable&& condition) noexcept; private: ConditionVariableImpl* m_impl; }; } +#include + #endif // NAZARA_CONDITIONVARIABLE_HPP diff --git a/include/Nazara/Core/ConditionVariable.inl b/include/Nazara/Core/ConditionVariable.inl new file mode 100644 index 000000000..636c98493 --- /dev/null +++ b/include/Nazara/Core/ConditionVariable.inl @@ -0,0 +1,25 @@ +// Copyright (C) 2015 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 +#include + +namespace Nz +{ + /*! + * \class Nz::ConditionVariable + */ + + /*! + * \brief Constructs a ConditionVariable object by moving another one + */ + inline ConditionVariable::ConditionVariable(ConditionVariable&& condition) noexcept : + m_impl(condition.m_impl) + { + condition.m_impl = nullptr; + } +} + +#include diff --git a/include/Nazara/Core/Config.hpp b/include/Nazara/Core/Config.hpp index eee521307..955b98c75 100644 --- a/include/Nazara/Core/Config.hpp +++ b/include/Nazara/Core/Config.hpp @@ -27,36 +27,41 @@ #ifndef NAZARA_CONFIG_CORE_HPP #define NAZARA_CONFIG_CORE_HPP -/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci +/*! +* \defgroup core (NazaraCore) Core module +* Core/System module including classes to handle threading, time, hardware info, memory management, etc... +*/ -// Précision des réels lors de la transformation en chaîne de caractère (Max. chiffres après la virgule) +/// Each modification of a parameter needs a recompilation of the module + +// Precision of reals when transformed into string (Max. numbers after the coma) #define NAZARA_CORE_DECIMAL_DIGITS 6 -// Duplique la sortie du log sur le flux de sortie standard (cout) +// Duplicate the log output on the standard output flux (cout) #define NAZARA_CORE_DUPLICATE_LOG_TO_COUT 0 -// Teste les assertions +// Checks the assertions #define NAZARA_CORE_ENABLE_ASSERTS 0 -// Appelle exit dès qu'une assertion est invalide +// Call exit when an assertion is invalid #define NAZARA_CORE_EXIT_ON_ASSERT_FAILURE 1 -// Taille du buffer lors d'une lecture complète d'un fichier (ex: Hash) +// Size of buffer when reading entirely a file (ex: Hash) #define NAZARA_CORE_FILE_BUFFERSIZE 4096 -// Incorpore la table Unicode Character Data (Nécessaires pour faire fonctionner le flag String::HandleUTF8) +// Incorporate the Unicode Character Data table (Necessary to make it work with the flag String::HandleUTF8) #define NAZARA_CORE_INCLUDE_UNICODEDATA 0 -// Utilise le MemoryManager 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_CORE_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_CORE_SAFE 1 -// Protège les classes des accès concurrentiels +// Protect the classes against data race #define NAZARA_CORE_THREADSAFE 1 -// Les classes à protéger des accès concurrentiels +// Classes to protect against data race #define NAZARA_THREADSAFETY_CLOCK 0 // Clock #define NAZARA_THREADSAFETY_DIRECTORY 1 // Directory #define NAZARA_THREADSAFETY_DYNLIB 1 // DynLib @@ -64,18 +69,19 @@ #define NAZARA_THREADSAFETY_LOG 1 // Log #define NAZARA_THREADSAFETY_REFCOUNTED 1 // RefCounted -// Le nombre de spinlocks à utiliser avec les sections critiques de Windows (0 pour désactiver) +// Number of spinlocks to use with the Windows critical sections (0 to disable) #define NAZARA_CORE_WINDOWS_CS_SPINLOCKS 4096 -// Optimise l'implémentation Windows avec certaines avancées de Windows vista (Casse la compatibilité XP) +// Optimize the Windows implementation with technologies of Windows vista (and greather) (Break the compatibility with XP) #define NAZARA_CORE_WINDOWS_VISTA 0 + /* -// Règle le temps entre le réveil du thread des timers et l'activation d'un timer (En millisecondes) +// Sets the time between waking thread timers and activating a timer (in milliseconds) #define NAZARA_CORE_TIMER_WAKEUPTIME 10 */ -/// 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/Core/ConfigCheck.hpp b/include/Nazara/Core/ConfigCheck.hpp index 310567eef..8329f4ff0 100644 --- a/include/Nazara/Core/ConfigCheck.hpp +++ b/include/Nazara/Core/ConfigCheck.hpp @@ -7,12 +7,12 @@ #ifndef NAZARA_CONFIG_CHECK_CORE_HPP #define NAZARA_CONFIG_CHECK_CORE_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_CORE_MANAGE_MEMORY #undef NAZARA_CORE_MANAGE_MEMORY #define NAZARA_CORE_MANAGE_MEMORY 0 diff --git a/include/Nazara/Core/DebugOff.hpp b/include/Nazara/Core/DebugOff.hpp index e042eff24..62788ee15 100644 --- a/include/Nazara/Core/DebugOff.hpp +++ b/include/Nazara/Core/DebugOff.hpp @@ -2,7 +2,7 @@ // This file is part of the "Nazara Engine - Core 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_CORE_MANAGE_MEMORY #undef delete #undef new diff --git a/include/Nazara/Core/DynLib.hpp b/include/Nazara/Core/DynLib.hpp index 767165fe4..4f2759e22 100644 --- a/include/Nazara/Core/DynLib.hpp +++ b/include/Nazara/Core/DynLib.hpp @@ -28,7 +28,7 @@ namespace Nz { - using DynLibFunc = int (*)(); // Type "générique" de pointeur sur fonction + using DynLibFunc = int (*)(); // "Generic" type of poiter to function class DynLibImpl; @@ -56,7 +56,7 @@ namespace Nz mutable String m_lastError; DynLibImpl* m_impl; -}; + }; } #endif // NAZARA_DYNLIB_HPP diff --git a/include/Nazara/Core/Endianness.hpp b/include/Nazara/Core/Endianness.hpp index 62cac093d..44a4e9f26 100644 --- a/include/Nazara/Core/Endianness.hpp +++ b/include/Nazara/Core/Endianness.hpp @@ -11,12 +11,12 @@ #include #if !defined(NAZARA_BIG_ENDIAN) && !defined(NAZARA_LITTLE_ENDIAN) - // Détection automatique selon les macros du compilateur + // Automatic detection following macroes of compiler #if defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || (defined(__MIPS__) && defined(__MISPEB__)) || \ defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || defined(__sparc__) || defined(__hppa__) #define NAZARA_BIG_ENDIAN #elif defined(__i386__) || defined(__i386) || defined(__X86__) || defined (__x86_64) || defined(_M_I86) || \ - defined(_M_IX86) || defined(_M_X64) + defined(_M_IX86) || defined(_M_X64) #define NAZARA_LITTLE_ENDIAN #else #error Failed to identify endianness, you must define either NAZARA_BIG_ENDIAN or NAZARA_LITTLE_ENDIAN diff --git a/include/Nazara/Core/Endianness.inl b/include/Nazara/Core/Endianness.inl index 96e4fdf8a..2949e3f19 100644 --- a/include/Nazara/Core/Endianness.inl +++ b/include/Nazara/Core/Endianness.inl @@ -7,6 +7,12 @@ namespace Nz { + /*! + * \ingroup core + * \brief Gets the platform endianness + * \return Type of the endianness + */ + inline constexpr Endianness GetPlatformEndianness() { #if defined(NAZARA_BIG_ENDIAN) @@ -16,11 +22,21 @@ namespace Nz #endif } + /*! + * \ingroup core + * \brief Swaps the byte for endianness operations + * + * \param buffer Raw memory + * \param size Size to change endianness + * + * \remark If size is greather than the preallocated buffer, the behaviour is undefined + */ + inline void SwapBytes(void* buffer, unsigned int size) { - UInt8* bytes = reinterpret_cast(buffer); + UInt8* bytes = static_cast(buffer); unsigned int i = 0; - unsigned int j = size-1; + unsigned int j = size - 1; while (i < j) std::swap(bytes[i++], bytes[j--]); diff --git a/include/Nazara/Core/Enums.hpp b/include/Nazara/Core/Enums.hpp index 1fd884799..0c0530489 100644 --- a/include/Nazara/Core/Enums.hpp +++ b/include/Nazara/Core/Enums.hpp @@ -19,9 +19,9 @@ namespace Nz enum CursorPosition { - CursorPosition_AtBegin, // Début du fichier - CursorPosition_AtCurrent, // Position du pointeur - CursorPosition_AtEnd, // Fin du fichier + CursorPosition_AtBegin, // beginning of the file + CursorPosition_AtCurrent, // Position of the cursor + CursorPosition_AtEnd, // End of the file CursorPosition_Max = CursorPosition_AtEnd }; @@ -45,7 +45,7 @@ namespace Nz ErrorFlag_ThrowException = 0x4, ErrorFlag_ThrowExceptionDisabled = 0x8, - ErrorFlag_Max = ErrorFlag_ThrowExceptionDisabled*2-1 + ErrorFlag_Max = ErrorFlag_ThrowExceptionDisabled * 2 - 1 }; enum ErrorType @@ -75,18 +75,18 @@ namespace Nz enum OpenModeFlags { - OpenMode_NotOpen = 0x00, // Utilise le mode d'ouverture actuel + OpenMode_NotOpen = 0x00, // Use the current mod of opening - OpenMode_Append = 0x01, // Empêche l'écriture sur la partie déjà existante et met le curseur à la fin - OpenMode_Lock = 0x02, // Empêche le fichier d'être modifié tant qu'il est ouvert - OpenMode_ReadOnly = 0x04, // Ouvre uniquement en lecture - OpenMode_Text = 0x10, // Ouvre en mode texte - OpenMode_Truncate = 0x20, // Créé le fichier s'il n'existe pas et le vide s'il existe - OpenMode_WriteOnly = 0x40, // Ouvre uniquement en écriture, créé le fichier s'il n'existe pas + OpenMode_Append = 0x01, // Disable writing on existing parts and put the cursor at the end + OpenMode_Lock = 0x02, // Disable modifying the file before it is open + OpenMode_ReadOnly = 0x04, // Open in read only + OpenMode_Text = 0x10, // Open in text mod + OpenMode_Truncate = 0x20, // Create the file if it doesn't exist and empty it if it exists + OpenMode_WriteOnly = 0x40, // Open in write only, create the file if it doesn't exist - OpenMode_ReadWrite = OpenMode_ReadOnly | OpenMode_WriteOnly, // Ouvre en lecture/écriture + OpenMode_ReadWrite = OpenMode_ReadOnly | OpenMode_WriteOnly, // Open in read and write - OpenMode_Max = OpenMode_WriteOnly + OpenMode_Max = OpenMode_WriteOnly * 2 - 1 }; enum ParameterType @@ -177,7 +177,7 @@ namespace Nz StreamOption_Sequential = 0x1, StreamOption_Text = 0x2, - StreamOption_Max = StreamOption_Text*2-1 + StreamOption_Max = StreamOption_Text * 2 - 1 }; enum Ternary diff --git a/include/Nazara/Core/Error.hpp b/include/Nazara/Core/Error.hpp index b210c2e1d..fbc4de38b 100644 --- a/include/Nazara/Core/Error.hpp +++ b/include/Nazara/Core/Error.hpp @@ -9,19 +9,18 @@ #include #include -#include #include #include #if NAZARA_CORE_ENABLE_ASSERTS || defined(NAZARA_DEBUG) - #define NazaraAssert(a, err) if (!(a)) Nz::Error::Trigger(Nz::ErrorType_AssertFailed, err, __LINE__, Nz::Directory::GetCurrentFileRelativeToEngine(__FILE__), NAZARA_FUNCTION) + #define NazaraAssert(a, err) if (!(a)) Nz::Error::Trigger(Nz::ErrorType_AssertFailed, err, __LINE__, __FILE__, NAZARA_FUNCTION) #else #define NazaraAssert(a, err) for (;;) break #endif -#define NazaraError(err) Nz::Error::Trigger(Nz::ErrorType_Normal, err, __LINE__, Nz::Directory::GetCurrentFileRelativeToEngine(__FILE__), NAZARA_FUNCTION) -#define NazaraInternalError(err) Nz::Error::Trigger(Nz::ErrorType_Internal, err, __LINE__, Nz::Directory::GetCurrentFileRelativeToEngine(__FILE__), NAZARA_FUNCTION) -#define NazaraWarning(err) Nz::Error::Trigger(Nz::ErrorType_Warning, err, __LINE__, Nz::Directory::GetCurrentFileRelativeToEngine(__FILE__), NAZARA_FUNCTION) +#define NazaraError(err) Nz::Error::Trigger(Nz::ErrorType_Normal, err, __LINE__, __FILE__, NAZARA_FUNCTION) +#define NazaraInternalError(err) Nz::Error::Trigger(Nz::ErrorType_Internal, err, __LINE__, __FILE__, NAZARA_FUNCTION) +#define NazaraWarning(err) Nz::Error::Trigger(Nz::ErrorType_Warning, err, __LINE__, __FILE__, NAZARA_FUNCTION) namespace Nz { diff --git a/include/Nazara/Core/File.inl b/include/Nazara/Core/File.inl index 818d0098f..ff0d148fd 100644 --- a/include/Nazara/Core/File.inl +++ b/include/Nazara/Core/File.inl @@ -8,11 +8,27 @@ namespace Nz { + /*! + * \brief Computes the hash for the file + * \return ByteArray represing the result of the hash of the file + * + * \param hash Hash to execute + * \param filePath Path for the file + */ + inline ByteArray File::ComputeHash(HashType hash, const String& filePath) { return ComputeHash(AbstractHash::Get(hash).get(), filePath); } + /*! + * \brief Computes the hash for the file + * \return ByteArray represing the result of the hash of the file + * + * \param hash Hash to execute + * \param filePath Path for the file + */ + inline ByteArray File::ComputeHash(AbstractHash* hash, const String& filePath) { return Nz::ComputeHash(hash, File(filePath)); diff --git a/include/Nazara/Core/Functor.hpp b/include/Nazara/Core/Functor.hpp index 994ecc5ae..7ce9995a7 100644 --- a/include/Nazara/Core/Functor.hpp +++ b/include/Nazara/Core/Functor.hpp @@ -9,7 +9,7 @@ #include -// Inspiré du code de la SFML par Laurent Gomila +// Inspired from the of code of the SFML by Laurent Gomila namespace Nz { diff --git a/include/Nazara/Core/Functor.inl b/include/Nazara/Core/Functor.inl index 84ea8e76f..d5cefe434 100644 --- a/include/Nazara/Core/Functor.inl +++ b/include/Nazara/Core/Functor.inl @@ -6,18 +6,44 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::FunctorWithoutArgs + * \brief Core class that represents a functor using a function without argument + */ + + /*! + * \brief Constructs a FunctorWithoutArgs object with a function + * + * \param func Function to execute + */ template FunctorWithoutArgs::FunctorWithoutArgs(F func) : m_func(func) { } + /*! + * \brief Runs the function + */ template void FunctorWithoutArgs::Run() { m_func(); } + /*! + * \ingroup core + * \class Nz::FunctorWithArgs + * \brief Core class that represents a functor using a function with arguments + */ + + /*! + * \brief Constructs a FunctorWithArgs object with a function and its arguments + * + * \param func Function to execute + * \param args Arguments for the function + */ template FunctorWithArgs::FunctorWithArgs(F func, Args&&... args) : m_func(func), @@ -25,13 +51,27 @@ namespace Nz { } + /*! + * \brief Runs the function + */ template void FunctorWithArgs::Run() { Apply(m_func, m_args); } + /*! + * \ingroup core + * \class Nz::MemberWithoutArgs + * \brief Core class that represents a functor using a member function + */ + /*! + * \brief Constructs a MemberWithoutArgs object with a member function and an object + * + * \param func Member function to execute + * \param object Object to execute on + */ template MemberWithoutArgs::MemberWithoutArgs(void (C::*func)(), C* object) : m_func(func), @@ -39,6 +79,9 @@ namespace Nz { } + /*! + * \brief Runs the function + */ template void MemberWithoutArgs::Run() { diff --git a/include/Nazara/Core/HandledObject.hpp b/include/Nazara/Core/HandledObject.hpp new file mode 100644 index 000000000..f1e096748 --- /dev/null +++ b/include/Nazara/Core/HandledObject.hpp @@ -0,0 +1,47 @@ +// 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 NAZARA_OBJECTHANDLER_HPP +#define NAZARA_OBJECTHANDLER_HPP + +#include +#include +#include + +namespace Nz +{ + template class ObjectHandle; + + template + class HandledObject + { + friend ObjectHandle; + + public: + HandledObject() = default; + HandledObject(const HandledObject& object); + HandledObject(HandledObject&& object); + ~HandledObject(); + + ObjectHandle CreateHandle(); + + HandledObject& operator=(const HandledObject& object); + HandledObject& operator=(HandledObject&& object); + + protected: + void UnregisterAllHandles(); + + private: + void RegisterHandle(ObjectHandle* handle); + void UnregisterHandle(ObjectHandle* handle); + + std::vector*> m_handles; + }; +} + +#include + +#endif // NAZARA_OBJECTHANDLER_HPP diff --git a/include/Nazara/Core/HandledObject.inl b/include/Nazara/Core/HandledObject.inl new file mode 100644 index 000000000..53a5065f4 --- /dev/null +++ b/include/Nazara/Core/HandledObject.inl @@ -0,0 +1,84 @@ +// 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 +#include +#include +#include + +namespace Nz +{ + template + HandledObject::HandledObject(const HandledObject& object) + { + // Don't copy anything, we're a copy of the object, we have no handle right now + } + + template + HandledObject::HandledObject(HandledObject&& object) : + m_handles(std::move(object.m_handles)) + { + for (ObjectHandle* handle : m_handles) + handle->OnObjectMoved(static_cast(this)); + } + + template + HandledObject::~HandledObject() + { + UnregisterAllHandles(); + } + + template + ObjectHandle HandledObject::CreateHandle() + { + return ObjectHandle(static_cast(this)); + } + + template + HandledObject& HandledObject::operator=(const HandledObject& object) + { + // Nothing to do + return *this; + } + + template + HandledObject& HandledObject::operator=(HandledObject&& object) + { + m_handles = std::move(object.m_handles); + for (ObjectHandle* handle : m_handles) + handle->OnObjectMoved(static_cast(this)); + + return *this; + } + + 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); + } + + template + void HandledObject::UnregisterAllHandles() + { + // Tell every handle we got destroyed, to null them + for (ObjectHandle* handle : m_handles) + handle->OnObjectDestroyed(); + + m_handles.clear(); + } + + template + void HandledObject::UnregisterHandle(ObjectHandle* handle) + { + ///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 registred"); + + // On échange cet élément avec le dernier, et on diminue la taille du vector de 1 + std::swap(*it, m_handles.back()); + m_handles.pop_back(); + } +} diff --git a/include/Nazara/Core/Hash/Whirlpool.hpp b/include/Nazara/Core/Hash/Whirlpool.hpp index 600e23b25..f7922254d 100644 --- a/include/Nazara/Core/Hash/Whirlpool.hpp +++ b/include/Nazara/Core/Hash/Whirlpool.hpp @@ -23,8 +23,8 @@ namespace Nz void Begin() override; ByteArray End() override; - std::size_t GetDigestLength() const; - const char* GetHashName() const; + std::size_t GetDigestLength() const override; + const char* GetHashName() const override; private: HashWhirlpool_state* m_state; diff --git a/include/Nazara/Core/Initializer.inl b/include/Nazara/Core/Initializer.inl index cb2529643..23cc7b2de 100644 --- a/include/Nazara/Core/Initializer.inl +++ b/include/Nazara/Core/Initializer.inl @@ -47,7 +47,17 @@ namespace Nz }; } + /*! + * \ingroup core + * \class Nz::Initializer + * \brief Core class that represents a module initializer + */ + /*! + * \brief Constructs a Initializer object with a boolean + * + * \param initialize Initialize the module + */ template Initializer::Initializer(bool initialize) : m_initialized(false) @@ -56,12 +66,22 @@ namespace Nz Initialize(); } + /*! + * \brief Destructs the object and call Uninitialize + * + * \see Uninitialize + */ template Initializer::~Initializer() { Uninitialize(); } + /*! + * \brief Initialize the module + * + * \see Uninitialize + */ template bool Initializer::Initialize() { @@ -71,12 +91,21 @@ namespace Nz return m_initialized; } + /*! + * \brief Checks whether the module is initialized + * \return true if initialized + */ template bool Initializer::IsInitialized() const { return m_initialized; } + /*! + * \brief Uninitialize the module + * + * \see Initialize + */ template void Initializer::Uninitialize() { @@ -84,6 +113,10 @@ namespace Nz Detail::Initializer::Uninit(); } + /*! + * \brief Converts the initializer to boolean + * \return true if initialized + */ template Initializer::operator bool() const { diff --git a/include/Nazara/Core/LockGuard.hpp b/include/Nazara/Core/LockGuard.hpp index 2dac4b578..d9ef5aa85 100644 --- a/include/Nazara/Core/LockGuard.hpp +++ b/include/Nazara/Core/LockGuard.hpp @@ -13,15 +13,22 @@ namespace Nz { class Mutex; - class NAZARA_CORE_API LockGuard + class LockGuard { public: - LockGuard(Mutex& mutex); - ~LockGuard(); + inline LockGuard(Mutex& mutex, bool lock = true); + inline ~LockGuard(); + + inline void Lock(); + inline bool TryLock(); + inline void Unlock(); private: Mutex& m_mutex; + bool m_locked; }; } +#include + #endif // NAZARA_LOCKGUARD_HPP diff --git a/include/Nazara/Core/LockGuard.inl b/include/Nazara/Core/LockGuard.inl new file mode 100644 index 000000000..73beab112 --- /dev/null +++ b/include/Nazara/Core/LockGuard.inl @@ -0,0 +1,83 @@ +// Copyright (C) 2015 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 +#include +#include + +namespace Nz +{ + /*! + * \ingroup core + * \class Nz::LockGuard + * \brief Core class that represents a mutex wrapper that provides a convenient RAII-style mechanism + */ + + /*! + * \brief Constructs a LockGuard object with a mutex + * + * \param mutex Mutex to lock + * \param lock Should the mutex be locked by the constructor + */ + inline LockGuard::LockGuard(Mutex& mutex, bool lock) : + m_mutex(mutex), + m_locked(false) + { + if (lock) + { + m_mutex.Lock(); + m_locked = true; + } + } + + /*! + * \brief Destructs a LockGuard object and unlocks the mutex if it was previously locked + */ + inline LockGuard::~LockGuard() + { + if (m_locked) + m_mutex.Unlock(); + } + + /*! + * \brief Locks the underlying mutex + * + * \see Mutex::Lock + */ + inline void LockGuard::Lock() + { + NazaraAssert(!m_locked, "Mutex is already locked"); + + m_mutex.Lock(); + } + + /*! + * \brief Tries to lock the underlying mutex + * + * \see Mutex::TryLock + * + * \return true if the lock was acquired successfully + */ + inline bool LockGuard::TryLock() + { + NazaraAssert(!m_locked, "Mutex is already locked"); + + return m_mutex.TryLock(); + } + + /*! + * \brief Unlocks the underlying mutex + * + * \see Mutex::Unlock + */ + inline void LockGuard::Unlock() + { + NazaraAssert(m_locked, "Mutex is not locked"); + + m_mutex.Unlock(); + } +} + +#include diff --git a/include/Nazara/Core/MemoryHelper.inl b/include/Nazara/Core/MemoryHelper.inl index 579e02ae2..7f77dd041 100644 --- a/include/Nazara/Core/MemoryHelper.inl +++ b/include/Nazara/Core/MemoryHelper.inl @@ -2,7 +2,7 @@ // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Config.hpp -// Je ne suis pas fier des cinq lignes qui suivent mais difficile de faire autrement pour le moment... +// I'm not proud of those five following lines but ti's hard to do with another way now #ifdef NAZARA_DEBUG_NEWREDEFINITION_DISABLE_REDEFINITION #define NAZARA_DEBUG_NEWREDEFINITION_DISABLE_REDEFINITION_DEFINED #else @@ -16,6 +16,17 @@ namespace Nz { + /*! + * \ingroup core + * \fn Nz::MemoryHelper + * \brief Core functions that helps the handle of memory in the engine + */ + + /*! + * \brief Calls the operator delete on the pointer + * + * \remark Uses MemoryManager with NAZARA_CORE_MANAGE_MEMORY defined else operator delete + */ inline void OperatorDelete(void* ptr) { #if NAZARA_CORE_MANAGE_MEMORY @@ -25,6 +36,11 @@ namespace Nz #endif } + /*! + * \brief Calls the operator new on the pointer + * + * \remark Uses MemoryManager with NAZARA_CORE_MANAGE_MEMORY defined else operator new + */ inline void* OperatorNew(std::size_t size) { #if NAZARA_CORE_MANAGE_MEMORY @@ -34,6 +50,13 @@ namespace Nz #endif } + /*! + * \brief Constructs the object inplace + * \return Pointer to the constructed object + * + * \param ptr Pointer to raw memory allocated + * \param args Arguments for the constructor + */ template T* PlacementNew(void* ptr, Args&&... args) { @@ -43,7 +66,7 @@ namespace Nz #include -// Si c'est nous qui avons défini la constante, alors il nous faut l'enlever (Pour éviter que le moteur entier n'en souffre) +// If we have defined the constant, then we have to undefine it (to avoid bloating in the engine) #ifndef NAZARA_DEBUG_NEWREDEFINITION_DISABLE_REDEFINITION_DEFINED #undef NAZARA_DEBUG_NEWREDEFINITION_DISABLE_REDEFINITION #endif diff --git a/include/Nazara/Core/MemoryPool.hpp b/include/Nazara/Core/MemoryPool.hpp index 71761b612..7aaa462bb 100644 --- a/include/Nazara/Core/MemoryPool.hpp +++ b/include/Nazara/Core/MemoryPool.hpp @@ -37,7 +37,7 @@ namespace Nz private: MemoryPool(MemoryPool* pool); - std::unique_ptr m_freeList; + std::unique_ptr m_freeList; std::unique_ptr m_pool; std::unique_ptr m_next; std::atomic_uint m_freeCount; diff --git a/include/Nazara/Core/MemoryPool.inl b/include/Nazara/Core/MemoryPool.inl index 9ba21fa0c..14c3868dd 100644 --- a/include/Nazara/Core/MemoryPool.inl +++ b/include/Nazara/Core/MemoryPool.inl @@ -4,10 +4,25 @@ #include #include +#include #include namespace Nz { + /*! + * \ingroup core + * \class Nz::MemoryPool + * \brief Core class that represents a memory pool + */ + + /*! + * \brief Constructs a MemoryPool object + * + * \param blockSize Size of blocks that will be allocated + * \param size Size of the pool + * \param canGrow Determine if the pool can allocate more memory + */ + inline MemoryPool::MemoryPool(unsigned int blockSize, unsigned int size, bool canGrow) : m_freeCount(size), m_previous(nullptr), @@ -16,27 +31,47 @@ namespace Nz m_size(size) { m_pool.reset(new UInt8[blockSize * size]); - m_freeList.reset(new void*[size]); + m_freeList.reset(new void* [size]); // Remplissage de la free list for (unsigned int i = 0; i < size; ++i) m_freeList[i] = &m_pool[m_blockSize * (size-i-1)]; } + /*! + * \brief Constructs a MemoryPool object by move semantic + * + * \param pool MemoryPool to move into this + */ + inline MemoryPool::MemoryPool(MemoryPool&& pool) noexcept { operator=(std::move(pool)); } + /*! + * \brief Constructs a MemoryPool object by chaining memory pool + * + * \param pool Previous MemoryPool + */ + inline MemoryPool::MemoryPool(MemoryPool* pool) : MemoryPool(pool->m_blockSize, pool->m_size, pool->m_canGrow) { m_previous = pool; } + /*! + * \brief Allocates enough memory for the size and returns a pointer to it + * \return A pointer to memory allocated + * + * \param size Size to allocate + * + * \remark If the size is greather than the blockSize of pool, new operator is called + */ + inline void* MemoryPool::Allocate(unsigned int size) { - ///DOC: Si la taille est supérieure à celle d'un bloc du pool, l'opérateur new est utilisé if (size <= m_blockSize) { if (m_freeCount > 0) @@ -53,10 +88,17 @@ namespace Nz return OperatorNew(size); } + /*! + * \brief Deletes the memory represented by the poiner + * + * Calls the destructor of the object before releasing it + * + * \remark If ptr is null, nothing is done + */ + template inline void MemoryPool::Delete(T* ptr) { - ///DOC: Va appeler le destructeur de l'objet avant de le libérer if (ptr) { ptr->~T(); @@ -64,12 +106,20 @@ namespace Nz } } + /*! + * \brief Frees the memory represented by the poiner + * + * If the pool gets empty after the call and we are the child of another pool, we commit suicide. If the pointer does not own to a block of the pool, operator delete is called + * + * \remark Throws a std::runtime_error if pointer does not point to an element of the pool with NAZARA_CORE_SAFE defined + * \remark If ptr is null, nothing is done + */ + inline void MemoryPool::Free(void* ptr) { - ///DOC: Si appelé avec un pointeur ne faisant pas partie du pool, l'opérateur delete est utilisé if (ptr) { - // Le pointeur nous appartient-il ? + // Does the pointer belong to us ? UInt8* freePtr = static_cast(ptr); UInt8* poolPtr = m_pool.get(); if (freePtr >= poolPtr && freePtr < poolPtr + m_blockSize*m_size) @@ -81,7 +131,7 @@ namespace Nz m_freeList[m_freeCount++] = ptr; - // Si nous sommes vide et l'extension d'un autre pool, nous nous suicidons + // If we are empty and the extension of another pool, we commit suicide if (m_freeCount == m_size && m_previous && !m_next) { m_previous->m_next.release(); @@ -98,31 +148,61 @@ namespace Nz } } + /*! + * \brief Gets the block size + * \return Size of the blocks + */ + inline unsigned int MemoryPool::GetBlockSize() const { return m_blockSize; } + /*! + * \brief Gets the number of free blocks + * \return Number of free blocks in the pool + */ + inline unsigned int MemoryPool::GetFreeBlocks() const { return m_freeCount; } + /*! + * \brief Gets the pool size + * \return Size of the pool + */ + inline unsigned int MemoryPool::GetSize() const { return m_size; } + /*! + * \brief Creates a new value of type T with arguments + * \return Pointer to the allocated object + * + * \param args Arguments for the new object + * + * \remark Constructs inplace in the pool + */ + template inline T* MemoryPool::New(Args&&... args) { - ///DOC: Permet de construire un objet directement dans le pook T* object = static_cast(Allocate(sizeof(T))); PlacementNew(object, std::forward(args)...); return object; } + /*! + * \brief Assigns the content of another pool by move semantic + * \return A reference to this + * + * \param pool Other pool to move into this + */ + inline MemoryPool& MemoryPool::operator=(MemoryPool&& pool) noexcept { m_blockSize = pool.m_blockSize; @@ -134,7 +214,7 @@ namespace Nz m_next = std::move(pool.m_next); m_size = pool.m_size; - // Si nous avons été créés par un autre pool, nous devons le faire pointer vers nous de nouveau + // If we have been created by another pool, we must make it point to us again if (m_previous) { m_previous->m_next.release(); diff --git a/include/Nazara/Core/MemoryStream.hpp b/include/Nazara/Core/MemoryStream.hpp new file mode 100644 index 000000000..f5d15fcf0 --- /dev/null +++ b/include/Nazara/Core/MemoryStream.hpp @@ -0,0 +1,53 @@ +// Copyright (C) 2015 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 + +#pragma once + +#ifndef NAZARA_MEMORYSTREAM_HPP +#define NAZARA_MEMORYSTREAM_HPP + +#include +#include + +namespace Nz +{ + class ByteArray; + + class NAZARA_CORE_API MemoryStream : public Stream + { + public: + inline MemoryStream(); + inline MemoryStream(ByteArray* byteArray, UInt32 openMode = OpenMode_ReadWrite); + MemoryStream(const MemoryStream&) = default; + MemoryStream(MemoryStream&&) = default; + ~MemoryStream() = default; + + void Clear(); + + bool EndOfStream() const override; + + inline ByteArray& GetBuffer(); + inline const ByteArray& GetBuffer() const; + UInt64 GetCursorPos() const override; + UInt64 GetSize() const override; + + void SetBuffer(ByteArray* byteArray, UInt32 openMode = OpenMode_ReadWrite); + bool SetCursorPos(UInt64 offset) override; + + MemoryStream& operator=(const MemoryStream&) = default; + MemoryStream& operator=(MemoryStream&&) = default; + + private: + void FlushStream() override; + std::size_t ReadBlock(void* buffer, std::size_t size) override; + std::size_t WriteBlock(const void* buffer, std::size_t size) override; + + ByteArray* m_buffer; + UInt64 m_pos; + }; +} + +#include + +#endif // NAZARA_MEMORYSTREAM_HPP diff --git a/include/Nazara/Core/MemoryStream.inl b/include/Nazara/Core/MemoryStream.inl new file mode 100644 index 000000000..79a19f467 --- /dev/null +++ b/include/Nazara/Core/MemoryStream.inl @@ -0,0 +1,60 @@ +// Copyright (C) 2015 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 +{ + /*! + * \ingroup core + * \class Nz::MemoryStream + * \brief Constructs a MemoryStream object by default + */ + inline MemoryStream::MemoryStream() : + Stream(StreamOption_None, OpenMode_ReadWrite), + m_pos(0) + { + } + + /*! + * \brief Constructs a MemoryStream object with a byte array + * + * \param byteArray Bytes to stream + * \param openMode Reading/writing mode for the stream + */ + inline MemoryStream::MemoryStream(ByteArray* byteArray, UInt32 openMode) : + MemoryStream() + { + SetBuffer(byteArray, openMode); + } + + /*! + * \brief Gets the internal buffer + * \return Buffer of bytes + * + * \remark Produces a NazaraAssert if buffer is invalid + */ + inline ByteArray& MemoryStream::GetBuffer() + { + NazaraAssert(m_buffer, "Invalid buffer"); + + return *m_buffer; + } + + /*! + * \brief Gets the internal buffer + * \return Buffer of bytes + * + * \remark Produces a NazaraAssert if buffer is invalid + */ + inline const ByteArray& MemoryStream::GetBuffer() const + { + NazaraAssert(m_buffer, "Invalid buffer"); + + return *m_buffer; + } +} + +#include diff --git a/include/Nazara/Core/MemoryView.hpp b/include/Nazara/Core/MemoryView.hpp index e746ef47a..41f4b1ee2 100644 --- a/include/Nazara/Core/MemoryView.hpp +++ b/include/Nazara/Core/MemoryView.hpp @@ -15,6 +15,7 @@ namespace Nz class NAZARA_CORE_API MemoryView : public Stream { public: + MemoryView(void* ptr, UInt64 size); MemoryView(const void* ptr, UInt64 size); MemoryView(const MemoryView&) = delete; MemoryView(MemoryView&&) = delete; ///TODO @@ -31,11 +32,11 @@ namespace Nz MemoryView& operator=(MemoryView&&) = delete; ///TODO private: - void FlushStream() override; + void FlushStream() override; std::size_t ReadBlock(void* buffer, std::size_t size) override; std::size_t WriteBlock(const void* buffer, std::size_t size) override; - const UInt8* m_ptr; + UInt8* m_ptr; UInt64 m_pos; UInt64 m_size; }; diff --git a/include/Nazara/Core/Mutex.hpp b/include/Nazara/Core/Mutex.hpp index e49de7512..14de9d282 100644 --- a/include/Nazara/Core/Mutex.hpp +++ b/include/Nazara/Core/Mutex.hpp @@ -20,7 +20,7 @@ namespace Nz public: Mutex(); Mutex(const Mutex&) = delete; - Mutex(Mutex&&) = delete; ///TODO + inline Mutex(Mutex&& mutex) noexcept; ~Mutex(); void Lock(); @@ -28,11 +28,13 @@ namespace Nz void Unlock(); Mutex& operator=(const Mutex&) = delete; - Mutex& operator=(Mutex&&) = delete; ///TODO + Mutex& operator=(Mutex&& mutex) noexcept; private: MutexImpl* m_impl; }; } +#include + #endif // NAZARA_MUTEX_HPP diff --git a/include/Nazara/Core/Mutex.inl b/include/Nazara/Core/Mutex.inl new file mode 100644 index 000000000..fe4a77577 --- /dev/null +++ b/include/Nazara/Core/Mutex.inl @@ -0,0 +1,26 @@ +// Copyright (C) 2015 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 +#include + +namespace Nz +{ + /*! + * \ingroup core + * \class Nz::Mutex + */ + + /*! + * \brief Constructs a Mutex object by moving another one + */ + inline Mutex::Mutex(Mutex&& mutex) noexcept : + m_impl(mutex.m_impl) + { + mutex.m_impl = nullptr; + } +} + +#include diff --git a/include/Nazara/Core/ObjectHandle.hpp b/include/Nazara/Core/ObjectHandle.hpp new file mode 100644 index 000000000..d7a53e281 --- /dev/null +++ b/include/Nazara/Core/ObjectHandle.hpp @@ -0,0 +1,93 @@ +// Copyright (C) 2015 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 + +#pragma once + +#ifndef NAZARA_OBJECTHANDLE_HPP +#define NAZARA_OBJECTHANDLE_HPP + +#include +#include + +namespace Nz +{ + template class HandledObject; + + template + class ObjectHandle + { + friend HandledObject; + + public: + ObjectHandle(); + explicit ObjectHandle(T* object); + ObjectHandle(const ObjectHandle& handle); + ~ObjectHandle(); + + T* GetObject() const; + + bool IsValid() const; + + void Reset(T* object = nullptr); + void Reset(const ObjectHandle& handle); + + ObjectHandle& Swap(ObjectHandle& handle); + + Nz::String ToString() const; + + operator bool() const; + operator T*() const; + T* operator->() const; + + ObjectHandle& operator=(T* object); + ObjectHandle& operator=(const ObjectHandle& handle); + + static const ObjectHandle InvalidHandle; + + protected: + void OnObjectDestroyed(); + void OnObjectMoved(T* newObject); + + T* m_object; + }; + + template std::ostream& operator<<(std::ostream& out, const ObjectHandle& handle); + + template bool operator==(const ObjectHandle& lhs, const ObjectHandle& rhs); + template bool operator==(const T& lhs, const ObjectHandle& rhs); + template bool operator==(const ObjectHandle& lhs, const T& rhs); + + template bool operator!=(const ObjectHandle& lhs, const ObjectHandle& rhs); + template bool operator!=(const T& lhs, const ObjectHandle& rhs); + template bool operator!=(const ObjectHandle& lhs, const T& rhs); + + template bool operator<(const ObjectHandle& lhs, const ObjectHandle& rhs); + template bool operator<(const T& lhs, const ObjectHandle& rhs); + template bool operator<(const ObjectHandle& lhs, const T& rhs); + + template bool operator<=(const ObjectHandle, const ObjectHandle& rhs); + template bool operator<=(const T& lhs, const ObjectHandle& rhs); + template bool operator<=(const ObjectHandle& lhs, const T& rhs); + + template bool operator>(const ObjectHandle& lhs, const ObjectHandle& rhs); + template bool operator>(const T& lhs, const ObjectHandle& rhs); + template bool operator>(const ObjectHandle& lhs, const T& rhs); + + template bool operator>=(const ObjectHandle& lhs, const ObjectHandle& rhs); + template bool operator>=(const T& lhs, const ObjectHandle& rhs); + template bool operator>=(const ObjectHandle& lhs, const T& rhs); + + template struct PointedType> { typedef T type; }; + template struct PointedType> { typedef T type; }; +} + +namespace std +{ + template + void swap(Nz::ObjectHandle& lhs, Nz::ObjectHandle& rhs); +} + +#include + +#endif // NAZARA_OBJECTHANDLE_HPP diff --git a/include/Nazara/Core/ObjectHandle.inl b/include/Nazara/Core/ObjectHandle.inl new file mode 100644 index 000000000..752cb96c0 --- /dev/null +++ b/include/Nazara/Core/ObjectHandle.inl @@ -0,0 +1,280 @@ +// Copyright (C) 2015 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 +#include +#include + +namespace Nz +{ + template + ObjectHandle::ObjectHandle() : + m_object(nullptr) + { + } + + template + ObjectHandle::ObjectHandle(T* object) : + ObjectHandle() + { + Reset(object); + } + + template + ObjectHandle::ObjectHandle(const ObjectHandle& handle) : + ObjectHandle() + { + Reset(handle); + } + + template + ObjectHandle::~ObjectHandle() + { + Reset(nullptr); + } + + template + T* ObjectHandle::GetObject() const + { + return m_object; + } + + template + bool ObjectHandle::IsValid() const + { + return m_object != nullptr; + } + + 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 (m_object) + m_object->UnregisterHandle(this); + + m_object = object; + if (m_object) + // On informe la nouvelle entité que nous pointons sur elle + m_object->RegisterHandle(this); + } + + template + void ObjectHandle::Reset(const ObjectHandle& handle) + { + Reset(handle.GetObject()); + } + + template + 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). + if (m_object) + { + m_object->UnregisterHandle(this); + m_object->RegisterHandle(&handle); + } + + if (handle.m_object) + { + handle.m_object->UnregisterHandle(&handle); + handle.m_object->RegisterHandle(this); + } + + // On effectue l'échange + std::swap(m_object, handle.m_object); + return *this; + } + + template + Nz::String ObjectHandle::ToString() const + { + Nz::StringStream ss; + ss << "ObjectHandle("; + if (IsValid()) + ss << m_object->ToString(); + else + ss << "Null"; + + ss << ')'; + + return ss; + } + + template + ObjectHandle::operator bool() const + { + return IsValid(); + } + + template + ObjectHandle::operator T*() const + { + return m_object; + } + + template + T* ObjectHandle::operator->() const + { + return m_object; + } + + template + ObjectHandle& ObjectHandle::operator=(T* entity) + { + Reset(entity); + + return *this; + } + + template + ObjectHandle& ObjectHandle::operator=(const ObjectHandle& handle) + { + Reset(handle); + + return *this; + } + + template + void ObjectHandle::OnObjectDestroyed() + { + // Shortcut + m_object = nullptr; + } + + template + void ObjectHandle::OnObjectMoved(T* newObject) + { + // The object has been moved, update our pointer + m_object = newObject; + } + + template + std::ostream& operator<<(std::ostream& out, const ObjectHandle& handle) + { + return handle.ToString(); + } + + template + bool operator==(const ObjectHandle& lhs, const ObjectHandle& rhs) + { + return lhs.GetObject() == rhs.GetObject(); + } + + template + bool operator==(const T& lhs, const ObjectHandle& rhs) + { + return &lhs == rhs.GetObject(); + } + + template + bool operator==(const ObjectHandle& lhs, const T& rhs) + { + return lhs.GetObject() == &rhs; + } + + template + bool operator!=(const ObjectHandle& lhs, const ObjectHandle& rhs) + { + return !(lhs == rhs); + } + + template + bool operator!=(const T& lhs, const ObjectHandle& rhs) + { + return !(lhs == rhs); + } + + template + bool operator!=(const ObjectHandle& lhs, const T& rhs) + { + return !(lhs == rhs); + } + + template + bool operator<(const ObjectHandle& lhs, const ObjectHandle& rhs) + { + return lhs.m_object < rhs.m_object; + } + + template + bool operator<(const T& lhs, const ObjectHandle& rhs) + { + return &lhs < rhs.m_object; + } + + template + bool operator<(const ObjectHandle& lhs, const T& rhs) + { + return lhs.m_object < &rhs; + } + + template + bool operator<=(const ObjectHandle& lhs, const ObjectHandle& rhs) + { + return !(lhs > rhs); + } + + template + bool operator<=(const T& lhs, const ObjectHandle& rhs) + { + return !(lhs > rhs); + } + + template + bool operator<=(const ObjectHandle& lhs, const T& rhs) + { + return !(lhs > rhs); + } + + template + bool operator>(const ObjectHandle& lhs, const ObjectHandle& rhs) + { + return rhs < lhs; + } + + template + bool operator>(const T& lhs, const ObjectHandle& rhs) + { + return rhs < lhs; + } + + template + bool operator>(const ObjectHandle& lhs, const T& rhs) + { + return rhs < lhs; + } + + template + bool operator>=(const ObjectHandle& lhs, const ObjectHandle& rhs) + { + return !(lhs < rhs); + } + + template + bool operator>=(const T& lhs, const ObjectHandle& rhs) + { + return !(lhs < rhs); + } + + template + bool operator>=(const ObjectHandle& lhs, const T& rhs) + { + return !(lhs < rhs); + } + + template + const ObjectHandle ObjectHandle::InvalidHandle; +} + +namespace std +{ + template + void swap(Nz::ObjectHandle& lhs, Nz::ObjectHandle& rhs) + { + lhs.Swap(rhs); + } +} diff --git a/include/Nazara/Core/ObjectLibrary.inl b/include/Nazara/Core/ObjectLibrary.inl index 998adece4..e656a820e 100644 --- a/include/Nazara/Core/ObjectLibrary.inl +++ b/include/Nazara/Core/ObjectLibrary.inl @@ -7,6 +7,20 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::ObjectRef + * \brief Core class that represents a reference to an object + */ + + /*! + * \brief Gets the ObjectRef object by name + * \return Optional reference + * + * \param name Name of the object + * + * \remark Produces a NazaraError if object not found + */ template ObjectRef ObjectLibrary::Get(const String& name) { @@ -17,18 +31,34 @@ namespace Nz return ref; } + /*! + * \brief Checks whether the library has the object with that name + * \return true if it the case + */ template bool ObjectLibrary::Has(const String& name) { return Type::s_library.find(name) != Type::s_library.end(); } + /*! + * \brief Registers the ObjectRef object with that name + * + * \param name Name of the object + * \param object Object to stock + */ template void ObjectLibrary::Register(const String& name, ObjectRef object) { Type::s_library.emplace(name, object); } + /*! + * \brief Gets the ObjectRef object by name + * \return Optional reference + * + * \param name Name of the object + */ template ObjectRef ObjectLibrary::Query(const String& name) { @@ -39,6 +69,11 @@ namespace Nz return nullptr; } + /*! + * \brief Unregisters the ObjectRef object with that name + * + * \param name Name of the object + */ template void ObjectLibrary::Unregister(const String& name) { @@ -48,7 +83,7 @@ namespace Nz template bool ObjectLibrary::Initialize() { - return true; // Que faire + return true; // Nothing to do } template diff --git a/include/Nazara/Core/ObjectRef.hpp b/include/Nazara/Core/ObjectRef.hpp index d39f228ab..6f3ccab9a 100644 --- a/include/Nazara/Core/ObjectRef.hpp +++ b/include/Nazara/Core/ObjectRef.hpp @@ -8,6 +8,7 @@ #define NAZARA_RESOURCEREF_HPP #include +#include #include #include @@ -42,6 +43,9 @@ namespace Nz private: T* m_object; }; + + 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 2e933bdf0..ad74b4f2e 100644 --- a/include/Nazara/Core/ObjectRef.inl +++ b/include/Nazara/Core/ObjectRef.inl @@ -7,12 +7,26 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::ObjectRef + * \brief Core class that represents a reference to an object + */ + + /*! + * \brief Constructs a ObjectRef object by default + */ template ObjectRef::ObjectRef() : m_object(nullptr) { } + /*! + * \brief Constructs a ObjectRef object with a pointer to an object + * + * \param object Pointer to handle like a reference (can be nullptr) + */ template ObjectRef::ObjectRef(T* object) : m_object(object) @@ -21,6 +35,11 @@ namespace Nz m_object->AddReference(); } + /*! + * \brief Constructs a ObjectRef object by assignation + * + * \param ref ObjectRef to assign into this + */ template ObjectRef::ObjectRef(const ObjectRef& ref) : m_object(ref.m_object) @@ -29,6 +48,11 @@ namespace Nz m_object->AddReference(); } + /*! + * \brief Constructs a ObjectRef object from another type of ObjectRef + * + * \param ref ObjectRef of type U to convert to type T + */ template template ObjectRef::ObjectRef(const ObjectRef& ref) : @@ -36,13 +60,21 @@ namespace Nz { } + /*! + * \brief Constructs a ObjectRef object by move semantic + * + * \param ref ObjectRef to move into this + */ template ObjectRef::ObjectRef(ObjectRef&& ref) noexcept : m_object(ref.m_object) { - ref.m_object = nullptr; // On vole la référence + ref.m_object = nullptr; // We steal the reference } + /*! + * \brief Destructs the object (remove a reference to the object when shared) + */ template ObjectRef::~ObjectRef() { @@ -50,27 +82,46 @@ namespace Nz m_object->RemoveReference(); } + /*! + * \brief Gets the underlying pointer + * \return Underlying pointer + */ template T* ObjectRef::Get() const { return m_object; } + /*! + * \brief Checks whether the reference is valid + * \return true if reference is not nullptr + */ template bool ObjectRef::IsValid() const { return m_object != nullptr; } + /*! + * \brief Releases the handle of the pointer + * \return Underlying pointer + */ template T* ObjectRef::Release() { + if (m_object) + m_object->RemoveReference(); + T* object = m_object; m_object = nullptr; return object; } + /*! + * \brief Resets the content of the ObjectRef with another pointer + * \return true if old handle is destroyed + */ template bool ObjectRef::Reset(T* object) { @@ -88,6 +139,12 @@ namespace Nz return destroyed; } + /*! + * \brief Swaps the content of the two ObjectRef + * \return A reference to this + * + * \param ref ObjectRef to swap + */ template ObjectRef& ObjectRef::Swap(ObjectRef& ref) { @@ -96,24 +153,44 @@ namespace Nz return *this; } + /*! + * \brief Converts the ObjectRef to bool + * \return true if reference is not nullptr + * + * \see IsValid + */ template ObjectRef::operator bool() const { return IsValid(); } + /*! + * \brief Dereferences the ObjectRef + * \return Underlying pointer + */ template ObjectRef::operator T*() const { return m_object; } + /*! + * \brief Dereferences the ObjectRef + * \return Underlying pointer + */ template T* ObjectRef::operator->() const { return m_object; } - + + /*! + * \brief Assigns the object into this + * \return A reference to this + * + * \param object Pointer to handle like a reference (can be nullptr) + */ template ObjectRef& ObjectRef::operator=(T* object) { @@ -122,6 +199,12 @@ namespace Nz return *this; } + /*! + * \brief Sets the reference of the ObjectRef with the handle from another + * \return A reference to this + * + * \param ref The other ObjectRef + */ template ObjectRef& ObjectRef::operator=(const ObjectRef& ref) { @@ -130,6 +213,12 @@ namespace Nz return *this; } + /*! + * \brief Sets the reference of the ObjectRef from another type of ObjectRef + * \return A reference to this + * + * \param ref ObjectRef of type U to convert + */ template template ObjectRef& ObjectRef::operator=(const ObjectRef& ref) @@ -141,6 +230,12 @@ namespace Nz return *this; } + /*! + * \brief Moves the ObjectRef into this + * \return A reference to this + * + * \param ref ObjectRef to move in this + */ template ObjectRef& ObjectRef::operator=(ObjectRef&& ref) noexcept { @@ -152,4 +247,24 @@ namespace Nz } } +namespace std +{ + /*! + * \ingroup core + * \brief Gives a hash representation of the object, specialisation of std + * \return Hash of the ObjectRef + * + * \param object Object to hash + */ + template + struct hash> + { + size_t operator()(const Nz::ObjectRef& object) const + { + hash h; + return h(object.Get()); + } + }; +} + #include diff --git a/include/Nazara/Core/OffsetOf.hpp b/include/Nazara/Core/OffsetOf.hpp index 7b97a44e2..d6596dcf2 100644 --- a/include/Nazara/Core/OffsetOf.hpp +++ b/include/Nazara/Core/OffsetOf.hpp @@ -7,7 +7,7 @@ #ifndef NAZARA_OFFSETOF_HPP #define NAZARA_OFFSETOF_HPP -// Par "Jesse Good" de SO: +// By "Jesse Good" from SO: // http://stackoverflow.com/questions/12811330/c-compile-time-offsetof-inside-a-template?answertab=votes#tab-top namespace Nz diff --git a/include/Nazara/Core/ParameterList.hpp b/include/Nazara/Core/ParameterList.hpp index bd4b01b58..2475185d7 100644 --- a/include/Nazara/Core/ParameterList.hpp +++ b/include/Nazara/Core/ParameterList.hpp @@ -86,10 +86,10 @@ namespace Nz Value value; }; - using ParameterMap = std::unordered_map; - + Parameter& CreateValue(const String& name); void DestroyValue(Parameter& parameter); + using ParameterMap = std::unordered_map; ParameterMap m_parameters; }; } diff --git a/include/Nazara/Core/PluginManager.hpp b/include/Nazara/Core/PluginManager.hpp index b2b20ffde..833f0c5d2 100644 --- a/include/Nazara/Core/PluginManager.hpp +++ b/include/Nazara/Core/PluginManager.hpp @@ -13,7 +13,7 @@ #include #include -///TODO: Révision +///TODO: Revision namespace Nz { class DynLib; diff --git a/include/Nazara/Core/Primitive.inl b/include/Nazara/Core/Primitive.inl index ce458739d..8124a7ed9 100644 --- a/include/Nazara/Core/Primitive.inl +++ b/include/Nazara/Core/Primitive.inl @@ -6,6 +6,20 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::PrimitiveList + * \brief Core class that represents a geometric primitive + */ + + /*! + * \brief Makes a box centered + * + * \param lengths (Width, Height, Depht) + * \param subdivision Number of subdivision for the axis + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeBox(const Vector3f& lengths, const Vector3ui& subdivision, const Matrix4f& transformMatrix, const Rectf& uvCoords) { matrix = transformMatrix; @@ -15,11 +29,29 @@ namespace Nz box.subdivision = subdivision; } + /*! + * \brief Makes a box centered + * + * \param lengths (Width, Height, Depht) + * \param subdivision Number of subdivision for the axis + * \param position Position of the box + * \param rotation Rotation of the box + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeBox(const Vector3f& lengths, const Vector3ui& subdivision, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { MakeBox(lengths, subdivision, Matrix4f::Transform(position, rotation), uvCoords); } + /*! + * \brief Makes a cone, centered in (0, 0, 0) and circle in (0, -length, 0) + * + * \param length Height of the cone + * \param radius Width of the radius + * \param subdivision Number of sides for the circle + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeCone(float length, float radius, unsigned int subdivision, const Matrix4f& transformMatrix, const Rectf& uvCoords) { matrix = transformMatrix; @@ -30,11 +62,29 @@ namespace Nz cone.subdivision = subdivision; } + /*! + * \brief Makes a cone, centered in (0, 0, 0) and circle in (0, -length, 0) + * + * \param length Height of the cone + * \param radius Width of the radius + * \param subdivision Number of sides for the circle + * \param position Position of the cone + * \param rotation Rotation of the cone + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeCone(float length, float radius, unsigned int subdivision, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { MakeCone(length, radius, subdivision, Matrix4f::Transform(position, rotation), uvCoords); } + /*! + * \brief Makes a cubic sphere, centered in (0, 0, 0) + * + * \param size Radius of the cubic sphere + * \param subdivision Number of subdivision for the box + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeCubicSphere(float size, unsigned int subdivision, const Matrix4f& transformMatrix, const Rectf& uvCoords) { matrix = transformMatrix; @@ -45,11 +95,28 @@ namespace Nz sphere.cubic.subdivision = subdivision; } + /*! + * \brief Adds a cubic sphere, centered in (0, 0, 0) + * + * \param size Radius of the cubic sphere + * \param subdivision Number of subdivision for the box + * \param position Position of the cubic sphere + * \param rotation Rotation of the cubic sphere + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeCubicSphere(float size, unsigned int subdivision, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { MakeCubicSphere(size, subdivision, Matrix4f::Transform(position, rotation), uvCoords); } + /*! + * \brief Makes a icosphere, centered in (0, 0, 0) + * + * \param size Radius of the icosphere + * \param recursionLevel Number of recursion for the icosphere + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeIcoSphere(float size, unsigned int recursionLevel, const Matrix4f& transformMatrix, const Rectf& uvCoords) { matrix = transformMatrix; @@ -60,11 +127,28 @@ namespace Nz sphere.ico.recursionLevel = recursionLevel; } + /*! + * \brief Makes a icosphere, centered in (0, 0, 0) + * + * \param size Radius of the sphere + * \param recursionLevel Number of recursion for the icosphere + * \param position Position of the icosphere + * \param rotation Rotation of the icosphere + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeIcoSphere(float size, unsigned int recursionLevel, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { MakeIcoSphere(size, recursionLevel, Matrix4f::Transform(position, rotation), uvCoords); } + /*! + * \brief Makes a plane, centered in (0, 0, 0) + * + * \param size (Width, Depth) + * \param subdivision Number of subdivision for the axis + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakePlane(const Vector2f& size, const Vector2ui& subdivision, const Matrix4f& transformMatrix, const Rectf& uvCoords) { matrix = transformMatrix; @@ -74,16 +158,42 @@ namespace Nz plane.subdivision = subdivision; } + /*! + * \brief Makes a plane, centered in (0, 0, 0) + * + * \param size (Width, Depth) + * \param subdivision Number of subdivision for the axis + * \param planeInfo Information for the plane + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakePlane(const Vector2f& size, const Vector2ui& subdivision, const Planef& planeInfo, const Rectf& uvCoords) { MakePlane(size, subdivision, Matrix4f::Transform(planeInfo.distance * planeInfo.normal, Quaternionf::RotationBetween(Vector3f::Up(), planeInfo.normal)), uvCoords); } + /*! + * \brief Makes a plane, centered in (0, 0, 0) + * + * \param size (Width, Depth) + * \param subdivision Number of subdivision for the axis + * \param position Position of the plane + * \param rotation Rotation of the plane + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakePlane(const Vector2f& size, const Vector2ui& subdivision, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { MakePlane(size, subdivision, Matrix4f::Transform(position, rotation), uvCoords); } + /*! + * \brief Makes a UV sphere, centered in (0, 0, 0) + * + * \param size Radius of the sphere + * \param sliceCount Number of slices + * \param stackCount Number of stacks + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeUVSphere(float size, unsigned int sliceCount, unsigned int stackCount, const Matrix4f& transformMatrix, const Rectf& uvCoords) { matrix = transformMatrix; @@ -95,11 +205,30 @@ namespace Nz sphere.uv.stackCount = stackCount; } + /*! + * \brief Makes a UV sphere, centered in (0, 0, 0) + * + * \param size Radius of the sphere + * \param sliceCount Number of slices + * \param stackCount Number of stacks + * \param position Position of the box + * \param rotation Rotation of the box + * \param uvCoords Coordinates for texture + */ inline void Primitive::MakeUVSphere(float size, unsigned int sliceCount, unsigned int stackCount, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { MakeUVSphere(size, sliceCount, stackCount, Matrix4f::Transform(position, rotation), uvCoords); } + /*! + * \brief Creates a box centered + * \return Primitive which is a box + * + * \param lengths (Width, Height, Depht) + * \param subdivision Number of subdivision for the axis + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::Box(const Vector3f& lengths, const Vector3ui& subdivision, const Matrix4f& transformMatrix, const Rectf& uvCoords) { Primitive primitive; @@ -108,6 +237,16 @@ namespace Nz return primitive; } + /*! + * \brief Creates a box centered + * \return Primitive which is a box + * + * \param lengths (Width, Height, Depht) + * \param subdivision Number of subdivision for the axis + * \param position Position of the box + * \param rotation Rotation of the box + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::Box(const Vector3f& lengths, const Vector3ui& subdivision, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { Primitive primitive; @@ -116,6 +255,16 @@ namespace Nz return primitive; } + /*! + * \brief Creates a cone, centered in (0, 0, 0) and circle in (0, -length, 0) + * \return Primitive which is a cone + * + * \param length Height of the cone + * \param radius Width of the radius + * \param subdivision Number of sides for the circle + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::Cone(float length, float radius, unsigned int subdivision, const Matrix4f& transformMatrix, const Rectf& uvCoords) { Primitive primitive; @@ -124,6 +273,17 @@ namespace Nz return primitive; } + /*! + * \brief Creates a cone, centered in (0, 0, 0) and circle in (0, -length, 0) + * \return Primitive which is a cone + * + * \param length Height of the cone + * \param radius Width of the radius + * \param subdivision Number of sides for the circle + * \param position Position of the cone + * \param rotation Rotation of the cone + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::Cone(float length, float radius, unsigned int subdivision, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { Primitive primitive; @@ -132,6 +292,15 @@ namespace Nz return primitive; } + /*! + * \brief Creates a cubic sphere, centered in (0, 0, 0) + * \return Primitive which is a cubic sphere + * + * \param size Radius of the cubic sphere + * \param subdivision Number of subdivision for the box + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::CubicSphere(float size, unsigned int subdivision, const Matrix4f& transformMatrix, const Rectf& uvCoords) { Primitive primitive; @@ -140,6 +309,16 @@ namespace Nz return primitive; } + /*! + * \brief Creates a cubic sphere, centered in (0, 0, 0) + * \return Primitive which is a cubic sphere + * + * \param size Radius of the cubic sphere + * \param subdivision Number of subdivision for the box + * \param position Position of the cubic sphere + * \param rotation Rotation of the cubic sphere + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::CubicSphere(float size, unsigned int subdivision, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { Primitive primitive; @@ -148,6 +327,15 @@ namespace Nz return primitive; } + /*! + * \brief Creates a icosphere, centered in (0, 0, 0) + * \return Primitive which is a icosphere + * + * \param size Radius of the icosphere + * \param recursionLevel Number of recursion for the icosphere + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::IcoSphere(float size, unsigned int recursionLevel, const Matrix4f& transformMatrix, const Rectf& uvCoords) { Primitive primitive; @@ -156,6 +344,16 @@ namespace Nz return primitive; } + /*! + * \brief Creates a icosphere, centered in (0, 0, 0) + * \return Primitive which is a icosphere + * + * \param size Radius of the sphere + * \param recursionLevel Number of recursion for the icosphere + * \param position Position of the icosphere + * \param rotation Rotation of the icosphere + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::IcoSphere(float size, unsigned int recursionLevel, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { Primitive primitive; @@ -164,6 +362,15 @@ namespace Nz return primitive; } + /*! + * \brief Creates a plane, centered in (0, 0, 0) + * \return Primitive which is a plane + * + * \param size (Width, Depth) + * \param subdivision Number of subdivision for the axis + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::Plane(const Vector2f& size, const Vector2ui& subdivision, const Matrix4f& transformMatrix, const Rectf& uvCoords) { Primitive primitive; @@ -172,6 +379,15 @@ namespace Nz return primitive; } + /*! + * \brief Creates a plane, centered in (0, 0, 0) + * \return Primitive which is a plane + * + * \param size (Width, Depth) + * \param subdivision Number of subdivision for the axis + * \param planeInfo Information for the plane + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::Plane(const Vector2f& size, const Vector2ui& subdivision, const Planef& plane, const Rectf& uvCoords) { Primitive primitive; @@ -180,6 +396,16 @@ namespace Nz return primitive; } + /*! + * \brief Creates a plane, centered in (0, 0, 0) + * \return Primitive which is a plane + * + * \param size (Width, Depth) + * \param subdivision Number of subdivision for the axis + * \param position Position of the plane + * \param rotation Rotation of the plane + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::Plane(const Vector2f& size, const Vector2ui& subdivision, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { Primitive primitive; @@ -188,6 +414,16 @@ namespace Nz return primitive; } + /*! + * \brief Creates a UV sphere, centered in (0, 0, 0) + * \return Primitive which is a uv sphere + * + * \param size Radius of the sphere + * \param sliceCount Number of slices + * \param stackCount Number of stacks + * \param transformMatrix Matrix to apply + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::UVSphere(float size, unsigned int sliceCount, unsigned int stackCount, const Matrix4f& transformMatrix, const Rectf& uvCoords) { Primitive primitive; @@ -196,6 +432,17 @@ namespace Nz return primitive; } + /*! + * \brief Creates a UV sphere, centered in (0, 0, 0) + * \return Primitive which is a uv sphere + * + * \param size Radius of the sphere + * \param sliceCount Number of slices + * \param stackCount Number of stacks + * \param position Position of the box + * \param rotation Rotation of the box + * \param uvCoords Coordinates for texture + */ inline Primitive Primitive::UVSphere(float size, unsigned int sliceCount, unsigned int stackCount, const Vector3f& position, const Quaternionf& rotation, const Rectf& uvCoords) { Primitive primitive; diff --git a/include/Nazara/Core/ResourceLoader.hpp b/include/Nazara/Core/ResourceLoader.hpp index 73b2dfa52..b0640dcbc 100644 --- a/include/Nazara/Core/ResourceLoader.hpp +++ b/include/Nazara/Core/ResourceLoader.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -21,6 +22,8 @@ namespace Nz template class ResourceLoader { + static_assert(std::is_base_of::value, "ResourceParameters must be a base of Parameters"); + friend Type; public: diff --git a/include/Nazara/Core/ResourceLoader.inl b/include/Nazara/Core/ResourceLoader.inl index e3f7c4577..af0a8a7f0 100644 --- a/include/Nazara/Core/ResourceLoader.inl +++ b/include/Nazara/Core/ResourceLoader.inl @@ -11,6 +11,18 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::ResourceLoader + * \brief Core class that represents a loader of resources + */ + + /*! + * \brief Checks whether the extension of the file is supported + * \return true if supported + * + * \param extension Extension of the file + */ template bool ResourceLoader::IsExtensionSupported(const String& extension) { @@ -25,16 +37,26 @@ namespace Nz return false; } + /*! + * \brief Loads a resource from a file + * \return true if successfully loaded + * + * \param resource Resource to load + * \param filePath Path to the resource + * \param parameters Parameters for the load + * + * \remark Produces a NazaraError if resource is nullptr with NAZARA_CORE_SAFE defined + * \remark Produces a NazaraError if parameters are invalid with NAZARA_CORE_SAFE defined + * \remark Produces a NazaraError if filePath has no extension + * \remark Produces a NazaraError if file count not be opened + * \remark Produces a NazaraWarning if loader failed + * \remark Produces a NazaraError if all loaders failed or no loader was found + */ template bool ResourceLoader::LoadFromFile(Type* resource, const String& filePath, const Parameters& parameters) { - #if NAZARA_CORE_SAFE - if (!parameters.IsValid()) - { - NazaraError("Invalid parameters"); - return false; - } - #endif + NazaraAssert(resource, "Invalid resource"); + NazaraAssert(parameters.IsValid(), "Invalid parameters"); String path = File::NormalizePath(filePath); String ext = path.SubStringFrom('.', -1, true).ToLower(); @@ -122,22 +144,28 @@ namespace Nz return false; } + /*! + * \brief Loads a resource from a raw memory, a size and parameters + * \return true if successfully loaded + * + * \param resource Resource to load + * \param data Raw memory of the resource + * \param size Size available for the read + * \param parameters Parameters for the load + * + * \remark Produces a NazaraError if resource is nullptr with NAZARA_CORE_SAFE defined + * \remark Produces a NazaraError if size is 0 with NAZARA_CORE_SAFE defined + * \remark Produces a NazaraError if parameters are invalid with NAZARA_CORE_SAFE defined + * \remark Produces a NazaraWarning if loader failed + * \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) { - #if NAZARA_CORE_SAFE - if (!parameters.IsValid()) - { - NazaraError("Invalid parameters"); - return false; - } - - if (size == 0) - { - NazaraError("No data to load"); - return false; - } - #endif + NazaraAssert(resource, "Invalid resource"); + NazaraAssert(data, "Invalid data pointer"); + NazaraAssert(size, "No data to load"); + NazaraAssert(parameters.IsValid(), "Invalid parameters"); MemoryView stream(data, size); @@ -198,22 +226,26 @@ namespace Nz return false; } + /*! + * \brief Loads a resource from a stream and parameters + * \return true if successfully loaded + * + * \param resource Resource to load + * \param stream Stream of the resource + * \param parameters Parameters for the load + * + * \remark Produces a NazaraError if resource is nullptr with NAZARA_CORE_SAFE defined + * \remark Produces a NazaraError if stream has no data with NAZARA_CORE_SAFE defined + * \remark Produces a NazaraError if parameters are invalid with NAZARA_CORE_SAFE defined + * \remark Produces a NazaraWarning if loader failed + * \remark Produces a NazaraError if all loaders failed or no loader was found + */ template bool ResourceLoader::LoadFromStream(Type* resource, Stream& stream, const Parameters& parameters) { - #if NAZARA_CORE_SAFE - if (!parameters.IsValid()) - { - NazaraError("Invalid parameters"); - return false; - } - - if (stream.GetSize() == 0 || stream.GetCursorPos() >= stream.GetSize()) - { - NazaraError("No data to load"); - return false; - } - #endif + NazaraAssert(resource, "Invalid resource"); + NazaraAssert(stream.GetCursorPos() < stream.GetSize(), "No data to load"); + NazaraAssert(parameters.IsValid(), "Invalid parameters"); UInt64 streamPos = stream.GetCursorPos(); bool found = false; @@ -224,17 +256,17 @@ namespace Nz stream.SetCursorPos(streamPos); - // Le loader supporte-t-il les données ? + // Does the loader support these data ? Ternary recognized = checkFunc(stream, parameters); if (recognized == Ternary_False) continue; else if (recognized == Ternary_True) found = true; - // On repositionne le stream à son ancienne position + // We move the stream to its old position stream.SetCursorPos(streamPos); - // Chargement de la ressource + // Load of the resource if (streamLoader(resource, stream, parameters)) return true; @@ -250,28 +282,33 @@ namespace Nz return false; } + /*! + * \brief Registers the loader + * + * \param extensionGetter A function to test whether the extension (as a string) is supported by this loader + * \param checkFunc A function to check the stream with the parser + * \param streamLoader A function to load the data from a stream in the resource + * \param fileLoader Optional function to load the data from a file in the resource + * \param memoryLoader Optional function to load the data from a raw memory in the resource + */ template void ResourceLoader::RegisterLoader(ExtensionGetter extensionGetter, StreamChecker checkFunc, StreamLoader streamLoader, FileLoader fileLoader, MemoryLoader memoryLoader) { - #if NAZARA_CORE_SAFE - if (streamLoader) - { - if (!checkFunc) - { - NazaraError("StreamLoader present without StreamChecker"); - return; - } - } - else if (!fileLoader && !memoryLoader) - { - NazaraError("Neither FileLoader nor MemoryLoader nor StreamLoader were present"); - return; - } - #endif + NazaraAssert(checkFunc || !streamLoader, "StreamLoader present without StreamChecker"); + NazaraAssert(fileLoader || memoryLoader || streamLoader, "A loader function is mandatory"); Type::s_loaders.push_front(std::make_tuple(extensionGetter, checkFunc, streamLoader, fileLoader, memoryLoader)); } + /*! + * \brief Unregisters the loader + * + * \param extensionGetter A function to test whether the extension (as a string) is supported by this loader + * \param checkFunc A function to check the stream with the parser + * \param streamLoader A function to load the data from a stream in the resource + * \param fileLoader Optional function to load the data from a file in the resource + * \param memoryLoader Optional function to load the data from a raw memory in the resource + */ template void ResourceLoader::UnregisterLoader(ExtensionGetter extensionGetter, StreamChecker checkFunc, StreamLoader streamLoader, FileLoader fileLoader, MemoryLoader memoryLoader) { diff --git a/include/Nazara/Core/ResourceManager.hpp b/include/Nazara/Core/ResourceManager.hpp index fa3dd63ec..ca5735811 100644 --- a/include/Nazara/Core/ResourceManager.hpp +++ b/include/Nazara/Core/ResourceManager.hpp @@ -8,6 +8,7 @@ #define NAZARA_RESOURCEMANAGER_HPP #include +#include #include #include diff --git a/include/Nazara/Core/ResourceManager.inl b/include/Nazara/Core/ResourceManager.inl index c5f37237e..edc85068b 100644 --- a/include/Nazara/Core/ResourceManager.inl +++ b/include/Nazara/Core/ResourceManager.inl @@ -9,12 +9,27 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::ResourceManager + * \brief Core class that represents a resource manager + */ + + /*! + * \brief Clears the content of the manager + */ template void ResourceManager::Clear() { Type::s_managerMap.clear(); } + /*! + * \brief Gets a reference to the object loaded from file + * \return Reference to the object + * + * \param filePath Path to the asset that will be loaded + */ template ObjectRef ResourceManager::Get(const String& filePath) { @@ -43,12 +58,19 @@ namespace Nz return it->second; } + /*! + * \brief Gets the defaults parameters for the load + * \return Default parameters for loading from file + */ template const Parameters& ResourceManager::GetDefaultParameters() { return Type::s_managerParameters; } + /*! + * \brief Purges the resource manager from every asset whose it is the only owner + */ template void ResourceManager::Purge() { @@ -56,16 +78,22 @@ namespace Nz while (it != Type::s_managerMap.end()) { const ObjectRef& ref = it->second; - if (ref.GetReferenceCount() == 1) // Sommes-nous les seuls à détenir la ressource ? + if (ref.GetReferenceCount() == 1) // Are we the only ones to own the resource ? { NazaraDebug("Purging resource from file " + ref->GetFilePath()); - Type::s_managerMap.erase(it++); // Alors on la supprime + Type::s_managerMap.erase(it++); // Then we erase it } else ++it; } } + /*! + * \brief Registers the resource under the filePath + * + * \param filePath Path for the resource + * \param resource Object to associate with + */ template void ResourceManager::Register(const String& filePath, ObjectRef resource) { @@ -74,12 +102,22 @@ namespace Nz Type::s_managerMap[absolutePath] = resource; } + /*! + * \brief Sets the defaults parameters for the load + * + * \param params Default parameters for loading from file + */ template void ResourceManager::SetDefaultParameters(const Parameters& params) { Type::s_managerParameters = params; } + /*! + * \brief Unregisters the resource under the filePath + * + * \param filePath Path for the resource + */ template void ResourceManager::Unregister(const String& filePath) { @@ -88,12 +126,19 @@ namespace Nz Type::s_managerMap.erase(absolutePath); } + /*! + * \brief Initializes the resource manager + * \return true + */ template bool ResourceManager::Initialize() { return true; } + /*! + * \brief Uninitialize the resource manager + */ template void ResourceManager::Uninitialize() { diff --git a/include/Nazara/Core/ResourceParameters.hpp b/include/Nazara/Core/ResourceParameters.hpp new file mode 100644 index 000000000..e4f32e06a --- /dev/null +++ b/include/Nazara/Core/ResourceParameters.hpp @@ -0,0 +1,20 @@ +// Copyright (C) 2015 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 + +#pragma once + +#ifndef NAZARA_RESOURCEPARAMETERS_HPP +#define NAZARA_RESOURCEPARAMETERS_HPP + +#include + +namespace Nz +{ + struct ResourceParameters + { + ParameterList custom; + }; +} + +#endif // NAZARA_RESOURCEPARAMETERS_HPP diff --git a/include/Nazara/Core/ResourceSaver.hpp b/include/Nazara/Core/ResourceSaver.hpp new file mode 100644 index 000000000..05b6d7175 --- /dev/null +++ b/include/Nazara/Core/ResourceSaver.hpp @@ -0,0 +1,54 @@ +// Copyright (C) 2015 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 + +#pragma once + +#ifndef NAZARA_RESOURCESAVER_HPP +#define NAZARA_RESOURCESAVER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class Stream; + + template + class ResourceSaver + { + static_assert(std::is_base_of::value, "ResourceParameters must be a base of Parameters"); + + friend Type; + + public: + using ExtensionGetter = bool (*)(const String& extension); + using FormatQuerier = bool (*)(const String& format); + using FileSaver = bool (*)(const Type& resource, const String& filePath, const Parameters& parameters); + using StreamSaver = bool (*)(const Type& resource, const String& format, Stream& stream, const Parameters& parameters); + + ResourceSaver() = delete; + ~ResourceSaver() = delete; + + static bool IsFormatSupported(const String& extension); + + static bool SaveToFile(const Type& resource, const String& filePath, const Parameters& parameters = Parameters()); + static bool SaveToStream(const Type& resource, Stream& stream, const String& format, const Parameters& parameters = Parameters()); + + static void RegisterSaver(FormatQuerier formatQuerier, StreamSaver streamSaver, FileSaver fileSaver = nullptr); + static void UnregisterSaver(FormatQuerier formatQuerier, StreamSaver streamSaver, FileSaver fileSaver = nullptr); + + private: + using Saver = std::tuple; + using SaverList = std::list; + }; +} + +#include + +#endif // NAZARA_RESOURCESAVER_HPP diff --git a/include/Nazara/Core/ResourceSaver.inl b/include/Nazara/Core/ResourceSaver.inl new file mode 100644 index 000000000..11d3f2a44 --- /dev/null +++ b/include/Nazara/Core/ResourceSaver.inl @@ -0,0 +1,190 @@ +// Copyright (C) 2015 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 +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup core + * \class Nz::ResourceSaver + * \brief Core class that represents a list of saver functions for a specific resource type + */ + + /*! + * \brief Checks whether the extension of the file is supported + * \return true if supported + * + * \param extension Extension of the file + */ + template + bool ResourceSaver::IsFormatSupported(const String& extension) + { + for (Saver& saver : Type::s_savers) + { + ExtensionGetter isExtensionSupported = std::get<0>(saver); + + if (isExtensionSupported && isExtensionSupported(extension)) + return true; + } + + return false; + } + + /*! + * \brief Saves a resource to a file + * \return true if successfully saved + * + * \param resource Resource to save + * \param filePath Path to the file where the resource will be written + * \param parameters Parameters for the save + * + * \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 + */ + template + bool ResourceSaver::SaveToFile(const Type& resource, const String& filePath, const Parameters& parameters) + { + NazaraAssert(parameters.IsValid(), "Invalid parameters"); + + String path = File::NormalizePath(filePath); + String ext = path.SubStringFrom('.', -1, true).ToLower(); + if (ext.IsEmpty()) + { + NazaraError("Failed to get file extension from \"" + filePath + '"'); + return false; + } + + File file(path); // Opened only is required + + bool found = false; + for (Saver& saver : Type::s_savers) + { + FormatQuerier formatQuerier = std::get<0>(saver); + if (!formatQuerier || !formatQuerier(ext)) + continue; + + found = true; + + StreamSaver streamSeaver = std::get<1>(saver); + FileSaver fileSaver = std::get<2>(saver); + + if (fileSaver) + { + if (fileSaver(resource, filePath, parameters)) + return true; + } + else + { + if (!file.Open(OpenMode_WriteOnly)) + { + NazaraError("Failed to save to file: unable to open \"" + filePath + "\" in write mode"); + return false; + } + + if (streamSeaver(resource, ext, file, parameters)) + return true; + } + + NazaraWarning("Saver failed"); + } + + if (found) + NazaraError("Failed to save resource: all savers failed"); + else + NazaraError("Failed to save resource: no saver found for extension \"" + ext + '"'); + + return false; + } + + /*! + * \brief Saves a resource to a stream + * \return true if successfully saved + * + * \param resource Resource to load + * \param stream Stream with write access where the resource data will be written + * \param format Data format to save the resource to + * \param parameters Parameters for the saving + * + * \seealso SaveToFile + */ + template + bool ResourceSaver::SaveToStream(const Type& resource, Stream& stream, const String& format, const Parameters& parameters) + { + NazaraAssert(stream.IsWritable(), "Stream is not writable"); + NazaraAssert(parameters.IsValid(), "Invalid parameters"); + + UInt64 streamPos = stream.GetCursorPos(); + bool found = false; + for (Saver& saver : Type::s_savers) + { + FormatQuerier formatQuerier = std::get<0>(saver); + if (!formatQuerier || !formatQuerier(format)) + continue; + + found = true; + + StreamSaver streamSeaver = std::get<1>(saver); + + // We move the stream to its old position + stream.SetCursorPos(streamPos); + + // Load of the resource + if (streamSeaver(resource, format, stream, parameters)) + return true; + + NazaraWarning("Saver failed"); + } + + if (found) + NazaraError("Failed to save resource: all savers failed"); + else + NazaraError("Failed to save resource: no saver found for format \"" + format + '"'); + + return false; + } + + /*! + * \brief Registers a saver + * + * \param formatQuerier A function to test whether the format (as a string) is supported by this saver + * \param streamSaver A function which saves the resource to a stream + * \param fileSaver Optional function which saves the resource directly to a file given a file path + * + * \remark The fileSaver argument is only present for compatibility with external savers which cannot be interfaced with streams + * \remark At least one saver is required + */ + template + void ResourceSaver::RegisterSaver(FormatQuerier formatQuerier, StreamSaver streamSaver, FileSaver fileSaver) + { + NazaraAssert(formatQuerier, "A format querier is mandaroty"); + NazaraAssert(streamSaver || fileSaver, "A saver function is mandaroty"); + + Type::s_savers.push_front(std::make_tuple(formatQuerier, streamSaver, fileSaver)); + } + + /*! + * \brief Unregisters a saver + * + * \param formatQuerier A function to test whether the format (as a string) is supported by this saver + * \param streamSaver A function which saves the resource to a stream + * \param fileSaver A function function which saves the resource directly to a file given a file path + * + * \remark The saver will only be unregistered if the function pointers are exactly the same + */ + template + void ResourceSaver::UnregisterSaver(FormatQuerier formatQuerier, StreamSaver streamSaver, FileSaver fileSaver) + { + Type::s_savers.remove(std::make_tuple(formatQuerier, streamSaver, fileSaver)); + } +} + +#include diff --git a/include/Nazara/Core/Serialization.hpp b/include/Nazara/Core/Serialization.hpp index 409aa6ace..47339b0a2 100644 --- a/include/Nazara/Core/Serialization.hpp +++ b/include/Nazara/Core/Serialization.hpp @@ -20,17 +20,9 @@ namespace Nz struct SerializationContext { Stream* stream; - Endianness endianness; - UInt8 currentBitPos; - UInt8 currentByte; - }; - - struct UnserializationContext - { - Stream* stream; - Endianness endianness; - UInt8 currentBitPos; - UInt8 currentByte; + Endianness endianness = Endianness_BigEndian; //< Default to Big Endian encoding + UInt8 currentBitPos = 8; //< 8 means no bit is currently wrote + UInt8 currentByte; //< Undefined value, will be initialized at the first bit write }; } diff --git a/include/Nazara/Core/Serializer.hpp b/include/Nazara/Core/Serializer.hpp deleted file mode 100644 index ae35f2254..000000000 --- a/include/Nazara/Core/Serializer.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2015 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 - -#pragma once - -#ifndef NAZARA_SERIALIZER_HPP -#define NAZARA_SERIALIZER_HPP - -#include -#include - -namespace Nz -{ - class Stream; - - class Serializer - { - public: - inline Serializer(Stream& stream); - Serializer(const Serializer&) = default; - Serializer(Serializer&&) = default; - ~Serializer(); - - inline Endianness GetDataEndianness() const; - inline Stream& GetStream() const; - - inline bool FlushBits(); - - inline void SetDataEndianness(Endianness endiannes); - inline void SetStream(Stream& stream); - - template - Serializer& operator<<(const T& value); - - Serializer& operator=(const Serializer&) = default; - Serializer& operator=(Serializer&&) = default; - - private: - SerializationContext m_serializationContext; - }; -} - -#include - -#endif // NAZARA_SERIALIZER_HPP diff --git a/include/Nazara/Core/Serializer.inl b/include/Nazara/Core/Serializer.inl deleted file mode 100644 index 69c433ad0..000000000 --- a/include/Nazara/Core/Serializer.inl +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2015 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 -{ - inline Serializer::Serializer(Stream& stream) - { - m_serializationContext.currentBitPos = 8; - m_serializationContext.endianness = Endianness_BigEndian; - m_serializationContext.stream = &stream; - } - - inline Serializer::~Serializer() - { - if (!FlushBits()) - NazaraWarning("Failed to flush bits at serializer destruction"); - } - - inline Endianness Serializer::GetDataEndianness() const - { - return m_serializationContext.endianness; - } - - inline Stream& Serializer::GetStream() const - { - return *m_serializationContext.stream; - } - - inline bool Serializer::FlushBits() - { - if (m_serializationContext.currentBitPos != 8) - { - m_serializationContext.currentBitPos = 8; //< To prevent Serialize to flush bits itself - - if (!Serialize(m_serializationContext, m_serializationContext.currentByte)) - return false; - } - - return true; - } - - inline void Serializer::SetDataEndianness(Endianness endiannes) - { - m_serializationContext.endianness = endiannes; - } - - inline void Serializer::SetStream(Stream& stream) - { - m_serializationContext.stream = &stream; - } - - template - Serializer& Serializer::operator<<(const T& value) - { - if (!Serialize(m_serializationContext, value)) - NazaraError("Failed to serialize value"); - - return *this; - } -} - -#include -#include "Serializer.hpp" diff --git a/include/Nazara/Core/Signal.inl b/include/Nazara/Core/Signal.inl index a635302ec..466d87537 100644 --- a/include/Nazara/Core/Signal.inl +++ b/include/Nazara/Core/Signal.inl @@ -8,18 +8,38 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Signal + * \brief Core class that represents a signal, a list of objects waiting for its message + */ + + /*! + * \brief Constructs a Signal object by default + */ + template Signal::Signal() : m_slotIterator(0) { } + /*! + * \brief Constructs a Signal object by move semantic + * + * \param signal Signal to move in this + */ + template Signal::Signal(Signal&& signal) { operator=(std::move(signal)); } + /*! + * \brief Clears the list of actions attached to the signal + */ + template void Signal::Clear() { @@ -27,12 +47,26 @@ namespace Nz m_slotIterator = 0; } + /*! + * \brief Connects a function to the signal + * \return Connection attached to the signal + * + * \param func Non-member function + */ + template typename Signal::Connection Signal::Connect(const Callback& func) { return Connect(Callback(func)); } + /*! + * \brief Connects a function to the signal + * \return Connection attached to the signal + * + * \param func Non-member function + */ + template typename Signal::Connection Signal::Connect(Callback&& func) { @@ -54,6 +88,14 @@ namespace Nz return Connection(m_slots.back()); } + /*! + * \brief Connects a member function and its object to the signal + * \return Connection attached to the signal + * + * \param object Object to send the message + * \param method Member function + */ + template template typename Signal::Connection Signal::Connect(O& object, void (O::*method) (Args...)) @@ -64,6 +106,14 @@ namespace Nz }); } + /*! + * \brief Connects a member function and its object to the signal + * \return Connection attached to the signal + * + * \param object Object to send the message + * \param method Member function + */ + template template typename Signal::Connection Signal::Connect(O* object, void (O::*method)(Args...)) @@ -74,6 +124,14 @@ namespace Nz }); } + /*! + * \brief Connects a member function and its object to the signal + * \return Connection attached to the signal + * + * \param object Object to send the message + * \param method Member function + */ + template template typename Signal::Connection Signal::Connect(const O& object, void (O::*method) (Args...) const) @@ -84,6 +142,14 @@ namespace Nz }); } + /*! + * \brief Connects a member function and its object to the signal + * \return Connection attached to the signal + * + * \param object Object to send the message + * \param method Member function + */ + template template typename Signal::Connection Signal::Connect(const O* object, void (O::*method)(Args...) const) @@ -94,6 +160,12 @@ namespace Nz }); } + /*! + * \brief Applies the list of arguments to every callback functions + * + * \param args Arguments to send with the message + */ + template void Signal::operator()(Args... args) const { @@ -101,6 +173,13 @@ namespace Nz m_slots[m_slotIterator]->callback(args...); } + /*! + * \brief Moves the signal into this + * \return A reference to this + * + * \param signal Signal to move in this + */ + template Signal& Signal::operator=(Signal&& signal) { @@ -114,17 +193,28 @@ namespace Nz return *this; } + /*! + * \brief Disconnects a listener from this signal + * + * \param slot Pointer to the ith listener of the signal + * + * \remark Produces a NazaraAssert if slot is invalid (nullptr) + * \remark Produces a NazaraAssert if index of slot is invalid + * \remark Produces a NazaraAssert if slot is not attached to this signal + */ + template void Signal::Disconnect(const SlotPtr& slot) { NazaraAssert(slot, "Invalid slot pointer"); NazaraAssert(slot->index < m_slots.size(), "Invalid slot index"); + NazaraAssert(slot->signal == this, "Slot is not attached to this signal"); // "Swap this slot with the last one and pop" idiom // This will preserve slot indexes // Can we safely "remove" this slot? - if (m_slotIterator >= m_slots.size()-1 || slot->index > m_slotIterator) + if (m_slotIterator >= (m_slots.size() - 1) || slot->index > m_slotIterator) { // Yes we can SlotPtr& newSlot = m_slots[slot->index]; @@ -150,6 +240,16 @@ namespace Nz m_slots.pop_back(); } + /*! + * \class Nz::Signal::Connection + * \brief Core class that represents a connection attached to a signal + */ + + /*! + * \brief Constructs a Signal::Connection object with a slot + * + * \param slot Slot of the listener + */ template Signal::Connection::Connection(const SlotPtr& slot) : @@ -157,6 +257,13 @@ namespace Nz { } + /*! + * \brief Connects to a signal with arguments + * + * \param signal New signal to listen + * \param args Arguments for the signal + */ + template template void Signal::Connection::Connect(BaseClass& signal, ConnectArgs&&... args) @@ -164,6 +271,10 @@ namespace Nz operator=(signal.Connect(std::forward(args)...)); } + /*! + * \brief Disconnects the connection from the signal + */ + template void Signal::Connection::Disconnect() { @@ -171,12 +282,27 @@ namespace Nz ptr->signal->Disconnect(ptr); } + /*! + * \brief Checks whether the connection is still active with the signal + * \return true if signal is still active + */ + template bool Signal::Connection::IsConnected() const { return !m_ptr.expired(); } + /*! + * \class Nz::Signal::ConnectionGuard + * \brief Core class that represents a RAII for a connection attached to a signal + */ + + /*! + * \brief Constructs a Signal::ConnectionGuard object with a connection + * + * \param connection Connection for the scope + */ template Signal::ConnectionGuard::ConnectionGuard(const Connection& connection) : @@ -184,18 +310,35 @@ namespace Nz { } + /*! + * \brief Constructs a Signal::ConnectionGuard object with a connection by move semantic + * + * \param connection Connection for the scope + */ + template Signal::ConnectionGuard::ConnectionGuard(Connection&& connection) : m_connection(std::move(connection)) { } + /*! + * \brief Destructs the object and disconnects the connection + */ + template Signal::ConnectionGuard::~ConnectionGuard() { m_connection.Disconnect(); } + /*! + * \brief Connects to a signal with arguments + * + * \param signal New signal to listen + * \param args Arguments for the signal + */ + template template void Signal::ConnectionGuard::Connect(BaseClass& signal, ConnectArgs&&... args) @@ -204,24 +347,45 @@ namespace Nz m_connection.Connect(signal, std::forward(args)...); } + /*! + * \brief Disconnects the connection from the signal + */ + template void Signal::ConnectionGuard::Disconnect() { m_connection.Disconnect(); } + /*! + * \brief Gets the connection attached to the signal + * \return Connection of the signal + */ + template typename Signal::Connection& Signal::ConnectionGuard::GetConnection() { return m_connection; } + /*! + * \brief Checks whether the connection is still active with the signal + * \return true if signal is still active + */ + template bool Signal::ConnectionGuard::IsConnected() const { return m_connection.IsConnected(); } + /*! + * \brief Assigns the connection into this + * \return A reference to this + * + * \param connection Connection to assign into this + */ + template typename Signal::ConnectionGuard& Signal::ConnectionGuard::operator=(const Connection& connection) { @@ -231,6 +395,13 @@ namespace Nz return *this; } + /*! + * \brief Moves the Connection into this + * \return A reference to this + * + * \param connection Connection to move in this + */ + template typename Signal::ConnectionGuard& Signal::ConnectionGuard::operator=(Connection&& connection) { @@ -240,6 +411,13 @@ namespace Nz return *this; } + /*! + * \brief Moves the ConnectionGuard into this + * \return A reference to this + * + * \param connection ConnectionGuard to move in this + */ + template typename Signal::ConnectionGuard& Signal::ConnectionGuard::operator=(ConnectionGuard&& connection) { diff --git a/include/Nazara/Core/SparsePtr.hpp b/include/Nazara/Core/SparsePtr.hpp index 79c87ca95..6e36fcc57 100644 --- a/include/Nazara/Core/SparsePtr.hpp +++ b/include/Nazara/Core/SparsePtr.hpp @@ -7,7 +7,7 @@ #ifndef NAZARA_SPARSEPTR_HPP #define NAZARA_SPARSEPTR_HPP -///FIXME: Est-ce que SparsePtr est vraiment le meilleur nom pour cette classe ? +///FIXME: Is SparsePtr a really good name for this class ? #include #include diff --git a/include/Nazara/Core/SparsePtr.inl b/include/Nazara/Core/SparsePtr.inl index d6b227226..0da69b0ad 100644 --- a/include/Nazara/Core/SparsePtr.inl +++ b/include/Nazara/Core/SparsePtr.inl @@ -7,24 +7,53 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::SparsePtr + * \brief Core class that represents a pointer and the step between two elements + */ + + /*! + * \brief Constructs a SparsePtr object by default + */ + template SparsePtr::SparsePtr() { Reset(); } + /*! + * \brief Constructs a SparsePtr object with a pointer + * + * \param ptr Pointer to data + */ + template SparsePtr::SparsePtr(T* ptr) { Reset(ptr); } + /*! + * \brief Constructs a SparsePtr object with a pointer and a step + * + * \param ptr Pointer to data + * \param stride Step between two elements + */ + template SparsePtr::SparsePtr(VoidPtr ptr, int stride) { Reset(ptr, stride); } + /*! + * \brief Constructs a SparsePtr object from another type of SparsePtr + * + * \param ptr Pointer to data of type U to convert to type T + */ + template template SparsePtr::SparsePtr(const SparsePtr& ptr) @@ -32,18 +61,32 @@ namespace Nz Reset(ptr); } + /*! + * \brief Gets the original pointer + * \return Pointer to the first data + */ + template typename SparsePtr::VoidPtr SparsePtr::GetPtr() const { return m_ptr; } + /*! + * \brief Gets the stride + * \return Step between two elements + */ + template int SparsePtr::GetStride() const { return m_stride; } + /*! + * \brief Resets the SparsePtr + */ + template void SparsePtr::Reset() { @@ -51,6 +94,14 @@ namespace Nz SetStride(0); } + /*! + * \brief Resets the SparsePtr with a pointer + * + * \param ptr Pointer to data + * + * \remark stride is set to sizeof(T) + */ + template void SparsePtr::Reset(T* ptr) { @@ -58,6 +109,13 @@ namespace Nz SetStride(sizeof(T)); } + /*! + * \brief Resets the SparsePtr with a pointer and its stride + * + * \param ptr Pointer to data + * \param stride Step between two elements + */ + template void SparsePtr::Reset(VoidPtr ptr, int stride) { @@ -65,6 +123,12 @@ namespace Nz SetStride(stride); } + /*! + * \brief Resets the SparsePtr with another SparsePtr + * + * \param ptr Another sparsePtr + */ + template void SparsePtr::Reset(const SparsePtr& ptr) { @@ -72,6 +136,12 @@ namespace Nz SetStride(ptr.GetStride()); } + /*! + * \brief Resets the SparsePtr with another type of SparsePtr + * + * \param ptr Another sparsePtr + */ + template template void SparsePtr::Reset(const SparsePtr& ptr) @@ -82,94 +152,187 @@ namespace Nz SetStride(ptr.GetStride()); } + /*! + * \brief Sets the pointer + * + * \param ptr Pointer to data + */ + template void SparsePtr::SetPtr(VoidPtr ptr) { - m_ptr = reinterpret_cast(ptr); + m_ptr = static_cast(ptr); } + /*! + * \brief Sets the stride + * + * \param stride Step between two elements + */ + template void SparsePtr::SetStride(int stride) { m_stride = stride; } + /*! + * \brief Converts the pointer to bool + * \return true if pointer is not nullptr + */ + template SparsePtr::operator bool() const { return m_ptr != nullptr; } + /*! + * \brief Converts the pointer to a pointer to the value + * \return The value of the pointer + */ + template SparsePtr::operator T*() const { return reinterpret_cast(m_ptr); } + /*! + * \brief Dereferences the pointer + * \return The dereferencing of the pointer + */ + template T& SparsePtr::operator*() const { return *reinterpret_cast(m_ptr); } + /*! + * \brief Dereferences the pointer + * \return The dereferencing of the pointer + */ + template T* SparsePtr::operator->() const { return reinterpret_cast(m_ptr); } + /*! + * \brief Gets the ith element of the stride pointer + * \return A reference to the ith value + * + * \param index Number of stride to do + */ + template T& SparsePtr::operator[](int index) const { - return *reinterpret_cast(m_ptr + index*m_stride); + return *reinterpret_cast(m_ptr + index * m_stride); } + /*! + * \brief Gets the SparsePtr with an offset + * \return A SparsePtr with the new stride + * + * \param count Number of stride to do + */ + template SparsePtr SparsePtr::operator+(int count) const { - return SparsePtr(m_ptr + count*m_stride, m_stride); + return SparsePtr(m_ptr + count * m_stride, m_stride); } + /*! + * \brief Gets the SparsePtr with an offset + * \return A SparsePtr with the new stride + * + * \param count Number of stride to do + */ + template SparsePtr SparsePtr::operator+(unsigned int count) const { - return SparsePtr(m_ptr + count*m_stride, m_stride); + return SparsePtr(m_ptr + count * m_stride, m_stride); } + /*! + * \brief Gets the SparsePtr with an offset + * \return A SparsePtr with the new stride + * + * \param count Number of stride to do + */ + template SparsePtr SparsePtr::operator-(int count) const { - return SparsePtr(m_ptr - count*m_stride, m_stride); + return SparsePtr(m_ptr - count * m_stride, m_stride); } + /*! + * \brief Gets the SparsePtr with an offset + * \return A SparsePtr with the new stride + * + * \param count Number of stride to do + */ + template SparsePtr SparsePtr::operator-(unsigned int count) const { - return SparsePtr(m_ptr - count*m_stride, m_stride); + return SparsePtr(m_ptr - count * m_stride, m_stride); } + /*! + * \brief Gets the difference between the two SparsePtr + * \return The difference of elements: ptr - this->ptr + * + * \param ptr Other ptr + */ + template std::ptrdiff_t SparsePtr::operator-(const SparsePtr& ptr) const { - return (m_ptr - ptr.m_ptr)/m_stride; + return (m_ptr - ptr.m_ptr) / m_stride; } + /*! + * \brief Gets the SparsePtr with an offset + * \return A reference to this pointer with the new stride + * + * \param count Number of stride to do + */ + template SparsePtr& SparsePtr::operator+=(int count) { - m_ptr += count*m_stride; + m_ptr += count * m_stride; return *this; } + /*! + * \brief Gets the SparsePtr with an offset + * \return A reference to this pointer with the new stride + * + * \param count Number of stride to do + */ + template SparsePtr& SparsePtr::operator-=(int count) { - m_ptr -= count*m_stride; + m_ptr -= count * m_stride; return *this; } + /*! + * \brief Gets the SparsePtr with the next element + * \return A reference to this pointer updated + */ + template SparsePtr& SparsePtr::operator++() { @@ -178,19 +341,29 @@ namespace Nz return *this; } + /*! + * \brief Gets the SparsePtr with the next element + * \return A SparsePtr not updated + */ + template SparsePtr SparsePtr::operator++(int) { - // On fait une copie de l'objet + // We copy the object SparsePtr tmp(*this); - // On modifie l'objet + // We modify it operator++(); - // On retourne la copie + // We return the copy return tmp; } + /*! + * \brief Gets the SparsePtr with the previous element + * \return A reference to this pointer updated + */ + template SparsePtr& SparsePtr::operator--() { @@ -198,49 +371,96 @@ namespace Nz return *this; } + /*! + * \brief Gets the SparsePtr with the previous element + * \return A SparsePtr not updated + */ + template SparsePtr SparsePtr::operator--(int) { - // On fait une copie de l'objet + // We copy the object SparsePtr tmp(*this); - // On modifie l'objet + // We modify it operator--(); - // On retourne la copie + // We return the copy return tmp; } + /*! + * \brief Compares the SparsePtr to another one + * \return true if the two SparsePtr are pointing to the same memory + * + * \param ptr Other SparsePtr to compare with + */ + template bool SparsePtr::operator==(const SparsePtr& ptr) const { return m_ptr == ptr.m_ptr; } + /*! + * \brief Compares the SparsePtr to another one + * \return false if the two SparsePtr are pointing to the same memory + * + * \param ptr Other SparsePtr to compare with + */ + template bool SparsePtr::operator!=(const SparsePtr& ptr) const { return m_ptr != ptr.m_ptr; } + /*! + * \brief Compares the SparsePtr to another one + * \return true if the first SparsePtr is pointing to memory inferior to the second one + * + * \param ptr Other SparsePtr to compare with + */ + template bool SparsePtr::operator<(const SparsePtr& ptr) const { return m_ptr < ptr.m_ptr; } + /*! + * \brief Compares the SparsePtr to another one + * \return true if the first SparsePtr is pointing to memory superior to the second one + * + * \param ptr Other SparsePtr to compare with + */ + template bool SparsePtr::operator>(const SparsePtr& ptr) const { return m_ptr > ptr.m_ptr; } + /*! + * \brief Compares the SparsePtr to another one + * \return true if the first SparsePtr is pointing to memory inferior or equal to the second one + * + * \param ptr Other SparsePtr to compare with + */ + template bool SparsePtr::operator<=(const SparsePtr& ptr) const { return m_ptr <= ptr.m_ptr; } + /*! + * \brief Compares the SparsePtr to another one + * \return true if the first SparsePtr is pointing to memory superior or equal to the second one + * + * \param ptr Other SparsePtr to compare with + */ + template bool SparsePtr::operator>=(const SparsePtr& ptr) const { diff --git a/include/Nazara/Core/Stream.inl b/include/Nazara/Core/Stream.inl index 104d5e300..aae7fe155 100644 --- a/include/Nazara/Core/Stream.inl +++ b/include/Nazara/Core/Stream.inl @@ -7,12 +7,26 @@ namespace Nz { + /*! + * \ingroup core + * \brief Constructs a Stream object with options + * + * \param streamOptions Options for the stream + * \param openMode Reading/writing mode for the stream + */ + inline Stream::Stream(UInt32 streamOptions, UInt32 openMode) : m_openMode(openMode), m_streamOptions(streamOptions) { } + /*! + * \brief Enables the text mode + * + * \param textMode Enables the mode or disables + */ + inline void Stream::EnableTextMode(bool textMode) { if (textMode) @@ -21,6 +35,12 @@ namespace Nz m_streamOptions &= ~StreamOption_Text; } + /*! + * \brief Flushes the stream + * + * \remark Produces a NazaraAssert if file is not writable + */ + inline void Stream::Flush() { NazaraAssert(IsWritable(), "Stream is not writable"); @@ -28,36 +48,77 @@ namespace Nz FlushStream(); } + /*! + * \brief Gets the open mode of the stream + * \return Reading/writing mode for the stream + */ + inline UInt32 Stream::GetOpenMode() const { return m_openMode; } + /*! + * \brief Gets the options of the stream + * \return Options of the stream + */ + inline UInt32 Stream::GetStreamOptions() const { return m_streamOptions; } + /*! + * \brief Checks whether the stream is readable + * \return true if it is the case + */ + inline bool Stream::IsReadable() const { return (m_openMode & OpenMode_ReadOnly) != 0; } + /*! + * \brief Checks whether the stream is sequential + * \return true if it is the case + */ + inline bool Stream::IsSequential() const { return (m_streamOptions & StreamOption_Sequential) != 0; } + /*! + * \brief Checks whether the stream has text mode enabled + * \return true if it is the case + */ + inline bool Stream::IsTextModeEnabled() const { return (m_streamOptions & StreamOption_Text) != 0; } + /*! + * \brief Checks whether the stream can be written + * \return true if it is the case + */ + inline bool Stream::IsWritable() const { return (m_openMode & OpenMode_WriteOnly) != 0; } + /*! + * \brief Reads the stream and puts the result in a buffer + * \return Size of the read + * + * \param buffer Buffer to stock data + * \param size Size meant to be read + * + * \remark Produces a NazaraAssert if stream is not readable + * \remark If preallocated space of buffer is less than the size, the behaviour is undefined + */ + inline std::size_t Stream::Read(void* buffer, std::size_t size) { NazaraAssert(IsReadable(), "Stream is not readable"); @@ -65,6 +126,17 @@ namespace Nz return ReadBlock(buffer, size); } + /*! + * \brief Writes in the stream the content of a buffer + * \return Size of the writing + * + * \param buffer Buffer to get data from + * \param size Size meant to be written + * + * \remark Produces a NazaraAssert if stream is not writable + * \remark If preallocated space of buffer is less than the size, the behaviour is undefined + */ + inline std::size_t Stream::Write(const void* buffer, std::size_t size) { NazaraAssert(IsWritable(), "Stream is not writable"); diff --git a/include/Nazara/Core/String.hpp b/include/Nazara/Core/String.hpp index 405eb7a19..70673ae8a 100644 --- a/include/Nazara/Core/String.hpp +++ b/include/Nazara/Core/String.hpp @@ -23,11 +23,11 @@ namespace Nz public: enum Flags { - None = 0x00, // Mode par défaut - CaseInsensitive = 0x01, // Insensible à la casse - HandleUtf8 = 0x02, // Traite les octets comme une suite de caractères UTF-8 - TrimOnlyLeft = 0x04, // Trim(med), ne coupe que la partie gauche de la chaîne - TrimOnlyRight = 0x08 // Trim(med), ne coupe que la partie droite de la chaîne + None = 0x00, // Default mode + CaseInsensitive = 0x01, // Case insensitive + HandleUtf8 = 0x02, // Considers bytes as a list of UTF-8 characters + TrimOnlyLeft = 0x04, // Trim(med), only cut the left part of the string + TrimOnlyRight = 0x08 // Trim(med), only cut the right part of the string }; String(); @@ -71,7 +71,7 @@ namespace Nz std::size_t FindAny(const char* string, std::intmax_t start = 0, UInt32 flags = None) const; std::size_t FindAny(const String& string, std::intmax_t start = 0, UInt32 flags = None) const; std::size_t FindLast(char character, std::intmax_t start = -1, UInt32 flags = None) const; - std::size_t FindLast(const char *string, std::intmax_t start = -1, UInt32 flags = None) const; + std::size_t FindLast(const char* string, std::intmax_t start = -1, UInt32 flags = None) const; std::size_t FindLast(const String& string, std::intmax_t start = -1, UInt32 flags = None) const; std::size_t FindLastAny(const char* string, std::intmax_t start = -1, UInt32 flags = None) const; std::size_t FindLastAny(const String& string, std::intmax_t start = -1, UInt32 flags = None) const; @@ -119,8 +119,8 @@ namespace Nz void Reserve(std::size_t bufferSize); - String& Resize(std::intmax_t size, char character = ' '); - String Resized(std::intmax_t size, char character = ' ') const; + String& Resize(std::intmax_t size, UInt32 flags = None); + String Resized(std::intmax_t size, UInt32 flags = None) const; String& Reverse(); String Reversed() const; @@ -326,7 +326,7 @@ namespace Nz inline bool HashAppend(AbstractHash* hash, const String& string); NAZARA_CORE_API bool Serialize(SerializationContext& context, const String& string); - NAZARA_CORE_API bool Unserialize(UnserializationContext& context, String* string); + NAZARA_CORE_API bool Unserialize(SerializationContext& context, String* string); } namespace std diff --git a/include/Nazara/Core/String.inl b/include/Nazara/Core/String.inl index 93ff32312..aa4343371 100644 --- a/include/Nazara/Core/String.inl +++ b/include/Nazara/Core/String.inl @@ -7,22 +7,43 @@ namespace Nz { + /*! + * \ingroup core + * \brief Constructs a String object with a shared string by move semantic + * + * \param sharedString Shared string to move into this + */ + inline String::String(std::shared_ptr&& sharedString) : m_sharedString(std::move(sharedString)) { } + /*! + * \brief Releases the content to the string + */ + inline void String::ReleaseString() { m_sharedString = std::move(GetEmptyString()); } + /*! + * \brief Constructs a SharedString object by default + */ + inline String::SharedString::SharedString() : // Special case: empty string capacity(0), size(0) { } + /*! + * \brief Constructs a SharedString object with a size + * + * \param strSize Number of characters in the string + */ + inline String::SharedString::SharedString(std::size_t strSize) : capacity(strSize), size(strSize), @@ -31,6 +52,13 @@ namespace Nz string[strSize] = '\0'; } + /*! + * \brief Constructs a SharedString object with a size and a capacity + * + * \param strSize Number of characters in the string + * \param strCapacity Capacity in characters in the string + */ + inline String::SharedString::SharedString(std::size_t strSize, std::size_t strCapacity) : capacity(strCapacity), size(strSize), @@ -39,6 +67,14 @@ namespace Nz string[strSize] = '\0'; } + /*! + * \brief Appends the string to the hash + * \return true if hash is successful + * + * \param hash Hash to append data of the file + * \param string String to hash + */ + inline bool HashAppend(AbstractHash* hash, const String& string) { hash->Append(reinterpret_cast(string.GetConstBuffer()), string.GetSize()); @@ -48,6 +84,13 @@ namespace Nz namespace std { + /*! + * \brief Specialisation of std to hash + * \return Result of the hash + * + * \param str String to hash + */ + template<> struct hash { diff --git a/include/Nazara/Core/TaskScheduler.inl b/include/Nazara/Core/TaskScheduler.inl index b8b99ed47..96e240081 100644 --- a/include/Nazara/Core/TaskScheduler.inl +++ b/include/Nazara/Core/TaskScheduler.inl @@ -6,18 +6,44 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::TaskScheduler + * \brief Core class that represents a thread pool + */ + + /*! + * \brief Adds a task to the pending list + * + * \param function Task that the pool will execute + */ + template void TaskScheduler::AddTask(F function) { AddTaskFunctor(new FunctorWithoutArgs(function)); } + /*! + * \brief Adds a task to the pending list + * + * \param function Task that the pool will execute + * \param args Arguments of the function + */ + template void TaskScheduler::AddTask(F function, Args&&... args) { AddTaskFunctor(new FunctorWithArgs(function, std::forward(args)...)); } + /*! + * \brief Adds a task to the pending list + * + * \param function Task that the pool will execute + * \param object Object on which the method will be called + */ + template void TaskScheduler::AddTask(void (C::*function)(), C* object) { diff --git a/include/Nazara/Core/Thread.inl b/include/Nazara/Core/Thread.inl index d6c76522a..f372c803f 100644 --- a/include/Nazara/Core/Thread.inl +++ b/include/Nazara/Core/Thread.inl @@ -7,18 +7,44 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Thread + * \brief Core class that represents a thread + */ + + /*! + * \brief Constructs a Thread object with a function + * + * \param function Task the thread will execute in parallel + */ + template Thread::Thread(F function) { CreateImpl(new FunctorWithoutArgs(function)); } + /*! + * \brief Constructs a Thread object with a function and its parameters + * + * \param function Task the thread will execute in parallel + * \param args Arguments of the function + */ + template Thread::Thread(F function, Args&&... args) { CreateImpl(new FunctorWithArgs(function, std::forward(args)...)); } + /*! + * \brief Constructs a Thread object with a member function and its object + * + * \param function Task the thread will execute in parallel + * \param object Object on which the method will be called + */ + template Thread::Thread(void (C::*function)(), C* object) { diff --git a/include/Nazara/Core/ThreadSafety.hpp b/include/Nazara/Core/ThreadSafety.hpp index 26b446ae6..5b1629b5b 100644 --- a/include/Nazara/Core/ThreadSafety.hpp +++ b/include/Nazara/Core/ThreadSafety.hpp @@ -2,12 +2,12 @@ // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Config.hpp -// Pas de header guard +// No header guard #include #include -// Ces macros peuvent changer pour n'importe quel fichier qui l'utilise dans une même unité de compilation +// These macroes can change for any file which uses it in the same unit of compilation #undef NazaraLock #undef NazaraMutex #undef NazaraMutexAttrib diff --git a/include/Nazara/Core/ThreadSafetyOff.hpp b/include/Nazara/Core/ThreadSafetyOff.hpp index e6a1ce208..29c327e0d 100644 --- a/include/Nazara/Core/ThreadSafetyOff.hpp +++ b/include/Nazara/Core/ThreadSafetyOff.hpp @@ -2,9 +2,9 @@ // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Config.hpp -// Pas de header guard +// No header guard -// Ces macros peuvent changer pour n'importe quel fichier qui l'utilise dans une même unité de compilation +// These macroes can change for any file which uses it in the same unit of compilation #undef NazaraLock #undef NazaraMutex #undef NazaraMutexAttrib diff --git a/include/Nazara/Core/Unicode.hpp b/include/Nazara/Core/Unicode.hpp index 631d8f8bd..a42e88e90 100644 --- a/include/Nazara/Core/Unicode.hpp +++ b/include/Nazara/Core/Unicode.hpp @@ -18,16 +18,16 @@ namespace Nz Unicode() = delete; ~Unicode() = delete; /* - Catégorie Unicode: - -Les valeurs de 0x01 à 0x80 indiquent la catégorie. - -Les valeurs de 0x100 à 0x10000 indiquent la sous-catégorie. + Unicode category: + -Values between 0x01 and 0x80 specify the category + -Values between 0x100 and 0x10000 specify the subcategory */ enum Category : UInt16 { - // Catégorie non-reconnue par Nazara + // Category not handled by Nazara Category_NoCategory = 0, - // Lettres + // Letters Category_Letter = 0x01, // L Category_Letter_Lowercase = Category_Letter | 0x0100, // Ll Category_Letter_Modifier = Category_Letter | 0x0200, // Lm @@ -35,19 +35,19 @@ namespace Nz Category_Letter_Titlecase = Category_Letter | 0x0800, // Lt Category_Letter_Uppercase = Category_Letter | 0x1000, // Lu - // Marques + // Marks Category_Mark = 0x02, // M Category_Mark_Enclosing = Category_Mark | 0x100, // Me Category_Mark_NonSpacing = Category_Mark | 0x200, // Mn Category_Mark_SpacingCombining = Category_Mark | 0x400, // Mc - // Nombres + // Numbers Category_Number = 0x04, // N Category_Number_DecimalDigit = Category_Number | 0x100, // Nd Category_Number_Letter = Category_Number | 0x200, // Nl Category_Number_Other = Category_Number | 0x400, // No - // Autres + // Others Category_Other = 0x08, // C Category_Other_Control = Category_Other | 0x0100, // Cc Category_Other_Format = Category_Other | 0x0200, // Cf @@ -65,13 +65,13 @@ namespace Nz Category_Punctuation_Open = Category_Punctuation | 0x2000, // Ps Category_Punctuation_Other = Category_Punctuation | 0x4000, // Po - // Espacements + // Spaces Category_Separator = 0x20, // Z Category_Separator_Line = Category_Separator | 0x0100, // Zl Category_Separator_Paragraph = Category_Separator | 0x0200, // Zp Category_Separator_Space = Category_Separator | 0x0400, // Zs - // Symboles + // Symbols Category_Symbol = 0x40, // S Category_Symbol_Currency = Category_Symbol | 0x0100, // Sc Category_Symbol_Math = Category_Symbol | 0x0200, // Sm diff --git a/include/Nazara/Core/Unserializer.hpp b/include/Nazara/Core/Unserializer.hpp deleted file mode 100644 index 359fc1a7d..000000000 --- a/include/Nazara/Core/Unserializer.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2015 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 - -#pragma once - -#ifndef NAZARA_UNSERIALIZER_HPP -#define NAZARA_UNSERIALIZER_HPP - -#include -#include - -namespace Nz -{ - class Stream; - - class Unserializer - { - public: - inline Unserializer(Stream& stream); - Unserializer(const Unserializer&) = default; - Unserializer(Unserializer&&) = default; - ~Unserializer() = default; - - inline Endianness GetDataEndianness() const; - inline Stream& GetStream() const; - - inline void SetDataEndianness(Endianness endiannes); - inline void SetStream(Stream& stream); - - template - Unserializer& operator>>(T& value); - - Unserializer& operator=(const Unserializer&) = default; - Unserializer& operator=(Unserializer&&) = default; - - private: - UnserializationContext m_unserializationContext; - }; -} - -#include - -#endif // NAZARA_UNSERIALIZER_HPP diff --git a/include/Nazara/Core/Unserializer.inl b/include/Nazara/Core/Unserializer.inl deleted file mode 100644 index 3a832504a..000000000 --- a/include/Nazara/Core/Unserializer.inl +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2015 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 - -namespace Nz -{ - inline Unserializer::Unserializer(Stream& stream) - { - m_unserializationContext.currentBitPos = 8; - m_unserializationContext.endianness = Endianness_BigEndian; - m_unserializationContext.stream = &stream; - } - - inline Endianness Unserializer::GetDataEndianness() const - { - return m_unserializationContext.endianness; - } - - inline Stream& Unserializer::GetStream() const - { - return *m_unserializationContext.stream; - } - - inline void Unserializer::SetDataEndianness(Endianness endiannes) - { - m_unserializationContext.endianness = endiannes; - } - - inline void Unserializer::SetStream(Stream& stream) - { - m_unserializationContext.stream = &stream; - } - - template - Unserializer& Unserializer::operator>>(T& value) - { - if (!Unserialize(m_unserializationContext, &value)) - NazaraError("Failed to serialize value"); - - return *this; - } -} - -#include diff --git a/include/Nazara/Graphics/AbstractRenderTechnique.hpp b/include/Nazara/Graphics/AbstractRenderTechnique.hpp index 5f268275e..d13ac88ee 100644 --- a/include/Nazara/Graphics/AbstractRenderTechnique.hpp +++ b/include/Nazara/Graphics/AbstractRenderTechnique.hpp @@ -28,6 +28,7 @@ namespace Nz AbstractRenderTechnique(AbstractRenderTechnique&&) = default; virtual ~AbstractRenderTechnique(); + virtual void Clear(const SceneData& sceneData) const = 0; virtual bool Draw(const SceneData& sceneData) const = 0; virtual void EnableInstancing(bool instancing); diff --git a/include/Nazara/Graphics/DeferredRenderQueue.hpp b/include/Nazara/Graphics/DeferredRenderQueue.hpp index 76bff6926..fecd507be 100644 --- a/include/Nazara/Graphics/DeferredRenderQueue.hpp +++ b/include/Nazara/Graphics/DeferredRenderQueue.hpp @@ -42,7 +42,7 @@ namespace Nz void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) override; - void Clear(bool fully = false); + void Clear(bool fully = false) override; struct MeshDataComparator { diff --git a/include/Nazara/Graphics/DeferredRenderTechnique.hpp b/include/Nazara/Graphics/DeferredRenderTechnique.hpp index 5d371b94f..99d1fc813 100644 --- a/include/Nazara/Graphics/DeferredRenderTechnique.hpp +++ b/include/Nazara/Graphics/DeferredRenderTechnique.hpp @@ -32,6 +32,7 @@ namespace Nz DeferredRenderTechnique(); ~DeferredRenderTechnique(); + void Clear(const SceneData& sceneData) const override; bool Draw(const SceneData& sceneData) const override; void EnablePass(RenderPassType renderPass, int position, bool enable); diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index 3a5d3ef33..9a17f5d2f 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -44,7 +44,7 @@ namespace Nz void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) override; - void Clear(bool fully = false); + void Clear(bool fully = false) override; void Sort(const AbstractViewer* viewer); diff --git a/include/Nazara/Graphics/ForwardRenderTechnique.hpp b/include/Nazara/Graphics/ForwardRenderTechnique.hpp index 3b9dd75a2..447818958 100644 --- a/include/Nazara/Graphics/ForwardRenderTechnique.hpp +++ b/include/Nazara/Graphics/ForwardRenderTechnique.hpp @@ -24,6 +24,7 @@ namespace Nz ForwardRenderTechnique(); ~ForwardRenderTechnique() = default; + void Clear(const SceneData& sceneData) const override; bool Draw(const SceneData& sceneData) const override; unsigned int GetMaxLightPassPerObject() const; diff --git a/include/Nazara/Graphics/GuillotineTextureAtlas.hpp b/include/Nazara/Graphics/GuillotineTextureAtlas.hpp index b40e4f610..42449712c 100644 --- a/include/Nazara/Graphics/GuillotineTextureAtlas.hpp +++ b/include/Nazara/Graphics/GuillotineTextureAtlas.hpp @@ -19,7 +19,7 @@ namespace Nz GuillotineTextureAtlas() = default; ~GuillotineTextureAtlas() = default; - UInt32 GetStorage() const; + UInt32 GetStorage() const override; private: AbstractImage* ResizeImage(AbstractImage* oldImage, const Vector2ui& size) const override; diff --git a/include/Nazara/Graphics/Light.inl b/include/Nazara/Graphics/Light.inl index f0cfc4c39..266f7939a 100644 --- a/include/Nazara/Graphics/Light.inl +++ b/include/Nazara/Graphics/Light.inl @@ -68,6 +68,16 @@ namespace Nz return m_innerAngle; } + inline float Light::GetInnerAngleCosine() const + { + return m_innerAngleCosine; + } + + inline float Light::GetInvRadius() const + { + return m_invRadius; + } + inline LightType Light::GetLightType() const { return m_type; diff --git a/include/Nazara/Graphics/Material.hpp b/include/Nazara/Graphics/Material.hpp index 2377de3ab..4adf19a33 100644 --- a/include/Nazara/Graphics/Material.hpp +++ b/include/Nazara/Graphics/Material.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,7 @@ namespace Nz { - struct NAZARA_GRAPHICS_API MaterialParams + struct NAZARA_GRAPHICS_API MaterialParams : ResourceParameters { bool loadAlphaMap = true; bool loadDiffuseMap = true; diff --git a/include/Nazara/Graphics/Model.hpp b/include/Nazara/Graphics/Model.hpp index 9f7506c12..b4564d1b8 100644 --- a/include/Nazara/Graphics/Model.hpp +++ b/include/Nazara/Graphics/Model.hpp @@ -10,13 +10,14 @@ #include #include #include +#include #include #include #include namespace Nz { - struct NAZARA_GRAPHICS_API ModelParameters + struct NAZARA_GRAPHICS_API ModelParameters : ResourceParameters { ModelParameters(); diff --git a/include/Nazara/Graphics/ParticleSystem.hpp b/include/Nazara/Graphics/ParticleSystem.hpp index 34dcbb137..76b834f6e 100644 --- a/include/Nazara/Graphics/ParticleSystem.hpp +++ b/include/Nazara/Graphics/ParticleSystem.hpp @@ -33,7 +33,7 @@ namespace Nz void AddController(ParticleControllerRef controller); void AddEmitter(ParticleEmitter* emitter); void AddGenerator(ParticleGeneratorRef generator); - void AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const; + void AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const override; void ApplyControllers(ParticleMapper& mapper, unsigned int particleCount, float elapsedTime); diff --git a/include/Nazara/Graphics/SkeletalModel.hpp b/include/Nazara/Graphics/SkeletalModel.hpp index 059d6b835..99791168a 100644 --- a/include/Nazara/Graphics/SkeletalModel.hpp +++ b/include/Nazara/Graphics/SkeletalModel.hpp @@ -18,7 +18,7 @@ namespace Nz { - struct NAZARA_GRAPHICS_API SkeletalModelParameters : public ModelParameters + struct NAZARA_GRAPHICS_API SkeletalModelParameters : ModelParameters { bool loadAnimation = true; AnimationParams animation; @@ -54,7 +54,7 @@ namespace Nz bool HasAnimation() const; - bool IsAnimated() const; + bool IsAnimated() const override; bool IsAnimationEnabled() const; bool LoadFromFile(const String& filePath, const SkeletalModelParameters& params = SkeletalModelParameters()); diff --git a/include/Nazara/Graphics/SkyboxBackground.hpp b/include/Nazara/Graphics/SkyboxBackground.hpp index b861dbbb8..95ce2872a 100644 --- a/include/Nazara/Graphics/SkyboxBackground.hpp +++ b/include/Nazara/Graphics/SkyboxBackground.hpp @@ -30,13 +30,17 @@ namespace Nz SkyboxBackground(TextureRef cubemapTexture = TextureRef()); ~SkyboxBackground() = default; - void Draw(const AbstractViewer* viewer) const; + void Draw(const AbstractViewer* viewer) const override; - BackgroundType GetBackgroundType() const; + BackgroundType GetBackgroundType() const override; + inline const Vector3f& GetMovementOffset() const; + inline float GetMovementScale() const; inline const TextureRef& GetTexture() const; inline TextureSampler& GetTextureSampler(); inline const TextureSampler& GetTextureSampler() const; + inline void SetMovementOffset(const Vector3f& offset); + inline void SetMovementScale(float scale); inline void SetTexture(TextureRef cubemapTexture); inline void SetTextureSampler(const TextureSampler& sampler); @@ -48,6 +52,8 @@ namespace Nz TextureRef m_texture; TextureSampler m_sampler; + Vector3f m_movementOffset; + float m_movementScale; }; } diff --git a/include/Nazara/Graphics/SkyboxBackground.inl b/include/Nazara/Graphics/SkyboxBackground.inl index 51b3bf123..8edb694c9 100644 --- a/include/Nazara/Graphics/SkyboxBackground.inl +++ b/include/Nazara/Graphics/SkyboxBackground.inl @@ -7,6 +7,16 @@ namespace Nz { + inline const Vector3f& Nz::SkyboxBackground::GetMovementOffset() const + { + return m_movementOffset; + } + + inline float SkyboxBackground::GetMovementScale() const + { + return m_movementScale; + } + inline const TextureRef& SkyboxBackground::GetTexture() const { return m_texture; @@ -22,6 +32,20 @@ namespace Nz return m_sampler; } + 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"); + + m_movementOffset = offset; + } + + inline void SkyboxBackground::SetMovementScale(float scale) + { + NazaraAssert(std::isfinite(scale), "Scale must be a finite value"); + + m_movementScale = scale; + } + inline void SkyboxBackground::SetTexture(TextureRef cubemapTexture) { NazaraAssert(!cubemapTexture || cubemapTexture->IsValid(), "Invalid texture"); diff --git a/include/Nazara/Graphics/Sprite.hpp b/include/Nazara/Graphics/Sprite.hpp index e61f19840..3660bf872 100644 --- a/include/Nazara/Graphics/Sprite.hpp +++ b/include/Nazara/Graphics/Sprite.hpp @@ -23,6 +23,9 @@ namespace Nz class NAZARA_GRAPHICS_API Sprite : public InstancedRenderable { + friend SpriteLibrary; + friend class Graphics; + public: inline Sprite(); inline Sprite(MaterialRef material); @@ -57,6 +60,9 @@ namespace Nz void MakeBoundingVolume() const override; void UpdateData(InstanceData* instanceData) const override; + static bool Initialize(); + static void Uninitialize(); + Color m_color; MaterialRef m_material; Rectf m_textureCoords; diff --git a/include/Nazara/Graphics/TextSprite.hpp b/include/Nazara/Graphics/TextSprite.hpp index 2a664a5c3..db99077e2 100644 --- a/include/Nazara/Graphics/TextSprite.hpp +++ b/include/Nazara/Graphics/TextSprite.hpp @@ -28,6 +28,7 @@ namespace Nz { public: inline TextSprite(); + inline TextSprite(const AbstractTextDrawer& drawer); inline TextSprite(const TextSprite& sprite); ~TextSprite() = default; @@ -65,6 +66,7 @@ namespace Nz struct AtlasSlots { + bool used; NazaraSlot(AbstractAtlas, OnAtlasCleared, clearSlot); NazaraSlot(AbstractAtlas, OnAtlasLayerChange, layerChangeSlot); NazaraSlot(AbstractAtlas, OnAtlasRelease, releaseSlot); diff --git a/include/Nazara/Graphics/TextSprite.inl b/include/Nazara/Graphics/TextSprite.inl index cc230873d..312658557 100644 --- a/include/Nazara/Graphics/TextSprite.inl +++ b/include/Nazara/Graphics/TextSprite.inl @@ -14,6 +14,12 @@ namespace Nz SetDefaultMaterial(); } + inline TextSprite::TextSprite(const AbstractTextDrawer& drawer) : + TextSprite() + { + Update(drawer); + } + inline TextSprite::TextSprite(const TextSprite& sprite) : InstancedRenderable(sprite), m_renderInfos(sprite.m_renderInfos), diff --git a/include/Nazara/Lua/LuaClass.hpp b/include/Nazara/Lua/LuaClass.hpp index 96e271a31..8f9990bef 100644 --- a/include/Nazara/Lua/LuaClass.hpp +++ b/include/Nazara/Lua/LuaClass.hpp @@ -8,31 +8,45 @@ #define NAZARA_LUACLASS_HPP #include +#include #include #include #include #include #include -//#include +#include +#include namespace Nz { - template + template class LuaClass { - //static_assert(std::is_same::value || std::is_base_of::value, "P must be a base of T"); + template + friend class LuaClass; public: using ClassFunc = std::function; using ClassIndexFunc = std::function; using ConstructorFunc = std::function; + template using ConvertToParent = std::function; using FinalizerFunc = std::function; using StaticIndexFunc = std::function; using StaticFunc = std::function; LuaClass(const String& name); - //void Inherit(LuaClass

& parent); + void BindMethod(const String& name, ClassFunc method); + template std::enable_if_t::value> BindMethod(const String& name, R(P::*func)(Args...), DefArgs&&... defArgs); + template std::enable_if_t::value> BindMethod(const String& name, R(P::*func)(Args...) const, DefArgs&&... defArgs); + template std::enable_if_t::type>::value> BindMethod(const String& name, R(P::*func)(Args...), DefArgs&&... defArgs); + template std::enable_if_t::type>::value> BindMethod(const String& name, R(P::*func)(Args...) const, DefArgs&&... defArgs); + + void BindStaticMethod(const String& name, StaticFunc func); + template void BindStaticMethod(const String& name, R(*func)(Args...), DefArgs&&... defArgs); + + template void Inherit(LuaClass

& parent); + template void Inherit(LuaClass

& parent, ConvertToParent

convertFunc); void Register(LuaInstance& lua); @@ -41,39 +55,41 @@ namespace Nz void SetConstructor(ConstructorFunc constructor); void SetFinalizer(FinalizerFunc finalizer); void SetGetter(ClassIndexFunc getter); - void SetMethod(const String& name, ClassFunc method); - template std::enable_if_t::value> SetMethod(const String& name, R(P::*func)(Args...)); - template std::enable_if_t::value> SetMethod(const String& name, R(P::*func)(Args...) const); void SetSetter(ClassIndexFunc setter); void SetStaticGetter(StaticIndexFunc getter); - void SetStaticMethod(const String& name, StaticFunc func); - template void SetStaticMethod(const String& name, R(*func)(Args...)); void SetStaticSetter(StaticIndexFunc getter); private: + using ParentFunc = std::function; + using InstanceGetter = std::function; + + struct ClassInfo + { + std::vector methods; + std::vector parentGetters; + std::vector staticMethods; + std::unordered_map instanceGetters; + ClassIndexFunc getter; + ClassIndexFunc setter; + ConstructorFunc constructor; + FinalizerFunc finalizer; + StaticIndexFunc staticGetter; + StaticIndexFunc staticSetter; + String name; + int globalTableRef = -1; + }; + static int ConstructorProxy(lua_State* state); static int FinalizerProxy(lua_State* state); static int InfoDestructor(lua_State* state); + static void Get(const std::shared_ptr& info, LuaInstance& lua, T* instance); static int GetterProxy(lua_State* state); static int MethodProxy(lua_State* state); static int SetterProxy(lua_State* state); static int StaticGetterProxy(lua_State* state); static int StaticMethodProxy(lua_State* state); static int StaticSetterProxy(lua_State* state); - - struct ClassInfo - { - std::vector methods; - std::vector staticMethods; - ClassIndexFunc getter = nullptr; - ClassIndexFunc setter = nullptr; - ConstructorFunc constructor = nullptr; - FinalizerFunc finalizer = nullptr; - StaticIndexFunc staticGetter = nullptr; - StaticIndexFunc staticSetter = nullptr; - String name; - int globalTableRef = -1; - }; + static int ToStringProxy(lua_State* state); std::map m_methods; std::map m_staticMethods; diff --git a/include/Nazara/Lua/LuaClass.inl b/include/Nazara/Lua/LuaClass.inl index 545fa08ba..913382f99 100644 --- a/include/Nazara/Lua/LuaClass.inl +++ b/include/Nazara/Lua/LuaClass.inl @@ -4,6 +4,7 @@ #include #include +#include #include namespace Nz @@ -14,15 +15,36 @@ namespace Nz { m_info->name = name; } - /* - template - void LuaClass::Inherit(LuaClass

& parent) - { - static_assert(std::is_base_of::value, "P must be a base of T"); - m_info->parentInfo = parent.m_info; + template + template + inline void LuaClass::Inherit(LuaClass

& parent) + { + Inherit

(parent, [] (T* instance) -> P* + { + return static_cast(instance); + }); } - */ + + template + template + inline void LuaClass::Inherit(LuaClass

& parent, ConvertToParent

convertFunc) + { + static_assert(!std::is_same::value || std::is_base_of::value, "P must be a base of T"); + + std::shared_ptr::ClassInfo>& parentInfo = parent.m_info; + + parentInfo->instanceGetters[m_info->name] = [info = m_info, convertFunc] (LuaInstance& lua) -> P* + { + return convertFunc(*static_cast(lua.CheckUserdata(1, info->name))); + }; + + m_info->parentGetters.emplace_back([parentInfo, convertFunc] (LuaInstance& lua, T* instance) + { + LuaClass

::Get(parentInfo, lua, convertFunc(instance)); + }); + } + template void LuaClass::Register(LuaInstance& lua) { @@ -48,27 +70,41 @@ namespace Nz if (!lua.NewMetatable(m_info->name)) NazaraWarning("Class \"" + m_info->name + "\" already registred in this instance"); { - lua.PushValue(1); // On associe l'UserData avec la fonction - lua.PushCFunction(FinalizerProxy, 1); - lua.SetField("__gc"); // Finalizer + // Set the type in a __type field + lua.PushString(m_info->name); + lua.SetField("__type"); - if (m_info->getter) + // In case a __tostring method is missing, add a default implementation returning the type + if (m_methods.find("__tostring") == m_methods.end()) { - lua.PushValue(1); - lua.PushValue(-2); + // Define the Finalizer + lua.PushValue(1); // shared_ptr on UserData + lua.PushCFunction(ToStringProxy, 1); + lua.SetField("__tostring"); + } + + // Define the Finalizer + lua.PushValue(1); + lua.PushCFunction(FinalizerProxy, 1); + lua.SetField("__gc"); + + if (m_info->getter || !m_info->parentGetters.empty()) + { + lua.PushValue(1); // shared_ptr on UserData + lua.PushValue(-2); // Metatable lua.PushCFunction(GetterProxy, 2); } else // Optimisation, plutôt que de rediriger vers une fonction C qui ne fera rien d'autre que rechercher // dans la table, nous envoyons directement la table, de sorte que Lua fasse directement la recherche // Ceci n'est possible que si nous n'avons ni getter, ni parent - lua.PushValue(-1); + lua.PushValue(-1); // Metatable lua.SetField("__index"); // Getter if (m_info->setter) { - lua.PushValue(1); + lua.PushValue(1); // shared_ptr on UserData lua.PushCFunction(SetterProxy, 1); lua.SetField("__newindex"); // Setter } @@ -76,14 +112,20 @@ namespace Nz m_info->methods.reserve(m_methods.size()); for (auto& pair : m_methods) { + std::size_t methodIndex = m_info->methods.size(); m_info->methods.push_back(pair.second); - lua.PushValue(1); - lua.PushInteger(m_info->methods.size() - 1); + lua.PushValue(1); // shared_ptr on UserData + lua.PushInteger(methodIndex); lua.PushCFunction(MethodProxy, 2); - lua.SetField(pair.first); // Méthode + lua.SetField(pair.first); // Method name } + + m_info->instanceGetters[m_info->name] = [info = m_info] (LuaInstance& lua) + { + return *static_cast(lua.CheckUserdata(1, info->name)); + }; } lua.Pop(); // On pop la metatable @@ -104,21 +146,21 @@ namespace Nz if (m_info->staticGetter) { - lua.PushValue(1); - lua.PushValue(-2); + lua.PushValue(1); // shared_ptr on UserData + lua.PushValue(-2); // ClassMeta lua.PushCFunction(StaticGetterProxy, 2); } else // Optimisation, plutôt que de rediriger vers une fonction C qui ne fera rien d'autre que rechercher // dans la table, nous envoyons directement la table, de sorte que Lua fasse directement la recherche // Ceci n'est possible que si nous n'avons ni getter, ni parent - lua.PushValue(-1); + lua.PushValue(-1); // ClassMeta lua.SetField("__index"); // ClassMeta.__index = StaticGetterProxy/ClassMeta if (m_info->staticSetter) { - lua.PushValue(1); + lua.PushValue(1); // shared_ptr on UserData lua.PushCFunction(StaticSetterProxy, 1); lua.SetField("__newindex"); // ClassMeta.__newindex = StaticSetterProxy } @@ -126,10 +168,11 @@ namespace Nz m_info->staticMethods.reserve(m_staticMethods.size()); for (auto& pair : m_staticMethods) { + std::size_t methodIndex = m_info->staticMethods.size(); m_info->staticMethods.push_back(pair.second); - lua.PushValue(1); - lua.PushInteger(m_info->staticMethods.size() - 1); + lua.PushValue(1); // shared_ptr on UserData + lua.PushInteger(methodIndex); lua.PushCFunction(StaticMethodProxy, 2); lua.SetField(pair.first); // ClassMeta.method = StaticMethodProxy @@ -170,34 +213,64 @@ namespace Nz } template - void LuaClass::SetMethod(const String& name, ClassFunc method) + void LuaClass::BindMethod(const String& name, ClassFunc method) { m_methods[name] = method; } - - template - template - std::enable_if_t::value> LuaClass::SetMethod(const String& name, R(P::*func)(Args...)) - { - SetMethod(name, [func] (LuaInstance& instance, T& object) -> int - { - LuaImplMethodProxy handler(instance, object); - handler.ProcessArgs(); - return handler.Invoke(func); + template + template + std::enable_if_t::value> LuaClass::BindMethod(const String& name, R(P::*func)(Args...), DefArgs&&... defArgs) + { + typename LuaImplMethodProxy::template Impl handler(std::forward(defArgs)...); + + BindMethod(name, [func, handler] (LuaInstance& lua, T& object) -> int + { + handler.ProcessArgs(lua); + + return handler.Invoke(lua, object, func); }); } template - template - std::enable_if_t::value> LuaClass::SetMethod(const String& name, R(P::*func)(Args...) const) + template + std::enable_if_t::value> LuaClass::BindMethod(const String& name, R(P::*func)(Args...) const, DefArgs&&... defArgs) { - SetMethod(name, [func] (LuaInstance& instance, T& object) -> int - { - LuaImplMethodProxy handler(instance, object); - handler.ProcessArgs(); + typename LuaImplMethodProxy::template Impl handler(std::forward(defArgs)...); - return handler.Invoke(func); + BindMethod(name, [func, handler] (LuaInstance& lua, T& object) -> int + { + handler.ProcessArgs(lua); + + return handler.Invoke(lua, object, func); + }); + } + + template + template + std::enable_if_t::type>::value> LuaClass::BindMethod(const String& name, R(P::*func)(Args...), DefArgs&&... defArgs) + { + typename LuaImplMethodProxy::template Impl handler(std::forward(defArgs)...); + + BindMethod(name, [func, handler] (LuaInstance& lua, T& object) -> int + { + handler.ProcessArgs(lua); + + return handler.Invoke(lua, object, func); + }); + } + + template + template + std::enable_if_t::type>::value> LuaClass::BindMethod(const String& name, R(P::*func)(Args...) const, DefArgs&&... defArgs) + { + typename LuaImplMethodProxy::template Impl handler(std::forward(defArgs)...); + + BindMethod(name, [func, handler] (LuaInstance& lua, T& object) -> int + { + handler.ProcessArgs(lua); + + return handler.Invoke(lua, object, func); }); } @@ -214,21 +287,22 @@ namespace Nz } template - void LuaClass::SetStaticMethod(const String& name, StaticFunc method) + void LuaClass::BindStaticMethod(const String& name, StaticFunc method) { m_staticMethods[name] = method; } template - template - void LuaClass::SetStaticMethod(const String& name, R(*func)(Args...)) + template + void LuaClass::BindStaticMethod(const String& name, R(*func)(Args...), DefArgs&&... defArgs) { - SetStaticMethod(name, [func] (LuaInstance& instance) -> int - { - LuaImplFunctionProxy handler(instance); - handler.ProcessArgs(); + typename LuaImplFunctionProxy::template Impl handler(std::forward(defArgs)...); - return handler.Invoke(func); + BindStaticMethod(name, [func, handler] (LuaInstance& lua) -> int + { + handler.ProcessArgs(lua); + + return handler.Invoke(lua, func); }); } @@ -243,8 +317,8 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - ConstructorFunc constructor = info->constructor; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const ConstructorFunc& constructor = info->constructor; lua.Remove(1); // On enlève l'argument "table" du stack @@ -255,10 +329,7 @@ namespace Nz return 0; // Normalement jamais exécuté (l'erreur provoquant une exception) } - T** ud = static_cast(lua.PushUserdata(sizeof(T*))); - *ud = instance; - lua.SetMetatable(info->name); - + lua.PushInstance(info->name.GetConstBuffer(), instance); return 1; } @@ -267,10 +338,11 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - FinalizerFunc finalizer = info->finalizer; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const FinalizerFunc& finalizer = info->finalizer; T* instance = *static_cast(lua.CheckUserdata(1, info->name)); + lua.Remove(1); //< Remove the instance from the Lua stack if (!finalizer || finalizer(lua, *instance)) delete instance; @@ -283,33 +355,54 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - std::shared_ptr& infoPtr = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - lua.DestroyReference(infoPtr->globalTableRef); + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + lua.DestroyReference(info->globalTableRef); using namespace std; // Obligatoire pour le destructeur - infoPtr.~shared_ptr(); // Si vous voyez une autre façon de faire, je suis preneur + info.~shared_ptr(); // Si vous voyez une autre façon de faire, je suis preneur return 0; } + template + void LuaClass::Get(const std::shared_ptr& info, LuaInstance& lua, T* instance) + { + const ClassIndexFunc& getter = info->getter; + + if (!getter || !getter(lua, *instance)) + { + // Query from the metatable + lua.GetMetatable(info->name); //< Metatable + lua.PushValue(1); //< Field + lua.GetTable(); // Metatable[Field] + + lua.Remove(-2); // Remove Metatable + + if (!lua.IsValid(-1)) + { + for (const ParentFunc& getter : info->parentGetters) + { + lua.Pop(); //< Pop the last nil value + + getter(lua, instance); + if (lua.IsValid(-1)) + return; + } + } + } + } + template int LuaClass::GetterProxy(lua_State* state) { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - ClassIndexFunc getter = info->getter; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); - - if (!getter(lua, instance)) - { - // On accède alors à la table - lua.PushValue(lua.GetIndexOfUpValue(2)); - lua.PushValue(-2); - lua.GetTable(); - } + T* instance = *static_cast(lua.CheckUserdata(1, info->name)); + lua.Remove(1); //< Remove the instance from the Lua stack + Get(info, lua, instance); return 1; } @@ -318,15 +411,33 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + + T* instance = nullptr; + if (lua.GetMetatable(1)) + { + LuaType type = lua.GetField("__type"); + if (type == LuaType_String) + { + String name = lua.ToString(-1); + auto it = info->instanceGetters.find(name); + if (it != info->instanceGetters.end()) + instance = it->second(lua); + } + lua.Pop(2); + + lua.Remove(1); //< Remove the instance from the Lua stack + } + + if (!instance) + { + lua.Error("Method cannot be called without an object"); + return 0; + } + unsigned int index = static_cast(lua.ToInteger(lua.GetIndexOfUpValue(2))); - ClassFunc method = info->methods[index]; - - T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); - - lua.Remove(1); // On enlève l'argument "userdata" du stack - - return method(lua, instance); + const ClassFunc& method = info->methods[index]; + return method(lua, *instance); } template @@ -334,17 +445,18 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - ClassIndexFunc setter = info->setter; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const ClassIndexFunc& setter = info->setter; T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); + lua.Remove(1); //< Remove the instance from the Lua stack if (!setter(lua, instance)) { std::size_t length; const char* str = lua.ToString(2, &length); - lua.Error("Class \"" + info->name + "\" has no field \"" + String(str, length) + ')'); + lua.Error("Class \"" + info->name + "\" has no field \"" + String(str, length) + "\")"); } return 1; @@ -355,8 +467,8 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - StaticIndexFunc getter = info->staticGetter; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const StaticIndexFunc& getter = info->staticGetter; if (!getter(lua)) { @@ -374,9 +486,9 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); unsigned int index = static_cast(lua.ToInteger(lua.GetIndexOfUpValue(2))); - StaticFunc method = info->staticMethods[index]; + const StaticFunc& method = info->staticMethods[index]; return method(lua); } @@ -386,8 +498,8 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - StaticIndexFunc setter = info->staticSetter; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const StaticIndexFunc& setter = info->staticSetter; if (!setter(lua)) { @@ -399,6 +511,17 @@ namespace Nz return 1; } + + template + int LuaClass::ToStringProxy(lua_State* state) + { + LuaInstance& lua = *LuaInstance::GetInstance(state); + + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + + lua.PushString(info->name); + return 1; + } } #include diff --git a/include/Nazara/Lua/LuaInstance.hpp b/include/Nazara/Lua/LuaInstance.hpp index 3a725472a..9dd5c3571 100644 --- a/include/Nazara/Lua/LuaInstance.hpp +++ b/include/Nazara/Lua/LuaInstance.hpp @@ -42,12 +42,23 @@ namespace Nz bool Call(unsigned int argCount); bool Call(unsigned int argCount, unsigned int resultCount); - template T Check(int index); + template T Check(int* index) const; + template T Check(int* index, T defValue) const; void CheckAny(int index) const; bool CheckBoolean(int index) const; bool CheckBoolean(int index, bool defValue) const; + template T CheckBoundInteger(int index) const; + template T CheckBoundInteger(int index, T defValue) const; + template T CheckField(const char* fieldName, int tableIndex = -1) const; + template T CheckField(const String& fieldName, int tableIndex = -1) const; + template T CheckField(const char* fieldName, T defValue, int tableIndex = -1) const; + template T CheckField(const String& fieldName, T defValue, int tableIndex = -1) const; long long CheckInteger(int index) const; long long CheckInteger(int index, long long defValue) const; + template T CheckGlobal(const char* fieldName) const; + template T CheckGlobal(const String& fieldName) const; + template T CheckGlobal(const char* fieldName, T defValue) const; + template T CheckGlobal(const String& fieldName, T defValue) const; double CheckNumber(int index) const; double CheckNumber(int index, double defValue) const; void CheckStack(int space, const char* error = nullptr) const; @@ -59,17 +70,17 @@ namespace Nz void* CheckUserdata(int index, const String& tname) const; bool Compare(int index1, int index2, LuaComparison comparison) const; - void Compute(LuaOperation operation); + void Compute(LuaOperation operation) const; - void Concatenate(int count); + void Concatenate(int count) const; int CreateReference(); void DestroyReference(int ref); String DumpStack() const; - void Error(const char* message); - void Error(const String& message); + void Error(const char* message) const; + void Error(const String& message) const; bool Execute(const String& code); bool ExecuteFromFile(const String& filePath); @@ -77,8 +88,8 @@ namespace Nz bool ExecuteFromStream(Stream& stream); int GetAbsIndex(int index) const; - LuaType GetField(const char* fieldName, int index = -1) const; - LuaType GetField(const String& fieldName, int index = -1) const; + LuaType GetField(const char* fieldName, int tableIndex = -1) const; + LuaType GetField(const String& fieldName, int tableIndex = -1) const; LuaType GetGlobal(const char* name) const; LuaType GetGlobal(const String& name) const; lua_State* GetInternalState() const; @@ -94,7 +105,7 @@ namespace Nz LuaType GetType(int index) const; const char* GetTypeName(LuaType type) const; - void Insert(int index); + void Insert(int index) const; bool IsOfType(int index, LuaType type) const; bool IsOfType(int index, const char* tname) const; @@ -103,45 +114,51 @@ namespace Nz long long Length(int index) const; - void MoveTo(LuaInstance* instance, int n); + void MoveTo(LuaInstance* instance, int n) const; bool NewMetatable(const char* str); bool NewMetatable(const String& str); - bool Next(int index = -2); + bool Next(int index = -2) const; - void Pop(unsigned int n = 1U); + void Pop(unsigned int n = 1U) const; - template int Push(T arg); - void PushBoolean(bool value); - void PushCFunction(LuaCFunction func, unsigned int upvalueCount = 0); - void PushFunction(LuaFunction func); - template void PushFunction(R(*func)(Args...)); - void PushInteger(long long value); - void PushLightUserdata(void* value); - void PushMetatable(const char* str); - void PushMetatable(const String& str); - void PushNil(); - void PushNumber(double value); - void PushReference(int ref); - void PushString(const char* str); - void PushString(const char* str, unsigned int size); - void PushString(const String& str); - void PushTable(unsigned int sequenceElementCount = 0, unsigned int arrayElementCount = 0); - void* PushUserdata(unsigned int size); - void PushValue(int index); + template int Push(T arg) const; + void PushBoolean(bool value) const; + void PushCFunction(LuaCFunction func, unsigned int upvalueCount = 0) const; + void PushFunction(LuaFunction func) const; + template void PushFunction(R(*func)(Args...), DefArgs&&... defArgs) const; + template void PushInstance(const char* tname, T* instance) const; + template void PushInstance(const char* tname, Args&&... args) const; + void PushInteger(long long value) const; + void PushLightUserdata(void* value) const; + void PushMetatable(const char* str) const; + void PushMetatable(const String& str) const; + void PushNil() const; + 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 String& str) const; + void PushTable(unsigned int sequenceElementCount = 0, unsigned int arrayElementCount = 0) const; + void* PushUserdata(unsigned int size) const; + void PushValue(int index) const; - void Remove(int index); - void Replace(int index); + void Remove(int index) const; + void Replace(int index) const; + template void SetField(const char* name, T&& arg, int tableIndex = -2); + template void SetField(const String& name, T&& arg, int tableIndex = -2); void SetField(const char* name, int tableIndex = -2); void SetField(const String& name, int tableIndex = -2); + template void SetGlobal(const char* name, T&& arg); + template void SetGlobal(const String& name, T&& arg); void SetGlobal(const char* name); void SetGlobal(const String& name); - void SetMetatable(const char* tname); - void SetMetatable(const String& tname); - void SetMetatable(int index); + void SetMetatable(const char* tname) const; + void SetMetatable(const String& tname) const; + void SetMetatable(int index) const; void SetMemoryLimit(UInt32 memoryLimit); - void SetTable(int index = -3); + void SetTable(int index = -3) const; void SetTimeLimit(UInt32 timeLimit); bool ToBoolean(int index) const; @@ -160,6 +177,7 @@ namespace Nz static LuaInstance* GetInstance(lua_State* state); private: + template T CheckBounds(int index, long long value) const; bool Run(int argCount, int resultCount); static void* MemoryAllocator(void *ud, void *ptr, std::size_t osize, std::size_t nsize); diff --git a/include/Nazara/Lua/LuaInstance.inl b/include/Nazara/Lua/LuaInstance.inl index c2153a4e1..a445df491 100644 --- a/include/Nazara/Lua/LuaInstance.inl +++ b/include/Nazara/Lua/LuaInstance.inl @@ -2,123 +2,187 @@ // This file is part of the "Nazara Engine - Lua scripting module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include +#include +#include #include +#include +#include namespace Nz { // Functions args - inline bool LuaImplQueryArg(LuaInstance& instance, unsigned int index, TypeTag) + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, bool* arg, TypeTag) { - return instance.CheckBoolean(index); + *arg = instance.CheckBoolean(index); + return 1; } - inline double LuaImplQueryArg(LuaInstance& instance, unsigned int index, TypeTag) + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, bool* arg, bool defValue, TypeTag) { - return instance.CheckNumber(index); + *arg = instance.CheckBoolean(index, defValue); + return 1; } - inline float LuaImplQueryArg(LuaInstance& instance, unsigned int index, TypeTag) - { - return static_cast(instance.CheckNumber(index)); - } - - template - std::enable_if_t::value, T> LuaImplQueryArg(LuaInstance& instance, unsigned int index, TypeTag) - { - return static_cast(LuaImplQueryArg(instance, index, TypeTag::type>())); - } - - template - std::enable_if_t::value && !std::is_unsigned::value, T> LuaImplQueryArg(LuaInstance& instance, unsigned int index, TypeTag) - { - return static_cast(instance.CheckInteger(index)); - } - - template - std::enable_if_t::value, T> LuaImplQueryArg(LuaInstance& instance, unsigned int index, TypeTag) - { - return static_cast(LuaImplQueryArg(instance, index, TypeTag::type>())); - } - - inline std::string LuaImplQueryArg(LuaInstance& instance, unsigned int index, TypeTag) + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, std::string* arg, TypeTag) { std::size_t strLength = 0; const char* str = instance.CheckString(index, &strLength); - return std::string(str, strLength); + arg->assign(str, strLength); + + return 1; } - inline String LuaImplQueryArg(LuaInstance& instance, unsigned int index, TypeTag) + inline unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, String* arg, TypeTag) { std::size_t strLength = 0; const char* str = instance.CheckString(index, &strLength); - return String(str, strLength); + arg->Set(str, strLength); + + return 1; } template - T LuaImplQueryArg(LuaInstance& instance, unsigned int index, TypeTag) + std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, TypeTag) { - return LuaImplQueryArg(instance, index, TypeTag()); + using UnderlyingT = std::underlying_type_t; + return LuaImplQueryArg(instance, index, reinterpret_cast(arg), TypeTag()); + } + + template + std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, T defValue, TypeTag) + { + using UnderlyingT = std::underlying_type_t; + return LuaImplQueryArg(instance, index, reinterpret_cast(arg), static_cast(defValue), TypeTag()); + } + + template + std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, TypeTag) + { + *arg = static_cast(instance.CheckNumber(index)); + return 1; + } + + template + std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, T defValue, TypeTag) + { + *arg = static_cast(instance.CheckNumber(index, static_cast(defValue))); + return 1; + } + + template + std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, TypeTag) + { + *arg = instance.CheckBoundInteger(index); + return 1; + } + + template + std::enable_if_t::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, T defValue, TypeTag) + { + *arg = instance.CheckBoundInteger(index, defValue); + return 1; + } + + template + std::enable_if_t::value && !std::is_enum::value && !std::is_floating_point::value, unsigned int> LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, const T& defValue, TypeTag tag) + { + if (instance.IsValid(index)) + return LuaImplQueryArg(instance, index, arg, tag); + else + { + *arg = defValue; + return 1; + } + } + + template + unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, TypeTag) + { + return LuaImplQueryArg(instance, index, arg, TypeTag()); + } + + template + unsigned int LuaImplQueryArg(const LuaInstance& instance, int index, T* arg, const T& defValue, TypeTag) + { + return LuaImplQueryArg(instance, index, arg, defValue, TypeTag()); } // Function returns - inline int LuaImplReplyVal(LuaInstance& instance, bool val, TypeTag) + inline int LuaImplReplyVal(const LuaInstance& instance, bool val, TypeTag) { instance.PushBoolean(val); return 1; } - inline int LuaImplReplyVal(LuaInstance& instance, double val, TypeTag) + inline int LuaImplReplyVal(const LuaInstance& instance, double val, TypeTag) { instance.PushNumber(val); return 1; } - inline int LuaImplReplyVal(LuaInstance& instance, float val, TypeTag) + inline int LuaImplReplyVal(const LuaInstance& instance, float val, TypeTag) { instance.PushNumber(val); return 1; } template - std::enable_if_t::value, int> LuaImplReplyVal(LuaInstance& instance, T val, TypeTag) + std::enable_if_t::value, int> LuaImplReplyVal(const LuaInstance& instance, T val, TypeTag) { using EnumT = typename std::underlying_type::type; - return LuaImplReplyVal(instance, static_cast(val), TypeTag()); } template - std::enable_if_t::value && !std::is_unsigned::value, int> LuaImplReplyVal(LuaInstance& instance, T val, TypeTag) + std::enable_if_t::value, int> LuaImplReplyVal(const LuaInstance& instance, T val, TypeTag) { instance.PushInteger(val); return 1; } - template - std::enable_if_t::value, int> LuaImplReplyVal(LuaInstance& instance, T val, TypeTag) - { - using SignedT = typename std::make_signed::type; - - return LuaImplReplyVal(instance, static_cast(val), TypeTag()); - } - - inline int LuaImplReplyVal(LuaInstance& instance, std::string val, TypeTag) + inline int LuaImplReplyVal(const LuaInstance& instance, std::string val, TypeTag) { instance.PushString(val.c_str(), val.size()); return 1; } - inline int LuaImplReplyVal(LuaInstance& instance, String val, TypeTag) + template + inline int LuaImplReplyVal(const LuaInstance& instance, std::vector valContainer, TypeTag>) + { + std::size_t index = 1; + instance.PushTable(valContainer.size()); + for (const T& val : valContainer) + { + instance.PushInteger(index++); + if (LuaImplReplyVal(instance, val, TypeTag()) != 1) + { + instance.Error("Couldn't create table: type need more than one place to store"); + return 0; + } + instance.SetTable(); + } + + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, ByteArray val, TypeTag) + { + instance.PushString(reinterpret_cast(val.GetConstBuffer()), val.GetSize()); + return 1; + } + + inline int LuaImplReplyVal(const LuaInstance& instance, String val, TypeTag) { instance.PushString(std::move(val)); return 1; } template - int LuaImplReplyVal(LuaInstance& instance, std::pair val, TypeTag>) + int LuaImplReplyVal(const LuaInstance& instance, std::pair val, TypeTag>) { int retVal = 0; @@ -128,143 +192,420 @@ namespace Nz return retVal; } + template + struct LuaImplArgProcesser; + + template<> + struct LuaImplArgProcesser + { + template + static unsigned int Process(const LuaInstance& instance, unsigned int argIndex, ArgContainer& args, DefArgContainer& defArgs) + { + return LuaImplQueryArg(instance, argIndex, &std::get(args), std::get() - N + FirstDefArg - 1>(defArgs), TypeTag()); + } + }; + + template<> + struct LuaImplArgProcesser + { + template + static unsigned int Process(const LuaInstance& instance, unsigned int argIndex, ArgContainer& args, DefArgContainer& defArgs) + { + NazaraUnused(defArgs); + + return LuaImplQueryArg(instance, argIndex, &std::get(args), TypeTag()); + } + }; + template class LuaImplFunctionProxy { public: - LuaImplFunctionProxy(LuaInstance& instance) : - m_instance(instance) + template + class Impl { - } + static constexpr std::size_t ArgCount = sizeof...(Args); + static constexpr std::size_t DefArgCount = sizeof...(DefArgs); - template - void ProcessArgs() - { - // No argument to process - } + static_assert(ArgCount >= DefArgCount, "There cannot be more default arguments than argument"); - template - void ProcessArgs() - { - std::get(m_args) = std::move(LuaImplQueryArg(m_instance, N+1, TypeTag())); - } + static constexpr std::size_t FirstDefArg = ArgCount - DefArgCount; - template - void ProcessArgs() - { - ProcessArgs(); - ProcessArgs(); - } + public: + Impl(DefArgs... defArgs) : + m_defaultArgs(std::forward(defArgs)...) + { + } - void ProcessArgs() - { - ProcessArgs<0, Args...>(); - } + void ProcessArgs(const LuaInstance& instance) const + { + m_index = 1; + ProcessArgs<0, Args...>(instance); + } - int Invoke(void (*func)(Args...)) - { - Apply(func, m_args); - return 0; - } + int Invoke(const LuaInstance& instance, void(*func)(Args...)) const + { + NazaraUnused(instance); - template - int Invoke(Ret (*func)(Args...)) - { - return LuaImplReplyVal(m_instance, std::move(Apply(func, m_args)), TypeTag()); - } + Apply(func, m_args); + return 0; + } - private: - std::tuple>...> m_args; - LuaInstance& m_instance; + template + int Invoke(const LuaInstance& instance, Ret(*func)(Args...)) const + { + return LuaImplReplyVal(instance, std::move(Apply(func, m_args)), TypeTag()); + } + + private: + using ArgContainer = std::tuple>...>; + using DefArgContainer = std::tuple>...>; + + template + void ProcessArgs(const LuaInstance& instance) const + { + NazaraUnused(instance); + + // No argument to process + } + + template + void ProcessArgs(const LuaInstance& instance) const + { + LuaImplArgProcesser<(N >= FirstDefArg)>::template Process(instance, m_index, m_args, m_defaultArgs); + } + + template + void ProcessArgs(const LuaInstance& instance) const + { + ProcessArgs(instance); + ProcessArgs(instance); + } + + mutable ArgContainer m_args; + DefArgContainer m_defaultArgs; + mutable unsigned int m_index; + }; }; - template + template class LuaImplMethodProxy { public: - LuaImplMethodProxy(LuaInstance& instance, T& object) : - m_instance(instance), - m_object(object) + template + class Impl { - } + static constexpr std::size_t ArgCount = sizeof...(Args); + static constexpr std::size_t DefArgCount = sizeof...(DefArgs); - template - void ProcessArgs() - { - // No argument to process - } + static_assert(ArgCount >= DefArgCount, "There cannot be more default arguments than argument"); - template - void ProcessArgs() - { - std::get(m_args) = std::move(LuaImplQueryArg(m_instance, N + 1, TypeTag())); - } + static constexpr std::size_t FirstDefArg = ArgCount - DefArgCount; - template - void ProcessArgs() - { - ProcessArgs(); - ProcessArgs(); - } + public: + Impl(DefArgs... defArgs) : + m_defaultArgs(std::forward(defArgs)...) + { + } - void ProcessArgs() - { - ProcessArgs<0, Args...>(); - } + void ProcessArgs(const LuaInstance& instance) const + { + m_index = 1; + ProcessArgs<0, Args...>(instance); + } - template - std::enable_if_t::value, int> Invoke(void(P::*func)(Args...)) - { - Apply(m_object, func, m_args); - return 0; - } + template + std::enable_if_t::value, int> Invoke(const LuaInstance& instance, T& object, void(P::*func)(Args...)) const + { + NazaraUnused(instance); - template - std::enable_if_t::value, int> Invoke(Ret(P::*func)(Args...)) - { - return LuaImplReplyVal(m_instance, std::move(Apply(m_object, func, m_args)), TypeTag()); - } + Apply(object, func, m_args); + return 0; + } - template - std::enable_if_t::value, int> Invoke(void(P::*func)(Args...) const) - { - Apply(m_object, func, m_args); - return 0; - } + template + std::enable_if_t::value, int> Invoke(const LuaInstance& instance, T& object, Ret(P::*func)(Args...)) const + { + return LuaImplReplyVal(instance, std::move(Apply(object, func, m_args)), TypeTag()); + } - template - std::enable_if_t::value, int> Invoke(Ret(P::*func)(Args...) const) - { - return LuaImplReplyVal(m_instance, std::move(Apply(m_object, func, m_args)), TypeTag()); - } + template + std::enable_if_t::value, int> Invoke(const LuaInstance& instance, const T& object, void(P::*func)(Args...) const) const + { + NazaraUnused(instance); - private: - std::tuple>...> m_args; - LuaInstance& m_instance; - T& m_object; + Apply(object, func, m_args); + return 0; + } + + template + std::enable_if_t::value, int> Invoke(const LuaInstance& instance, const T& object, Ret(P::*func)(Args...) const) const + { + return LuaImplReplyVal(instance, std::move(Apply(object, func, m_args)), TypeTag()); + } + + template + std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, T& object, void(P::*func)(Args...)) const + { + NazaraUnused(instance); + + if (!object) + { + instance.Error("Invalid object"); + return 0; + } + + Apply(*object, func, m_args); + return 0; + } + + template + std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, T& object, Ret(P::*func)(Args...)) const + { + if (!object) + { + instance.Error("Invalid object"); + return 0; + } + + return LuaImplReplyVal(instance, std::move(Apply(*object, func, m_args)), TypeTag()); + } + + template + std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, const T& object, void(P::*func)(Args...) const) const + { + NazaraUnused(instance); + + if (!object) + { + instance.Error("Invalid object"); + return 0; + } + + Apply(*object, func, m_args); + return 0; + } + + template + std::enable_if_t::type>::value, int> Invoke(const LuaInstance& instance, const T& object, Ret(P::*func)(Args...) const) const + { + if (!object) + { + instance.Error("Invalid object"); + return 0; + } + + return LuaImplReplyVal(instance, std::move(Apply(*object, func, m_args)), TypeTag()); + } + + private: + using ArgContainer = std::tuple>...>; + using DefArgContainer = std::tuple>...>; + + template + void ProcessArgs(const LuaInstance& instance) const + { + NazaraUnused(instance); + + // No argument to process + } + + template + void ProcessArgs(const LuaInstance& instance) const + { + m_index += LuaImplArgProcesser<(N >= FirstDefArg)>::template Process(instance, m_index, m_args, m_defaultArgs); + } + + template + void ProcessArgs(const LuaInstance& instance) const + { + ProcessArgs(instance); + ProcessArgs(instance); + } + + mutable ArgContainer m_args; + DefArgContainer m_defaultArgs; + mutable unsigned int m_index; + }; }; template - T LuaInstance::Check(int index) + T LuaInstance::Check(int* index) const { - return LuaImplQueryArg(*this, index, TypeTag()); + NazaraAssert(index, "Invalid index pointer"); + + T object; + *index += LuaImplQueryArg(*this, *index, &object, TypeTag()); + + return object; } template - int LuaInstance::Push(T arg) + T LuaInstance::Check(int* index, T defValue) const + { + NazaraAssert(index, "Invalid index pointer"); + + T object; + *index += LuaImplQueryArg(*this, *index, &object, defValue, TypeTag()); + + return object; + } + + template + inline T LuaInstance::CheckBoundInteger(int index) const + { + return CheckBounds(index, CheckInteger(index)); + } + + template + inline T LuaInstance::CheckBoundInteger(int index, T defValue) const + { + return CheckBounds(index, CheckInteger(index, defValue)); + } + + template + T LuaInstance::CheckField(const char* fieldName, int tableIndex) const + { + T object; + + GetField(fieldName, tableIndex); + tableIndex += LuaImplQueryArg(*this, -1, &object, TypeTag()); + Pop(); + + return object; + } + + template + T LuaInstance::CheckField(const String& fieldName, int tableIndex) const + { + return CheckField(fieldName.GetConstBuffer(), tableIndex); + } + + template + T LuaInstance::CheckField(const char* fieldName, T defValue, int tableIndex) const + { + T object; + + GetField(fieldName, tableIndex); + tableIndex += LuaImplQueryArg(*this, -1, &object, defValue, TypeTag()); + Pop(); + + return object; + } + + template + T LuaInstance::CheckField(const String& fieldName, T defValue, int tableIndex) const + { + return CheckField(fieldName.GetConstBuffer(), defValue, tableIndex); + } + + template + T LuaInstance::CheckGlobal(const char* fieldName) const + { + T object; + + GetGlobal(fieldName); + LuaImplQueryArg(*this, -1, &object, TypeTag()); + Pop(); + + return object; + } + + template + T LuaInstance::CheckGlobal(const String& fieldName) const + { + return CheckGlobal(fieldName.GetConstBuffer()); + } + + template + T LuaInstance::CheckGlobal(const char* fieldName, T defValue) const + { + T object; + + GetGlobal(fieldName); + LuaImplQueryArg(*this, -1, &object, defValue, TypeTag()); + Pop(); + + return object; + } + + template + T LuaInstance::CheckGlobal(const String& fieldName, T defValue) const + { + return CheckGlobal(fieldName.GetConstBuffer(), defValue); + } + + template + int LuaInstance::Push(T arg) const { return LuaImplReplyVal(*this, std::move(arg), TypeTag()); } - template - void LuaInstance::PushFunction(R (*func)(Args...)) + template + void LuaInstance::PushFunction(R(*func)(Args...), DefArgs&&... defArgs) const { - PushFunction([func](LuaInstance& instance) -> int - { - LuaImplFunctionProxy handler(instance); - handler.ProcessArgs(); + typename LuaImplFunctionProxy::template Impl handler(std::forward(defArgs)...); - return handler.Invoke(func); + PushFunction([func, handler] (LuaInstance& lua) -> int + { + handler.ProcessArgs(lua); + + return handler.Invoke(lua, func); }); } + + template + void LuaInstance::PushInstance(const char* tname, T* instance) const + { + T** userdata = static_cast(PushUserdata(sizeof(T*))); + *userdata = instance; + SetMetatable(tname); + } + + template + void LuaInstance::PushInstance(const char* tname, Args&&... args) const + { + PushInstance(tname, new T(std::forward(args)...)); + } + + template + void LuaInstance::SetField(const char* name, T&& arg, int tableIndex) + { + Push(std::forward(arg)); + SetField(name, tableIndex); + } + + template + void LuaInstance::SetField(const String& name, T&& arg, int tableIndex) + { + SetField(name.GetConstBuffer(), std::forward(arg), tableIndex); + } + + template + void LuaInstance::SetGlobal(const char* name, T&& arg) + { + Push(std::forward(arg)); + SetGlobal(name); + } + + template + void LuaInstance::SetGlobal(const String& name, T&& arg) + { + SetGlobal(name.GetConstBuffer(), std::forward(arg)); + } + + template + T LuaInstance::CheckBounds(int index, long long value) const + { + long long minBounds = std::numeric_limits::min(); + long long maxBounds = std::numeric_limits::max(); + if (value < minBounds || value > maxBounds) + { + Nz::StringStream stream; + stream << "Argument #" << index << " is outside value range [" << minBounds << ", " << maxBounds << "] (" << value << ')'; + Error(stream); + } + + return static_cast(value); + } } diff --git a/include/Nazara/Math/Algorithm.hpp b/include/Nazara/Math/Algorithm.hpp index 841c3569a..74cc45593 100644 --- a/include/Nazara/Math/Algorithm.hpp +++ b/include/Nazara/Math/Algorithm.hpp @@ -30,32 +30,32 @@ namespace Nz { - template T Approach(T value, T objective, T increment); + template /*constexpr*/ T Approach(T value, T objective, T increment); template constexpr T Clamp(T value, T min, T max); - template T CountBits(T value); + template /*constexpr*/ T CountBits(T value); template constexpr T FromDegrees(T degrees); template constexpr T FromRadians(T radians); template constexpr T DegreeToRadian(T degrees); - template T GetNearestPowerOfTwo(T number); - unsigned int GetNumberLength(signed char number); - unsigned int GetNumberLength(unsigned char number); + template /*constexpr*/ T GetNearestPowerOfTwo(T number); + /*constexpr*/ unsigned int GetNumberLength(signed char number); + /*constexpr*/ unsigned int GetNumberLength(unsigned char number); unsigned int GetNumberLength(int number); - unsigned int GetNumberLength(unsigned int number); + /*constexpr*/ unsigned int GetNumberLength(unsigned int number); unsigned int GetNumberLength(long long number); - unsigned int GetNumberLength(unsigned long long number); + /*constexpr*/ unsigned int GetNumberLength(unsigned long long number); unsigned int GetNumberLength(float number, UInt8 precision = NAZARA_CORE_DECIMAL_DIGITS); unsigned int GetNumberLength(double number, UInt8 precision = NAZARA_CORE_DECIMAL_DIGITS); unsigned int GetNumberLength(long double number, UInt8 precision = NAZARA_CORE_DECIMAL_DIGITS); - template unsigned int IntegralLog2(T number); - template unsigned int IntegralLog2Pot(T pot); - unsigned int IntegralPow(unsigned int base, unsigned int exponent); - template T Lerp(T from, T to, T2 interpolation); - template T MultiplyAdd(T x, T y, T z); - template T NormalizeAngle(T angle); - template bool NumberEquals(T a, T b); - template bool NumberEquals(T a, T b, T maxDifference); + template /*constexpr*/ unsigned int IntegralLog2(T number); + template /*constexpr*/ unsigned int IntegralLog2Pot(T pot); + /*constexpr*/ unsigned int IntegralPow(unsigned int base, unsigned int exponent); + template constexpr T Lerp(const T& from, const T& to, const T2& interpolation); + template constexpr T MultiplyAdd(T x, T y, T z); + template /*constexpr*/ T NormalizeAngle(T angle); + template /*constexpr*/ bool NumberEquals(T a, T b); + template /*constexpr*/ bool NumberEquals(T a, T b, T maxDifference); String NumberToString(long long number, UInt8 radix = 10); - template T RadianToDegree(T radians); + template constexpr T RadianToDegree(T radians); long long StringToNumber(String str, UInt8 radix = 10, bool* ok = nullptr); template constexpr T ToDegrees(T angle); template constexpr T ToRadians(T angle); diff --git a/include/Nazara/Math/Algorithm.inl b/include/Nazara/Math/Algorithm.inl index 1a4fceded..e7e205e45 100644 --- a/include/Nazara/Math/Algorithm.inl +++ b/include/Nazara/Math/Algorithm.inl @@ -98,10 +98,20 @@ namespace Nz } } + /*! + * \ingroup math + * \brief Approaches the objective, beginning with value and with increment + * \return The nearest value of the objective you can get with the value and the increment for one step + * + * \param value Initial value + * \param objective Target value + * \parma increment One step value + */ + template - T Approach(T value, T objective, T increment) + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline T Approach(T value, T objective, T increment) { - ///TODO: Marquer comme constexpr en C++14 if (value < objective) return std::min(value + increment, objective); else if (value > objective) @@ -110,14 +120,33 @@ namespace Nz return value; } + /*! + * \ingroup math + * \brief Clamps value between min and max and returns the expected value + * \return If value is not in the interval of min..max, value obtained is the nearest limit of this interval + * + * \param value Value to clamp + * \param min Minimum of the interval + * \param max Maximum of the interval + */ + template constexpr T Clamp(T value, T min, T max) { return std::max(std::min(value, max), min); } + /*! + * \ingroup math + * \brief Gets number of bits set in the number + * \return The number of bits set to 1 + * + * \param value The value to count bits + */ + template - T CountBits(T value) + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline T CountBits(T value) { // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan unsigned int count = 0; @@ -130,12 +159,28 @@ namespace Nz return count; } + /*! + * \ingroup math + * \brief Converts degree to radian + * \return The representation in radian of the angle in degree (0..2*pi) + * + * \param degrees Angle in degree (this is expected between 0..360) + */ + template constexpr T DegreeToRadian(T degrees) { return degrees * T(M_PI/180.0); } + /*! + * \ingroup math + * \brief Gets the unit from degree and convert it according to NAZARA_MATH_ANGLE_RADIAN + * \return Express the degrees + * + * \param degrees Convert degree to NAZARA_MATH_ANGLE_RADIAN unit + */ + template constexpr T FromDegrees(T degrees) { @@ -146,6 +191,14 @@ namespace Nz #endif } + /*! + * \ingroup math + * \brief Gets the unit from radian and convert it according to NAZARA_MATH_ANGLE_RADIAN + * \return Express the radians + * + * \param radians Convert radian to NAZARA_MATH_ANGLE_RADIAN unit + */ + template constexpr T FromRadians(T radians) { @@ -156,22 +209,37 @@ namespace Nz #endif } + /*! + * \ingroup math + * \brief Gets the nearest power of two for the number + * \return First power of two containing the number + * + * \param number Number to get nearest power + */ + template - T GetNearestPowerOfTwo(T number) + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline T GetNearestPowerOfTwo(T number) { - ///TODO: Marquer comme constexpr en C++14 T x = 1; - // Tant que x est plus petit que n, on décale ses bits vers la gauche, ce qui revient à multiplier par deux while (x < number) - x <<= 1; + x <<= 1; // We multiply by 2 return x; } - inline unsigned int GetNumberLength(signed char number) + /*! + * \ingroup math + * \brief Gets the number of digits to represent the number in base 10 + * \return Number of digits + * + * \param number Number to get number of digits + */ + + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline unsigned int GetNumberLength(signed char number) { - ///TODO: Marquer comme constexpr en C++14 - // Le standard définit le char comme étant codé sur un octet + // Char is expected to be 1 byte static_assert(sizeof(number) == 1, "Signed char must be one byte-sized"); if (number >= 100) @@ -188,10 +256,18 @@ namespace Nz return 4; } - inline unsigned int GetNumberLength(unsigned char number) + /*! + * \ingroup math + * \brief Gets the number of digits to represent the number in base 10 + * \return Number of digits + * + * \param number Number to get number of digits + */ + + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline unsigned int GetNumberLength(unsigned char number) { - ///TODO: Marquer comme constexpr en C++14 - // Le standard définit le char comme étant codé sur un octet + // Char is expected to be 1 byte static_assert(sizeof(number) == 1, "Unsigned char must be one byte-sized"); if (number >= 100) @@ -202,6 +278,14 @@ namespace Nz return 1; } + /*! + * \ingroup math + * \brief Gets the number of digits to represent the number in base 10 + * \return Number of digits + * + * \param number Number to get number of digits + */ + inline unsigned int GetNumberLength(int number) { if (number == 0) @@ -210,7 +294,16 @@ namespace Nz return static_cast(std::log10(std::abs(number))) + (number < 0 ? 2 : 1); } - inline unsigned int GetNumberLength(unsigned int number) + /*! + * \ingroup math + * \brief Gets the number of digits to represent the number in base 10 + * \return Number of digits + * + * \param number Number to get number of digits + */ + + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline unsigned int GetNumberLength(unsigned int number) { if (number == 0) return 1; @@ -218,6 +311,14 @@ namespace Nz return static_cast(std::log10(number))+1; } + /*! + * \ingroup math + * \brief Gets the number of digits to represent the number in base 10 + * \return Number of digits + * + * \param number Number to get number of digits + */ + inline unsigned int GetNumberLength(long long number) { if (number == 0) @@ -226,7 +327,16 @@ namespace Nz return static_cast(std::log10(std::abs(number))) + (number < 0 ? 2 : 1); } - inline unsigned int GetNumberLength(unsigned long long number) + /*! + * \ingroup math + * \brief Gets the number of digits to represent the number in base 10 + * \return Number of digits + * + * \param number Number to get number of digits + */ + + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline unsigned int GetNumberLength(unsigned long long number) { if (number == 0) return 1; @@ -234,40 +344,99 @@ namespace Nz return static_cast(std::log10(number)) + 1; } + /*! + * \ingroup math + * \brief Gets the number of digits to represent the number in base 10 + * \return Number of digits + 1 for the dot + * + * \param number Number to get number of digits + * \param precision Number of digit after the dot + */ + inline unsigned int GetNumberLength(float number, UInt8 precision) { - // L'imprécision des flottants nécessite un cast (log10(9.99999) = 0.99999) - return GetNumberLength(static_cast(number)) + precision + 1; // Plus un pour le point + // The imprecision of floats need a cast (log10(9.99999) = 0.99999) + return GetNumberLength(static_cast(number)) + precision + 1; // Plus one for the dot } + /*! + * \ingroup math + * \brief Gets the number of digits to represent the number in base 10 + * \return Number of digits + 1 for the dot + * + * \param number Number to get number of digits + * \param precision Number of digit after the dot + */ + inline unsigned int GetNumberLength(double number, UInt8 precision) { - // L'imprécision des flottants nécessite un cast (log10(9.99999) = 0.99999) - return GetNumberLength(static_cast(number)) + precision + 1; // Plus un pour le point + // The imprecision of floats need a cast (log10(9.99999) = 0.99999) + return GetNumberLength(static_cast(number)) + precision + 1; // Plus one for the dot } + /*! + * \ingroup math + * \brief Gets the number of digits to represent the number in base 10 + * \return Number of digits + 1 for the dot + * + * \param number Number to get number of digits + * \param precision Number of digit after the dot + */ + inline unsigned int GetNumberLength(long double number, UInt8 precision) { - // L'imprécision des flottants nécessite un cast (log10(9.99999) = 0.99999) - return GetNumberLength(static_cast(number)) + precision + 1; // Plus un pour le point + // The imprecision of floats need a cast (log10(9.99999) = 0.99999) + return GetNumberLength(static_cast(number)) + precision + 1; // Plus one for the dot } + /*! + * \ingroup math + * \brief Gets the log in base 2 of integral number + * \return Log of the number (floor) + * + * \param number To get log in base 2 + * + * \remark If number is 0, 0 is returned + */ + template - unsigned int IntegralLog2(T number) + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline unsigned int IntegralLog2(T number) { - // Proxy nécessaire pour éviter un problème de surcharge + // Proxy needed to avoid an overload problem return Detail::IntegralLog2(number); } + /*! + * \ingroup math + * \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 + * + * \remark Only works for power of two + * \remark If number is 0, 0 is returned + */ + template - unsigned int IntegralLog2Pot(T pot) + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline unsigned int IntegralLog2Pot(T pot) { return Detail::IntegralLog2Pot(pot); } - inline unsigned int IntegralPow(unsigned int base, unsigned int exponent) + /*! + * \ingroup math + * \brief Gets the power of integrals + * \return base^exponent for integral + * + * \param base Base of the exponentation + * \parma exponent Power for the base + */ + + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline unsigned int IntegralPow(unsigned int base, unsigned int exponent) { - ///TODO: Marquer comme constexpr en C++14 unsigned int r = 1; for (unsigned int i = 0; i < exponent; ++i) r *= base; @@ -275,26 +444,48 @@ namespace Nz return r; } - template - T Lerp(T from, T to, T2 interpolation) - { - #ifdef NAZARA_DEBUG - if (interpolation < T2(0.0) || interpolation > T2(1.0)) - NazaraWarning("Interpolation should be in range [0..1] (Got " + String::Number(interpolation) + ')'); - #endif + /*! + * \ingroup math + * \brief Interpolates the value to other one with a factor of interpolation + * \return A new value which is the interpolation of two values + * + * \param from Initial value + * \param to Target value + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * \remark With NAZARA_DEBUG, a NazaraWarning is produced + * + * \see Lerp + */ + template + constexpr T Lerp(const T& from, const T& to, const T2& interpolation) + { return from + interpolation * (to - from); } + /*! + * \ingroup math + * \brief Multiplies X and Y, then add Z + * \return The result of X * Y + Z + * + * \param x is X + * \param y is Y + * \param z is Z + * + * \remark This function is meant to use a special faster instruction in CPU if possible + */ + template - T MultiplyAdd(T x, T y, T z) + constexpr T MultiplyAdd(T x, T y, T z) { - return x*y + z; + return x * y + z; } #ifdef FP_FAST_FMAF template<> - inline float MultiplyAdd(float x, float y, float z) + constexpr float MultiplyAdd(float x, float y, float z) { return std::fmaf(x, y, z); } @@ -302,7 +493,7 @@ namespace Nz #ifdef FP_FAST_FMA template<> - inline double MultiplyAdd(double x, double y, double z) + constexpr double MultiplyAdd(double x, double y, double z) { return std::fma(x, y, z); } @@ -310,14 +501,23 @@ namespace Nz #ifdef FP_FAST_FMAL template<> - inline long double MultiplyAdd(long double x, long double y, long double z) + constexpr long double MultiplyAdd(long double x, long double y, long double z) { return std::fmal(x, y, z); } #endif + /*! + * \ingroup math + * \brief Normalizes the angle + * \return Normalized value between 0..2*(pi if radian or 180 if degrees) + * + * \param angle Angle to normalize + */ + template - T NormalizeAngle(T angle) + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline T NormalizeAngle(T angle) { #if NAZARA_MATH_ANGLE_RADIAN const T limit = T(M_PI); @@ -333,14 +533,35 @@ namespace Nz return angle - limit; } + /*! + * \ingroup math + * \brief Checks whether two numbers are equal + * \return true if they are equal within a certain epsilon + * + * \param a First value + * \param b Second value + */ + template - bool NumberEquals(T a, T b) + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline bool NumberEquals(T a, T b) { return NumberEquals(a, b, std::numeric_limits::epsilon()); } + /*! + * \ingroup math + * \brief Checks whether two numbers are equal + * \return true if they are equal within the max difference + * + * \param a First value + * \param b Second value + * \param maxDifference Epsilon of comparison (expected to be positive) + */ + template - bool NumberEquals(T a, T b, T maxDifference) + //TODO: Mark as constexpr when supported by all major compilers + /*constexpr*/ inline bool NumberEquals(T a, T b, T maxDifference) { if (b > a) std::swap(a, b); @@ -349,6 +570,18 @@ namespace Nz return diff <= maxDifference; } + /*! + * \ingroup math + * \brief Converts the number to String + * \return String representation of the number + * + * \param number Number to represent + * \param radix Base of the number + * + * \remark radix is meant to be between 2 and 36, other values are potentially undefined behavior + * \remark With NAZARA_MATH_SAFE, a NazaraError is produced and String() is returned + */ + inline String NumberToString(long long number, UInt8 radix) { #if NAZARA_MATH_SAFE @@ -389,12 +622,33 @@ namespace Nz return str.Reverse(); } + /*! + * \ingroup math + * \brief Converts radian to degree + * \return The representation in degree of the angle in radian (0..360) + * + * \param radians Angle in radian (this is expected between 0..2*pi) + */ + template - T RadianToDegree(T radians) + constexpr T RadianToDegree(T radians) { return radians * T(180.0/M_PI); } + /*! + * \ingroup math + * \brief Converts the string to number + * \return Number which is represented by the string + * + * \param str String representation + * \param radix Base of the number + * \param ok Optional argument to know if convertion is correct + * + * \remark radix is meant to be between 2 and 36, other values are potentially undefined behavior + * \remark With NAZARA_MATH_SAFE, a NazaraError is produced and 0 is returned + */ + inline long long StringToNumber(String str, UInt8 radix, bool* ok) { #if NAZARA_MATH_SAFE @@ -444,6 +698,14 @@ namespace Nz return (negative) ? -static_cast(total) : total; } + /*! + * \ingroup math + * \brief Gets the degree from unit and convert it according to NAZARA_MATH_ANGLE_RADIAN + * \return Express in degrees + * + * \param angle Convert degree from NAZARA_MATH_ANGLE_RADIAN unit to degrees + */ + template constexpr T ToDegrees(T angle) { @@ -454,6 +716,14 @@ namespace Nz #endif } + /*! + * \ingroup math + * \brief Gets the radian from unit and convert it according to NAZARA_MATH_ANGLE_RADIAN + * \return Express in radians + * + * \param angle Convert degree from NAZARA_MATH_ANGLE_RADIAN unit to radians + */ + template constexpr T ToRadians(T angle) { @@ -461,8 +731,8 @@ namespace Nz return angle; #else return DegreeToRadian(angle); + #endif } - #endif } #include diff --git a/include/Nazara/Math/BoundingVolume.hpp b/include/Nazara/Math/BoundingVolume.hpp index e0ca2f556..6f1f4b804 100644 --- a/include/Nazara/Math/BoundingVolume.hpp +++ b/include/Nazara/Math/BoundingVolume.hpp @@ -14,6 +14,8 @@ namespace Nz { + struct SerializationContext; + template class BoundingVolume { @@ -28,6 +30,8 @@ namespace Nz BoundingVolume(const BoundingVolume& volume) = default; ~BoundingVolume() = default; + BoundingVolume& ExtendTo(const BoundingVolume& volume); + bool IsFinite() const; bool IsInfinite() const; bool IsNull() const; @@ -66,6 +70,9 @@ namespace Nz typedef BoundingVolume BoundingVolumed; typedef BoundingVolume BoundingVolumef; + + template bool Serialize(SerializationContext& context, const BoundingVolume& boundingVolume); + template bool Unserialize(SerializationContext& context, BoundingVolume* boundingVolume); } template diff --git a/include/Nazara/Math/BoundingVolume.inl b/include/Nazara/Math/BoundingVolume.inl index 13b41a63e..a1efd8364 100644 --- a/include/Nazara/Math/BoundingVolume.inl +++ b/include/Nazara/Math/BoundingVolume.inl @@ -1,7 +1,8 @@ -// Copyright (C) 2015 Jérôme Leclercq +// 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 +#include #include #include #include @@ -13,42 +14,109 @@ namespace Nz { + + /*! + * \ingroup math + * \class Nz::BoundingVolume + * \brief Math class that represents a bounding volume, a combination of a box and an oriented box + * + * \remark You need to call Update not to have undefined behaviour + */ + + /*! + * \brief Constructs a BoundingVolume object by default + * + * \remark extend is set to Extend_Null, aabb and obb are uninitialized + */ + template BoundingVolume::BoundingVolume() : extend(Extend_Null) { } + /*! + * \brief Constructs a BoundingVolume object from Extend + * \param Extend Extend of the volume part of enumeration Extend + * + * \remark Aabb and obb are uninitialized + */ + template BoundingVolume::BoundingVolume(Extend Extend) { Set(Extend); } + /*! + * \brief Constructs a BoundingVolume object from its position and sizes + * + * \param X X component of position + * \param Y Y component of position + * \param Z Z component of position + * \param Width Width of the box (following X) + * \param Height Height of the box (following Y) + * \param Depth Depth of the box (following Z) + * + * \remark Aabb is uninitialized + */ + template BoundingVolume::BoundingVolume(T X, T Y, T Z, T Width, T Height, T Depth) { Set(X, Y, Z, Width, Height, Depth); } + /*! + * \brief Constructs a BoundingVolume object from a box + * + * \param box Box object + * + * \remark Aabb is uninitialized + */ + template BoundingVolume::BoundingVolume(const Box& box) { Set(box); } + /*! + * \brief Constructs a BoundingVolume object from an oriented box + * + * \param orientedBox OrientedBox object + * + * \remark Aabb is uninitialized + */ + template BoundingVolume::BoundingVolume(const OrientedBox& orientedBox) { Set(orientedBox); } + /*! + * \brief Constructs a BoundingVolume object from two vectors representing point of the space + * (X, Y, Z) will be the components minimum of the two vectors and the (width, height, depth) will be the components maximum - minimum + * + * \param vec1 First point + * \param vec2 Second point + * + * \remark Aabb is uninitialized + */ + template BoundingVolume::BoundingVolume(const Vector3& vec1, const Vector3& vec2) { Set(vec1, vec2); } + /*! + * \brief Constructs a BoundingVolume object from another type of BoundingVolume + * + * \param volume BoundingVolume of type U to convert to type T + */ + template template BoundingVolume::BoundingVolume(const BoundingVolume& volume) @@ -56,24 +124,92 @@ namespace Nz Set(volume); } + + /*! + * \brief Extends the bounding volume to contain another bounding volume + * \return A reference to the the bounding volume + * + * \param volume Other volume to contain + * + * \remark Extending to a null bounding volume has no effect while extending to a infinite bounding volume will set it as infinite + */ + template + BoundingVolume& BoundingVolume::ExtendTo(const BoundingVolume& volume) + { + switch (extend) + { + case Extend_Finite: + { + switch (volume.extend) + { + case Extend_Finite: + { + // Extend the OBB local box + obb.localBox.ExtendTo(volume.obb.localBox); + break; + } + + case Extend_Infinite: + MakeInfinite(); + break; + + case Extend_Null: + break; + } + break; + } + + case Extend_Infinite: + break; //< We already contain the bounding volume + + case Extend_Null: + Set(volume); + break; + } + + return *this; + } + + /*! + * \brief Checks whether the volume is finite + * \return true if extend is Extend_Finite + */ + template bool BoundingVolume::IsFinite() const { return extend == Extend_Finite; } + /*! + * \brief Checks whether the volume is infinite + * \return true if extend is Extend_Infinite + */ + template bool BoundingVolume::IsInfinite() const { return extend == Extend_Infinite; } + /*! + * \brief Checks whether the volume is null + * \return true if extend is Extend_Null + */ + template bool BoundingVolume::IsNull() const { return extend == Extend_Null; } + /*! + * \brief Makes the bounding volume infinite + * \return A reference to this bounding volume with Extend_Infinite for extend + * + * \see Infinite + */ + template BoundingVolume& BoundingVolume::MakeInfinite() { @@ -82,6 +218,13 @@ namespace Nz return *this; } + /*! + * \brief Makes the bounding volume null + * \return A reference to this bounding volume with Extend_Null for extend + * + * \see Null + */ + template BoundingVolume& BoundingVolume::MakeNull() { @@ -90,6 +233,15 @@ namespace Nz return *this; } + /*! + * \brief Sets the extend of the bounding volume from Extend + * \return A reference to this bounding volume + * + * \param Extend New extend + * + * \remark This method is meant to be called with Extend_Infinite or Extend_Null + */ + template BoundingVolume& BoundingVolume::Set(Extend Extend) { @@ -98,6 +250,18 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the bounding volume + * \return A reference to this bounding volume + * + * \param X X position + * \param Y Y position + * \param Z Z position + * \param Width Width of the oriented box (following X) + * \param Height Height of the oriented box (following Y) + * \param Depth Depth of the oriented box (following Z) + */ + template BoundingVolume& BoundingVolume::Set(T X, T Y, T Z, T Width, T Height, T Depth) { @@ -107,15 +271,29 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the bounding volume from another bounding volume + * \return A reference to this bounding volume + * + * \param volume The other bounding volume + */ + template BoundingVolume& BoundingVolume::Set(const BoundingVolume& volume) { - obb.Set(volume.obb); // Seul l'OBB est importante pour la suite + obb.Set(volume.obb); // Only OBB is important for the moment extend = volume.extend; return *this; } + /*! + * \brief Sets the components of the bounding volume from a box + * \return A reference to this bounding volume + * + * \param box Box object + */ + template BoundingVolume& BoundingVolume::Set(const Box& box) { @@ -125,6 +303,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the bounding volume from an oriented box + * \return A reference to this bounding volume + * + * \param orientedBox OrientedBox object + */ + template BoundingVolume& BoundingVolume::Set(const OrientedBox& orientedBox) { @@ -134,6 +319,14 @@ namespace Nz return *this; } + /*! + * \brief Sets a BoundingVolume object from two vectors representing point of the space + * (X, Y, Z) will be the components minimum of the two vectors and the (width, height, depth) will be the components maximum - minimum + * + * \param vec1 First point + * \param vec2 Second point + */ + template BoundingVolume& BoundingVolume::Set(const Vector3& vec1, const Vector3& vec2) { @@ -143,6 +336,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the bounding volume from another type of BoundingVolume + * \return A reference to this bounding volume + * + * \param volume BoundingVolume of type U to convert its components + */ + template template BoundingVolume& BoundingVolume::Set(const BoundingVolume& volume) @@ -153,6 +353,13 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "BoundingVolume(localBox="")" if finite, or "BoundingVolume(Infinite)" or "BoundingVolume(Null)" + * + * \remark If enumeration is not defined in Extend, a NazaraError is thrown and "BoundingVolume(ERROR)" is returned + */ + template String BoundingVolume::ToString() const { @@ -173,6 +380,12 @@ namespace Nz return "BoundingVolume(ERROR)"; } + /*! + * \brief Updates the obb and the aabb of the bounding volume + * + * \param transformMatrix Matrix4 which represents the transformation to apply + */ + template void BoundingVolume::Update(const Matrix4& transformMatrix) { @@ -183,6 +396,12 @@ namespace Nz aabb.ExtendTo(obb(i)); } + /*! + * \brief Updates the obb and the aabb of the bounding volume + * + * \param translation Vector3 which represents the translation to apply + */ + template void BoundingVolume::Update(const Vector3& translation) { @@ -193,6 +412,13 @@ namespace Nz aabb.ExtendTo(obb(i)); } + /*! + * \brief Multiplies the lengths of the obb with the scalar + * \return A BoundingVolume 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 + */ + template BoundingVolume BoundingVolume::operator*(T scalar) const { @@ -202,6 +428,13 @@ namespace Nz return volume; } + /*! + * \brief Multiplies the lengths of this bounding volume with the scalar + * \return A reference to this bounding volume where lengths are the product of these lengths and the scalar + * + * \param scalar The scalar to multiply width, height and depth with + */ + template BoundingVolume& BoundingVolume::operator*=(T scalar) { @@ -210,6 +443,13 @@ namespace Nz return *this; } + /*! + * \brief Compares the bounding volume to other one + * \return true if the two bounding volumes are the same + * + * \param volume Other bounding volume to compare with + */ + template bool BoundingVolume::operator==(const BoundingVolume& volume) const { @@ -222,12 +462,26 @@ namespace Nz return false; } + /*! + * \brief Compares the bounding volume to other one + * \return false if the two bounding volumes are the same + * + * \param volume Other bounding volume to compare with + */ + template bool BoundingVolume::operator!=(const BoundingVolume& volume) const { return !operator==(volume); } + /*! + * \brief Shorthand for the bounding volume (Extend_Infinite) + * \return A bounding volume with Extend_Infinite + * + * \see MakeInfinite + */ + template BoundingVolume BoundingVolume::Infinite() { @@ -237,6 +491,21 @@ namespace Nz return volume; } + /*! + * \brief Interpolates the bounding volume to other one with a factor of interpolation + * \return A new bounding volume box which is the interpolation of two bounding volumes + * + * \param from Initial bounding volume + * \param to Target bounding volume + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * \remark With NAZARA_DEBUG, a NazaraError is thrown and Null() is returned + * \remark If enumeration is not defined in Extend, a NazaraError is thrown and Null() is returned + * + * \see Lerp + */ + template BoundingVolume BoundingVolume::Lerp(const BoundingVolume& from, const BoundingVolume& to, T interpolation) { @@ -275,13 +544,13 @@ namespace Nz return from.obb * interpolation; } - // Si nous arrivons ici c'est que l'extend est invalide + // If we arrive here, the extend is invalid NazaraError("Invalid extend type (From) (0x" + String::Number(from.extend, 16) + ')'); return Null(); } case Extend_Infinite: - return Infinite(); // Un petit peu d'infini est infini quand même ;) + return Infinite(); // A little bit of infinity is already too much ;) case Extend_Null: { @@ -297,17 +566,24 @@ namespace Nz return Null(); } - // Si nous arrivons ici c'est que l'extend est invalide + // If we arrive here, the extend is invalid NazaraError("Invalid extend type (From) (0x" + String::Number(from.extend, 16) + ')'); return Null(); } } - // Si nous arrivons ici c'est que l'extend est invalide + // If we arrive here, the extend is invalid NazaraError("Invalid extend type (To) (0x" + String::Number(to.extend, 16) + ')'); return Null(); } + /*! + * \brief Shorthand for the bounding volume (Extend_Null) + * \return A bounding volume with Extend_Null + * + * \see MakeNull + */ + template BoundingVolume BoundingVolume::Null() { @@ -317,12 +593,74 @@ namespace Nz return volume; } + /*! + * \brief Serializes a BoundingVolume + * \return true if successfully serialized + * + * \param context Serialization context + * \param boundingVolume Input bounding volume + * + * \remark Does not save OBB corners + */ template - std::ostream& operator<<(std::ostream& out, const BoundingVolume& volume) + bool Serialize(SerializationContext& context, const BoundingVolume& boundingVolume) { - out << volume.ToString(); - return out; + if (!Serialize(context, static_cast(boundingVolume.extend))) + return false; + + if (!Serialize(context, boundingVolume.aabb)) + return false; + + if (!Serialize(context, boundingVolume.obb)) + return false; + + return true; + } + + /*! + * \brief Unserializes a BoundingVolume + * \return true if successfully unserialized + * + * \param context Serialization context + * \param boundingVolume Output bounding volume + * + * \remark The resulting oriented box corners will *not* be updated, a call to Update is required + */ + template + bool Unserialize(SerializationContext& context, BoundingVolume* boundingVolume) + { + UInt8 extend; + if (!Unserialize(context, &extend)) + return false; + + if (extend > Extend_Max) + return false; + + boundingVolume->extend = static_cast(extend); + + if (!Unserialize(context, &boundingVolume->aabb)) + return false; + + if (!Unserialize(context, &boundingVolume->obb)) + return false; + + return true; + } } + +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param volume The bounding volume to output +*/ + +template +std::ostream& operator<<(std::ostream& out, const Nz::BoundingVolume& volume) +{ + out << volume.ToString(); + return out; } #undef F diff --git a/include/Nazara/Math/Box.hpp b/include/Nazara/Math/Box.hpp index 158d7753c..a2a6f25d1 100644 --- a/include/Nazara/Math/Box.hpp +++ b/include/Nazara/Math/Box.hpp @@ -16,6 +16,8 @@ namespace Nz { + struct SerializationContext; + template class Box { @@ -40,8 +42,8 @@ namespace Nz Box& ExtendTo(const Vector3& point); Sphere GetBoundingSphere() const; - Vector3 GetCorner(BoxCorner corner) const; Vector3 GetCenter() const; + Vector3 GetCorner(BoxCorner corner) const; Vector3 GetLengths() const; Vector3 GetMaximum() const; Vector3 GetMinimum() const; @@ -96,6 +98,9 @@ namespace Nz typedef Box Boxui; typedef Box Boxi32; typedef Box Boxui32; + + template bool Serialize(SerializationContext& context, const Box& box); + template bool Unserialize(SerializationContext& context, Box* box); } template diff --git a/include/Nazara/Math/Box.inl b/include/Nazara/Math/Box.inl index a39f867da..a6200cbe4 100644 --- a/include/Nazara/Math/Box.inl +++ b/include/Nazara/Math/Box.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -12,41 +13,104 @@ namespace Nz { + /*! + * \ingroup math + * \class Nz::Box + * \brief Math class that represents a three dimensional box + */ + + /*! + * \brief Constructs a Box object from its width, height and depth + * + * \param Width Width of the box (following X) + * \param Height Height of the box (following Y) + * \param Depth Depth of the box (following Z) + * + * \remark Position will be (0, 0, 0) + */ + template Box::Box(T Width, T Height, T Depth) { Set(Width, Height, Depth); } + /*! + * \brief Constructs a Rect object from its position, width, height and depth + * + * \param X X position + * \param Y Y position + * \param Z Z position + * \param Width Width of the box (following X) + * \param Height Height of the box (following Y) + * \param Depth Depth of the box (following Z) + */ + template Box::Box(T X, T Y, T Z, T Width, T Height, T Depth) { Set(X, Y, Z, Width, Height, Depth); } + /*! + * \brief Constructs a Box object from an array of six elements + * + * \param vec[6] vec[0] is X position, vec[1] is Y position, vec[2] is Z position, vec[3] is width, vec[4] is height and vec[5] is depth + */ + + template + Box::Box(const T vec[6]) + { + Set(vec); + } + + /*! + * \brief Constructs a Box object from a Rect + * + * \param rect Rectangle which describes (X, Y) position and (width, height) lenghts + * + * \remark Z position is 0 and depth is 1 + */ + template Box::Box(const Rect& rect) { Set(rect); } + /*! + * \brief Constructs a Box object from a vector representing width, height and depth + * + * \param lengths (Width, Height, Depth) of the box + * + * \remark Positions will be (0, 0, 0) + */ + template Box::Box(const Vector3& lengths) { Set(lengths); } + /*! + * \brief Constructs a Box object from two vectors representing point of the space + * (X, Y, Z) will be the components minimum of the two vectors and the (width, height, depth) will be the components maximum - minimum + * + * \param vec1 First point + * \param vec2 Second point + */ + template Box::Box(const Vector3& vec1, const Vector3& vec2) { Set(vec1, vec2); } - template - Box::Box(const T vec[6]) - { - Set(vec); - } + /*! + * \brief Constructs a Box object from another type of Box + * + * \param box Box of type U to convert to type T + */ template template @@ -55,27 +119,67 @@ namespace Nz Set(box); } + /*! + * \brief Tests whether the box contains the provided point inclusive of the edge of the box + * \return true if inclusive + * + * \param X X position of the point + * \param Y Y position of the point + * \param Z Z position of the point + * + * \see Contains + */ + template bool Box::Contains(T X, T Y, T Z) const { - return X >= x && X <= x+width && - Y >= y && Y <= y+height && - Z >= z && Z <= z+depth; + return X >= x && X <= x + width && + Y >= y && Y <= y + height && + Z >= z && Z <= z + depth; } + /*! + * \brief Tests whether the box contains the provided box inclusive of the edge of the box + * \return true if inclusive + * + * \param box Other box to test + * + * \see Contains + */ + template bool Box::Contains(const Box& box) const { return Contains(box.x, box.y, box.z) && - Contains(box.x + box.width, box.y + box.height, box.z + box.depth); + Contains(box.x + box.width, box.y + box.height, box.z + box.depth); } + /*! + * \brief Tests whether the box contains the provided point inclusive of the edge of the box + * \return true if inclusive + * + * \param point Position of the point + * + * \see Contains + */ + template bool Box::Contains(const Vector3& point) const { return Contains(point.x, point.y, point.z); } + /*! + * \brief Extends the box to contain the point in the boundary + * \return A reference to this box extended + * + * \param X X position of the point + * \param Y Y position of the point + * \param Z Z position of the point + * + * \see ExtendTo + */ + template Box& Box::ExtendTo(T X, T Y, T Z) { @@ -94,6 +198,15 @@ namespace Nz return *this; } + /*! + * \brief Extends the box to contain the box + * \return A reference to this box extended + * + * \param box Other box to contain + * + * \see ExtendTo + */ + template Box& Box::ExtendTo(const Box& box) { @@ -112,12 +225,54 @@ namespace Nz return *this; } + /*! + * \brief Extends the box to contain the point in the boundary + * \return A reference to this box extended + * + * \param point Position of the point + * + * \see ExtendTo + */ + template Box& Box::ExtendTo(const Vector3& point) { return ExtendTo(point.x, point.y, point.z); } + /*! + * \brief Gets the bounding sphere for the box + * \return A sphere containing the box + * + * \see GetSquaredBoundingSphere + */ + + template + Sphere Box::GetBoundingSphere() const + { + return Sphere(GetCenter(), GetRadius()); + } + + /*! + * \brief Gets a Vector3 for the center + * \return The position of the center of the box + */ + + template + Vector3 Box::GetCenter() const + { + return GetPosition() + GetLengths() / F(2.0); + } + + /*! + * \brief Gets the Vector3 for the corner + * \return The position of the corner of the box according to enum BoxCorner + * + * \param corner Enumeration of type BoxCorner + * + * \remark If enumeration is not defined in BoxCorner, a NazaraError is thrown and a Vector3 uninitialised is returned + */ + template Vector3 Box::GetCorner(BoxCorner corner) const { @@ -152,17 +307,10 @@ namespace Nz return Vector3(); } - template - Sphere Box::GetBoundingSphere() const - { - return Sphere(GetCenter(), GetRadius()); - } - - template - Vector3 Box::GetCenter() const - { - return GetPosition() + GetLengths()/F(2.0); - } + /*! + * \brief Gets a Vector3 for the lengths + * \return The lengths of the box (width, height, depth) + */ template Vector3 Box::GetLengths() const @@ -170,19 +318,41 @@ namespace Nz return Vector3(width, height, depth); } + /*! + * \brief Gets a Vector3 for the maximum point + * \return The BoxCorner_NearRightTop of the box + * + * \see GetCorner + */ + template Vector3 Box::GetMaximum() const { return GetPosition() + GetLengths(); } + /*! + * \brief Gets a Vector3 for the minimum point + * \return The BoxCorner_FarLeftBottom of the box + * + * \see GetCorner, GetPosition + */ + template Vector3 Box::GetMinimum() const { - ///DOC: Alias de GetPosition() return GetPosition(); } + /*! + * \brief Computes the negative vertex of one direction + * \return The position of the vertex on the box in the opposite way of the normal while considering the center. It means that if the normal has one component negative, the component is set to width, height or depth corresponding to the sign + * + * \param normal Vector indicating a direction + * + * \see GetPositiveVertex + */ + template Vector3 Box::GetNegativeVertex(const Vector3& normal) const { @@ -200,12 +370,28 @@ namespace Nz return neg; } + /*! + * \brief Gets a Vector3 for the position + * \return The BoxCorner_FarLeftBottom of the box + * + * \see GetCorner, GetMinimum + */ + template Vector3 Box::GetPosition() const { return Vector3(x, y, z); } + /*! + * \brief Computes the positive vertex of one direction + * \return The position of the vertex on the box in the same way of the normal while considering the center. It means that if the normal has one component positive, the component is set to width or height corresponding to the sign + * + * \param normal Vector indicating a direction + * + * \see GetNegativeVertex + */ + template Vector3 Box::GetPositiveVertex(const Vector3& normal) const { @@ -223,27 +409,52 @@ namespace Nz return pos; } + /*! + * \brief Gets the radius of the box + * \return Value of the radius which is the biggest distance between a corner and the center + */ + template T Box::GetRadius() const { return std::sqrt(GetSquaredRadius()); } + /*! + * \brief Gets the squared bounding sphere for the box + * \return A sphere containing the box + * + * \see GetBoundingSphere + */ + template Sphere Box::GetSquaredBoundingSphere() const { return Sphere(GetCenter(), GetSquaredRadius()); } + /*! + * \brief Gets the squared radius of the box + * \return Value of the squared radius which is the squared of biggest distance between a corner and the center + */ + template T Box::GetSquaredRadius() const { Vector3 size(GetLengths()); - size /= F(2.0); // La taille étant relative à la position (minimum) de la boite et non pas à son centre + size /= F(2.0); // The size only depends on the lengths and not the center return size.GetSquaredLength(); } + /*! + * \brief Checks whether or not this box intersects another one + * \return true if the box intersects + * + * \param box Box to check + * \param intersection Optional argument for the box which represent the intersection + */ + template bool Box::Intersect(const Box& box, Box* intersection) const { @@ -275,12 +486,24 @@ namespace Nz return true; } + /*! + * \brief Checks whether this box is valid + * \return true if the box has a strictly positive width, height and depth + */ + template bool Box::IsValid() const { return width > F(0.0) && height > F(0.0) && depth > F(0.0); } + /*! + * \brief Makes the box position (0, 0, 0) and lengths (0, 0, 0) + * \return A reference to this box with position (0, 0, 0) and lengths (0, 0, 0) + * + * \see Zero + */ + template Box& Box::MakeZero() { @@ -294,6 +517,17 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the box with width, height and depth + * \return A reference to this box + * + * \param Width Width of the box (following X) + * \param Height Height of the box (following Y) + * \param Depth Depth of the box (following Z) + * + * \remark Position will be (0, 0, 0) + */ + template Box& Box::Set(T Width, T Height, T Depth) { @@ -307,6 +541,17 @@ namespace Nz return *this; } + /*! + * \brief Constructs a Box object from its position and sizes + * + * \param X X component of position + * \param Y Y component of position + * \param Z Z component of position + * \param Width Width of the box (following X) + * \param Height Height of the box (following Y) + * \param Depth Depth of the box (following Z) + */ + template Box& Box::Set(T X, T Y, T Z, T Width, T Height, T Depth) { @@ -320,6 +565,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the box from an array of six elements + * \return A reference to this box + * + * \param box[6] box[0] is X position, box[1] is Y position, box[2] is Z position, box[3] is width, box[4] is height and box[5] is depth + */ + template Box& Box::Set(const T box[6]) { @@ -333,6 +585,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the box with components from another + * \return A reference to this box + * + * \param box The other box + */ + template Box& Box::Set(const Box& box) { @@ -341,6 +600,15 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the box with components from a Rect + * \return A reference to this box + * + * \param rect Rectangle which describes (X, Y) position and (width, height) lenghts + * + * \remark Z position is 0 and depth is 1 + */ + template Box& Box::Set(const Rect& rect) { @@ -354,25 +622,50 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the box from a vector representing width, height and depth + * \return A reference to this box + * + * \param lengths (Width, Height, depth) of the box + * + * \remark Position will be (0, 0, 0) + */ + template Box& Box::Set(const Vector3& lengths) { return Set(lengths.x, lengths.y, lengths.z); } + /*! + * \brief Sets the components of the box from two vectors representing point of the space + * (X, Y, Z) will be the components minimum of the two vectors and the (width, height, depth) will be the components maximum - minimum + * \return A reference to this box + * + * \param vec1 First point + * \param vec2 Second point + */ + template Box& Box::Set(const Vector3& vec1, const Vector3& vec2) { x = std::min(vec1.x, vec2.x); y = std::min(vec1.y, vec2.y); z = std::min(vec1.z, vec2.z); - width = (vec2.x > vec1.x) ? vec2.x-vec1.x : vec1.x-vec2.x; - height = (vec2.y > vec1.y) ? vec2.y-vec1.y : vec1.y-vec2.y; - depth = (vec2.z > vec1.z) ? vec2.z-vec1.z : vec1.z-vec2.z; + width = (vec2.x > vec1.x) ? vec2.x - vec1.x : vec1.x - vec2.x; + height = (vec2.y > vec1.y) ? vec2.y - vec1.y : vec1.y - vec2.y; + depth = (vec2.z > vec1.z) ? vec2.z - vec1.z : vec1.z - vec2.z; return *this; } + /*! + * \brief Sets the components of the box from another type of Box + * \return A reference to this box + * + * \param box Box of type U to convert its components + */ + template template Box& Box::Set(const Box& box) @@ -387,6 +680,11 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Box(x, y, z, width, height, depth)" + */ + template String Box::ToString() const { @@ -395,19 +693,34 @@ namespace Nz return ss << "Box(" << x << ", " << y << ", " << z << ", " << width << ", " << height << ", " << depth << ')'; } + /*! + * \brief Transforms the box according to the matrix + * \return A reference to this box transformed + * + * \param matrix Matrix4 representing the transformation + * \param applyTranslation Should transform the position or the direction + */ + template Box& Box::Transform(const Matrix4& matrix, bool applyTranslation) { - Vector3 center = matrix.Transform(GetCenter(), (applyTranslation) ? F(1.0) : F(0.0)); // Valeur multipliant la translation - Vector3 halfSize = GetLengths()/F(2.0); + Vector3 center = matrix.Transform(GetCenter(), (applyTranslation) ? F(1.0) : F(0.0)); // Value multiplying the translation + Vector3 halfSize = GetLengths() / F(2.0); - halfSize.Set(std::abs(matrix(0,0))*halfSize.x + std::abs(matrix(1,0))*halfSize.y + std::abs(matrix(2,0))*halfSize.z, - std::abs(matrix(0,1))*halfSize.x + std::abs(matrix(1,1))*halfSize.y + std::abs(matrix(2,1))*halfSize.z, - std::abs(matrix(0,2))*halfSize.x + std::abs(matrix(1,2))*halfSize.y + std::abs(matrix(2,2))*halfSize.z); + halfSize.Set(std::abs(matrix(0,0)) * halfSize.x + std::abs(matrix(1,0)) * halfSize.y + std::abs(matrix(2,0)) * halfSize.z, + std::abs(matrix(0,1)) * halfSize.x + std::abs(matrix(1,1)) * halfSize.y + std::abs(matrix(2,1)) * halfSize.z, + std::abs(matrix(0,2)) * halfSize.x + std::abs(matrix(1,2)) * halfSize.y + std::abs(matrix(2,2)) * halfSize.z); return Set(center - halfSize, center + halfSize); } + /*! + * \brief Translates the box + * \return A reference to this box translated + * + * \param translation Vector3 which is the translation for the position + */ + template Box& Box::Translate(const Vector3& translation) { @@ -418,6 +731,15 @@ namespace Nz return *this; } + /*! + * \brief Returns the ith element of the box + * \return A reference to the ith element of the box + * + * \remark Access to index greather than 6 is undefined behavior + * \remark Produce a NazaraError if you try to acces to index greather than 6 with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 6 + */ + template T& Box::operator[](unsigned int i) { @@ -435,6 +757,15 @@ namespace Nz return *(&x+i); } + /*! + * \brief Returns the ith element of the box + * \return A value to the ith element of the box + * + * \remark Access to index greather than 6 is undefined behavior + * \remark Produce a NazaraError if you try to acces to index greather than 6 with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 6 + */ + template T Box::operator[](unsigned int i) const { @@ -452,18 +783,39 @@ namespace Nz return *(&x+i); } + /*! + * \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 + */ + template Box Box::operator*(T scalar) const { - return Box(x, y, z, width*scalar, height*scalar, depth*scalar); + return Box(x, y, z, width * scalar, height * scalar, depth * scalar); } + /*! + * \brief Multiplies the lengths with the vector + * \return A box where the position is the same and width, height and depth are the product of the old width, height and depth with the vec + * + * \param vec The vector where component one multiply width, two height and three depth + */ + template Box Box::operator*(const Vector3& vec) const { - return Box(x, y, z, width*vec.x, height*vec.y, depth*vec.z); + return Box(x, y, z, width * vec.x, height * vec.y, depth * vec.z); } + /*! + * \brief Multiplies the lengths of this box with the scalar + * \return A reference to this box where lengths are the product of these lengths and the scalar + * + * \param scalar The scalar to multiply width, height and depth with + */ + template Box& Box::operator*=(T scalar) { @@ -474,6 +826,13 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the lengths of this box with the vector + * \return A reference to this box where width, height and depth are the product of the old width, height and depth with the vec + * + * \param vec The vector where component one multiply width, two height and three depth + */ + template Box& Box::operator*=(const Vector3& vec) { @@ -484,19 +843,47 @@ namespace Nz return *this; } + /*! + * \brief Compares the box to other one + * \return true if the boxes are the same + * + * \param box Other box to compare with + */ + template bool Box::operator==(const Box& box) const { return NumberEquals(x, box.x) && NumberEquals(y, box.y) && NumberEquals(z, box.z) && - NumberEquals(width, box.width) && NumberEquals(height, box.height) && NumberEquals(depth, box.depth); + NumberEquals(width, box.width) && NumberEquals(height, box.height) && NumberEquals(depth, box.depth); } + /*! + * \brief Compares the box to other one + * \return false if the boxes are the same + * + * \param box Other box to compare with + */ + template bool Box::operator!=(const Box& box) const { return !operator==(box); } + /*! + * \brief Interpolates the box to other one with a factor of interpolation + * \return A new box which is the interpolation of two rectangles + * + * \param from Initial box + * \param to Target box + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * \remark With NAZARA_DEBUG, a NazaraError is thrown and Zero() is returned + * + * \see Lerp + */ + template Box Box::Lerp(const Box& from, const Box& to, T interpolation) { @@ -519,6 +906,13 @@ namespace Nz return box; } + /*! + * \brief Shorthand for the box (0, 0, 0, 0, 0, 0) + * \return A box with position (0, 0, 0) and lengths (0, 0, 0) + * + * \see MakeZero + */ + template Box Box::Zero() { @@ -527,8 +921,78 @@ namespace Nz return box; } + + /*! + * \brief Serializes a Box + * \return true if successfully serialized + * + * \param context Serialization context + * \param box Input Box + */ + template + bool Serialize(SerializationContext& context, const Box& box) + { + if (!Serialize(context, box.x)) + return false; + + if (!Serialize(context, box.y)) + return false; + + if (!Serialize(context, box.z)) + return false; + + if (!Serialize(context, box.width)) + return false; + + if (!Serialize(context, box.height)) + return false; + + if (!Serialize(context, box.depth)) + return false; + + return true; + } + + /*! + * \brief Unserializes a Box + * \return true if successfully unserialized + * + * \param context Serialization context + * \param box Output Box + */ + template + bool Unserialize(SerializationContext& context, Box* box) + { + if (!Unserialize(context, &box->x)) + return false; + + if (!Unserialize(context, &box->y)) + return false; + + if (!Unserialize(context, &box->z)) + return false; + + if (!Unserialize(context, &box->width)) + return false; + + if (!Unserialize(context, &box->height)) + return false; + + if (!Unserialize(context, &box->depth)) + return false; + + return true; + } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param box The box to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::Box& box) { diff --git a/include/Nazara/Math/Config.hpp b/include/Nazara/Math/Config.hpp index f265fdd52..a4891a18a 100644 --- a/include/Nazara/Math/Config.hpp +++ b/include/Nazara/Math/Config.hpp @@ -28,15 +28,20 @@ #ifndef NAZARA_CONFIG_MATH_HPP #define NAZARA_CONFIG_MATH_HPP -/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci +/*! +* \defgroup math (NazaraMath) Mathematics module +* 2D/3D mathematics module including matrix, vector, box, sphere, quaternion, ... +*/ -// Définit le radian comme l'unité utilisée pour les angles +/// Each modification of a paramater of the module needs a recompilation of the unit + +// Define the radian as unit for angles #define NAZARA_MATH_ANGLE_RADIAN 0 -// Optimise automatiquement les opérations entre matrices affines (Demande plusieurs comparaisons pour déterminer si une matrice est affine) +// Optimize automatically the operation on affine matrices (Ask several comparisons to determine if the matrix is affine) #define NAZARA_MATH_MATRIX4_CHECK_AFFINE 0 -// Active les tests de sécurité basés sur le code (Conseillé pour le développement) +// Enable tests of security based on the code (Advised for the developpement) #define NAZARA_MATH_SAFE 1 #endif // NAZARA_CONFIG_MATH_HPP diff --git a/include/Nazara/Math/EulerAngles.hpp b/include/Nazara/Math/EulerAngles.hpp index d323fe641..6ada39982 100644 --- a/include/Nazara/Math/EulerAngles.hpp +++ b/include/Nazara/Math/EulerAngles.hpp @@ -13,6 +13,8 @@ namespace Nz { + struct SerializationContext; + template class EulerAngles { @@ -28,14 +30,14 @@ namespace Nz void MakeZero(); - void Normalize(); + EulerAngles& Normalize(); - void Set(T P, T Y, T R); - void Set(const T angles[3]); - void Set(const EulerAngles& angles); - //void Set(const Matrix3& mat); - void Set(const Quaternion& quat); - template void Set(const EulerAngles& angles); + EulerAngles& Set(T P, T Y, T R); + EulerAngles& Set(const T angles[3]); + EulerAngles& Set(const EulerAngles& angles); + //EulerAngles& Set(const Matrix3& mat); + EulerAngles& Set(const Quaternion& quat); + template EulerAngles& Set(const EulerAngles& angles); //Matrix3 ToRotationMatrix() const; Quaternion ToQuaternion() const; @@ -61,6 +63,9 @@ namespace Nz typedef EulerAngles EulerAnglesd; typedef EulerAngles EulerAnglesf; + + template bool Serialize(SerializationContext& context, const EulerAngles& eulerAngles); + template bool Unserialize(SerializationContext& context, EulerAngles* eulerAngles); } template std::ostream& operator<<(std::ostream& out, const Nz::EulerAngles& angles); diff --git a/include/Nazara/Math/EulerAngles.inl b/include/Nazara/Math/EulerAngles.inl index 79e3c1ded..24bfd4630 100644 --- a/include/Nazara/Math/EulerAngles.inl +++ b/include/Nazara/Math/EulerAngles.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -14,24 +15,59 @@ namespace Nz { + + /*! + * \ingroup math + * \class Nz::EulerAngles + * \brief Math class that represents an Euler angle. Those describe a rotation transformation by rotating an object on its various axes in specified amounts per axis, and a specified axis order + * + * \remark Rotation are "left-handed", it means that you take your left hand, put your thumb finger in the direction you want and you other fingers represent the way of rotating + */ + + /*! + * \brief Constructs a EulerAngles object from its components + * + * \param P Pitch component = X axis + * \param Y Yaw component = Y axis + * \param R Roll component = Z axis + */ + template EulerAngles::EulerAngles(T P, T Y, T R) { Set(P, Y, R); } + /*! + * \brief Constructs a EulerAngles object from an array of three elements + * + * \param angles[3] angles[0] is pitch component, angles[1] is yaw component and angles[2] is roll component + */ + template EulerAngles::EulerAngles(const T angles[3]) { Set(angles); } + /*! + * \brief Constructs a EulerAngles object from a quaternion + * + * \param quat Quaternion representing a rotation of space + */ + template EulerAngles::EulerAngles(const Quaternion& quat) { Set(quat); } + /*! + * \brief Constructs a EulerAngles object from another type of EulerAngles + * + * \param angles EulerAngles of type U to convert to type T + */ + template template EulerAngles::EulerAngles(const EulerAngles& angles) @@ -39,57 +75,125 @@ namespace Nz Set(angles); } + /*! + * \brief Makes the euler angle (0, 0, 0) + * \return A reference to this euler angle with components (0, 0, 0) + * + * \see Zero + */ + template void EulerAngles::MakeZero() { Set(F(0.0), F(0.0), F(0.0)); } + /*! + * \brief Normalizes the euler angle + * \return A reference to this euler angle with has been normalized + * + * \remark Normalization depends on NAZARA_MATH_ANGLE_RADIAN, between 0..2*pi + * + * \see NormalizeAngle + */ + template - void EulerAngles::Normalize() + EulerAngles& EulerAngles::Normalize() { pitch = NormalizeAngle(pitch); yaw = NormalizeAngle(yaw); roll = NormalizeAngle(roll); + + return *this; } + /*! + * \brief Sets the components of the euler angle + * \return A reference to this euler angle + * + * \param P Pitch component = X axis + * \param Y Yaw component = Y axis + * \param R Roll component = Z axis + */ + template - void EulerAngles::Set(T P, T Y, T R) + EulerAngles& EulerAngles::Set(T P, T Y, T R) { pitch = P; yaw = Y; roll = R; + + return *this; } + /*! + * \brief Sets the components of the euler angle from an array of three elements + * \return A reference to this euler angle + * + * \param angles[3] angles[0] is pitch component, angles[1] is yaw component and angles[2] is roll component + */ + template - void EulerAngles::Set(const T angles[3]) + EulerAngles& EulerAngles::Set(const T angles[3]) { pitch = angles[0]; yaw = angles[1]; roll = angles[2]; + + return *this; } + /*! + * \brief Sets the components of the euler angle from another euler angle + * \return A reference to this euler angle + * + * \param angles The other euler angle + */ + template - void EulerAngles::Set(const EulerAngles& angles) + EulerAngles& EulerAngles::Set(const EulerAngles& angles) { std::memcpy(this, &angles, sizeof(EulerAngles)); + + return *this; } + /*! + * \brief Sets the components of the euler angle from a quaternion + * \return A reference to this euler angle + * + * \param quat Quaternion representing a rotation of space + */ + template - void EulerAngles::Set(const Quaternion& quat) + EulerAngles& EulerAngles::Set(const Quaternion& quat) { - Set(quat.ToEulerAngles()); + return Set(quat.ToEulerAngles()); } + /*! + * \brief Sets the components of the euler angle from another type of EulerAngles + * \return A reference to this euler angle + * + * \param angles EulerAngles of type U to convert its components + */ + template template - void EulerAngles::Set(const EulerAngles& angles) + EulerAngles& EulerAngles::Set(const EulerAngles& angles) { pitch = F(angles.pitch); yaw = F(angles.yaw); roll = F(angles.roll); + + return *this; } + /*! + * \brief Converts the euler angle to quaternion + * \return A Quaternion which represents the rotation of this euler angle + */ + template Quaternion EulerAngles::ToQuaternion() const { @@ -102,11 +206,16 @@ namespace Nz T s3 = std::sin(ToRadians(pitch) / F(2.0)); return Quaternion(c1 * c2 * c3 - s1 * s2 * s3, - s1 * s2 * c3 + c1 * c2 * s3, - s1 * c2 * c3 + c1 * s2 * s3, - c1 * s2 * c3 - s1 * c2 * s3); + s1 * s2 * c3 + c1 * c2 * s3, + s1 * c2 * c3 + c1 * s2 * s3, + c1 * s2 * c3 - s1 * c2 * s3); } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "EulerAngles(pitch, yaw, roll)" + */ + template String EulerAngles::ToString() const { @@ -115,22 +224,43 @@ namespace Nz return ss << "EulerAngles(" << pitch << ", " << yaw << ", " << roll << ')'; } + /*! + * \brief Adds the components of the euler angle with other euler angle + * \return A euler angle where components are the sum of this euler angle and the other one + * + * \param angles The other euler angle to add components with + */ + template EulerAngles EulerAngles::operator+(const EulerAngles& angles) const { return EulerAngles(pitch + angles.pitch, - yaw + angles.yaw, - roll + angles.roll); + yaw + angles.yaw, + roll + angles.roll); } + /*! + * \brief Substracts the components of the euler angle with other euler angle + * \return A euler angle where components are the difference of this euler angle and the other one + * + * \param angles The other euler angle to substract components with + */ + template EulerAngles EulerAngles::operator-(const EulerAngles& angles) const { return EulerAngles(pitch - angles.pitch, - yaw - angles.yaw, - roll - angles.roll); + yaw - angles.yaw, + roll - angles.roll); } + /*! + * \brief Adds the components of other euler angle to this euler angle + * \return A reference to this euler angle where components are the sum of this euler angle and the other one + * + * \param angles The other euler angle to add components with + */ + template EulerAngles& EulerAngles::operator+=(const EulerAngles& angles) { @@ -141,6 +271,13 @@ namespace Nz return *this; } + /*! + * \brief Substracts the components of other euler angle to this euler angle + * \return A reference to this euler angle where components are the difference of this euler angle and the other one + * + * \param angle The other euler angle to substract components with + */ + template EulerAngles& EulerAngles::operator-=(const EulerAngles& angles) { @@ -151,20 +288,41 @@ namespace Nz return *this; } + /*! + * \brief Compares the euler angle to other one + * \return true if the euler angles are the same + * + * \param angles Other euler angle to compare with + */ + template bool EulerAngles::operator==(const EulerAngles& angles) const { return NumberEquals(pitch, angles.pitch) && - NumberEquals(yaw, angles.yaw) && - NumberEquals(roll, angles.roll); + NumberEquals(yaw, angles.yaw) && + NumberEquals(roll, angles.roll); } + /*! + * \brief Compares the euler angle to other one + * \return false if the euler angles are the same + * + * \param angles Other euler angle to compare with + */ + template bool EulerAngles::operator!=(const EulerAngles& angles) const { return !operator==(angles); } + /*! + * \brief Shorthand for the euler angle (0, 0, 0) + * \return A euler angle with components (0, 0, 0) + * + * \see MakeZero + */ + template EulerAngles EulerAngles::Zero() { @@ -173,8 +331,60 @@ namespace Nz return angles; } + + /*! + * \brief Serializes a EulerAngles + * \return true if successfully serialized + * + * \param context Serialization context + * \param angles Input euler angles + */ + template + bool Serialize(SerializationContext& context, const EulerAngles& angles) + { + if (!Serialize(context, angles.pitch)) + return false; + + if (!Serialize(context, angles.yaw)) + return false; + + if (!Serialize(context, angles.roll)) + return false; + + return true; + } + + /*! + * \brief Unserializes a EulerAngles + * \return true if successfully unserialized + * + * \param context Serialization context + * \param angles Output euler angles + */ + template + bool Unserialize(SerializationContext& context, EulerAngles* angles) + { + if (!Unserialize(context, &angles->pitch)) + return false; + + if (!Unserialize(context, &angles->yaw)) + return false; + + if (!Unserialize(context, &angles->roll)) + return false; + + return true; + } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param angles The euler angle to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::EulerAngles& angles) { diff --git a/include/Nazara/Math/Frustum.hpp b/include/Nazara/Math/Frustum.hpp index 44c3bc0e7..2e51c0bd6 100644 --- a/include/Nazara/Math/Frustum.hpp +++ b/include/Nazara/Math/Frustum.hpp @@ -18,6 +18,8 @@ namespace Nz { + struct SerializationContext; + template class Frustum { @@ -53,6 +55,11 @@ namespace Nz String ToString() const; + template + friend bool Serialize(SerializationContext& context, const Frustum& frustum); + template + friend bool Unserialize(SerializationContext& context, Frustum* frustum); + private: Vector3 m_corners[BoxCorner_Max+1]; Plane m_planes[FrustumPlane_Max+1]; diff --git a/include/Nazara/Math/Frustum.inl b/include/Nazara/Math/Frustum.inl index e5bc2081b..b9f56c1e1 100644 --- a/include/Nazara/Math/Frustum.inl +++ b/include/Nazara/Math/Frustum.inl @@ -6,6 +6,7 @@ // http://www.crownandcutlass.com/features/technicaldetails/frustum.html // http://www.lighthouse3d.com/tutorials/view-frustum-culling/ +#include #include #include #include @@ -15,6 +16,21 @@ namespace Nz { + + /*! + * \ingroup math + * \class Nz::Frustum + * \brief Math class that represents a frustum in the three dimensional vector space + * + * Frustums are used to determine what is inside the camera's field of view. They help speed up the rendering process + */ + + /*! + * \brief Constructs a Frustum object from another type of Frustum + * + * \param frustum Frustum of type U to convert to type T + */ + template template Frustum::Frustum(const Frustum& frustum) @@ -22,6 +38,19 @@ namespace Nz Set(frustum); } + /*! + * \brief Builds the frustum object + * \return A reference to this frustum which is the build up camera's field of view + * + * \param angle Unit depends on NAZARA_MATH_ANGLE_RADIAN + * \param ratio Rendering ratio (typically 16/9 or 4/3) + * \param zNear Distance where 'vision' begins + * \param zFar Distance where 'vision' ends + * \param eye Position of the camera + * \param target Position of the target of the camera + * \param up Direction of up vector according to the orientation of camera + */ + template Frustum& Frustum::Build(T angle, T ratio, T zNear, T zFar, const Vector3& eye, const Vector3& target, const Vector3& up) { @@ -45,18 +74,18 @@ namespace Nz Vector3 nc = eye + f * zNear; Vector3 fc = eye + f * zFar; - // Calcul du frustum - m_corners[BoxCorner_FarLeftBottom] = fc - u*farH - s*farW; - m_corners[BoxCorner_FarLeftTop] = fc + u*farH - s*farW; - m_corners[BoxCorner_FarRightTop] = fc + u*farH + s*farW; - m_corners[BoxCorner_FarRightBottom] = fc - u*farH + s*farW; + // Computing the frustum + m_corners[BoxCorner_FarLeftBottom] = fc - u * farH - s * farW; + m_corners[BoxCorner_FarLeftTop] = fc + u * farH - s * farW; + m_corners[BoxCorner_FarRightTop] = fc + u * farH + s * farW; + m_corners[BoxCorner_FarRightBottom] = fc - u * farH + s * farW; - m_corners[BoxCorner_NearLeftBottom] = nc - u*nearH - s*nearW; - m_corners[BoxCorner_NearLeftTop] = nc + u*nearH - s*nearW; - m_corners[BoxCorner_NearRightTop] = nc + u*nearH + s*nearW; - m_corners[BoxCorner_NearRightBottom] = nc - u*nearH + s*nearW; + m_corners[BoxCorner_NearLeftBottom] = nc - u * nearH - s * nearW; + m_corners[BoxCorner_NearLeftTop] = nc + u * nearH - s * nearW; + m_corners[BoxCorner_NearRightTop] = nc + u * nearH + s * nearW; + m_corners[BoxCorner_NearRightBottom] = nc - u * nearH + s * nearW; - // Construction des plans du frustum + // Construction of frustum's planes m_planes[FrustumPlane_Bottom].Set(m_corners[BoxCorner_NearLeftBottom], m_corners[BoxCorner_NearRightBottom], m_corners[BoxCorner_FarRightBottom]); m_planes[FrustumPlane_Far].Set(m_corners[BoxCorner_FarRightTop], m_corners[BoxCorner_FarLeftTop], m_corners[BoxCorner_FarLeftBottom]); m_planes[FrustumPlane_Left].Set(m_corners[BoxCorner_NearLeftTop], m_corners[BoxCorner_NearLeftBottom], m_corners[BoxCorner_FarLeftBottom]); @@ -67,6 +96,18 @@ namespace Nz return *this; } + /*! + * \brief Checks whether or not a bounding volume is contained in the frustum + * \return true if the bounding volume is entirely in the frustum + * + * \param volume Volume to check + * + * \remark If volume is infinite, true is returned + * \remark If volume is null, false 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 + */ + template bool Frustum::Contains(const BoundingVolume& volume) const { @@ -102,11 +143,18 @@ namespace Nz return false; } + /*! + * \brief Checks whether or not a box is contained in the frustum + * \return true if the box is entirely in the frustum + * + * \param box Box to check + */ + template bool Frustum::Contains(const Box& box) const { // http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ - for(unsigned int i = 0; i <= FrustumPlane_Max; i++) + for (unsigned int i = 0; i <= FrustumPlane_Max; i++) { if (m_planes[i].Distance(box.GetPositiveVertex(m_planes[i].normal)) < F(0.0)) return false; @@ -115,16 +163,30 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not an oriented box is contained in the frustum + * \return true if the oriented box is entirely in the frustum + * + * \param orientedbox Oriented box to check + */ + template bool Frustum::Contains(const OrientedBox& orientedbox) const { return Contains(&orientedbox[0], 8); } + /*! + * \brief Checks whether or not a sphere is contained in the frustum + * \return true if the sphere is entirely in the frustum + * + * \param sphere Sphere to check + */ + template bool Frustum::Contains(const Sphere& sphere) const { - for(unsigned int i = 0; i <= FrustumPlane_Max; i++) + for (unsigned int i = 0; i <= FrustumPlane_Max; i++) { if (m_planes[i].Distance(sphere.GetPosition()) < -sphere.radius) return false; @@ -133,10 +195,17 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not a Vector3 is contained in the frustum + * \return true if the Vector3 is in the frustum + * + * \param point Vector3 which represents a point in the space + */ + template bool Frustum::Contains(const Vector3& point) const { - for(unsigned int i = 0; i <= FrustumPlane_Max; ++i) + for (unsigned int i = 0; i <= FrustumPlane_Max; ++i) { if (m_planes[i].Distance(point) < F(0.0)) return false; @@ -145,6 +214,14 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not a set of Vector3 is contained in the frustum + * \return true if the set of Vector3 is in the frustum + * + * \param points Pointer to Vector3 which represents a set of points in the space + * \param pointCount Number of points to check + */ + template bool Frustum::Contains(const Vector3* points, unsigned int pointCount) const { @@ -164,6 +241,15 @@ namespace Nz return true; } + /*! + * \brief Constructs the frustum from a Matrix4 + * \return A reference to this frustum which is the build up of projective matrix + * + * \param clipMatrix Matrix which represents the transformation of the frustum + * + * \remark A NazaraWarning is produced if clipMatrix is not inversible and corners are unchanged + */ + template Frustum& Frustum::Extract(const Matrix4& clipMatrix) { @@ -178,7 +264,7 @@ namespace Nz plane[3] = clipMatrix[15] - clipMatrix[12]; // Normalize the result - invLength = F(1.0) / std::sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); + invLength = F(1.0) / std::sqrt(plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2]); plane[0] *= invLength; plane[1] *= invLength; plane[2] *= invLength; @@ -193,7 +279,7 @@ namespace Nz plane[3] = clipMatrix[15] + clipMatrix[12]; // Normalize the result - invLength = F(1.0) / std::sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); + invLength = F(1.0) / std::sqrt(plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2]); plane[0] *= invLength; plane[1] *= invLength; plane[2] *= invLength; @@ -208,7 +294,7 @@ namespace Nz plane[3] = clipMatrix[15] + clipMatrix[13]; // Normalize the result - invLength = F(1.0) / std::sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); + invLength = F(1.0) / std::sqrt(plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2]); plane[0] *= invLength; plane[1] *= invLength; plane[2] *= invLength; @@ -223,7 +309,7 @@ namespace Nz plane[3] = clipMatrix[15] - clipMatrix[13]; // Normalize the result - invLength = F(1.0) / std::sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); + invLength = F(1.0) / std::sqrt(plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2]); plane[0] *= invLength; plane[1] *= invLength; plane[2] *= invLength; @@ -238,7 +324,7 @@ namespace Nz plane[3] = clipMatrix[15] - clipMatrix[14]; // Normalize the result - invLength = F(1.0) / std::sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); + invLength = F(1.0) / std::sqrt(plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2]); plane[0] *= invLength; plane[1] *= invLength; plane[2] *= invLength; @@ -253,7 +339,7 @@ namespace Nz plane[3] = clipMatrix[15] + clipMatrix[14]; // Normalize the result - invLength = F(1.0) / std::sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); + invLength = F(1.0) / std::sqrt(plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2]); plane[0] *= invLength; plane[1] *= invLength; plane[2] *= invLength; @@ -261,8 +347,8 @@ namespace Nz m_planes[FrustumPlane_Near].Set(plane); - // Une fois les plans extraits, il faut extraire les points du frustum - // Je me base sur cette page: http://www.gamedev.net/topic/393309-calculating-the-view-frustums-vertices/ + // Once planes have been extracted, we must extract points of the frustum + // Based on: http://www.gamedev.net/topic/393309-calculating-the-view-frustums-vertices/ Matrix4 invClipMatrix; if (clipMatrix.GetInverse(&invClipMatrix)) @@ -331,12 +417,31 @@ namespace Nz return *this; } + /*! + * \brief Constructs the frustum from the view matrix and the projection matrix + * \return A reference to this frustum which is the build up of projective matrix + * + * \param view Matrix which represents the view + * \param projection Matrix which represents the projection (the perspective) + * + * \remark A NazaraWarning is produced if the product of these matrices is not inversible and corners are unchanged + */ + template Frustum& Frustum::Extract(const Matrix4& view, const Matrix4& projection) { return Extract(Matrix4::Concatenate(view, projection)); } + /*! + * \brief Gets the Vector3 for the corner + * \return The position of the corner of the frustum according to enum BoxCorner + * + * \param corner Enumeration of type BoxCorner + * + * \remark If enumeration is not defined in BoxCorner and NAZARA_DEBUG defined, a NazaraError is thrown and a Vector3 uninitialised is returned + */ + template const Vector3& Frustum::GetCorner(BoxCorner corner) const { @@ -353,6 +458,15 @@ namespace Nz return m_corners[corner]; } + /*! + * \brief Gets the Plane for the face + * \return The face of the frustum according to enum FrustumPlane + * + * \param plane Enumeration of type FrustumPlane + * + * \remark If enumeration is not defined in FrustumPlane and NAZARA_DEBUG defined, a NazaraError is thrown and a Plane uninitialised is returned + */ + template const Plane& Frustum::GetPlane(FrustumPlane plane) const { @@ -369,6 +483,18 @@ namespace Nz return m_planes[plane]; } + /*! + * \brief Checks whether or not a bounding volume intersects with the frustum + * \return IntersectionSide How the bounding volume is intersecting with the frustum + * + * \param volume Volume to check + * + * \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 + */ + template IntersectionSide Frustum::Intersect(const BoundingVolume& volume) const { @@ -394,7 +520,7 @@ namespace Nz } case Extend_Infinite: - return IntersectionSide_Intersecting; // On ne peut pas contenir l'infini + return IntersectionSide_Intersecting; // We can not contain infinity case Extend_Null: return IntersectionSide_Outside; @@ -404,13 +530,20 @@ namespace Nz return IntersectionSide_Outside; } + /*! + * \brief Checks whether or not a box intersects with the frustum + * \return IntersectionSide How the box is intersecting with the frustum + * + * \param box Box to check + */ + template IntersectionSide Frustum::Intersect(const Box& box) const { // http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ IntersectionSide side = IntersectionSide_Inside; - for(unsigned int i = 0; i <= FrustumPlane_Max; i++) + for (unsigned int i = 0; i <= FrustumPlane_Max; i++) { if (m_planes[i].Distance(box.GetPositiveVertex(m_planes[i].normal)) < F(0.0)) return IntersectionSide_Outside; @@ -421,19 +554,33 @@ namespace Nz return side; } + /*! + * \brief Checks whether or not an oriented box intersects with the frustum + * \return IntersectionSide How the oriented box is intersecting with the frustum + * + * \param oriented box OrientedBox to check + */ + template IntersectionSide Frustum::Intersect(const OrientedBox& orientedbox) const { return Intersect(&orientedbox[0], 8); } + /*! + * \brief Checks whether or not a sphere intersects with the frustum + * \return IntersectionSide How the sphere is intersecting with the frustum + * + * \param sphere Sphere to check + */ + template IntersectionSide Frustum::Intersect(const Sphere& sphere) const { // http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-points-and-spheres/ IntersectionSide side = IntersectionSide_Inside; - for(unsigned int i = 0; i <= FrustumPlane_Max; i++) + for (unsigned int i = 0; i <= FrustumPlane_Max; i++) { T distance = m_planes[i].Distance(sphere.GetPosition()); if (distance < -sphere.radius) @@ -445,6 +592,14 @@ namespace Nz return side; } + /*! + * \brief Checks whether or not a set of Vector3 intersects with the frustum + * \return IntersectionSide How the set of Vector3 is intersecting with the frustum + * + * \param points Pointer to Vector3 which represents a set of points in the space + * \param pointCount Number of points to check + */ + template IntersectionSide Frustum::Intersect(const Vector3* points, unsigned int pointCount) const { @@ -468,6 +623,13 @@ namespace Nz return (c == 6) ? IntersectionSide_Inside : IntersectionSide_Intersecting; } + /*! + * \brief Sets the components of the frustum from another frustum + * \return A reference to this frustum + * + * \param frustum The other frustum + */ + template Frustum& Frustum::Set(const Frustum& frustum) { @@ -476,6 +638,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the frustum from another type of Frustum + * \return A reference to this frustum + * + * \param frustum Frustum of type U to convert its components + */ + template template Frustum& Frustum::Set(const Frustum& frustum) @@ -489,20 +658,83 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Frustum(Plane ...)" + */ + template String Frustum::ToString() const { StringStream ss; return ss << "Frustum(Bottom: " << m_planes[FrustumPlane_Bottom].ToString() << "\n" - << " Far: " << m_planes[FrustumPlane_Far].ToString() << "\n" - << " Left: " << m_planes[FrustumPlane_Left].ToString() << "\n" - << " Near: " << m_planes[FrustumPlane_Near].ToString() << "\n" - << " Right: " << m_planes[FrustumPlane_Right].ToString() << "\n" - << " Top: " << m_planes[FrustumPlane_Top].ToString() << ")\n"; + << " Far: " << m_planes[FrustumPlane_Far].ToString() << "\n" + << " Left: " << m_planes[FrustumPlane_Left].ToString() << "\n" + << " Near: " << m_planes[FrustumPlane_Near].ToString() << "\n" + << " Right: " << m_planes[FrustumPlane_Right].ToString() << "\n" + << " Top: " << m_planes[FrustumPlane_Top].ToString() << ")\n"; + } + + /*! + * \brief Serializes a Frustum + * \return true if successfully serialized + * + * \param context Serialization context + * \param matrix Input frustum + */ + template + bool Serialize(SerializationContext& context, const Frustum& frustum) + { + for (unsigned int i = 0; i <= BoxCorner_Max; ++i) + { + if (!Serialize(context, frustum.m_corners[i])) + return false; + } + + for (unsigned int i = 0; i <= FrustumPlane_Max; ++i) + { + if (!Serialize(context, frustum.m_planes[i])) + return false; + } + + return true; + } + + /*! + * \brief Unserializes a Frustum + * \return true if successfully unserialized + * + * \param context Serialization context + * \param matrix Output frustum + */ + template + bool Unserialize(SerializationContext& context, Frustum* frustum) + { + for (unsigned int i = 0; i <= BoxCorner_Max; ++i) + { + if (!Unserialize(context, &frustum->m_corners[i])) + return false; + } + + for (unsigned int i = 0; i <= FrustumPlane_Max; ++i) + { + if (!Unserialize(context, &frustum->m_planes[i])) + return false; + } + + return true; } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param frustum The frustum to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::Frustum& frustum) { diff --git a/include/Nazara/Math/Matrix4.hpp b/include/Nazara/Math/Matrix4.hpp index d72529d92..91927d68a 100644 --- a/include/Nazara/Math/Matrix4.hpp +++ b/include/Nazara/Math/Matrix4.hpp @@ -14,6 +14,8 @@ namespace Nz { + struct SerializationContext; + template class EulerAngles; template class Quaternion; template class Vector2; @@ -26,9 +28,9 @@ namespace Nz public: Matrix4() = default; Matrix4(T r11, T r12, T r13, T r14, - T r21, T r22, T r23, T r24, - T r31, T r32, T r33, T r34, - T r41, T r42, T r43, T r44); + T r21, T r22, T r23, T r24, + T r31, T r32, T r33, T r34, + T r41, T r42, T r43, T r44); //Matrix4(const Matrix3& matrix); Matrix4(const T matrix[16]); template explicit Matrix4(const Matrix4& matrix); @@ -77,9 +79,9 @@ namespace Nz Matrix4& MakeZero(); Matrix4& Set(T r11, T r12, T r13, T r14, - T r21, T r22, T r23, T r24, - T r31, T r32, T r33, T r34, - T r41, T r42, T r43, T r44); + T r21, T r22, T r23, T r24, + T r31, T r32, T r33, T r34, + T r41, T r42, T r43, T r44); Matrix4& Set(const T matrix[16]); //Matrix4(const Matrix3& matrix); Matrix4& Set(const Matrix4& matrix); @@ -96,8 +98,8 @@ namespace Nz Matrix4& Transpose(); - operator T*(); - operator const T*() const; + operator T* (); + operator const T* () const; T& operator()(unsigned int x, unsigned int y); T operator()(unsigned int x, unsigned int y) const; @@ -131,13 +133,16 @@ namespace Nz static Matrix4 Zero(); T m11, m12, m13, m14, - m21, m22, m23, m24, - m31, m32, m33, m34, - m41, m42, m43, m44; + m21, m22, m23, m24, + m31, m32, m33, m34, + m41, m42, m43, m44; }; typedef Matrix4 Matrix4d; typedef Matrix4 Matrix4f; + + template bool Serialize(SerializationContext& context, const Matrix4& matrix); + template bool Unserialize(SerializationContext& context, Matrix4* matrix); } template std::ostream& operator<<(std::ostream& out, const Nz::Matrix4& matrix); diff --git a/include/Nazara/Math/Matrix4.inl b/include/Nazara/Math/Matrix4.inl index ec4064f46..b0decee2a 100644 --- a/include/Nazara/Math/Matrix4.inl +++ b/include/Nazara/Math/Matrix4.inl @@ -1,7 +1,8 @@ -// Copyright (C) 2015 Jérôme Leclercq +// 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 +#include #include #include #include @@ -20,24 +21,51 @@ namespace Nz { + + /*! + * \ingroup math + * \class Nz::Matrix4 + * \brief Math class that represents a transformation of the four dimensional vector space with the notion of projectivity + * + * \remark Matrix4 is said to be "row-major" and affine if last column is made of (0, 0, 0, 1) + */ + + /*! + * \brief Constructs a Matrix4 object from its components + * + * \param rIJ Matrix components at index(I, J) + */ + template Matrix4::Matrix4(T r11, T r12, T r13, T r14, T r21, T r22, T r23, T r24, - T r31, T r32, T r33, T r34, - T r41, T r42, T r43, T r44) + T r31, T r32, T r33, T r34, + T r41, T r42, T r43, T r44) { Set(r11, r12, r13, r14, r21, r22, r23, r24, - r31, r32, r33, r34, - r41, r42, r43, r44); + r31, r32, r33, r34, + r41, r42, r43, r44); } + /*! + * \brief Constructs a Matrix4 object from an array of sixteen elements + * + * \param matrix[16] Matrix components + */ + template Matrix4::Matrix4(const T matrix[16]) { Set(matrix); } + /*! + * \brief Constructs a Matrix4 object from another type of Matrix4 + * + * \param matrix Matrix4 of type U to convert to type T + */ + template template Matrix4::Matrix4(const Matrix4& matrix) @@ -45,12 +73,26 @@ namespace Nz Set(matrix); } + /*! + * \brief Apply the rotation represented by the quaternion to this matrix + * \return A reference to this matrix which has been rotated + * + * \param rotation Quaternion representing a rotation of space + */ + template Matrix4& Matrix4::ApplyRotation(const Quaternion& rotation) { return Concatenate(Matrix4::Rotate(rotation)); } + /*! + * \brief Apply the scale represented by the vector to this matrix + * \return A reference to this matrix which has been scaled + * + * \param scale Vector3 representing the homothety + */ + template Matrix4& Matrix4::ApplyScale(const Vector3& scale) { @@ -69,6 +111,13 @@ namespace Nz return *this; } + /*! + * \brief Apply the translation represented by the vector to this matrix + * \return A reference to this matrix which has been translated + * + * \param translation Vector3 representing the translation + */ + template Matrix4& Matrix4::ApplyTranslation(const Vector3& translation) { @@ -79,6 +128,17 @@ namespace Nz return *this; } + /*! + * \brief Concatenates this matrix to other one + * \return A reference to this matrix which is the product with other one + * + * \param matrix Matrix to multiply with + * + * \remark if NAZARA_MATH_MATRIX4_CHECK_AFFINE is defined, ConcatenateAffine is called + * + * \see ConcatenateAffine + */ + template Matrix4& Matrix4::Concatenate(const Matrix4& matrix) { @@ -88,26 +148,37 @@ namespace Nz #endif return Set(m11*matrix.m11 + m12*matrix.m21 + m13*matrix.m31 + m14*matrix.m41, - m11*matrix.m12 + m12*matrix.m22 + m13*matrix.m32 + m14*matrix.m42, - m11*matrix.m13 + m12*matrix.m23 + m13*matrix.m33 + m14*matrix.m43, - m11*matrix.m14 + m12*matrix.m24 + m13*matrix.m34 + m14*matrix.m44, + m11*matrix.m12 + m12*matrix.m22 + m13*matrix.m32 + m14*matrix.m42, + m11*matrix.m13 + m12*matrix.m23 + m13*matrix.m33 + m14*matrix.m43, + m11*matrix.m14 + m12*matrix.m24 + m13*matrix.m34 + m14*matrix.m44, - m21*matrix.m11 + m22*matrix.m21 + m23*matrix.m31 + m24*matrix.m41, - m21*matrix.m12 + m22*matrix.m22 + m23*matrix.m32 + m24*matrix.m42, - m21*matrix.m13 + m22*matrix.m23 + m23*matrix.m33 + m24*matrix.m43, - m21*matrix.m14 + m22*matrix.m24 + m23*matrix.m34 + m24*matrix.m44, + m21*matrix.m11 + m22*matrix.m21 + m23*matrix.m31 + m24*matrix.m41, + m21*matrix.m12 + m22*matrix.m22 + m23*matrix.m32 + m24*matrix.m42, + m21*matrix.m13 + m22*matrix.m23 + m23*matrix.m33 + m24*matrix.m43, + m21*matrix.m14 + m22*matrix.m24 + m23*matrix.m34 + m24*matrix.m44, - m31*matrix.m11 + m32*matrix.m21 + m33*matrix.m31 + m34*matrix.m41, - m31*matrix.m12 + m32*matrix.m22 + m33*matrix.m32 + m34*matrix.m42, - m31*matrix.m13 + m32*matrix.m23 + m33*matrix.m33 + m34*matrix.m43, - m31*matrix.m14 + m32*matrix.m24 + m33*matrix.m34 + m34*matrix.m44, + m31*matrix.m11 + m32*matrix.m21 + m33*matrix.m31 + m34*matrix.m41, + m31*matrix.m12 + m32*matrix.m22 + m33*matrix.m32 + m34*matrix.m42, + m31*matrix.m13 + m32*matrix.m23 + m33*matrix.m33 + m34*matrix.m43, + m31*matrix.m14 + m32*matrix.m24 + m33*matrix.m34 + m34*matrix.m44, - m41*matrix.m11 + m42*matrix.m21 + m43*matrix.m31 + m44*matrix.m41, - m41*matrix.m12 + m42*matrix.m22 + m43*matrix.m32 + m44*matrix.m42, - m41*matrix.m13 + m42*matrix.m23 + m43*matrix.m33 + m44*matrix.m43, - m41*matrix.m14 + m42*matrix.m24 + m43*matrix.m34 + m44*matrix.m44); + m41*matrix.m11 + m42*matrix.m21 + m43*matrix.m31 + m44*matrix.m41, + m41*matrix.m12 + m42*matrix.m22 + m43*matrix.m32 + m44*matrix.m42, + m41*matrix.m13 + m42*matrix.m23 + m43*matrix.m33 + m44*matrix.m43, + m41*matrix.m14 + m42*matrix.m24 + m43*matrix.m34 + m44*matrix.m44); } + /*! + * \brief Concatenates this matrix to other one + * \return A reference to this matrix which is the product with other one + * + * \param matrix Matrix to multiply with + * + * \remark if NAZARA_DEBUG is defined and matrices are not affine, a NazaraWarning is produced and Concatenate is called + * + * \see Concatenate + */ + template Matrix4& Matrix4::ConcatenateAffine(const Matrix4& matrix) { @@ -126,26 +197,36 @@ namespace Nz #endif return Set(m11*matrix.m11 + m12*matrix.m21 + m13*matrix.m31, - m11*matrix.m12 + m12*matrix.m22 + m13*matrix.m32, - m11*matrix.m13 + m12*matrix.m23 + m13*matrix.m33, - F(0.0), + m11*matrix.m12 + m12*matrix.m22 + m13*matrix.m32, + m11*matrix.m13 + m12*matrix.m23 + m13*matrix.m33, + F(0.0), - m21*matrix.m11 + m22*matrix.m21 + m23*matrix.m31, - m21*matrix.m12 + m22*matrix.m22 + m23*matrix.m32, - m21*matrix.m13 + m22*matrix.m23 + m23*matrix.m33, - F(0.0), + m21*matrix.m11 + m22*matrix.m21 + m23*matrix.m31, + m21*matrix.m12 + m22*matrix.m22 + m23*matrix.m32, + m21*matrix.m13 + m22*matrix.m23 + m23*matrix.m33, + F(0.0), - m31*matrix.m11 + m32*matrix.m21 + m33*matrix.m31, - m31*matrix.m12 + m32*matrix.m22 + m33*matrix.m32, - m31*matrix.m13 + m32*matrix.m23 + m33*matrix.m33, - F(0.0), + m31*matrix.m11 + m32*matrix.m21 + m33*matrix.m31, + m31*matrix.m12 + m32*matrix.m22 + m33*matrix.m32, + m31*matrix.m13 + m32*matrix.m23 + m33*matrix.m33, + F(0.0), - m41*matrix.m11 + m42*matrix.m21 + m43*matrix.m31 + matrix.m41, - m41*matrix.m12 + m42*matrix.m22 + m43*matrix.m32 + matrix.m42, - m41*matrix.m13 + m42*matrix.m23 + m43*matrix.m33 + matrix.m43, - F(1.0)); + m41*matrix.m11 + m42*matrix.m21 + m43*matrix.m31 + matrix.m41, + m41*matrix.m12 + m42*matrix.m22 + m43*matrix.m32 + matrix.m42, + m41*matrix.m13 + m42*matrix.m23 + m43*matrix.m33 + matrix.m43, + F(1.0)); } + /*! + * \brief Gets the ith column of the matrix + * \return Vector4 which is the transformation of this axis + * + * \param column Index of the column you want + * + * \remark Produce a NazaraError if you try to access index greater than 3 with NAZARA_MATH_SAFE defined + * \throw std::out_of_range if NAZARA_MATH_SAFE is defined and if you try to access index greater than 3 + */ + template Vector4 Matrix4::GetColumn(unsigned int column) const { @@ -154,10 +235,10 @@ namespace Nz #if NAZARA_MATH_SAFE if (column > 3) { - StringStream ss; - ss << "Row out of range: (" << column << ") > 3"; + String error("Column out of range: (" + String::Number(column) + ") > 3"); - throw std::out_of_range(ss.ToString()); + NazaraError(error); + throw std::out_of_range(error); } #endif @@ -165,9 +246,23 @@ namespace Nz return Vector4(ptr); } + /*! + * \brief Calcultes the determinant of this matrix + * \return The value of the determinant + * + * \remark if NAZARA_MATH_MATRIX4_CHECK_AFFINE is defined, GetDeterminantAffine is called + * + * \see GetDeterminantAffine + */ + template T Matrix4::GetDeterminant() const { + #if NAZARA_MATH_MATRIX4_CHECK_AFFINE + if (IsAffine()) + return GetDeterminantAffine(); + #endif + T A = m22*(m33*m44 - m43*m34) - m32*(m23*m44 - m43*m24) + m42*(m23*m34 - m33*m24); T B = m12*(m33*m44 - m43*m34) - m32*(m13*m44 - m43*m14) + m42*(m13*m34 - m33*m14); T C = m12*(m23*m44 - m43*m24) - m22*(m13*m44 - m43*m14) + m42*(m13*m24 - m23*m14); @@ -176,9 +271,26 @@ namespace Nz return m11*A - m21*B + m31*C - m41*D; } + /*! + * \brief Calcultes the determinant of this matrix + * \return The value of the determinant + * + * \remark if NAZARA_DEBUG is defined and matrix is not affine, a NazaraWarning is produced and GetDeterminant is called + * + * \see GetDeterminant + */ + template T Matrix4::GetDeterminantAffine() const { + #ifdef NAZARA_DEBUG + if (!IsAffine()) + { + NazaraWarning("First matrix not affine"); + return GetDeterminant(); + } + #endif + T A = m22*m33 - m32*m23; T B = m12*m33 - m32*m13; T C = m12*m23 - m22*m13; @@ -186,10 +298,27 @@ namespace Nz return m11*A - m21*B + m31*C; } + /*! + * \brief Gets the inverse of this matrix + * \return true if matrix can be inverted + * + * \param dest Matrix to put the result + * + * \remark You can call this method on the same object + * \remark if NAZARA_MATH_MATRIX4_CHECK_AFFINE is defined, GetInverseAffine is called + * \remark if NAZARA_DEBUG is defined, a NazaraError is produced if dest is null and false is returned + * + * \see GetInverseAffine + */ + template bool Matrix4::GetInverse(Matrix4* dest) const { - ///DOC: Il est possible d'appeler cette méthode avec la même matrice en argument qu'en appelant + #if NAZARA_MATH_MATRIX4_CHECK_AFFINE + if (IsAffine()) + return GetInverseAffine(dest); + #endif + #ifdef NAZARA_DEBUG if (!dest) { @@ -199,121 +328,121 @@ namespace Nz #endif T det = GetDeterminant(); - if (!NumberEquals(det, F(0.0))) + if (det != T(0.0)) { // http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix T inv[16]; inv[0] = m22 * m33 * m44 - - m22 * m34 * m43 - - m32 * m23 * m44 + - m32 * m24 * m43 + - m42 * m23 * m34 - - m42 * m24 * m33; + m22 * m34 * m43 - + m32 * m23 * m44 + + m32 * m24 * m43 + + m42 * m23 * m34 - + m42 * m24 * m33; inv[1] = -m12 * m33 * m44 + - m12 * m34 * m43 + - m32 * m13 * m44 - - m32 * m14 * m43 - - m42 * m13 * m34 + - m42 * m14 * m33; + m12 * m34 * m43 + + m32 * m13 * m44 - + m32 * m14 * m43 - + m42 * m13 * m34 + + m42 * m14 * m33; inv[2] = m12 * m23 * m44 - - m12 * m24 * m43 - - m22 * m13 * m44 + - m22 * m14 * m43 + - m42 * m13 * m24 - - m42 * m14 * m23; + m12 * m24 * m43 - + m22 * m13 * m44 + + m22 * m14 * m43 + + m42 * m13 * m24 - + m42 * m14 * m23; inv[3] = -m12 * m23 * m34 + - m12 * m24 * m33 + - m22 * m13 * m34 - - m22 * m14 * m33 - - m32 * m13 * m24 + - m32 * m14 * m23; + m12 * m24 * m33 + + m22 * m13 * m34 - + m22 * m14 * m33 - + m32 * m13 * m24 + + m32 * m14 * m23; inv[4] = -m21 * m33 * m44 + - m21 * m34 * m43 + - m31 * m23 * m44 - - m31 * m24 * m43 - - m41 * m23 * m34 + - m41 * m24 * m33; + m21 * m34 * m43 + + m31 * m23 * m44 - + m31 * m24 * m43 - + m41 * m23 * m34 + + m41 * m24 * m33; inv[5] = m11 * m33 * m44 - - m11 * m34 * m43 - - m31 * m13 * m44 + - m31 * m14 * m43 + - m41 * m13 * m34 - - m41 * m14 * m33; + m11 * m34 * m43 - + m31 * m13 * m44 + + m31 * m14 * m43 + + m41 * m13 * m34 - + m41 * m14 * m33; inv[6] = -m11 * m23 * m44 + - m11 * m24 * m43 + - m21 * m13 * m44 - - m21 * m14 * m43 - - m41 * m13 * m24 + - m41 * m14 * m23; + m11 * m24 * m43 + + m21 * m13 * m44 - + m21 * m14 * m43 - + m41 * m13 * m24 + + m41 * m14 * m23; inv[7] = m11 * m23 * m34 - - m11 * m24 * m33 - - m21 * m13 * m34 + - m21 * m14 * m33 + - m31 * m13 * m24 - - m31 * m14 * m23; + m11 * m24 * m33 - + m21 * m13 * m34 + + m21 * m14 * m33 + + m31 * m13 * m24 - + m31 * m14 * m23; inv[8] = m21 * m32 * m44 - - m21 * m34 * m42 - - m31 * m22 * m44 + - m31 * m24 * m42 + - m41 * m22 * m34 - - m41 * m24 * m32; + m21 * m34 * m42 - + m31 * m22 * m44 + + m31 * m24 * m42 + + m41 * m22 * m34 - + m41 * m24 * m32; inv[9] = -m11 * m32 * m44 + - m11 * m34 * m42 + - m31 * m12 * m44 - - m31 * m14 * m42 - - m41 * m12 * m34 + - m41 * m14 * m32; + m11 * m34 * m42 + + m31 * m12 * m44 - + m31 * m14 * m42 - + m41 * m12 * m34 + + m41 * m14 * m32; inv[10] = m11 * m22 * m44 - - m11 * m24 * m42 - - m21 * m12 * m44 + - m21 * m14 * m42 + - m41 * m12 * m24 - - m41 * m14 * m22; + m11 * m24 * m42 - + m21 * m12 * m44 + + m21 * m14 * m42 + + m41 * m12 * m24 - + m41 * m14 * m22; inv[11] = -m11 * m22 * m34 + - m11 * m24 * m32 + - m21 * m12 * m34 - - m21 * m14 * m32 - - m31 * m12 * m24 + - m31 * m14 * m22; + m11 * m24 * m32 + + m21 * m12 * m34 - + m21 * m14 * m32 - + m31 * m12 * m24 + + m31 * m14 * m22; inv[12] = -m21 * m32 * m43 + - m21 * m33 * m42 + - m31 * m22 * m43 - - m31 * m23 * m42 - - m41 * m22 * m33 + - m41 * m23 * m32; + m21 * m33 * m42 + + m31 * m22 * m43 - + m31 * m23 * m42 - + m41 * m22 * m33 + + m41 * m23 * m32; inv[13] = m11 * m32 * m43 - - m11 * m33 * m42 - - m31 * m12 * m43 + - m31 * m13 * m42 + - m41 * m12 * m33 - - m41 * m13 * m32; + m11 * m33 * m42 - + m31 * m12 * m43 + + m31 * m13 * m42 + + m41 * m12 * m33 - + m41 * m13 * m32; inv[14] = -m11 * m22 * m43 + - m11 * m23 * m42 + - m21 * m12 * m43 - - m21 * m13 * m42 - - m41 * m12 * m23 + - m41 * m13 * m22; + m11 * m23 * m42 + + m21 * m12 * m43 - + m21 * m13 * m42 - + m41 * m12 * m23 + + m41 * m13 * m22; inv[15] = m11 * m22 * m33 - - m11 * m23 * m32 - - m21 * m12 * m33 + - m21 * m13 * m32 + - m31 * m12 * m23 - - m31 * m13 * m22; + m11 * m23 * m32 - + m21 * m12 * m33 + + m21 * m13 * m32 + + m31 * m12 * m23 - + m31 * m13 * m22; T invDet = F(1.0) / det; for (unsigned int i = 0; i < 16; ++i) @@ -326,15 +455,27 @@ namespace Nz return false; } + /*! + * \brief Gets the inverse of this matrix + * \return true if matrix can be inverted + * + * \param dest Matrix to put the result + * + * \remark You can call this method on the same object + * \remark if NAZARA_DEBUG is defined and matrix is not affine, a NazaraWarning is produced and GetInverse is called + * \remark if NAZARA_DEBUG is defined, a NazaraError is produced if dest is null and false is returned + * + * \see GetInverse + */ + template bool Matrix4::GetInverseAffine(Matrix4* dest) const { - ///DOC: Il est possible d'appeler cette méthode avec la même matrice en argument qu'en appelant - #if NAZARA_MATH_SAFE + #ifdef NAZARA_DEBUG if (!IsAffine()) { - NazaraError("Matrix is not affine"); - return false; + NazaraWarning("Matrix is not affine"); + return GetInverse(dest); } if (!dest) @@ -345,63 +486,63 @@ namespace Nz #endif T det = GetDeterminantAffine(); - if (!NumberEquals(det, F(0.0))) + if (det != F(0.0)) { // http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix T inv[16]; inv[0] = m22 * m33 - - m32 * m23; + m32 * m23; inv[1] = -m12 * m33 + - m32 * m13; + m32 * m13; inv[2] = m12 * m23 - - m22 * m13; + m22 * m13; inv[3] = F(0.0); inv[4] = -m21 * m33 + - m31 * m23; + m31 * m23; inv[5] = m11 * m33 - - m31 * m13; + m31 * m13; inv[6] = -m11 * m23 + - m21 * m13; + m21 * m13; inv[7] = F(0.0); inv[8] = m21 * m32 - - m31 * m22; + m31 * m22; inv[9] = -m11 * m32 + - m31 * m12; + m31 * m12; inv[10] = m11 * m22 - - m21 * m12; + m21 * m12; inv[11] = F(0.0); inv[12] = -m21 * m32 * m43 + - m21 * m33 * m42 + - m31 * m22 * m43 - - m31 * m23 * m42 - - m41 * m22 * m33 + - m41 * m23 * m32; + m21 * m33 * m42 + + m31 * m22 * m43 - + m31 * m23 * m42 - + m41 * m22 * m33 + + m41 * m23 * m32; inv[13] = m11 * m32 * m43 - - m11 * m33 * m42 - - m31 * m12 * m43 + - m31 * m13 * m42 + - m41 * m12 * m33 - - m41 * m13 * m32; + m11 * m33 * m42 - + m31 * m12 * m43 + + m31 * m13 * m42 + + m41 * m12 * m33 - + m41 * m13 * m32; inv[14] = -m11 * m22 * m43 + - m11 * m23 * m42 + - m21 * m12 * m43 - - m21 * m13 * m42 - - m41 * m12 * m23 + - m41 * m13 * m22; + m11 * m23 * m42 + + m21 * m12 * m43 - + m21 * m13 * m42 - + m41 * m12 * m23 + + m41 * m13 * m22; T invDet = F(1.0) / det; for (unsigned int i = 0; i < 16; ++i) @@ -416,6 +557,11 @@ namespace Nz return false; } + /*! + * \brief Gets the rotation from this matrix + * \return Quaternion which is the representation of the rotation in this matrix + */ + template Quaternion Matrix4::GetRotation() const { @@ -425,7 +571,7 @@ namespace Nz T trace = m11 + m22 + m33; if (trace > F(0.0)) { - T s = F(0.5)/std::sqrt(trace + F(1.0)); + T s = F(0.5) / std::sqrt(trace + F(1.0)); quat.w = F(0.25) / s; quat.x = (m23 - m32) * s; quat.y = (m31 - m13) * s; @@ -465,6 +611,16 @@ namespace Nz return quat; } + /*! + * \brief Gets the ith row of the matrix + * \return Vector4 which is the ith row of the matrix + * + * \param row Index of the row you want + * + * \remark Produce a NazaraError if you try to access index greater than 3 with NAZARA_MATH_SAFE defined + * \throw std::out_of_range if NAZARA_MATH_SAFE is defined and if you try to access index greater than 3 + */ + template Vector4 Matrix4::GetRow(unsigned int row) const { @@ -473,10 +629,10 @@ namespace Nz #if NAZARA_MATH_SAFE if (row > 3) { - StringStream ss; - ss << "Column out of range: (" << row << ") > 3"; + String error("Row out of range: (" + String::Number(row) + ") > 3"); - throw std::out_of_range(ss.ToString()); + NazaraError(error); + throw std::out_of_range(error); } #endif @@ -484,6 +640,13 @@ namespace Nz return Vector4(ptr[row], ptr[row+4], ptr[row+8], ptr[row+12]); } + /*! + * \brief Gets the scale from this matrix + * \return Vector3 which is the representation of the scale in this matrix + * + * \see GetSquaredScale + */ + template Vector3 Matrix4::GetScale() const { @@ -491,35 +654,80 @@ namespace Nz return Vector3(std::sqrt(squaredScale.x), std::sqrt(squaredScale.y), std::sqrt(squaredScale.z)); } + /*! + * \brief Gets the squared scale from this matrix + * \return Vector3 which is the representation of the squared scale in this matrix + * + * \see GetScale + */ + template Vector3 Matrix4::GetSquaredScale() const { return Vector3(m11*m11 + m21*m21 + m31*m31, - m12*m12 + m22*m22 + m32*m32, - m13*m13 + m23*m23 + m33*m33); + m12*m12 + m22*m22 + m32*m32, + m13*m13 + m23*m23 + m33*m33); } + /*! + * \brief Gets the translation from this matrix + * \return Vector3 which is the representation of the translation in this matrix + */ + template Vector3 Matrix4::GetTranslation() const { return Vector3(m41, m42, m43); } + /*! + * \brief Gets the transposed of this matrix + * + * \param dest Matrix to put the result + * + * \remark You can call this method on the same object + * \remark if NAZARA_DEBUG is defined, a NazaraError is produced if dest is null and dest is not changed + * + * \see Transpose + */ + template void Matrix4::GetTransposed(Matrix4* dest) const { + #ifdef NAZARA_DEBUG + if (!dest) + { + NazaraError("Destination matrix must be valid"); + return; + } + #endif + dest->Set(m11, m21, m31, m41, - m12, m22, m32, m42, - m13, m23, m33, m43, - m14, m24, m34, m44); + m12, m22, m32, m42, + m13, m23, m33, m43, + m14, m24, m34, m44); } + /*! + * \brief Checks whetever matrix has negative scale + * \return true if determinant is negative + * + * \see GetDeterminant + */ + template bool Matrix4::HasNegativeScale() const { return GetDeterminant() < F(0.0); } + /*! + * \brief Checks whetever matrix has scale + * \return true if determinant has scale + * + * \see HasNegativeScale + */ + template bool Matrix4::HasScale() const { @@ -538,6 +746,15 @@ namespace Nz return false; } + /*! + * \brief Inverts this matrix + * \return A reference to this matrix inverted + * + * \param bool Optional argument to know if matrix has been successfully inverted + * + * \see InverseAffine + */ + template Matrix4& Matrix4::Inverse(bool* succeeded) { @@ -548,6 +765,15 @@ namespace Nz return *this; } + /*! + * \brief Inverts this matrix + * \return A reference to this matrix inverted + * + * \param bool Optional argument to know if matrix has been successfully inverted + * + * \see Inverse + */ + template Matrix4& Matrix4::InverseAffine(bool* succeeded) { @@ -558,35 +784,60 @@ namespace Nz return *this; } + /*! + * \brief Checks whether the matrix is affine + * \return true if matrix is affine + */ + template bool Matrix4::IsAffine() const { - return NumberEquals(m14, F(0.0)) && - NumberEquals(m24, F(0.0)) && - NumberEquals(m34, F(0.0)) && - NumberEquals(m44, F(1.0)); + return m14 == F(0.0) && m24 == F(0.0) && m34 == F(0.0) && m44 == F(1.0); } + /*! + * \brief Checks whether the matrix is identity + * \return true if matrix is identity + */ + template bool Matrix4::IsIdentity() const { return (NumberEquals(m11, F(1.0)) && NumberEquals(m12, F(0.0)) && NumberEquals(m13, F(0.0)) && NumberEquals(m14, F(0.0)) && - NumberEquals(m21, F(0.0)) && NumberEquals(m22, F(1.0)) && NumberEquals(m23, F(0.0)) && NumberEquals(m24, F(0.0)) && - NumberEquals(m31, F(0.0)) && NumberEquals(m32, F(0.0)) && NumberEquals(m33, F(1.0)) && NumberEquals(m34, F(0.0)) && - NumberEquals(m41, F(0.0)) && NumberEquals(m42, F(0.0)) && NumberEquals(m43, F(0.0)) && NumberEquals(m44, F(1.0))); + NumberEquals(m21, F(0.0)) && NumberEquals(m22, F(1.0)) && NumberEquals(m23, F(0.0)) && NumberEquals(m24, F(0.0)) && + NumberEquals(m31, F(0.0)) && NumberEquals(m32, F(0.0)) && NumberEquals(m33, F(1.0)) && NumberEquals(m34, F(0.0)) && + NumberEquals(m41, F(0.0)) && NumberEquals(m42, F(0.0)) && NumberEquals(m43, F(0.0)) && NumberEquals(m44, F(1.0))); } + /*! + * \brief Makes the matrix identity (with 1 on diagonal and 0 for others) + * \return A reference to this matrix with components (1 on diagonal and 0 for others) + * + * \see Identity + */ + template Matrix4& Matrix4::MakeIdentity() { Set(F(1.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(1.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(1.0), F(0.0), - F(0.0), F(0.0), F(0.0), F(1.0)); + F(0.0), F(1.0), F(0.0), F(0.0), + F(0.0), F(0.0), F(1.0), F(0.0), + F(0.0), F(0.0), F(0.0), F(1.0)); return *this; } + /*! + * \brief Makes the matrix a 'look at matrix' + * \return A reference to this matrix transformed in 'look at matrix' + * + * \param eye Position of the camera + * \param target Position of the target of the camera + * \param up Direction of up vector according to the orientation of camera + * + * \see LookAt + */ + template Matrix4& Matrix4::MakeLookAt(const Vector3& eye, const Vector3& target, const Vector3& up) { @@ -595,25 +846,51 @@ namespace Nz Vector3 u = s.CrossProduct(f); Set(s.x, u.x, -f.x, T(0.0), - s.y, u.y, -f.y, T(0.0), - s.z, u.z, -f.z, T(0.0), - -s.DotProduct(eye), -u.DotProduct(eye), f.DotProduct(eye), T(1.0)); + s.y, u.y, -f.y, T(0.0), + s.z, u.z, -f.z, T(0.0), + -s.DotProduct(eye), -u.DotProduct(eye), f.DotProduct(eye), T(1.0)); return *this; } + /*! + * \brief Makes the matrix a 'orthographic matrix' + * \return A reference to this matrix transformed in 'orthographic matrix' + * + * \param left Distance between center and left + * \param right Distance between center and right + * \param top Distance between center and top + * \param bottom Distance between center and bottom + * \param zNear Distance where 'vision' begins + * \param zFar Distance where 'vision' ends + * + * \see Ortho + */ + template Matrix4& Matrix4::MakeOrtho(T left, T right, T top, T bottom, T zNear, T zFar) { // http://msdn.microsoft.com/en-us/library/windows/desktop/bb204942(v=vs.85).aspx Set(F(2.0) / (right - left), F(0.0), F(0.0), F(0.0), - F(0.0), F(2.0) / (top - bottom), F(0.0), F(0.0), - F(0.0), F(0.0), F(1.0) / (zNear - zFar), F(0.0), - (left + right) / (left - right), (top + bottom) / (bottom - top), zNear/(zNear - zFar), F(1.0)); + F(0.0), F(2.0) / (top - bottom), F(0.0), F(0.0), + F(0.0), F(0.0), F(1.0) / (zNear - zFar), F(0.0), + (left + right) / (left - right), (top + bottom) / (bottom - top), zNear/(zNear - zFar), F(1.0)); return *this; } + /*! + * \brief Makes the matrix a 'perspective matrix' + * \return A reference to this matrix transformed in 'perspective matrix' + * + * \param angle Unit depends on NAZARA_MATH_ANGLE_RADIAN + * \param ratio Rendering ratio (typically 16/9 or 4/3) + * \param zNear Distance where 'vision' begins + * \param zFar Distance where 'vision' ends + * + * \see Perspective + */ + template Matrix4& Matrix4::MakePerspective(T angle, T ratio, T zNear, T zFar) { @@ -627,19 +904,28 @@ namespace Nz T yScale = std::tan(static_cast(M_PI_2) - angle); Set(yScale / ratio, F(0.0), F(0.0), F(0.0), - F(0.0), yScale, F(0.0), F(0.0), - F(0.0), F(0.0), - (zFar + zNear) / (zFar - zNear), F(-1.0), - F(0.0), F(0.0), F(-2.0) * (zNear * zFar) / (zFar - zNear), F(0.0)); + F(0.0), yScale, F(0.0), F(0.0), + F(0.0), F(0.0), - (zFar + zNear) / (zFar - zNear), F(-1.0), + F(0.0), F(0.0), F(-2.0) * (zNear * zFar) / (zFar - zNear), F(0.0)); return *this; } + /*! + * \brief Makes the matrix the representation of the quaternion + * \return A reference to this matrix which is the rotation of the quaternion + * + * \param rotation Quaternion representing a rotation of space + * + * \see Rotate + */ + template Matrix4& Matrix4::MakeRotation(const Quaternion& rotation) { SetRotation(rotation); - // On complète la matrice + // We complete the matrix m14 = F(0.0); m24 = F(0.0); m34 = F(0.0); @@ -651,36 +937,66 @@ namespace Nz return *this; } + /*! + * \brief Makes the matrix with the scale + * \return A reference to this matrix which is the scale + * + * \param scale Vector3 representing the homothety + * + * \see Scale + */ + template Matrix4& Matrix4::MakeScale(const Vector3& scale) { Set(scale.x, F(0.0), F(0.0), F(0.0), - F(0.0), scale.y, F(0.0), F(0.0), - F(0.0), F(0.0), scale.z, F(0.0), - F(0.0), F(0.0), F(0.0), F(1.0)); + F(0.0), scale.y, F(0.0), F(0.0), + F(0.0), F(0.0), scale.z, F(0.0), + F(0.0), F(0.0), F(0.0), F(1.0)); return *this; } + /*! + * \brief Makes the matrix with the translation + * \return A reference to this matrix which is the translation + * + * \param translation Vector3 representing the translation + * + * \see Translate + */ + template Matrix4& Matrix4::MakeTranslation(const Vector3& translation) { Set(F(1.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(1.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(1.0), F(0.0), - translation.x, translation.y, translation.z, F(1.0)); + F(0.0), F(1.0), F(0.0), F(0.0), + F(0.0), F(0.0), F(1.0), F(0.0), + translation.x, translation.y, translation.z, F(1.0)); return *this; } + /*! + * \brief Makes the matrix with the translation and the rotation + * \return A reference to this matrix which is transformation obtained by the translation and the rotation + * + * \param translation Vector3 representing the translation + * \param rotation Quaternion representing a rotation of space + * + * \remark Rotation is applied first + * + * \see Transform + */ + template Matrix4& Matrix4::MakeTransform(const Vector3& translation, const Quaternion& rotation) { - // La rotation et la translation peuvent être appliquées directement + // The rotation and the translation may be directly applied SetRotation(rotation); SetTranslation(translation); - // On complète la matrice (les transformations sont affines) + // We complete the matrix (the transformations are affine) m14 = F(0.0); m24 = F(0.0); m34 = F(0.0); @@ -689,40 +1005,77 @@ namespace Nz return *this; } + /*! + * \brief Makes the matrix with the translation, the rotation and the scale + * \return A reference to this matrix which is transformation obtained by the translation, the rotation and the scale + * + * \param translation Vector3 representing the translation + * \param rotation Quaternion representing a rotation of space + * \param scale Vector3 representing the homothety + * + * \remark Rotation is applied first, then translation + * + * \see Transform + */ + template Matrix4& Matrix4::MakeTransform(const Vector3& translation, const Quaternion& rotation, const Vector3& scale) { MakeTransform(translation, rotation); - // Ensuite on fait une mise à l'échelle des valeurs déjà présentes + // Then we apply the homothety to current values return ApplyScale(scale); } + /*! + * \brief Makes the matrix a 'view matrix' + * \return A reference to this matrix transformed in 'view matrix' + * + * \param translation Vector3 representing the translation + * \param rotation Quaternion representing a rotation of space + * + * \see ViewMatrix + */ + template Matrix4& Matrix4::MakeViewMatrix(const Vector3& translation, const Quaternion& rotation) { - // Une matrice de vue doit appliquer une transformation opposée à la matrice "monde" - Quaternion invRot = rotation.GetConjugate(); // Inverse de la rotation + // A view matrix must apply an inverse transformation of the 'world' matrix + Quaternion invRot = rotation.GetConjugate(); // Inverse of the rotation return MakeTransform(-(invRot * translation), invRot); } + /*! + * \brief Makes the matrix zero (with 0 everywhere) + * \return A reference to this matrix with components (0 everywhere) + * + * \see Zero + */ + template Matrix4& Matrix4::MakeZero() { Set(F(0.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(0.0), F(0.0)); + F(0.0), F(0.0), F(0.0), F(0.0), + F(0.0), F(0.0), F(0.0), F(0.0), + F(0.0), F(0.0), F(0.0), F(0.0)); return *this; } + /*! + * \brief Sets the components of the matrix + * \return A reference to this matrix + * + * \param rIJ Matrix components at index(I, J) + */ + template Matrix4& Matrix4::Set(T r11, T r12, T r13, T r14, - T r21, T r22, T r23, T r24, - T r31, T r32, T r33, T r34, - T r41, T r42, T r43, T r44) + T r21, T r22, T r23, T r24, + T r31, T r32, T r33, T r34, + T r41, T r42, T r43, T r44) { m11 = r11; m12 = r12; @@ -744,15 +1097,29 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the matrix from an array of sixteen elements + * \return A reference to this matrix + * + * \param matrix[16] Matrix components + */ + template Matrix4& Matrix4::Set(const T matrix[16]) { - // Ici nous sommes certains de la continuité des éléments en mémoire - std::memcpy(&m11, matrix, 16*sizeof(T)); + // Here we are confident of the continuity of memory elements + std::memcpy(&m11, matrix, 16 * sizeof(T)); return *this; } + /*! + * \brief Sets the components of the matrix from another matrix + * \return A reference to this matrix + * + * \param matrix The other matrix + */ + template Matrix4& Matrix4::Set(const Matrix4& matrix) { @@ -761,18 +1128,34 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the matrix from another type of Matrix4 + * \return A reference to this matrix + * + * \param matrix Matrix4 of type U to convert its components + */ + template template Matrix4& Matrix4::Set(const Matrix4& matrix) { Set(F(matrix[ 0]), F(matrix[ 1]), F(matrix[ 2]), F(matrix[ 3]), - F(matrix[ 4]), F(matrix[ 5]), F(matrix[ 6]), F(matrix[ 7]), - F(matrix[ 8]), F(matrix[ 9]), F(matrix[10]), F(matrix[11]), - F(matrix[12]), F(matrix[13]), F(matrix[14]), F(matrix[15])); + F(matrix[ 4]), F(matrix[ 5]), F(matrix[ 6]), F(matrix[ 7]), + F(matrix[ 8]), F(matrix[ 9]), F(matrix[10]), F(matrix[11]), + F(matrix[12]), F(matrix[13]), F(matrix[14]), F(matrix[15])); return *this; } + /*! + * \brief Sets the components of the matrix from a quaternion + * \return A reference to this matrix which is the rotation of the quaternion + * + * \param rotation Quaternion representing a rotation of space + * + * \remark 3rd column and row are unchanged + */ + template Matrix4& Matrix4::SetRotation(const Quaternion& rotation) { @@ -804,6 +1187,15 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the matrix from a scale + * \return A reference to this matrix which is the scale of the Vector3 + * + * \param scale Vector3 representing the homothety + * + * \remark Components are unchanged, except the three first on the diagonal + */ + template Matrix4& Matrix4::SetScale(const Vector3& scale) { @@ -814,6 +1206,15 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the matrix from a translation + * \return A reference to this matrix which is the translation of the Vector3 + * + * \param translation Vector3 representing the translation + * + * \remark Components are unchanged, except the three first on the third row + */ + template Matrix4& Matrix4::SetTranslation(const Vector3& translation) { @@ -824,40 +1225,76 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Matrix4(m11, m12, m13, m14,\n ...)" + */ + template String Matrix4::ToString() const { StringStream ss; return ss << "Matrix4(" << m11 << ", " << m12 << ", " << m13 << ", " << m14 << ",\n" - << " " << m21 << ", " << m22 << ", " << m23 << ", " << m24 << ",\n" - << " " << m31 << ", " << m32 << ", " << m33 << ", " << m34 << ",\n" - << " " << m41 << ", " << m42 << ", " << m43 << ", " << m44 << ')'; + << " " << m21 << ", " << m22 << ", " << m23 << ", " << m24 << ",\n" + << " " << m31 << ", " << m32 << ", " << m33 << ", " << m34 << ",\n" + << " " << m41 << ", " << m42 << ", " << m43 << ", " << m44 << ')'; } + /*! + * \brief Transforms the Vector2 and two components by the matrix + * \return Vector2 transformed by the matrix + * + * \param vector To transform + * \param z Z Component of the imaginary Vector4 + * \param w W Component of the imaginary Vector4 + */ + template Vector2 Matrix4::Transform(const Vector2& vector, T z, T w) const { - return Vector2(m11*vector.x + m21*vector.y + m31*z + m41*w, - m12*vector.x + m22*vector.y + m32*z + m42*w); + return Vector2(m11 * vector.x + m21 * vector.y + m31 * z + m41 * w, + m12 * vector.x + m22 * vector.y + m32 * z + m42 * w); } + /*! + * \brief Transforms the Vector3 and one component by the matrix + * \return Vector3 transformed by the matrix + * + * \param vector To transform + * \param w W Component of the imaginary Vector4 + */ + template Vector3 Matrix4::Transform(const Vector3& vector, T w) const { - return Vector3(m11*vector.x + m21*vector.y + m31*vector.z + m41*w, - m12*vector.x + m22*vector.y + m32*vector.z + m42*w, - m13*vector.x + m23*vector.y + m33*vector.z + m43*w); + return Vector3(m11 * vector.x + m21 * vector.y + m31 * vector.z + m41 * w, + m12 * vector.x + m22 * vector.y + m32 * vector.z + m42 * w, + m13 * vector.x + m23 * vector.y + m33 * vector.z + m43 * w); } + /*! + * \brief Transforms the Vector4 by the matrix + * \return Vector4 transformed by the matrix + * + * \param vector To transform + */ + template Vector4 Matrix4::Transform(const Vector4& vector) const { - return Vector4(m11*vector.x + m21*vector.y + m31*vector.z + m41*vector.w, - m12*vector.x + m22*vector.y + m32*vector.z + m42*vector.w, - m13*vector.x + m23*vector.y + m33*vector.z + m43*vector.w, - m14*vector.x + m24*vector.y + m34*vector.z + m44*vector.w); + return Vector4(m11 * vector.x + m21 * vector.y + m31 * vector.z + m41 * vector.w, + m12 * vector.x + m22 * vector.y + m32 * vector.z + m42 * vector.w, + m13 * vector.x + m23 * vector.y + m33 * vector.z + m43 * vector.w, + m14 * vector.x + m24 * vector.y + m34 * vector.z + m44 * vector.w); } + /*! + * \brief Transposes the matrix + * \return A reference to this matrix transposed + * + * \see GetTransposed + */ + template Matrix4& Matrix4::Transpose() { @@ -871,51 +1308,87 @@ namespace Nz return *this; } + /*! + * \brief Converts matrix to pointer to its own data + * \return A pointer to the own data + * + * \remark Access to index greather than 15 is undefined behavior + */ + template - Matrix4::operator T*() + Matrix4::operator T* () { return &m11; } + /*! + * \brief Converts matrix to pointer to its own data + * \return A constant pointer to the own data + * + * \remark Access to index greather than 15 is undefined behavior + */ + template - Matrix4::operator const T*() const + Matrix4::operator const T* () const { return &m11; } + /*! + * \brief Gets the component (x, y) of the matrix + * \return A reference to the component (x, y) + * + * \remark Produce a NazaraError if you try to access index greater than 3 for x or y with NAZARA_MATH_SAFE defined + * \throw std::out_of_range if NAZARA_MATH_SAFE is defined and if you try to access index greater than 3 for x or y + */ + template T& Matrix4::operator()(unsigned int x, unsigned int y) { #if NAZARA_MATH_SAFE if (x > 3 || y > 3) { - StringStream ss; - ss << "Index out of range: (" << x << ", " << y << ") > (3,3)"; + String error("Index out of range: (" + String::Number(x) + ", " + String::Number(y) +") > (3, 3)"); - throw std::out_of_range(ss.ToString()); + NazaraError(error); + throw std::out_of_range(error); } #endif return (&m11)[y*4+x]; } + /*! + * \brief Gets the component (x, y) of the matrix + * \return The value of the component (x, y) + * + * \remark Produce a NazaraError if you try to access index greater than 3 for x or y with NAZARA_MATH_SAFE defined + * \throw std::out_of_range if NAZARA_MATH_SAFE is defined and if you try to access index greater than 3 for x or y + */ + template T Matrix4::operator()(unsigned int x, unsigned int y) const { #if NAZARA_MATH_SAFE if (x > 3 || y > 3) { - StringStream ss; - ss << "Index out of range: (" << x << ", " << y << ") > (3,3)"; + String error("Index out of range: (" + String::Number(x) + ", " + String::Number(y) +") > (3, 3)"); - NazaraError(ss); - throw std::out_of_range(ss.ToString()); + NazaraError(error); + throw std::out_of_range(error); } #endif return (&m11)[y*4+x]; } + /*! + * \brief Multiplies the components of the matrix with other matrix + * \return A matrix where components are the product of this matrix and the other one according to matrix product + * + * \param matrix The other matrix to multiply components with + */ + template Matrix4 Matrix4::operator*(const Matrix4& matrix) const { @@ -923,24 +1396,52 @@ namespace Nz return result.Concatenate(matrix); } + /*! + * \brief Multiplies the components of the matrix with a vector + * \return A vector transposed by this matrix + * + * \param vector The vector to multiply the matrix with + */ + template Vector2 Matrix4::operator*(const Vector2& vector) const { return Transform(vector); } + /*! + * \brief Multiplies the components of the matrix with a vector + * \return A vector transposed by this matrix + * + * \param vector The vector to multiply the matrix with + */ + template Vector3 Matrix4::operator*(const Vector3& vector) const { return Transform(vector); } + /*! + * \brief Multiplies the components of the matrix with a vector + * \return A vector transposed by this matrix + * + * \param vector The vector to multiply the matrix with + */ + template Vector4 Matrix4::operator*(const Vector4& vector) const { return Transform(vector); } + /*! + * \brief Multiplies the components of the matrix with a scalar + * \return A Matrix4 where components are the product of matrix'components and the scalar + * + * \param scalar The scalar to multiply the matrix'components with + */ + template Matrix4 Matrix4::operator*(T scalar) const { @@ -951,6 +1452,13 @@ namespace Nz return mat; } + /*! + * \brief Multiplies this matrix with another one + * \return A reference to this matrix which is the product with the other one + * + * \param matrix The matrix to multiply with + */ + template Matrix4& Matrix4::operator*=(const Matrix4& matrix) { @@ -959,6 +1467,13 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of the matrix with a scalar + * \return A reference to this matrix where components are the product with the scalar + * + * \param scalar The scalar to multiply with + */ + template Matrix4& Matrix4::operator*=(T scalar) { @@ -968,6 +1483,13 @@ namespace Nz return *this; } + /*! + * \brief Compares the matrix to other one + * \return true if the matrices are the same + * + * \param matrix Other matrix to compare with + */ + template bool Matrix4::operator==(const Matrix4& mat) const { @@ -978,30 +1500,64 @@ namespace Nz return true; } + /*! + * \brief Compares the matrix to other one + * \return false if the matrices are the same + * + * \param matrix Other matrix to compare with + */ + template bool Matrix4::operator!=(const Matrix4& mat) const { return !operator==(mat); } + /*! + * \brief Shorthand for the concatenation of two matrices + * \return A Matrix4 which is the product of two + * + * \param left Left-hand side matrix + * \param right Right-hand side matrix + * + * \see Concatenate + */ + template Matrix4 Matrix4::Concatenate(const Matrix4& left, const Matrix4& right) { - Matrix4 matrix(left); // Copie de la matrice de gauche - matrix.Concatenate(right); // Concaténation avec la matrice de droite + Matrix4 matrix(left); // Copy of left-hand side matrix + matrix.Concatenate(right); // Concatenation with right-hand side - return matrix; // Et on renvoie la matrice + return matrix; } + /*! + * \brief Shorthand for the concatenation of two affine matrices + * \return A Matrix4 which is the product of two + * + * \param left Left-hand side matrix + * \param right Right-hand side matrix + * + * \see ConcatenateAffine + */ + template Matrix4 Matrix4::ConcatenateAffine(const Matrix4& left, const Matrix4& right) { - Matrix4 matrix(left); // Copie de la matrice de gauche - matrix.ConcatenateAffine(right); // Concaténation (affine) avec la matrice de droite + Matrix4 matrix(left); // Copy of left-hand side matrix + matrix.ConcatenateAffine(right); // Affine concatenation with right-hand side - return matrix; // Et on renvoie la matrice + return matrix; } + /*! + * \brief Shorthand for the identity matrix + * \return A Matrix4 which is the identity matrix + * + * \see MakeIdentity + */ + template Matrix4 Matrix4::Identity() { @@ -1011,6 +1567,17 @@ namespace Nz return matrix; } + /*! + * \brief Shorthand for the 'look at' matrix + * \return A Matrix4 which is the 'look at' matrix + * + * \param eye Position of the camera + * \param target Position of the target of the camera + * \param up Direction of up vector according to the orientation of camera + * + * \see MakeLookAt + */ + template Matrix4 Matrix4::LookAt(const Vector3& eye, const Vector3& target, const Vector3& up) { @@ -1020,6 +1587,20 @@ namespace Nz return matrix; } + /*! + * \brief Shorthand for the 'orthographic' matrix + * \return A Matrix4 which is the 'orthographic' matrix + * + * \param left Distance between center and left + * \param right Distance between center and right + * \param top Distance between center and top + * \param bottom Distance between center and bottom + * \param zNear Distance where 'vision' begins + * \param zFar Distance where 'vision' ends + * + * \see MakeOrtho + */ + template Matrix4 Matrix4::Ortho(T left, T right, T top, T bottom, T zNear, T zFar) { @@ -1029,6 +1610,18 @@ namespace Nz return matrix; } + /*! + * \brief Shorthand for the 'perspective' matrix + * \return A Matrix4 which is the 'perspective' matrix + * + * \param angle Unit depends on NAZARA_MATH_ANGLE_RADIAN + * \param ratio Rendering ratio (typically 16/9 or 4/3) + * \param zNear Distance where 'vision' begins + * \param zFar Distance where 'vision' ends + * + * \see MakePerspective + */ + template Matrix4 Matrix4::Perspective(T angle, T ratio, T zNear, T zFar) { @@ -1038,6 +1631,15 @@ namespace Nz return matrix; } + /*! + * \brief Shorthand for the 'rotation' matrix + * \return A Matrix4 which is the rotation of the quaternion + * + * \param rotation Quaternion representing a rotation of space + * + * \see MakeRotation + */ + template Matrix4 Matrix4::Rotate(const Quaternion& rotation) { @@ -1047,6 +1649,15 @@ namespace Nz return matrix; } + /*! + * \brief Shorthand for the 'scale' matrix + * \return A Matrix4 which is is the scale + * + * \param scale Vector3 representing the homothety + * + * \see MakeScale + */ + template Matrix4 Matrix4::Scale(const Vector3& scale) { @@ -1056,6 +1667,15 @@ namespace Nz return matrix; } + /*! + * \brief Shorthand for the 'translation' matrix + * \return A Matrix4 which is is the translation + * + * \param translation Vector3 representing the translation + * + * \see MakeTranslation + */ + template Matrix4 Matrix4::Translate(const Vector3& translation) { @@ -1065,6 +1685,18 @@ namespace Nz return mat; } + /*! + * \brief Shorthand for the 'transform' matrix + * \return A Matrix4 which is transformation obtained by the translation and the rotation + * + * \param translation Vector3 representing the translation + * \param rotation Quaternion representing a rotation of space + * + * \remark Rotation is applied first + * + * \see MakeTransform + */ + template Matrix4 Matrix4::Transform(const Vector3& translation, const Quaternion& rotation) { @@ -1074,6 +1706,19 @@ namespace Nz return mat; } + /*! + * \brief Shorthand for the 'transform' matrix + * \return A Matrix4 which is transformation obtained by the translation, the rotation and the scale + * + * \param translation Vector3 representing the translation + * \param rotation Quaternion representing a rotation of space + * \param scale Vector3 representing the homothety + * + * \remark Rotation is applied first, then translation + * + * \see MakeTransform + */ + template Matrix4 Matrix4::Transform(const Vector3& translation, const Quaternion& rotation, const Vector3& scale) { @@ -1083,6 +1728,16 @@ namespace Nz return mat; } + /*! + * \brief Shorthand for the 'view' matrix + * \return A Matrix4 which is the 'view matrix' + * + * \param translation Vector3 representing the translation + * \param rotation Quaternion representing a rotation of space + * + * \see MakeViewMatrix + */ + template Matrix4 Matrix4::ViewMatrix(const Vector3& translation, const Quaternion& rotation) { @@ -1092,6 +1747,13 @@ namespace Nz return mat; } + /*! + * \brief Shorthand for the 'zero' matrix + * \return A Matrix4 with components (0 everywhere) + * + * \see MakeZero + */ + template Matrix4 Matrix4::Zero() { @@ -1100,14 +1762,69 @@ namespace Nz return matrix; } + + /*! + * \brief Serializes a Matrix4 + * \return true if successfully serialized + * + * \param context Serialization context + * \param matrix Input matrix + */ + template + bool Serialize(SerializationContext& context, const Matrix4& matrix) + { + for (unsigned int i = 0; i < 16; ++i) + { + if (!Serialize(context, matrix[i])) + return false; + } + + return true; + } + + /*! + * \brief Unserializes a Matrix4 + * \return true if successfully unserialized + * + * \param context Serialization context + * \param matrix Output matrix + */ + template + bool Unserialize(SerializationContext& context, Matrix4* matrix) + { + T* head = matrix->operator T*(); + for (unsigned int i = 0; i < 16; ++i) + { + if (!Unserialize(context, head + i)) + return false; + } + + return true; + } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param matrix The matrix to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::Matrix4& matrix) { return out << matrix.ToString(); } +/*! +* \brief Multiplies the components of the matrix with a scalar +* \return A Matrix4 where components are the product of matrix'components and the scalar +* +* \param scale The scalar to multiply the matrix'components with +* \param matrix Matrix to multiply with +*/ + template Nz::Matrix4 operator*(T scale, const Nz::Matrix4& matrix) { diff --git a/include/Nazara/Math/OrientedBox.hpp b/include/Nazara/Math/OrientedBox.hpp index 0b579fcba..43d4b1aaf 100644 --- a/include/Nazara/Math/OrientedBox.hpp +++ b/include/Nazara/Math/OrientedBox.hpp @@ -14,6 +14,8 @@ namespace Nz { + struct SerializationContext; + template class OrientedBox { @@ -43,8 +45,8 @@ namespace Nz void Update(const Matrix4& transformMatrix); void Update(const Vector3& transformMatrix); - operator Vector3*(); - operator const Vector3*() const; + operator Vector3* (); + operator const Vector3* () const; Vector3& operator()(unsigned int i); Vector3 operator()(unsigned int i) const; @@ -67,6 +69,9 @@ namespace Nz typedef OrientedBox OrientedBoxd; typedef OrientedBox OrientedBoxf; + + template bool Serialize(SerializationContext& context, const OrientedBox& obb); + template bool Unserialize(SerializationContext& context, OrientedBox* obb); } template diff --git a/include/Nazara/Math/OrientedBox.inl b/include/Nazara/Math/OrientedBox.inl index a54cdae79..9bcd94e1d 100644 --- a/include/Nazara/Math/OrientedBox.inl +++ b/include/Nazara/Math/OrientedBox.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -13,24 +14,63 @@ namespace Nz { + /*! + * \ingroup math + * \class Nz::OrientedBox + * \brief Math class that represents an oriented three dimensional box + * + * \remark You need to call Update not to have undefined behaviour + */ + + /*! + * \brief Constructs a OrientedBox object from its position and sizes + * + * \param X X component of position + * \param Y Y component of position + * \param Z Z component of position + * \param Width Width of the box (following X) + * \param Height Height of the box (following Y) + * \param Depth Depth of the box (following Z) + */ + template OrientedBox::OrientedBox(T X, T Y, T Z, T Width, T Height, T Depth) { Set(X, Y, Z, Width, Height, Depth); } + /*! + * \brief Constructs a OrientedBox object from a box + * + * \param box Box object + */ + template OrientedBox::OrientedBox(const Box& box) { Set(box); } + /*! + * \brief Constructs a OrientedBox object from two vectors representing point of the space + * (X, Y, Z) will be the components minimum of the two vectors and the (width, height, depth) will be the components maximum - minimum + * + * \param vec1 First point + * \param vec2 Second point + */ + template OrientedBox::OrientedBox(const Vector3& vec1, const Vector3& vec2) { Set(vec1, vec2); } + /*! + * \brief Constructs a OrientedBox object from another type of OrientedBox + * + * \param orientedBox OrientedBox of type U to convert to type T + */ + template template OrientedBox::OrientedBox(const OrientedBox& orientedBox) @@ -38,6 +78,15 @@ namespace Nz Set(orientedBox); } + /*! + * \brief Gets the Vector3 for the corner + * \return The position of the corner of the oriented box according to enum BoxCorner + * + * \param corner Enumeration of type BoxCorner + * + * \remark If enumeration is not defined in BoxCorner, a NazaraError is thrown and a Vector3 uninitialised is returned + */ + template const Vector3& OrientedBox::GetCorner(BoxCorner corner) const { @@ -54,12 +103,24 @@ namespace Nz return m_corners[corner]; } + /*! + * \brief Checks whether this oriented box is valid + * \return true if the oriented box has a strictly positive width, height and depth + */ + template bool OrientedBox::IsValid() const { return localBox.IsValid(); } + /*! + * \brief Makes the oriented box position (0, 0, 0) and lengths (0, 0, 0) + * \return A reference to this oriented box with position (0, 0, 0) and lengths (0, 0, 0) + * + * \see Zero + */ + template OrientedBox& OrientedBox::MakeZero() { @@ -68,6 +129,18 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the oriented box + * \return A reference to this oriented box + * + * \param X X position + * \param Y Y position + * \param Z Z position + * \param Width Width of the oriented box (following X) + * \param Height Height of the oriented box (following Y) + * \param Depth Depth of the oriented box (following Z) + */ + template OrientedBox& OrientedBox::Set(T X, T Y, T Z, T Width, T Height, T Depth) { @@ -76,6 +149,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the oriented box from a box + * \return A reference to this oriented box + * + * \param box Box object + */ + template OrientedBox& OrientedBox::Set(const Box& box) { @@ -84,6 +164,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the oriented box with components from another + * \return A reference to this oriented box + * + * \param orientedBox The other OrientedBox + */ + template OrientedBox& OrientedBox::Set(const OrientedBox& orientedBox) { @@ -92,6 +179,14 @@ namespace Nz return *this; } + /*! + * \brief Sets a OrientedBox object from two vectors representing point of the space + * (X, Y, Z) will be the components minimum of the two vectors and the (width, height, depth) will be the components maximum - minimum + * + * \param vec1 First point + * \param vec2 Second point + */ + template OrientedBox& OrientedBox::Set(const Vector3& vec1, const Vector3& vec2) { @@ -100,6 +195,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the orientedBox from another type of OrientedBox + * \return A reference to this orientedBox + * + * \param orientedBox OrientedBox of type U to convert its components + */ + template template OrientedBox& OrientedBox::Set(const OrientedBox& orientedBox) @@ -112,21 +214,32 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "OrientedBox(...)" + */ + template String OrientedBox::ToString() const { StringStream ss; return ss << "OrientedBox(FLB: " << m_corners[BoxCorner_FarLeftBottom].ToString() << "\n" - << " FLT: " << m_corners[BoxCorner_FarLeftTop].ToString() << "\n" - << " FRB: " << m_corners[BoxCorner_FarRightBottom].ToString() << "\n" - << " FRT: " << m_corners[BoxCorner_FarRightTop].ToString() << "\n" - << " NLB: " << m_corners[BoxCorner_NearLeftBottom].ToString() << "\n" - << " NLT: " << m_corners[BoxCorner_NearLeftTop].ToString() << "\n" - << " NRB: " << m_corners[BoxCorner_NearRightBottom].ToString() << "\n" - << " NRT: " << m_corners[BoxCorner_NearRightTop].ToString() << ")\n"; + << " FLT: " << m_corners[BoxCorner_FarLeftTop].ToString() << "\n" + << " FRB: " << m_corners[BoxCorner_FarRightBottom].ToString() << "\n" + << " FRT: " << m_corners[BoxCorner_FarRightTop].ToString() << "\n" + << " NLB: " << m_corners[BoxCorner_NearLeftBottom].ToString() << "\n" + << " NLT: " << m_corners[BoxCorner_NearLeftTop].ToString() << "\n" + << " NRB: " << m_corners[BoxCorner_NearRightBottom].ToString() << "\n" + << " NRT: " << m_corners[BoxCorner_NearRightTop].ToString() << ")\n"; } + /*! + * \brief Updates the corners of the box + * + * \param transformMatrix Matrix4 which represents the transformation to apply on the local box + */ + template void OrientedBox::Update(const Matrix4& transformMatrix) { @@ -134,6 +247,12 @@ namespace Nz m_corners[i] = transformMatrix.Transform(localBox.GetCorner(static_cast(i))); } + /*! + * \brief Updates the corners of the box + * + * \param translation Vector3 which represents the translation to apply on the local box + */ + template void OrientedBox::Update(const Vector3& translation) { @@ -141,18 +260,40 @@ namespace Nz m_corners[i] = localBox.GetCorner(static_cast(i)) + translation; } + /*! + * \brief Converts oriented box to pointer of Vector3 to its own corners + * \return A pointer to the own corners + * + * \remark Access to index greather than BoxCorner_Max is undefined behavior + */ + template - OrientedBox::operator Vector3*() + OrientedBox::operator Vector3* () { return &m_corners[0]; } + /*! + * \brief Converts oriented box to pointer of Vector3 to its own corners + * \return A const pointer to the own corners + * + * \remark Access to index greather than BoxCorner_Max is undefined behavior + */ + template - OrientedBox::operator const Vector3*() const + OrientedBox::operator const Vector3* () const { return &m_corners[0]; } + /*! + * \brief Gets the ith corner of the oriented box + * \return A reference to this corner + * + * \remark Produce a NazaraError if you try to access to index greather than BoxCorner_Max with NAZARA_MATH_SAFE defined. If not, it is undefined behaviour + * \throw std::out_of_range if NAZARA_MATH_SAFE is defined and you try to acces to index greather than BoxCorner_Max + */ + template Vector3& OrientedBox::operator()(unsigned int i) { @@ -170,6 +311,14 @@ namespace Nz return m_corners[i]; } + /*! + * \brief Gets the ith corner of the oriented box + * \return A reference to this corner + * + * \remark Produce a NazaraError if you try to access to index greather than BoxCorner_Max with NAZARA_MATH_SAFE defined. If not, it is undefined behaviour + * \throw std::out_of_range if NAZARA_MATH_SAFE is defined and you try to acces to index greather than BoxCorner_Max + */ + template Vector3 OrientedBox::operator()(unsigned int i) const { @@ -187,6 +336,13 @@ namespace Nz return m_corners[i]; } + /*! + * \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 + */ + template OrientedBox OrientedBox::operator*(T scalar) const { @@ -196,6 +352,13 @@ namespace Nz return box; } + /*! + * \brief Multiplies the lengths of this oriented box with the scalar + * \return A reference to this oriented box where lengths are the product of these lengths and the scalar + * + * \param scalar The scalar to multiply width, height and depth with + */ + template OrientedBox& OrientedBox::operator*=(T scalar) { @@ -204,18 +367,46 @@ namespace Nz return *this; } + /*! + * \brief Compares the oriented box to other one + * \return true if the two oriented boxes are the same + * + * \param box Other oriented box to compare with + */ + template bool OrientedBox::operator==(const OrientedBox& box) const { return localBox == box.localBox; } + /*! + * \brief Compares the oriented box to other one + * \return false if the two oriented boxes are the same + * + * \param box Other oriented box to compare with + */ + template bool OrientedBox::operator!=(const OrientedBox& box) const { return !operator==(box); } + /*! + * \brief Interpolates the oriented box to other one with a factor of interpolation + * \return A new oriented box which is the interpolation of two oriented boxes + * + * \param from Initial oriented box + * \param to Target oriented box + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * \remark With NAZARA_DEBUG, a NazaraError is thrown and Zero() is returned + * + * \see Lerp + */ + template OrientedBox OrientedBox::Lerp(const OrientedBox& from, const OrientedBox& to, T interpolation) { @@ -225,6 +416,13 @@ namespace Nz return orientedBox; } + /*! + * \brief Shorthand for the oriented box (0, 0, 0, 0, 0, 0) + * \return A oriented box with position (0, 0, 0) and lengths (0, 0, 0) + * + * \see MakeZero + */ + template OrientedBox OrientedBox::Zero() { @@ -233,8 +431,52 @@ namespace Nz return orientedBox; } + + /*! + * \brief Serializes a OrientedBox + * \return true if successfully serialized + * + * \param context Serialization context + * \param obb Input oriented box + * + * \remark Does not save OBB corners + */ + template + bool Serialize(SerializationContext& context, const OrientedBox& obb) + { + if (!Serialize(context, obb.localBox)) + return false; + + return true; + } + + /*! + * \brief Unserializes a Matrix4 + * \return true if successfully unserialized + * + * \param context Serialization context + * \param obb Output oriented box + * + * \remark The resulting oriented box corners will *not* be updated, a call to Update is required + */ + template + bool Unserialize(SerializationContext& context, OrientedBox* obb) + { + if (!Unserialize(context, &obb->localBox)) + return false; + + return true; + } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param orientedBox The orientedBox to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::OrientedBox& orientedBox) { diff --git a/include/Nazara/Math/Plane.hpp b/include/Nazara/Math/Plane.hpp index cd0856aa5..f37fc90c7 100644 --- a/include/Nazara/Math/Plane.hpp +++ b/include/Nazara/Math/Plane.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2015 Jérôme Leclercq +// 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 @@ -12,6 +12,8 @@ namespace Nz { + struct SerializationContext; + template class Plane { @@ -26,8 +28,12 @@ namespace Nz Plane(const Plane& plane) = default; ~Plane() = default; - T Distance(const Vector3& point) const; T Distance(T x, T y, T z) const; + T Distance(const Vector3& point) const; + + Plane& MakeXY(); + Plane& MakeXZ(); + Plane& MakeYZ(); Plane& Set(T normalX, T normalY, T normalZ, T Distance); Plane& Set(const T plane[4]); @@ -53,6 +59,9 @@ namespace Nz typedef Plane Planed; typedef Plane Planef; + + template bool Serialize(SerializationContext& context, const Plane& plane); + template bool Unserialize(SerializationContext& context, Plane* plane); } template diff --git a/include/Nazara/Math/Plane.inl b/include/Nazara/Math/Plane.inl index fceeb9cc3..625abe3e4 100644 --- a/include/Nazara/Math/Plane.inl +++ b/include/Nazara/Math/Plane.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -11,36 +12,89 @@ namespace Nz { + /*! + * \ingroup math + * \class Nz::Plane + * \brief Math class that represents a plane in 3D + * + * \remark The convention used in this class is: If you ask for plane with normal (0, 1, 0) and distance 1, you will get 0 * X + 1 * Y + 0 * Z - 1 = 0 or Y = 1. Notice the sign minus before the distance on the left side of the equation + */ + + /*! + * \brief Constructs a Plane object from its components + * + * \param normalX X component of the normal + * \param normalY Y component of the normal + * \param normalZ Z component of the normal + * \param D Distance to origin + */ + template Plane::Plane(T normalX, T normalY, T normalZ, T D) { Set(normalX, normalY, normalZ, D); } + /*! + * \brief Constructs a Plane object from an array of four elements + * + * \param plane[4] plane[0] is X component, plane[1] is Y component, plane[2] is Z component and plane[3] is D + */ + template Plane::Plane(const T plane[4]) { Set(plane); } + /*! + * \brief Constructs a Plane object from a normal and a distance + * + * \param Normal normal of the vector + * \param D Distance to origin + */ + template Plane::Plane(const Vector3& Normal, T D) { Set(Normal, D); } + /*! + * \brief Constructs a Plane object from a normal and a point + * + * \param Normal Normal of the plane + * \param point Point which verifies the equation of the plane + */ + template Plane::Plane(const Vector3& Normal, const Vector3& point) { Set(Normal, point); } + /*! + * \brief Constructs a Plane object from three points + * + * \param point1 First point + * \param point2 Second point + * \param point3 Third point + * + * \remark They are expected not to be colinear + */ + template Plane::Plane(const Vector3& point1, const Vector3& point2, const Vector3& point3) { Set(point1, point2, point3); } + /*! + * \brief Constructs a Plane object from another type of Plane + * + * \param plane Plane of type U to convert to type T + */ + template template Plane::Plane(const Plane& plane) @@ -48,11 +102,18 @@ namespace Nz Set(plane); } - template - T Plane::Distance(const Vector3& point) const - { - return normal.DotProduct(point) - distance; // ax + by + cd - d = 0. - } + /*! + * \brief Returns the distance from the plane to the point + * \return Distance to the point + * + * \param X X position of the point + * \param Y Y position of the point + * \param Z Z position of the point + * + * \remark If T is negative, it means that the point is in the opposite direction of the normal + * + * \see Distance + */ template T Plane::Distance(T x, T y, T z) const @@ -60,6 +121,72 @@ namespace Nz return Distance(Vector3(x, y, z)); } + /*! + * \brief Returns the distance from the plane to the point + * \return Distance to the point + * + * \param point Position of the point + * + * \remark If T is negative, it means that the point is in the opposite direction of the normal + * + * \see Distance + */ + + template + T Plane::Distance(const Vector3& point) const + { + return normal.DotProduct(point) - distance; // ax + by + cd - d = 0. + } + + /*! + * \brief Makes the plane (0, 0, 1, 0) + * \return A reference to this plane with components (0, 0, 1, 0) + * + * \see XY + */ + + template + Plane& Plane::MakeXY() + { + return Set(F(0.0), F(0.0), F(1.0), F(0.0)); + } + + /*! + * \brief Makes the plane (0, 1, 0, 0) + * \return A reference to this plane with components (0, 1, 0, 0) + * + * \see XZ + */ + + template + Plane& Plane::MakeXZ() + { + return Set(F(0.0), F(1.0), F(0.0), F(0.0)); + } + + /*! + * \brief Makes the plane (1, 0, 0, 0) + * \return A reference to this plane with components (1, 0, 0, 0) + * + * \see YZ + */ + + template + Plane& Plane::MakeYZ() + { + return Set(F(1.0), F(0.0), F(0.0), F(0.0)); + } + + /*! + * \brief Sets the components of the plane + * \return A reference to this plane + * + * \param normalX X component of the normal + * \param normalY Y component of the normal + * \param normalZ Z component of the normal + * \param D Distance to origin + */ + template Plane& Plane::Set(T normalX, T normalY, T normalZ, T D) { @@ -69,6 +196,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the plane from an array of four elements + * \return A reference to this plane + * + * \param plane[4] plane[0] is X component, plane[1] is Y component, plane[2] is Z component and plane[3] is D + */ + template Plane& Plane::Set(const T plane[4]) { @@ -78,6 +212,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the plane from another plane + * \return A reference to this plane + * + * \param plane The other plane + */ + template Plane& Plane::Set(const Plane& plane) { @@ -86,6 +227,14 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the plane from a normal and a distance + * \return A reference to this plane + * + * \param Normal Normal of the vector + * \param D Distance to origin + */ + template Plane& Plane::Set(const Vector3& Normal, T D) { @@ -95,6 +244,14 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the plane from a normal and a point + * \return A reference to this plane + * + * \param Normal Normal of the plane + * \param point Point which verifies the equation of the plane + */ + template Plane& Plane::Set(const Vector3& Normal, const Vector3& point) { @@ -104,6 +261,17 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the plane from three points + * \return A reference to this plane + * + * \param point1 First point + * \param point2 Second point + * \param point3 Third point + * + * \remark They are expected not to be colinear + */ + template Plane& Plane::Set(const Vector3& point1, const Vector3& point2, const Vector3& point3) { @@ -117,6 +285,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the plane from another type of Plane + * \return A reference to this plane + * + * \param plane Plane of type U to convert its components + */ + template template Plane& Plane::Set(const Plane& plane) @@ -127,6 +302,11 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Plane(Normal: Vector3(x, y, z); Distance: w)" + */ + template String Plane::ToString() const { @@ -135,18 +315,50 @@ namespace Nz return ss << "Plane(Normal: " << normal.ToString() << "; Distance: " << distance << ')'; } + /*! + * \brief Compares the plane to other one + * \return true if the planes are the same + * + * \param vec Other vector to compare with + * + * \remark Plane with normal N and distance D is the same than with normal -N et distance -D + */ + template bool Plane::operator==(const Plane& plane) const { return (normal == plane.normal && NumberEquals(distance, plane.distance)) || (normal == -plane.normal && NumberEquals(distance, -plane.distance)); } + /*! + * \brief Compares the plane to other one + * \return false if the planes are the same + * + * \param plane Other plane to compare with + * + * \remark Plane with normal N and distance D is the same than with normal -N et distance -D + */ + template bool Plane::operator!=(const Plane& plane) const { return !operator==(plane); } + /*! + * \brief Interpolates the plane to other one with a factor of interpolation + * \return A new plane which is the interpolation of two planes + * + * \param from Initial plane + * \param to Target plane + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * \remark With NAZARA_DEBUG, a NazaraError is thrown and Plane() is returned + * + * \see Lerp + */ + template Plane Plane::Lerp(const Plane& from, const Plane& to, T interpolation) { @@ -159,32 +371,108 @@ namespace Nz #endif Plane plane; - plane.distance = Lerp(from.distance, to.distance, interpolation); + plane.distance = Nz::Lerp(from.distance, to.distance, interpolation); plane.normal = Vector3::Lerp(from.normal, to.normal, interpolation); plane.normal.Normalize(); return plane; } + /*! + * \brief Shorthand for the plane (0, 0, 1, 0) + * \return A plane with components (0, 0, 1, 0) + * + * \see MakeXY + */ + template Plane Plane::XY() { - return Plane(F(0.0), F(0.0), F(1.0), F(0.0)); + Plane plane; + plane.MakeXY(); + + return plane; } + /*! + * \brief Shorthand for the plane (0, 1, 0, 0) + * \return A plane with components (0, 1, 0, 0) + * + * \see MakeXZ + */ + template Plane Plane::XZ() { - return Plane(F(0.0), F(1.0), F(0.0), F(0.0)); + Plane plane; + plane.MakeXZ(); + + return plane; } + /*! + * \brief Shorthand for the plane (1, 0, 0, 0) + * \return A plane with components (1, 0, 0, 0) + * + * \see MakeYZ + */ + template Plane Plane::YZ() { - return Plane(F(1.0), F(0.0), F(0.0), F(0.0)); + Plane plane; + plane.MakeYZ(); + + return plane; + } + + /*! + * \brief Serializes a Vector2 + * \return true if successfully serialized + * + * \param context Serialization context + * \param plane Input Vector2 + */ + template + bool Serialize(SerializationContext& context, const Plane& plane) + { + if (!Serialize(context, plane.normal)) + return false; + + if (!Serialize(context, plane.distance)) + return false; + + return true; + } + + /*! + * \brief Unserializes a Plane + * \return true if successfully unserialized + * + * \param context Serialization context + * \param plane Output Plane + */ + template + bool Unserialize(SerializationContext& context, Plane* plane) + { + if (!Unserialize(context, &plane->normal)) + return false; + + if (!Unserialize(context, &plane->distance)) + return false; + + return true; } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param plane The plane to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::Plane& plane) { diff --git a/include/Nazara/Math/Quaternion.hpp b/include/Nazara/Math/Quaternion.hpp index b8ca310c1..bbb313bd5 100644 --- a/include/Nazara/Math/Quaternion.hpp +++ b/include/Nazara/Math/Quaternion.hpp @@ -11,6 +11,8 @@ namespace Nz { + struct SerializationContext; + template class EulerAngles; template class Vector3; @@ -19,9 +21,9 @@ namespace Nz public: Quaternion() = default; Quaternion(T W, T X, T Y, T Z); - Quaternion(const T quat[4]); - Quaternion(T angle, const Vector3& axis); Quaternion(const EulerAngles& angles); + Quaternion(T angle, const Vector3& axis); + Quaternion(const T quat[4]); //Quaternion(const Matrix3& mat); template explicit Quaternion(const Quaternion& quat); Quaternion(const Quaternion& quat) = default; @@ -47,9 +49,9 @@ namespace Nz Quaternion& Normalize(T* length = nullptr); Quaternion& Set(T W, T X, T Y, T Z); - Quaternion& Set(const T quat[4]); - Quaternion& Set(T angle, const Vector3& normalizedAxis); Quaternion& Set(const EulerAngles& angles); + Quaternion& Set(T angle, const Vector3& normalizedAxis); + Quaternion& Set(const T quat[4]); //Quaternion& Set(const Matrix3& mat); Quaternion& Set(const Quaternion& quat); template Quaternion& Set(const Quaternion& quat); @@ -60,7 +62,7 @@ namespace Nz //Matrix3 ToRotationMatrix() const; String ToString() const; - Quaternion& operator=(const Quaternion& quat); + Quaternion& operator=(const Quaternion& quat) = default; Quaternion operator+(const Quaternion& quat) const; Quaternion operator*(const Quaternion& quat) const; @@ -88,6 +90,9 @@ namespace Nz typedef Quaternion Quaterniond; typedef Quaternion Quaternionf; + + template bool Serialize(SerializationContext& context, const Quaternion& quat); + template bool Unserialize(SerializationContext& context, Quaternion* quat); } template std::ostream& operator<<(std::ostream& out, const Nz::Quaternion& quat); diff --git a/include/Nazara/Math/Quaternion.inl b/include/Nazara/Math/Quaternion.inl index 50c8ec8e3..bc672cd81 100644 --- a/include/Nazara/Math/Quaternion.inl +++ b/include/Nazara/Math/Quaternion.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -15,29 +16,68 @@ namespace Nz { + /*! + * \ingroup math + * \class Nz::Quaternion + * \brief Math class that represents an element of the quaternions + * + * \remark The quaternion is meant to be 'unit' to represent rotations in a three dimensional space + */ + + /*! + * \brief Constructs a Quaternion object from its components + * + * \param W W component + * \param X X component + * \param Y Y component + * \param Z Z component + */ + template Quaternion::Quaternion(T W, T X, T Y, T Z) { Set(W, X, Y, Z); } + /*! + * \brief Constructs a Quaternion object from a EulerAngles + * + * \param angles Easier representation of rotation of space + * + * \see EulerAngles + */ + template - Quaternion::Quaternion(const T quat[4]) + Quaternion::Quaternion(const EulerAngles& angles) { - Set(quat); + Set(angles); } + /*! + * \brief Constructs a Quaternion object from an angle and a direction + * + * \param angle Unit depends of NAZARA_MATH_ANGLE_RADIAN + * \param axis Vector3 which represents a direction, no need to be normalized + */ + template Quaternion::Quaternion(T angle, const Vector3& axis) { Set(angle, axis); } + /*! + * \brief Constructs a Quaternion object from an array of four elements + * + * \param quat[4] quat[0] is W component, quat[1] is X component, quat[2] is Y component and quat[3] is Z component + */ + template - Quaternion::Quaternion(const EulerAngles& angles) + Quaternion::Quaternion(const T quat[4]) { - Set(angles); + Set(quat); } + /* template Quaternion::Quaternion(const Matrix3& mat) @@ -45,6 +85,13 @@ namespace Nz Set(mat); } */ + + /*! + * \brief Constructs a Quaternion object from another type of Quaternion + * + * \param quat Quaternion of type U to convert to type T + */ + template template Quaternion::Quaternion(const Quaternion& quat) @@ -52,6 +99,11 @@ namespace Nz Set(quat); } + /*! + * \brief Computes the w component of the quaternion to make it unit + * \return A reference to this quaternion + */ + template Quaternion& Quaternion::ComputeW() { @@ -65,6 +117,15 @@ namespace Nz return *this; } + /*! + * \brief Returns the rotational conjugate of this quaternion + * \return A reference to this quaternion + * + * The conjugate of a quaternion represents the same rotation in the opposite direction about the rotational axis + * + * \see GetConjugate + */ + template Quaternion& Quaternion::Conjugate() { @@ -75,12 +136,28 @@ namespace Nz return *this; } + /*! + * \brief Calculates the dot (scalar) product with two quaternions + * \return The value of the dot product + * + * \param quat The other quaternion to calculate the dot product with + */ + template T Quaternion::DotProduct(const Quaternion& quat) const { - return w*quat.w + x*quat.x + y*quat.y + z*quat.z; + return w * quat.w + x * quat.x + y * quat.y + z * quat.z; } + /*! + * \brief Gets the rotational conjugate of this quaternion + * \return A new quaternion which is the conjugate of this quaternion + * + * The conjugate of a quaternion represents the same rotation in the opposite direction about the rotational axis + * + * \see Conjugate + */ + template Quaternion Quaternion::GetConjugate() const { @@ -90,6 +167,15 @@ namespace Nz return quat; } + /*! + * \brief Gets the inverse of this quaternion + * \return A new quaternion which is the inverse of this quaternion + * + * \remark If this quaternion is (0, 0, 0, 0), then it returns (0, 0, 0, 0) + * + * \see Inverse + */ + template Quaternion Quaternion::GetInverse() const { @@ -99,6 +185,17 @@ namespace Nz return quat; } + /*! + * \brief Gets the normalization of this quaternion + * \return A new quaternion which is the normalization of this quaternion + * + * \param length Optional argument to obtain the length's ratio of the quaternion and the unit-length + * + * \remark If this quaternion is (0, 0, 0, 0), then it returns (0, 0, 0, 0) and length is 0 + * + * \see Normalize + */ + template Quaternion Quaternion::GetNormal(T* length) const { @@ -108,6 +205,15 @@ namespace Nz return quat; } + /*! + * \brief Inverts this quaternion + * \return A reference to this quaternion which is now inverted + * + * \remark If this quaternion is (0, 0, 0, 0), then it returns (0, 0, 0, 0) + * + * \see GetInverse + */ + template Quaternion& Quaternion::Inverse() { @@ -125,15 +231,34 @@ namespace Nz return *this; } + /*! + * \brief Makes the quaternion (1, 0, 0, 0) + * \return A reference to this vector with components (1, 0, 0, 0) + * + * \see Unit + */ + template Quaternion& Quaternion::MakeIdentity() { return Set(F(1.0), F(0.0), F(0.0), F(0.0)); } + /*! + * \brief Makes this quaternion to the rotation required to rotate direction Vector3 from to direction Vector3 to + * \return A reference to this vector which is the rotation needed + * + * \param from Initial vector + * \param to Target vector + * + * \see RotationBetween + */ + template Quaternion& Quaternion::MakeRotationBetween(const Vector3& from, const Vector3& to) { + // TODO (Gawaboumga): Replace by http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors ? + T dot = from.DotProduct(to); if (NumberEquals(dot, F(-1.0))) { @@ -157,28 +282,55 @@ namespace Nz } } + /*! + * \brief Makes the quaternion (0, 0, 0, 0) + * \return A reference to this vector with components (0, 0, 0, 0) + * + * \see Zero + */ + template Quaternion& Quaternion::MakeZero() { return Set(F(0.0), F(0.0), F(0.0), F(0.0)); } + /*! + * \brief Calculates the magnitude (length) of the quaternion + * \return The magnitude + * + * \see SquaredMagnitude + */ + template T Quaternion::Magnitude() const { return std::sqrt(SquaredMagnitude()); } + /*! + * \brief Normalizes the current quaternion + * \return A reference to this quaternion which is now normalized + * + * \param length Optional argument to obtain the length's ratio of the quaternion and the unit-length + * + * \remark If the quaternion is (0, 0, 0, 0), then it returns (0, 0, 0, 0) and length is 0 + * + * \see GetNormal + */ + template Quaternion& Quaternion::Normalize(T* length) { T norm = std::sqrt(SquaredMagnitude()); - T invNorm = F(1.0) / norm; - - w *= invNorm; - x *= invNorm; - y *= invNorm; - z *= invNorm; + if (norm > F(0.0)) + { + T invNorm = F(1.0) / norm; + w *= invNorm; + x *= invNorm; + y *= invNorm; + z *= invNorm; + } if (length) *length = norm; @@ -186,6 +338,16 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the quaternion + * \return A reference to this quaternion + * + * \param W W component + * \param X X component + * \param Y Y component + * \param Z Z component + */ + template Quaternion& Quaternion::Set(T W, T X, T Y, T Z) { @@ -197,17 +359,29 @@ namespace Nz return *this; } - template - Quaternion& Quaternion::Set(const T quat[4]) - { - w = quat[0]; - x = quat[1]; - y = quat[2]; - z = quat[3]; + /*! + * \brief Sets this quaternion from rotation specified by Euler angle + * \return A reference to this quaternion + * + * \param angles Easier representation of rotation of space + * + * \see EulerAngles + */ - return *this; + template + Quaternion& Quaternion::Set(const EulerAngles& angles) + { + return Set(angles.ToQuaternion()); } + /*! + * \brief Sets this quaternion from rotation specified by axis and angle + * \return A reference to this quaternion + * + * \param angle Unit depends of NAZARA_MATH_ANGLE_RADIAN + * \param axis Vector3 which represents a direction, no need to be normalized + */ + template Quaternion& Quaternion::Set(T angle, const Vector3& axis) { @@ -229,12 +403,46 @@ namespace Nz return Normalize(); } + /*! + * \brief Sets the components of the quaternion from an array of four elements + * \return A reference to this quaternion + * + * \param quat[4] quat[0] is W component, quat[1] is X component, quat[2] is Y component and quat[3] is Z component + */ + template - Quaternion& Quaternion::Set(const EulerAngles& angles) + Quaternion& Quaternion::Set(const T quat[4]) { - return Set(angles.ToQuaternion()); + w = quat[0]; + x = quat[1]; + y = quat[2]; + z = quat[3]; + + return *this; } + /*! + * \brief Sets the components of the quaternion from another quaternion + * \return A reference to this quaternion + * + * \param vec The other quaternion + */ + + template + Quaternion& Quaternion::Set(const Quaternion& quat) + { + std::memcpy(this, &quat, sizeof(Quaternion)); + + return *this; + } + + /*! + * \brief Sets the components of the quaternion from another type of Quaternion + * \return A reference to this quaternion + * + * \param quat Quaternion of type U to convert its components + */ + template template Quaternion& Quaternion::Set(const Quaternion& quat) @@ -247,36 +455,48 @@ namespace Nz return *this; } - template - Quaternion& Quaternion::Set(const Quaternion& quat) - { - std::memcpy(this, &quat, sizeof(Quaternion)); - - return *this; - } + /*! + * \brief Calculates the squared magnitude (length) of the quaternion + * \return The squared magnitude + * + * \see Magnitude + */ template T Quaternion::SquaredMagnitude() const { - return w*w + x*x + y*y + z*z; + return w * w + x * x + y * y + z * z; } + /*! + * \brief Converts this quaternion to Euler angles representation + * \return EulerAngles which is the representation of this rotation + * + * \remark Rotation are "left-handed" + */ + template EulerAngles Quaternion::ToEulerAngles() const { - T test = x*y + z*w; + T test = x * y + z * w; if (test > F(0.499)) // singularity at north pole return EulerAngles(FromDegrees(F(90.0)), FromRadians(F(2.0) * std::atan2(x, w)), F(0.0)); if (test < F(-0.499)) + // singularity at south pole return EulerAngles(FromDegrees(F(-90.0)), FromRadians(F(-2.0) * std::atan2(x, w)), F(0.0)); - return EulerAngles(FromRadians(std::atan2(F(2.0)*x*w - F(2.0)*y*z, F(1.0) - F(2.0)*x*x - F(2.0)*z*z)), - FromRadians(std::atan2(F(2.0)*y*w - F(2.0)*x*z, F(1.0) - F(2.0)*y*y - F(2.0)*z*z)), - FromRadians(std::asin(F(2.0)*test))); + return EulerAngles(FromRadians(std::atan2(F(2.0) * x * w - F(2.0) * y * z, F(1.0) - F(2.0) * x * x - F(2.0) * z * z)), + FromRadians(std::atan2(F(2.0) * y * w - F(2.0) * x * z, F(1.0) - F(2.0) * y * y - F(2.0) * z * z)), + FromRadians(std::asin(F(2.0) * test))); } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Quaternion(w | x, y, z)" + */ + template String Quaternion::ToString() const { @@ -285,11 +505,12 @@ namespace Nz return ss << "Quaternion(" << w << " | " << x << ", " << y << ", " << z << ')'; } - template - Quaternion& Quaternion::operator=(const Quaternion& quat) - { - return Set(quat); - } + /*! + * \brief Adds the components of the quaternion with other quaternion + * \return A quaternion where components are the sum of this quaternion and the other one + * + * \param quat The other quaternion to add components with + */ template Quaternion Quaternion::operator+(const Quaternion& quat) const @@ -303,18 +524,32 @@ namespace Nz return result; } + /*! + * \brief Multiplies of the quaternion with other quaternion + * \return A quaternion which is the product of those two according to operator* in quaternions + * + * \param quat The other quaternion to multiply with + */ + template Quaternion Quaternion::operator*(const Quaternion& quat) const { Quaternion result; - result.w = w*quat.w - x*quat.x - y*quat.y - z*quat.z; - result.x = w*quat.x + x*quat.w + y*quat.z - z*quat.y; - result.y = w*quat.y + y*quat.w + z*quat.x - x*quat.z; - result.z = w*quat.z + z*quat.w + x*quat.y - y*quat.x; + result.w = w * quat.w - x * quat.x - y * quat.y - z * quat.z; + result.x = w * quat.x + x * quat.w + y * quat.z - z * quat.y; + result.y = w * quat.y + y * quat.w + z * quat.x - x * quat.z; + result.z = w * quat.z + z * quat.w + x * quat.y - y * quat.x; return result; } + /*! + * \brief Apply the quaternion to the Vector3 + * \return A Vector3f which is the vector rotated by this quaternion + * + * \param vec The vector to multiply with + */ + template Vector3 Quaternion::operator*(const Vector3& vec) const { @@ -327,60 +562,123 @@ namespace Nz return vec + uv + uuv; } + /*! + * \brief Multiplies the components of the quaternion with a scalar + * \return A quaternion where components are the product of this quaternion and the scalar + * + * \param scale The scalar to multiply components with + */ + template Quaternion Quaternion::operator*(T scale) const { return Quaternion(w * scale, - x * scale, - y * scale, - z * scale); + x * scale, + y * scale, + z * scale); } + /*! + * \brief Divides the quaternion with other quaternion + * \return A quaternion which is the quotient of those two according to operator* in quaternions + * + * \param quat The other quaternion to divide with + */ + template Quaternion Quaternion::operator/(const Quaternion& quat) const { return quat.GetConjugate() * (*this); } + /*! + * \brief Adds the components of the quaternion with other quaternion + * \return A reference to this quaternion where components are the sum of this quaternion and the other one + * + * \param quat The other quaternion to add components with + */ + template Quaternion& Quaternion::operator+=(const Quaternion& quat) { return operator=(operator+(quat)); } + /*! + * \brief Multiplies of the quaternion with other quaternion + * \return A reference to this quaternion which is the product of those two according to operator* in quaternions + * + * \param quat The other quaternion to multiply with + */ + template Quaternion& Quaternion::operator*=(const Quaternion& quat) { return operator=(operator*(quat)); } + /*! + * \brief Multiplies the components of the quaternion with a scalar + * \return A reference to this quaternion where components are the product of this quaternion and the scalar + * + * \param scale The scalar to multiply components with + */ + template Quaternion& Quaternion::operator*=(T scale) { return operator=(operator*(scale)); } + /*! + * \brief Divides the quaternion with other quaternion + * \return A reference to this quaternion which is the quotient of those two according to operator* in quaternions + * + * \param quat The other quaternion to divide with + */ + template Quaternion& Quaternion::operator/=(const Quaternion& quat) { return operator=(operator/(quat)); } + /*! + * \brief Compares the quaternion to other one + * \return true if the quaternions are the same + * + * \param vec Other quaternion to compare with + */ + template bool Quaternion::operator==(const Quaternion& quat) const { return NumberEquals(w, quat.w) && - NumberEquals(x, quat.x) && - NumberEquals(y, quat.y) && - NumberEquals(z, quat.z); + NumberEquals(x, quat.x) && + NumberEquals(y, quat.y) && + NumberEquals(z, quat.z); } + /*! + * \brief Compares the quaternion to other one + * \return false if the quaternions are the same + * + * \param vec Other quaternion to compare with + */ + template bool Quaternion::operator!=(const Quaternion& quat) const { return !operator==(quat); } + /*! + * \brief Shorthand for the quaternion (1, 0, 0, 0) + * \return A quaternion with components (1, 0, 0, 0) + * + * \see MakeIdentity + */ + template Quaternion Quaternion::Identity() { @@ -390,6 +688,20 @@ namespace Nz return quaternion; } + /*! + * \brief Interpolates the quaternion to other one with a factor of interpolation + * \return A new quaternion which is the interpolation of two quaternions + * + * \param from Initial quaternion + * \param to Target quaternion + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * \remark With NAZARA_DEBUG, a NazaraError is thrown and Zero() is returned + * + * \see Lerp, Slerp + */ + template Quaternion Quaternion::Lerp(const Quaternion& from, const Quaternion& to, T interpolation) { @@ -410,12 +722,32 @@ namespace Nz return interpolated; } + /*! + * \brief Gives the normalized quaternion + * \return A normalized quaternion from the quat + * + * \param quat Quaternion to normalize + * \param length Optional argument to obtain the length's ratio of the vector and the unit-length + * + * \see GetNormal + */ + template Quaternion Quaternion::Normalize(const Quaternion& quat, T* length) { return quat.GetNormal(length); } + /*! + * \brief Gets the rotation required to rotate direction Vector3 from to direction Vector3 to + * \return A quaternion which is the rotation needed between those two Vector3 + * + * \param from Initial vector + * \param to Target vector + * + * \see MakeRotationBetween + */ + template Quaternion Quaternion::RotationBetween(const Vector3& from, const Vector3& to) { @@ -425,6 +757,20 @@ namespace Nz return quaternion; } + /*! + * \brief Interpolates spherically the quaternion to other one with a factor of interpolation + * \return A new quaternion which is the interpolation of two quaternions + * + * \param from Initial quaternion + * \param to Target quaternion + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * \remark With NAZARA_DEBUG, a NazaraError is thrown and Zero() is returned + * + * \see Lerp + */ + template Quaternion Quaternion::Slerp(const Quaternion& from, const Quaternion& to, T interpolation) { @@ -441,7 +787,7 @@ namespace Nz T cosOmega = from.DotProduct(to); if (cosOmega < F(0.0)) { - // On inverse tout + // We invert everything q.Set(-to.w, -to.x, -to.y, -to.z); cosOmega = -cosOmega; } @@ -451,7 +797,7 @@ namespace Nz T k0, k1; if (cosOmega > F(0.9999)) { - // Interpolation linéaire pour éviter une division par zéro + // Linear interpolation to avoid division by zero k0 = F(1.0) - interpolation; k1 = interpolation; } @@ -460,7 +806,7 @@ namespace Nz T sinOmega = std::sqrt(F(1.0) - cosOmega*cosOmega); T omega = std::atan2(sinOmega, cosOmega); - // Pour éviter deux divisions + // To avoid two divisions sinOmega = F(1.0)/sinOmega; k0 = std::sin((F(1.0) - interpolation) * omega) * sinOmega; @@ -468,9 +814,16 @@ namespace Nz } Quaternion result(k0 * from.w, k0 * from.x, k0 * from.y, k0 * from.z); - return result += q*k1; + return result += q * k1; } + /*! + * \brief Shorthand for the quaternion (0, 0, 0, 0) + * \return A quaternion with components (0, 0, 0, 0) + * + * \see MakeZero + */ + template Quaternion Quaternion::Zero() { @@ -479,8 +832,67 @@ namespace Nz return quaternion; } + + + /*! + * \brief Serializes a Quaternion + * \return true if successfully serialized + * + * \param context Serialization context + * \param quat Input Quaternion + */ + template + bool Serialize(SerializationContext& context, const Quaternion& quat) + { + if (!Serialize(context, quat.x)) + return false; + + if (!Serialize(context, quat.y)) + return false; + + if (!Serialize(context, quat.z)) + return false; + + if (!Serialize(context, quat.w)) + return false; + + return true; + } + + /*! + * \brief Unserializes a Quaternion + * \return true if successfully unserialized + * + * \param context Serialization context + * \param quat Output Quaternion + */ + template + bool Unserialize(SerializationContext& context, Quaternion* quat) + { + if (!Unserialize(context, &quat->x)) + return false; + + if (!Unserialize(context, &quat->y)) + return false; + + if (!Unserialize(context, &quat->z)) + return false; + + if (!Unserialize(context, &quat->w)) + return false; + + return true; + } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param quat The quaternion to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::Quaternion& quat) { diff --git a/include/Nazara/Math/Ray.hpp b/include/Nazara/Math/Ray.hpp index e0ae68363..b391ad866 100644 --- a/include/Nazara/Math/Ray.hpp +++ b/include/Nazara/Math/Ray.hpp @@ -18,15 +18,17 @@ namespace Nz { + struct SerializationContext; + template class Ray { public: Ray() = default; Ray(T X, T Y, T Z, T directionX, T directionY, T directionZ); + Ray(const Vector3& origin, const Vector3& direction); Ray(const T origin[3], const T direction[3]); Ray(const Plane& planeOne, const Plane& planeTwo); - Ray(const Vector3& origin, const Vector3& direction); template explicit Ray(const Ray& ray); template explicit Ray(const Vector3& origin, const Vector3& direction); Ray(const Ray& ray) = default; @@ -42,16 +44,17 @@ namespace Nz bool Intersect(const OrientedBox& orientedBox, T* closestHit = nullptr, T* furthestHit = nullptr) const; bool Intersect(const Plane& plane, T* hit = nullptr) const; bool Intersect(const Sphere& sphere, T* closestHit = nullptr, T* furthestHit = nullptr) const; + bool Intersect(const Vector3& firstPoint, const Vector3& secondPoint, const Vector3& thirdPoint, T* hit = nullptr) const; Ray& MakeAxisX(); Ray& MakeAxisY(); Ray& MakeAxisZ(); Ray& Set(T X, T Y, T Z, T directionX, T directionY, T directionZ); + Ray& Set(const Vector3& origin, const Vector3& direction); Ray& Set(const T origin[3], const T direction[3]); Ray& Set(const Plane& planeOne, const Plane& planeTwo); Ray& Set(const Ray& ray); - Ray& Set(const Vector3& origin, const Vector3& direction); template Ray& Set(const Ray& ray); template Ray& Set(const Vector3& origin, const Vector3& direction); @@ -72,6 +75,9 @@ namespace Nz typedef Ray Rayd; typedef Ray Rayf; + + template bool Serialize(SerializationContext& context, const Ray& ray); + template bool Unserialize(SerializationContext& context, Ray* ray); } template std::ostream& operator<<(std::ostream& out, const Nz::Ray& vec); diff --git a/include/Nazara/Math/Ray.inl b/include/Nazara/Math/Ray.inl index 9f65b5382..6045615ed 100644 --- a/include/Nazara/Math/Ray.inl +++ b/include/Nazara/Math/Ray.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -10,29 +11,78 @@ namespace Nz { + /*! + * \ingroup math + * \class Nz::Ray + * \brief Math class that represents a ray or a straight line in 3D space + * + * This ray is meant to be understood like origin + lambda * direction, where lambda is a real positive parameter + */ + + /*! + * \brief Constructs a Ray object from its position and direction + * + * \param X X position + * \param Y Y position + * \param Z Z position + * \param DirectionX X component of the vector direction + * \param DirectionY Y component of the vector direction + * \param DirectionY Y component of the vector direction + */ + template Ray::Ray(T X, T Y, T Z, T DirectionX, T DirectionY, T DirectionZ) { Set(X, Y, Z, DirectionX, DirectionY, DirectionZ); } + /*! + * \brief Constructs a Ray object from two Vector3 + * + * \param Origin Vector which represents the origin of the ray + * \param Direction Vector which represents the direction of the ray + */ + + template + Ray::Ray(const Vector3& Origin, const Vector3& Direction) + { + Set(Origin, Direction); + } + + /*! + * \brief Constructs a Ray object from two arrays of three elements + * + * \param Origin[3] Origin[0] is X position, Origin[1] is Y position and Origin[2] is Z position + * \param Direction[3] Direction[0] is X direction, Direction[1] is Y direction and Direction[2] is Z direction + */ + template Ray::Ray(const T Origin[3], const T Direction[3]) { Set(Origin, Direction); } + /*! + * \brief Constructs a Ray object from the intersection of two planes + * + * \param planeOne First plane + * \param planeTwo Second secant plane + * + * \remark Produce a NazaraError if planes are parallel with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and planes are parallel + */ + template Ray::Ray(const Plane& planeOne, const Plane& planeTwo) { Set(planeOne, planeTwo); } - template - Ray::Ray(const Vector3& Origin, const Vector3& Direction) - { - Set(Origin, Direction); - } + /*! + * \brief Constructs a Ray object from another type of Ray + * + * \param ray Ray of type U to convert to type T + */ template template @@ -41,6 +91,13 @@ namespace Nz Set(ray); } + /*! + * \brief Constructs a Ray object from two Vector3 from another type of Ray + * + * \param Origin Origin of type U to convert to type T + * \param Direction Direction of type U to convert to type T + */ + template template Ray::Ray(const Vector3& Origin, const Vector3& Direction) @@ -48,6 +105,13 @@ namespace Nz Set(Origin, Direction); } + /*! + * \brief Finds the closest point of the ray from point + * \return The parameter where the point along this ray that is closest to the point provided + * + * \param point The point to get the closest approach to + */ + template T Ray::ClosestPoint(const Vector3& point) const { @@ -55,15 +119,37 @@ namespace Nz T vsq = direction.GetSquaredLength(); T proj = delta.DotProduct(direction); - return proj/vsq; + return proj / vsq; } + /*! + * \brief Gets the point along the ray for this parameter + * \return The point on the ray + * + * \param lambda Parameter to obtain a particular point on the ray + */ + template Vector3 Ray::GetPoint(T lambda) const { return origin + lambda * direction; } + /*! + * \brief Checks whether or not this ray intersects with the BoundingVolume + * \return true if it intersects + * + * \param volume BoundingVolume to check + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \remark If BoundingVolume is Extend_Infinite, then closestHit and furthestHit are equal to 0 et infinity + * \remark If BoundingVolume is Extend_Null, then closestHit and furthestHit are unchanged + * \remark If enumeration of BoundingVolume is not defined in Extend, a NazaraError is thrown and closestHit and furthestHit are unchanged + * + * \see Intersect + */ + template bool Ray::Intersect(const BoundingVolume& volume, T* closestHit, T* furthestHit) const { @@ -96,6 +182,17 @@ namespace Nz return false; } + /*! + * \brief Checks whether or not this ray intersects with the Box + * \return true if it intersects + * + * \param box Box to check + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const Box& box, T* closestHit, T* furthestHit) const { @@ -142,6 +239,18 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not this ray intersects with the transform Matrix4 applied to the Box + * \return true if it intersects + * + * \param box Box to check + * \param transform Matrix4 which represents the transformation of the box + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const Box& box, const Matrix4& transform, T* closestHit, T* furthestHit) const { @@ -200,6 +309,17 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not this ray intersects with the OrientedBox + * \return true if it intersects + * + * \param orientedBox OrientedBox to check + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const OrientedBox& orientedBox, T* closestHit, T* furthestHit) const { @@ -212,9 +332,9 @@ namespace Nz // Construction de la matrice de transformation de l'OBB Matrix4 matrix(width.x, height.x, depth.x, corner.x, - width.y, height.y, depth.y, corner.y, - width.z, height.z, depth.z, corner.z, - F(0.0), F(0.0), F(0.0), F(1.0)); + width.y, height.y, depth.y, corner.y, + width.z, height.z, depth.z, corner.z, + F(0.0), F(0.0), F(0.0), F(1.0)); matrix.InverseAffine(); @@ -227,12 +347,22 @@ namespace Nz return tmpRay.Intersect(tmpBox, closestHit, furthestHit); } + /*! + * \brief Checks whether or not this ray intersects with the plane + * \return true if it intersects + * + * \param plane Plane to check + * \param hit Optional argument to get the parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const Plane& plane, T* hit) const { T divisor = plane.normal.DotProduct(direction); if (NumberEquals(divisor, F(0.0))) - return false; // perpendicular + return false; // Perpendicular T lambda = -(plane.normal.DotProduct(origin) - plane.distance) / divisor; // The plane is ax + by + cz = d if (lambda < F(0.0)) @@ -244,6 +374,17 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not this ray intersects with the sphere + * \return true if it intersects + * + * \param sphere Sphere to check + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const Sphere& sphere, T* closestHit, T* furthestHit) const { @@ -253,8 +394,8 @@ namespace Nz if (length < F(0.0)) return false; // ray is perpendicular to the vector origin - center - T squaredDistance = sphereRay.GetSquaredLength() - length*length; - T squaredRadius = sphere.radius*sphere.radius; + T squaredDistance = sphereRay.GetSquaredLength() - length * length; + T squaredRadius = sphere.radius * sphere.radius; if (squaredDistance > squaredRadius) return false; // if the ray is further than the radius @@ -274,24 +415,102 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not this ray intersects with the triangle + * \return true if it intersects + * + * \param firstPoint First vertex of the triangle + * \param secondPoint Second vertex of the triangle + * \param thirdPoint Third vertex of the triangle + * \param hit Optional argument to get the parameter where the intersection is only if it happened + * + * \see Intersect + */ + + template + bool Ray::Intersect(const Vector3& firstPoint, const Vector3& secondPoint, const Vector3& thirdPoint, T* hit) const + { + // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm + Vector3 firstEdge = secondPoint - firstPoint; + Vector3 secondEdge = thirdPoint - firstPoint; + + Vector3 P = Vector3::CrossProduct(direction, secondEdge); + const T divisor = firstEdge.DotProduct(P); + if (NumberEquals(divisor, F(0.0))) + return false; // Ray lies in plane of triangle + + Vector3 directionToPoint = origin - firstPoint; + T u = directionToPoint.DotProduct(P) / divisor; + if (u < F(0.0) || u > F(1.0)) + return 0; // The intersection lies outside of the triangle + + Vector3 Q = Vector3::CrossProduct(directionToPoint, firstEdge); + T v = directionToPoint.DotProduct(Q) / divisor; + if (v < F(0.0) || u + v > F(1.0)) + return 0; // The intersection lies outside of the triangle + + T t = secondEdge.DotProduct(Q) / divisor; + if (t > F(0.0)) + { + if (hit) + *hit = t; + return true; + } + + return false; + } + + /*! + * \brief Makes the ray with position (0, 0, 0) and direction (1, 0, 0) + * \return A reference to this ray with position (0, 0, 0) and direction (1, 0, 0) + * + * \see AxisX + */ + template Ray& Ray::MakeAxisX() { return Set(Vector3::Zero(), Vector3::UnitX()); } + /*! + * \brief Makes the ray with position (0, 0, 0) and direction (0, 1, 0) + * \return A reference to this ray with position (0, 0, 0) and direction (0, 1, 0) + * + * \see AxisY + */ + template Ray& Ray::MakeAxisY() { return Set(Vector3::Zero(), Vector3::UnitY()); } + /*! + * \brief Makes the ray with position (0, 0, 0) and direction (0, 0, 1) + * \return A reference to this ray with position (0, 0, 0) and direction (0, 0, 1) + * + * \see AxisZ + */ + template Ray& Ray::MakeAxisZ() { return Set(Vector3::Zero(), Vector3::UnitZ()); } + /*! + * \brief Sets the components of the ray with position and direction + * \return A reference to this ray + * + * \param X X position + * \param Y Y position + * \param Z Z position + * \param DirectionX X component of the vector direction + * \param DirectionY Y component of the vector direction + * \param DirectionY Y component of the vector direction + */ + template Ray& Ray::Set(T X, T Y, T Z, T directionX, T directionY, T directionZ) { @@ -301,6 +520,31 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the ray with position and direction + * \return A reference to this ray + * + * \param Origin Vector which represents the origin of the ray + * \param Direction Vector which represents the direction of the ray + */ + + template + Ray& Ray::Set(const Vector3& Origin, const Vector3& Direction) + { + direction = Direction; + origin = Origin; + + return *this; + } + + /*! + * \brief Sets the components of this ray from two arrays of three elements + * \return A reference to this ray + * + * \param Origin[3] Origin[0] is X position, Origin[1] is Y position and Origin[2] is Z position + * \param Direction[3] Direction[0] is X direction, Direction[1] is Y direction and Direction[2] is Z direction + */ + template Ray& Ray::Set(const T Origin[3], const T Direction[3]) { @@ -310,6 +554,17 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of this ray from the intersection of two planes + * \return A reference to this ray + * + * \param planeOne First plane + * \param planeTwo Second secant plane + * + * \remark Produce a NazaraError if planes are parallel with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and planes are parallel + */ + template Ray& Ray::Set(const Plane& planeOne, const Plane& planeTwo) { @@ -321,7 +576,7 @@ namespace Nz #if NAZARA_MATH_SAFE if (NumberEquals(det, F(0.0))) { - String error("Planes are parallel."); + String error("Planes are parallel"); NazaraError(error); throw std::domain_error(error); @@ -338,6 +593,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the ray with components from another + * \return A reference to this ray + * + * \param ray The other ray + */ + template Ray& Ray::Set(const Ray& ray) { @@ -346,14 +608,12 @@ namespace Nz return *this; } - template - Ray& Ray::Set(const Vector3& Origin, const Vector3& Direction) - { - direction = Direction; - origin = Origin; - - return *this; - } + /*! + * \brief Sets the components of the ray from another type of Ray + * \return A reference to this ray + * + * \param ray Ray of type U to convert its components + */ template template @@ -365,6 +625,14 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the ray from another type of Ray + * \return A reference to this ray + * + * \param Origin Origin of type U to convert to type T + * \param Direction Direction of type U to convert to type T + */ + template template Ray& Ray::Set(const Vector3& Origin, const Vector3& Direction) @@ -375,6 +643,11 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Ray(origin: Vector3(origin.x, origin.y, origin.z), direction: Vector3(direction.x, direction.y, direction.z))" + */ + template String Ray::ToString() const { @@ -383,24 +656,54 @@ namespace Nz return ss << "Ray(origin: " << origin.ToString() << ", direction: " << direction.ToString() << ")"; } + /*! + * \brief Multiplies the direction ray with the lambda to get the point along the ray for this parameter + * \return The point on the ray + * + * \param lambda Parameter to obtain a particular point on the ray + * + * \see GetPoint + */ + template Vector3 Ray::operator*(T lambda) const { return GetPoint(lambda); } + /*! + * \brief Compares the ray to other one + * \return true if the ray are the same + * + * \param rec Other ray to compare with + */ + template bool Ray::operator==(const Ray& ray) const { return direction == ray.direction && origin == ray.origin; } + /*! + * \brief Compares the ray to other one + * \return false if the ray are the same + * + * \param rec Other ray to compare with + */ + template bool Ray::operator!=(const Ray& ray) const { return !operator==(ray); } + /*! + * \brief Shorthand for the ray (0, 0, 0), (1, 0, 0) + * \return A ray with position (0, 0, 0) and direction (1, 0, 0) + * + * \see MakeAxisX + */ + template Ray Ray::AxisX() { @@ -410,6 +713,13 @@ namespace Nz return axis; } + /*! + * \brief Shorthand for the ray (0, 0, 0), (0, 1, 0) + * \return A ray with position (0, 0, 0) and direction (0, 1, 0) + * + * \see MakeAxisY + */ + template Ray Ray::AxisY() { @@ -419,6 +729,13 @@ namespace Nz return axis; } + /*! + * \brief Shorthand for the ray (0, 0, 0), (0, 0, 1) + * \return A ray with position (0, 0, 0) and direction (0, 0, 1) + * + * \see MakeAxisZ + */ + template Ray Ray::AxisZ() { @@ -428,13 +745,72 @@ namespace Nz return axis; } + /*! + * \brief Interpolates the ray to other one with a factor of interpolation + * \return A new ray which is the interpolation of two rectangles + * + * \param from Initial ray + * \param to Target ray + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * + * \see Lerp + */ + template Ray Ray::Lerp(const Ray& from, const Ray& to, T interpolation) { - return Ray(from.origin.Lerp(to.origin, interpolation), from.direction.Lerp(to.direction, interpolation)); + return Ray(Nz::Vector3::Lerp(from.origin, to.origin, interpolation), Nz::Vector3::Lerp(from.direction, to.direction, interpolation)); + } + + /*! + * \brief Serializes a Ray + * \return true if successfully serialized + * + * \param context Serialization context + * \param ray Input Ray + */ + template + bool Serialize(SerializationContext& context, const Ray& ray) + { + if (!Serialize(context, ray.origin)) + return false; + + if (!Serialize(context, ray.direction)) + return false; + + return true; + } + + /*! + * \brief Unserializes a Ray + * \return true if successfully unserialized + * + * \param context Serialization context + * \param ray Output Ray + */ + template + bool Unserialize(SerializationContext& context, Ray* ray) + { + if (!Unserialize(context, &ray->origin)) + return false; + + if (!Unserialize(context, &ray->direction)) + return false; + + return true; } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param ray The ray to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::Ray& ray) { diff --git a/include/Nazara/Math/Rect.hpp b/include/Nazara/Math/Rect.hpp index ea468966c..393d8d116 100644 --- a/include/Nazara/Math/Rect.hpp +++ b/include/Nazara/Math/Rect.hpp @@ -13,6 +13,8 @@ namespace Nz { + struct SerializationContext; + template class Rect { @@ -28,12 +30,12 @@ namespace Nz ~Rect() = default; bool Contains(T X, T Y) const; - bool Contains(const Vector2& point) const; bool Contains(const Rect& rect) const; + bool Contains(const Vector2& point) const; Rect& ExtendTo(T X, T Y); - Rect& ExtendTo(const Vector2& point); Rect& ExtendTo(const Rect& rect); + Rect& ExtendTo(const Vector2& point); Vector2 GetCenter() const; Vector2 GetCorner(RectCorner corner) const; @@ -90,6 +92,9 @@ namespace Nz typedef Rect Rectui; typedef Rect Recti32; typedef Rect Rectui32; + + template bool Serialize(SerializationContext& context, const Rect& rect); + template bool Unserialize(SerializationContext& context, Rect* rect); } template diff --git a/include/Nazara/Math/Rect.inl b/include/Nazara/Math/Rect.inl index 80c47ae29..f53dd0c0f 100644 --- a/include/Nazara/Math/Rect.inl +++ b/include/Nazara/Math/Rect.inl @@ -1,7 +1,8 @@ -// Copyright (C) 2015 Jérôme Leclercq +// 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 +#include #include #include #include @@ -12,36 +13,90 @@ namespace Nz { + /*! + * \ingroup math + * \class Nz::Rect + * \brief Math class that represents an axis-aligned rectangle in two dimensions + * + * \remark The basis is said to be "left-hand". It means that with your left hand, the thumb is X positive, the index finger Y positive pointing to the bottom + */ + + /*! + * \brief Constructs a Rect object from its width and height + * + * \param Width Width of the rectangle (following X) + * \param Height Height of the rectangle (following Y) + * + * \remark Position will be (0, 0) + */ + template Rect::Rect(T Width, T Height) { Set(Width, Height); } + /*! + * \brief Constructs a Rect object from its position, width and height + * + * \param X X position + * \param Y Y position + * \param Width Width of the rectangle (following X) + * \param Height Height of the rectangle (following Y) + */ + template Rect::Rect(T X, T Y, T Width, T Height) { Set(X, Y, Width, Height); } + /*! + * \brief Constructs a Rect object from an array of four elements + * + * \param vec[4] vec[0] is X position, vec[1] is Y position, vec[2] is width and vec[3] is height + */ + template Rect::Rect(const T vec[4]) { Set(vec); } + /*! + * \brief Constructs a Rect object from a vector representing width and height + * + * \param lengths (Width, Height) of the rectangle + * + * \remark X and Y will be (0, 0) + */ + template Rect::Rect(const Vector2& lengths) { Set(lengths); } + /*! + * \brief Constructs a Rect object from two vectors representing point of the space + * (X, Y) will be the components minimum of the two vectors and the width and height will be the components maximum - minimum + * + * \param vec1 First point + * \param vec2 Second point + */ + template Rect::Rect(const Vector2& vec1, const Vector2& vec2) { Set(vec1, vec2); } + /*! + * \brief Constructs a Rect object from another type of Rect + * + * \param rect Rect of type U to convert to type T + */ + template template Rect::Rect(const Rect& rect) @@ -49,25 +104,63 @@ namespace Nz Set(rect); } + /*! + * \brief Tests whether the rectangle contains the provided point inclusive of the edge of the rectangle + * \return true if inclusive + * + * \param X X position of the point + * \param Y Y position of the point + * + * \see Contains + */ + template bool Rect::Contains(T X, T Y) const { - return X >= x && X <= x+width && - Y >= y && Y <= y+height; + return X >= x && X <= (x + width) && + Y >= y && Y <= (y + height); } + /*! + * \brief Tests whether the rectangle contains the provided rectangle inclusive of the edge of the rectangle + * \return true if inclusive + * + * \param rect Other rectangle to test + * + * \see Contains + */ + + template + bool Rect::Contains(const Rect& rect) const + { + return Contains(rect.x, rect.y) && + Contains(rect.x + rect.width, rect.y + rect.height); + } + + /*! + * \brief Tests whether the rectangle contains the provided point inclusive of the edge of the rectangle + * \return true if inclusive + * + * \param point Position of the point + * + * \see Contains + */ + template bool Rect::Contains(const Vector2& point) const { return Contains(point.x, point.y); } - template - bool Rect::Contains(const Rect& rect) const - { - return Contains(rect.x, rect.y) && - Contains(rect.x + rect.width, rect.y + rect.height); - } + /*! + * \brief Extends the rectangle to contain the point in the boundary + * \return A reference to this rectangle extended + * + * \param X X position of the point + * \param Y Y position of the point + * + * \see ExtendTo + */ template Rect& Rect::ExtendTo(T X, T Y) @@ -84,11 +177,14 @@ namespace Nz return *this; } - template - Rect& Rect::ExtendTo(const Vector2& point) - { - return ExtendTo(point.x, point.y); - } + /*! + * \brief Extends the rectangle to contain the rectangle + * \return A reference to this rectangle extended + * + * \param rect Other rectangle to contain + * + * \see ExtendTo + */ template Rect& Rect::ExtendTo(const Rect& rect) @@ -105,12 +201,41 @@ namespace Nz return *this; } + /*! + * \brief Extends the rectangle to contain the point in the boundary + * \return A reference to this rectangle extended + * + * \param point Position of the point + * + * \see ExtendTo + */ + + template + Rect& Rect::ExtendTo(const Vector2& point) + { + return ExtendTo(point.x, point.y); + } + + /*! + * \brief Gets a Vector2 for the center + * \return The position of the center of the rectangle + */ + template Vector2 Rect::GetCenter() const { return GetPosition() + GetLengths() / F(2.0); } + /*! + * \brief Gets the Vector2 for the corner + * \return The position of the corner of the rectangle according to enum RectCorner + * + * \param corner Enumeration of type RectCorner + * + * \remark If enumeration is not defined in RectCorner, a NazaraError is thrown and a Vector2 uninitialised is returned + */ + template Vector2 Rect::GetCorner(RectCorner corner) const { @@ -133,25 +258,52 @@ namespace Nz return Vector2(); } + /*! + * \brief Gets a Vector2 for the lengths + * \return The lengths of the rectangle (width, height) + */ + template Vector2 Rect::GetLengths() const { return Vector2(width, height); } + /*! + * \brief Gets a Vector2 for the maximum point + * \return The RectCorner_RightBottom of the rectangle + * + * \see GetCorner + */ + template Vector2 Rect::GetMaximum() const { return GetPosition() + GetLengths(); } + /*! + * \brief Gets a Vector2 for the minimum point + * \return The RectCorner_LeftTop of the rectangle + * + * \see GetCorner, GetPosition + */ + template Vector2 Rect::GetMinimum() const { - ///DOC: Alias de GetPosition() return GetPosition(); } + /*! + * \brief Computes the negative vertex of one direction + * \return The position of the vertex on the rectangle in the opposite way of the normal while considering the center. It means that if the normal has one component negative, the component is set to width or height corresponding to the sign + * + * \param normal Vector indicating a direction + * + * \see GetPositiveVertex + */ + template Vector2 Rect::GetNegativeVertex(const Vector2& normal) const { @@ -166,12 +318,28 @@ namespace Nz return neg; } + /*! + * \brief Gets a Vector2 for the position + * \return The RectCorner_LeftTop of the rectangle + * + * \see GetCorner, GetMinimum + */ + template Vector2 Rect::GetPosition() const { return Vector2(x, y); } + /*! + * \brief Computes the positive vertex of one direction + * \return The position of the vertex on the rectangle in the same way of the normal while considering the center. It means that if the normal has one component positive, the component is set to width or height corresponding to the sign + * + * \param normal Vector indicating a direction + * + * \see GetNegativeVertex + */ + template Vector2 Rect::GetPositiveVertex(const Vector2& normal) const { @@ -186,6 +354,14 @@ namespace Nz return pos; } + /*! + * \brief Checks whether or not this rectangle intersects another one + * \return true if the rectangle intersects + * + * \param rect Rectangle to check + * \param intersection Optional argument for the rectangle which represent the intersection + */ + template bool Rect::Intersect(const Rect& rect, Rect* intersection) const { @@ -210,12 +386,24 @@ namespace Nz return true; } + /*! + * \brief Checks whether this rectangle is valid + * \return true if the rectangle has a strictly positive width and height + */ + template bool Rect::IsValid() const { return width > F(0.0) && height > F(0.0); } + /*! + * \brief Makes the rectangle position (0, 0) and lengths (0, 0) + * \return A reference to this box with position (0, 0) and lengths (0, 0) + * + * \see Zero + */ + template Rect& Rect::MakeZero() { @@ -227,6 +415,16 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the rectangle with width and height + * \return A reference to this rectangle + * + * \param Width Width of the rectangle (following X) + * \param Height Height of the rectangle (following Y) + * + * \remark Position will be (0, 0) + */ + template Rect& Rect::Set(T Width, T Height) { @@ -238,6 +436,16 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the rectangle + * \return A reference to this rectangle + * + * \param X X position + * \param Y Y position + * \param Width Width of the rectangle (following X) + * \param Height Height of the rectangle (following Y) + */ + template Rect& Rect::Set(T X, T Y, T Width, T Height) { @@ -249,6 +457,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the rectangle from an array of four elements + * \return A reference to this rectangle + * + * \param rect[4] rect[0] is X position, rect[1] is Y position, rect[2] is width and rect[3] is height + */ + template Rect& Rect::Set(const T rect[4]) { @@ -260,6 +475,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the rectangle with components from another + * \return A reference to this rectangle + * + * \param rect The other Rect + */ + template Rect& Rect::Set(const Rect& rect) { @@ -268,23 +490,48 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the rectange from a vector representing width and height + * \return A reference to this rectangle + * + * \param lengths (Width, Height) of the rectangle + * + * \remark Position will be (0, 0) + */ + template Rect& Rect::Set(const Vector2& lengths) { return Set(lengths.x, lengths.y); } + /*! + * \brief Sets a Rect object from two vectors representing point of the space + * (X, Y) will be the components minimum of the two vectors and the width and height will be the components maximum - minimum + * \return A reference to this rectangle + * + * \param vec1 First point + * \param vec2 Second point + */ + template Rect& Rect::Set(const Vector2& vec1, const Vector2& vec2) { x = std::min(vec1.x, vec2.x); y = std::min(vec1.y, vec2.y); - width = (vec2.x > vec1.x) ? vec2.x-vec1.x : vec1.x-vec2.x; - height = (vec2.y > vec1.y) ? vec2.y-vec1.y : vec1.y-vec2.y; + width = (vec2.x > vec1.x) ? vec2.x - vec1.x : vec1.x - vec2.x; + height = (vec2.y > vec1.y) ? vec2.y - vec1.y : vec1.y - vec2.y; return *this; } + /*! + * \brief Sets the components of the rectangle from another type of Rect + * \return A reference to this rectangle + * + * \param rect Rectangle of type U to convert its components + */ + template template Rect& Rect::Set(const Rect& rect) @@ -297,6 +544,11 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Rect(x, y, width, height)" + */ + template String Rect::ToString() const { @@ -305,6 +557,13 @@ namespace Nz return ss << "Rect(" << x << ", " << y << ", " << width << ", " << height << ')'; } + /*! + * \brief Translates the rectangle + * \return A reference to this rectangle translated + * + * \param translation Vector2 which is the translation for the position + */ + template Rect& Rect::Translate(const Vector2& translation) { @@ -314,6 +573,15 @@ namespace Nz return *this; } + /*! + * \brief Returns the ith element of the rectangle + * \return A reference to the ith element of the rectangle + * + * \remark Access to index greather than 4 is undefined behavior + * \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4 + */ + template T& Rect::operator[](unsigned int i) { @@ -331,6 +599,15 @@ namespace Nz return *(&x+i); } + /*! + * \brief Returns the ith element of the rectangle + * \return A value to the ith element of the rectangle + * + * \remark Access to index greather than 4 is undefined behavior + * \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4 + */ + template T Rect::operator[](unsigned int i) const { @@ -348,30 +625,65 @@ namespace Nz return *(&x+i); } + /*! + * \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 + */ + template Rect Rect::operator*(T scalar) const { - return Rect(x, y, width*scalar, height*scalar); + return Rect(x, y, width * scalar, height * scalar); } + /*! + * \brief Multiplies the lengths with the vector + * \return A rectangle where the position is the same and width and height are the product of the old width and height with the vec + * + * \param vec The vector where component one multiply width and two height + */ + template Rect Rect::operator*(const Vector2& vec) const { return Rect(x, y, width*vec.x, height*vec.y); } + /*! + * \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 + */ + template Rect Rect::operator/(T scalar) const { return Rect(x, y, width/scalar, height/scalar); } + /*! + * \brief Divides the lengths with the vector + * \return A rectangle where the position is the same and width and height are the quotient of the old width and height with the vec + * + * \param vec The vector where component one divide width and two height + */ + template Rect Rect::operator/(const Vector2& vec) const { return Rect(x, y, width/vec.x, height/vec.y); } + /*! + * \brief Multiplies the lengths of this rectangle with the scalar + * \return A reference to this rectangle where lengths are the product of these lengths and the scalar + * + * \param scalar The scalar to multiply width and height with + */ + template Rect& Rect::operator*=(T scalar) { @@ -381,6 +693,13 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the lengths of this rectangle with the vector + * \return A reference to this rectangle where width and height are the product of the old width and height with the vec + * + * \param vec The vector where component one multiply width and two height + */ + template Rect& Rect::operator*=(const Vector2& vec) { @@ -390,6 +709,13 @@ namespace Nz return *this; } + /*! + * \brief Divides the lengths of this rectangle with the scalar + * \return A reference to this rectangle where lengths are the quotient of these lengths and the scalar + * + * \param scalar The scalar to divide width and height with + */ + template Rect& Rect::operator/=(T scalar) { @@ -399,6 +725,13 @@ namespace Nz return *this; } + /*! + * \brief Divives the lengths of this rectangle with the vector + * \return A reference to this rectangle where width and height are the quotient of the old width and height with the vec + * + * \param vec The vector where component one divide width and two height + */ + template Rect& Rect::operator/=(const Vector2& vec) { @@ -408,19 +741,47 @@ namespace Nz return *this; } + /*! + * \brief Compares the rectangle to other one + * \return true if the rectangles are the same + * + * \param rec Other rectangle to compare with + */ + template bool Rect::operator==(const Rect& rect) const { return NumberEquals(x, rect.x) && NumberEquals(y, rect.y) && - NumberEquals(width, rect.width) && NumberEquals(height, rect.height); + NumberEquals(width, rect.width) && NumberEquals(height, rect.height); } + /*! + * \brief Compares the rectangle to other one + * \return false if the rectangles are the same + * + * \param rec Other rectangle to compare with + */ + template bool Rect::operator!=(const Rect& rect) const { return !operator==(rect); } + /*! + * \brief Interpolates the rectangle to other one with a factor of interpolation + * \return A new rectangle which is the interpolation of two rectangles + * + * \param from Initial rectangle + * \param to Target rectangle + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * \remark With NAZARA_DEBUG, a NazaraError is thrown and Zero() is returned + * + * \see Lerp + */ + template Rect Rect::Lerp(const Rect& from, const Rect& to, T interpolation) { @@ -433,14 +794,21 @@ namespace Nz #endif Rect rect; - rect.x = Lerp(from.x, to.x, interpolation); - rect.y = Lerp(from.y, to.y, interpolation); - rect.width = Lerp(from.width, to.width, interpolation); - rect.height = Lerp(from.height, to.height, interpolation); + rect.x = Nz::Lerp(from.x, to.x, interpolation); + rect.y = Nz::Lerp(from.y, to.y, interpolation); + rect.width = Nz::Lerp(from.width, to.width, interpolation); + rect.height = Nz::Lerp(from.height, to.height, interpolation); return rect; } + /*! + * \brief Shorthand for the rectangle (0, 0, 0, 0) + * \return A rectangle with position (0, 0) and lengths (0, 0) + * + * \see MakeZero + */ + template Rect Rect::Zero() { @@ -449,8 +817,66 @@ namespace Nz return rect; } + + /*! + * \brief Serializes a Rect + * \return true if successfully serialized + * + * \param context Serialization context + * \param rect Input Rect + */ + template + bool Serialize(SerializationContext& context, const Rect& rect) + { + if (!Serialize(context, rect.x)) + return false; + + if (!Serialize(context, rect.y)) + return false; + + if (!Serialize(context, rect.width)) + return false; + + if (!Serialize(context, rect.height)) + return false; + + return true; + } + + /*! + * \brief Unserializes a Rect + * \return true if successfully unserialized + * + * \param context Serialization context + * \param rect Output Rect + */ + template + bool Unserialize(SerializationContext& context, Rect* rect) + { + if (!Unserialize(context, &rect->x)) + return false; + + if (!Unserialize(context, &rect->y)) + return false; + + if (!Unserialize(context, &rect->width)) + return false; + + if (!Unserialize(context, &rect->height)) + return false; + + return true; + } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param rect The rectangle to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::Rect& rect) { diff --git a/include/Nazara/Math/Sphere.hpp b/include/Nazara/Math/Sphere.hpp index d3d6daf81..929c1ec71 100644 --- a/include/Nazara/Math/Sphere.hpp +++ b/include/Nazara/Math/Sphere.hpp @@ -12,6 +12,8 @@ namespace Nz { + struct SerializationContext; + template class Box; template @@ -46,6 +48,7 @@ namespace Nz bool IsValid() const; + Sphere& MakeUnit(); Sphere& MakeZero(); Sphere& Set(T X, T Y, T Z, T Radius); @@ -71,6 +74,7 @@ namespace Nz bool operator!=(const Sphere& sphere) const; static Sphere Lerp(const Sphere& from, const Sphere& to, T interpolation); + static Sphere Unit(); static Sphere Zero(); T x, y, z, radius; @@ -78,6 +82,9 @@ namespace Nz typedef Sphere Sphered; typedef Sphere Spheref; + + template bool Serialize(SerializationContext& context, const Sphere& sphere); + template bool Unserialize(SerializationContext& context, Sphere* sphere); } template diff --git a/include/Nazara/Math/Sphere.inl b/include/Nazara/Math/Sphere.inl index 0af536b93..3438b5080 100644 --- a/include/Nazara/Math/Sphere.inl +++ b/include/Nazara/Math/Sphere.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -13,6 +14,21 @@ namespace Nz { + /*! + * \ingroup math + * \class Nz::Sphere + * \brief Math class that represents a sphere "S2" in a three dimensional euclidean space + */ + + /*! + * \brief Constructs a Sphere object from its center position and radius + * + * \param X X position + * \param Y Y position + * \param Z Z position + * \param Radius half of the diameter + */ + template Sphere::Sphere(T X, T Y, T Z, T Radius) { @@ -25,18 +41,38 @@ namespace Nz Set(rect); } */ + + /*! + * \brief Constructs a Sphere object from its position and radius + * + * \param center Center of the sphere + * \param Radius Half of the diameter + */ + template Sphere::Sphere(const Vector3& center, T Radius) { Set(center, Radius); } + /*! + * \brief Constructs a Sphere object from an array of four elements + * + * \param sphere[4] sphere[0] is X component, sphere[1] is Y component, sphere[2] is Z component and sphere[3] is radius + */ + template Sphere::Sphere(const T sphere[4]) { Set(sphere); } + /*! + * \brief Constructs a Sphere object from another type of Sphere + * + * \param sphere Sphere of type U to convert to type T + */ + template template Sphere::Sphere(const Sphere& sphere) @@ -44,12 +80,32 @@ namespace Nz Set(sphere); } + /*! + * \brief Tests whether the sphere contains the provided point inclusive of the edge of the sphere + * \return true if inclusive + * + * \param X X position of the point + * \param Y Y position of the point + * \param Z Z position of the point + * + * \see Contains + */ + template bool Sphere::Contains(T X, T Y, T Z) const { - return SquaredDistance(X, Y, Z) <= radius*radius; + return SquaredDistance(X, Y, Z) <= radius * radius; } + /*! + * \brief Tests whether the sphere contains the provided box inclusive of the edge of the sphere + * \return true if all inclusive + * + * \param box Three dimensional box + * + * \see Contains + */ + template bool Sphere::Contains(const Box& box) const { @@ -62,24 +118,61 @@ namespace Nz return false; } + /*! + * \brief Tests whether the sphere contains the provided point inclusive of the edge of the sphere + * \return true if inclusive + * + * \param point Position of the point + */ template bool Sphere::Contains(const Vector3& point) const { return Contains(point.x, point.y, point.z); } + /*! + * \brief Returns the distance from the center of the sphere to the point + * \return Distance to the point + * + * \param X X position of the point + * \param Y Y position of the point + * \param Z Z position of the point + * + * \see SquaredDistance + */ + template T Sphere::Distance(T X, T Y, T Z) const { return Distance({X, Y, Z}); } + /*! + * \brief Returns the distance from the center of the sphere to the point + * \return Distance to the point + * + * \param point Position of the point + * + * \see SquaredDistance + */ + template T Sphere::Distance(const Vector3& point) const { return Vector3f::Distance(point, GetPosition()) - radius; } + /*! + * \brief Extends the sphere to contain the point in the boundary + * \return A reference to this sphere extended + * + * \param X X position of the point + * \param Y Y position of the point + * \param Z Z position of the point + * + * \see ExtendTo + */ + template Sphere& Sphere::ExtendTo(T X, T Y, T Z) { @@ -90,12 +183,30 @@ namespace Nz return *this; } + /*! + * \brief Extends the sphere to contain the point in the boundary + * \return A reference to this sphere extended + * + * \param point Position of the point + * + * \see ExtendTo + */ + template Sphere& Sphere::ExtendTo(const Vector3& point) { return ExtendTo(point.x, point.y, point.z); } + /*! + * \brief Computes the negative vertex of one direction + * \return The position of the vertex on the sphere in the opposite way of the normal while considering the center + * + * \param normal Vector normalized indicating a direction + * + * \see GetPositiveVertex + */ + template Vector3 Sphere::GetNegativeVertex(const Vector3& normal) const { @@ -105,12 +216,26 @@ namespace Nz return neg; } + /*! + * \brief Gets a Vector3 of the position + * \return The position of the center of the sphere + */ + template Vector3 Sphere::GetPosition() const { return Vector3(x, y, z); } + /*! + * \brief Computes the positive vertex of one direction + * \return The position of the vertex on the sphere in the same way of the normal while considering the center + * + * \param normal Vector normalized indicating a direction + * + * \see GetNegativeVertex + */ + template Vector3 Sphere::GetPositiveVertex(const Vector3& normal) const { @@ -120,6 +245,13 @@ namespace Nz return pos; } + /*! + * \brief Checks whether or not this sphere intersects a box + * \return true if the box intersects + * + * \param box Box to check + */ + template bool Sphere::Intersect(const Box& box) const { @@ -128,51 +260,88 @@ namespace Nz if (x < box.x) { T diff = x - box.x; - squaredDistance += diff*diff; + squaredDistance += diff * diff; } else if (x > box.x + box.width) { T diff = x - (box.x + box.width); - squaredDistance += diff*diff; + squaredDistance += diff * diff; } if (y < box.y) { T diff = y - box.y; - squaredDistance += diff*diff; + squaredDistance += diff * diff; } else if (y > box.y + box.height) { T diff = y - (box.y + box.height); - squaredDistance += diff*diff; + squaredDistance += diff * diff; } if (z < box.z) { T diff = z - box.z; - squaredDistance += diff*diff; + squaredDistance += diff * diff; } else if (z > box.z + box.depth) { T diff = z - (box.z + box.depth); - squaredDistance += diff*diff; + squaredDistance += diff * diff; } return squaredDistance <= radius * radius; } + /*! + * \brief Checks whether or not this sphere intersects another sphere + * \return true if the spheres intersect or if one is in the other + * + * \param sphere Sphere to check + */ + 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) - radius * radius <= sphere.radius * sphere.radius; } + /*! + * \brief Checks whether this sphere is valid + * \return true if the sphere has a strictly positive radius + */ + template bool Sphere::IsValid() const { return radius > F(0.0); } + /*! + * \brief Makes the sphere position (0, 0, 0) and radius 1 + * \return A reference to this vector with position (0, 0, 0) and radius 1 + * + * \see Unit + */ + + template + Sphere& Sphere::MakeUnit() + { + x = F(0.0); + y = F(0.0); + z = F(0.0); + radius = F(1.0); + + return *this; + } + + /*! + * \brief Makes the sphere position (0, 0, 0) and radius 0 + * \return A reference to this vector with position (0, 0, 0) and radius 0 + * + * \see Zero + */ + template Sphere& Sphere::MakeZero() { @@ -184,6 +353,16 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the sphere with center and radius + * \return A reference to this sphere + * + * \param X X position + * \param Y Y position + * \param Z Z position + * \param Radius half of the diameter + */ + template Sphere& Sphere::Set(T X, T Y, T Z, T Radius) { @@ -195,6 +374,14 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the sphere with center and radius + * \return A reference to this sphere + * + * \param center Center of the sphere + * \param Radius Half of the diameter + */ + template Sphere& Sphere::Set(const Vector3& center, T Radius) { @@ -219,6 +406,12 @@ namespace Nz } */ + /*! + * \brief Sets the components of the sphere with center and radius from another sphere + * \return A reference to this sphere + * + * \param sphere The other sphere + */ template Sphere& Sphere::Set(const Sphere& sphere) { @@ -227,6 +420,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the sphere from an array of four elements + * \return A reference to this sphere + * + * \param sphere[4] sphere[0] is X position, sphere[1] is Y position, sphere[2] is Z position and sphere[3] is radius + */ + template Sphere& Sphere::Set(const T sphere[4]) { @@ -238,6 +438,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the sphere from another type of Sphere + * \return A reference to this sphere + * + * \param sphere Sphere of type U to convert its components + */ + template template Sphere& Sphere::Set(const Sphere& sphere) @@ -250,18 +457,43 @@ namespace Nz return *this; } + /*! + * \brief Returns the squared distance from the center of the sphere to the point + * \return Squared distance to the point + * + * \param X X position of the point + * \param Y Y position of the point + * \param Z Z position of the point + * + * \see Distance + */ + template T Sphere::SquaredDistance(T X, T Y, T Z) const { return SquaredDistance({X, Y, Z}); } + /*! + * \brief Returns the squared distance from the center of the sphere to the point + * \return Squared distance to the point + * + * \param point Position of the point + * + * \see Distance + */ + template T Sphere::SquaredDistance(const Vector3& point) const { return Vector3f::Distance(point, GetPosition()) - radius * radius; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Sphere(x, y, z; radius)" + */ + template String Sphere::ToString() const { @@ -270,6 +502,15 @@ namespace Nz return ss << "Sphere(" << x << ", " << y << ", " << z << "; " << radius << ')'; } + /*! + * \brief Returns the ith element of the sphere + * \return A reference to the ith element of the sphere + * + * \remark Access to index greather than 4 is undefined behavior + * \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4 + */ + template T& Sphere::operator[](unsigned int i) { @@ -287,6 +528,15 @@ namespace Nz return *(&x+i); } + /*! + * \brief Returns the ith element of the sphere + * \return A value to the ith element of the sphere + * + * \remark Access to index greather than 4 is undefined behavior + * \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4 + */ + template T Sphere::operator[](unsigned int i) const { @@ -304,31 +554,89 @@ namespace Nz return *(&x+i); } + /*! + * \brief Multiplies the radius of the sphere with a scalar + * \return A sphere where the center is the same and radius is the product of this radius and the scalar + * + * \param scale The scalar to multiply radius with + */ + template Sphere Sphere::operator*(T scalar) const { - return Sphere(x, y, z, radius*scalar); + return Sphere(x, y, z, radius * scalar); } + /*! + * \brief Multiplies the radius of other sphere with a scalar + * \return A reference to this sphere where the center is the same and radius is the product of this radius and the scalar + * + * \param scale The scalar to multiply radius with + */ + template Sphere& Sphere::operator*=(T scalar) { radius *= scalar; } + /*! + * \brief Compares the sphere to other one + * \return true if the spheres are the same + * + * \param sphere Other sphere to compare with + */ + template bool Sphere::operator==(const Sphere& sphere) const { return NumberEquals(x, sphere.x) && NumberEquals(y, sphere.y) && NumberEquals(z, sphere.z) && - NumberEquals(radius, sphere.radius); + NumberEquals(radius, sphere.radius); } + /*! + * \brief Compares the sphere to other one + * \return false if the spheres are the same + * + * \param sphere Other sphere to compare with + */ + template bool Sphere::operator!=(const Sphere& sphere) const { return !operator==(sphere); } + /*! + * \brief Shorthand for the sphere (0, 0, 0, 1) + * \return A sphere with center (0, 0, 0) and radius 1 + * + * \see MakeUnit + */ + + template + Sphere Sphere::Unit() + { + Sphere sphere; + sphere.MakeUnit(); + + return sphere; + } + + /*! + * \brief Interpolates the sphere to other one with a factor of interpolation + * \return A new sphere which is the interpolation of two spheres + * + * \param from Initial sphere + * \param to Target sphere + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * \remark With NAZARA_DEBUG, a NazaraError is thrown and Zero() is returned + * + * \see Lerp + */ + template Sphere Sphere::Lerp(const Sphere& from, const Sphere& to, T interpolation) { @@ -341,14 +649,21 @@ namespace Nz #endif Sphere sphere; - sphere.x = Lerp(from.x, to.x, interpolation); - sphere.y = Lerp(from.y, to.y, interpolation); - sphere.z = Lerp(from.z, to.z, interpolation); - sphere.radius = Lerp(from.radius, to.radius, interpolation); + sphere.x = Nz::Lerp(from.x, to.x, interpolation); + sphere.y = Nz::Lerp(from.y, to.y, interpolation); + sphere.z = Nz::Lerp(from.z, to.z, interpolation); + sphere.radius = Nz::Lerp(from.radius, to.radius, interpolation); return sphere; } + /*! + * \brief Shorthand for the sphere (0, 0, 0, 0) + * \return A sphere with center (0, 0, 0) and radius 0 + * + * \see MakeZero + */ + template Sphere Sphere::Zero() { @@ -357,8 +672,66 @@ namespace Nz return sphere; } + + /*! + * \brief Serializes a Sphere + * \return true if successfully serialized + * + * \param context Serialization context + * \param sphere Input Sphere + */ + template + bool Serialize(SerializationContext& context, const Sphere& sphere) + { + if (!Serialize(context, sphere.x)) + return false; + + if (!Serialize(context, sphere.y)) + return false; + + if (!Serialize(context, sphere.z)) + return false; + + if (!Serialize(context, sphere.radius)) + return false; + + return true; + } + + /*! + * \brief Unserializes a Sphere + * \return true if successfully unserialized + * + * \param context Serialization context + * \param sphere Output Sphere + */ + template + bool Unserialize(SerializationContext& context, Sphere* sphere) + { + if (!Unserialize(context, &sphere->x)) + return false; + + if (!Unserialize(context, &sphere->y)) + return false; + + if (!Unserialize(context, &sphere->z)) + return false; + + if (!Unserialize(context, &sphere->radius)) + return false; + + return true; + } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param sphere The sphere to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::Sphere& sphere) { diff --git a/include/Nazara/Math/Vector2.hpp b/include/Nazara/Math/Vector2.hpp index f10d8e1bd..4a6a83c16 100644 --- a/include/Nazara/Math/Vector2.hpp +++ b/include/Nazara/Math/Vector2.hpp @@ -11,6 +11,8 @@ namespace Nz { + struct SerializationContext; + template class Vector3; template class Vector4; @@ -62,8 +64,8 @@ namespace Nz String ToString() const; - operator T*(); - operator const T*() const; + operator T* (); + operator const T* () const; const Vector2& operator+() const; Vector2 operator-() const; @@ -89,7 +91,9 @@ namespace Nz bool operator>(const Vector2& vec) const; bool operator>=(const Vector2& vec) const; + static T DotProduct(const Vector2& vec1, const Vector2& vec2); static Vector2 Lerp(const Vector2& from, const Vector2& to, T interpolation); + static Vector2 Normalize(const Vector2& vec); static Vector2 Unit(); static Vector2 UnitX(); static Vector2 UnitY(); @@ -109,6 +113,9 @@ namespace Nz typedef Vector2 Vector2ui; typedef Vector2 Vector2i32; typedef Vector2 Vector2ui32; + + template bool Serialize(SerializationContext& context, const Vector2& vector); + template bool Unserialize(SerializationContext& context, Vector2* vector); } #include diff --git a/include/Nazara/Math/Vector2.inl b/include/Nazara/Math/Vector2.inl index e3561b84c..80e588ba4 100644 --- a/include/Nazara/Math/Vector2.inl +++ b/include/Nazara/Math/Vector2.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -13,24 +14,55 @@ namespace Nz { + /*! + * \ingroup math + * \class Nz::Vector2 + * \brief Math class that represents an element of the two dimensional vector space + */ + + /*! + * \brief Constructs a Vector2 object from its coordinates + * + * \param X X component + * \param Y Y component + */ + template Vector2::Vector2(T X, T Y) { Set(X, Y); } + /*! + * \brief Constructs explicitely a Vector2 object from its "scale" + * + * \param scale X component = Y component + */ + template Vector2::Vector2(T scale) { Set(scale); } + /*! + * \brief Constructs a Vector2 object from an array of two elements + * + * \param vec[2] vec[0] is X component and vec[1] is Y component + */ + template Vector2::Vector2(const T vec[2]) { Set(vec); } + /*! + * \brief Constructs a Vector2 object from another type of Vector2 + * + * \param vec Vector of type U to convert to type T + */ + template template Vector2::Vector2(const Vector2& vec) @@ -38,60 +70,140 @@ namespace Nz Set(vec); } + /*! + * \brief Constructs a Vector2 object from a Vector3 + * + * \param vec Vector3 where only the first two components are taken + */ + template Vector2::Vector2(const Vector3& vec) { Set(vec); } + /*! + * \brief Constructs a Vector2 object from a Vector4 + * + * \param vec Vector4 where only the first two components are taken + */ + template Vector2::Vector2(const Vector4& vec) { Set(vec); } + /*! + * \brief Calculates the absolute dot (scalar) product with two vectors + * \return The dot product with absolutes values on each component + * + * \param vec The other vector to calculate the absolute dot product with + * + * \see DotProduct + */ + template T Vector2::AbsDotProduct(const Vector2& vec) const { return std::abs(x * vec.x) + std::abs(y * vec.y); } + /*! + * \brief Calculates the angle between two vectors in orthonormal basis + * \return The angle unit depends of NAZARA_MATH_ANGLE_RADIAN, you may want to normalize it to the range 0..2*pi with NormalizeAngle + * + * \param vec The other vector to measure the angle with + * + * \remark The vectors do not need to be normalised and if the angle is normalised, it represents the rotation from *this to vec in anti-clockwise direction + * + * \see NormalizeAngle + */ + template T Vector2::AngleBetween(const Vector2& vec) const { return FromRadians(std::atan2(vec.y, vec.x) - std::atan2(y, x)); } + /*! + * \brief Calculates the distance between two vectors + * \return The metric distance between two vectors with euclidean norm + * + * \param vec The other vector to measure the distance with + * + * \see SquaredDistance + */ + template T Vector2::Distance(const Vector2& vec) const { return std::sqrt(SquaredDistance(vec)); } + /*! + * \brief Calculates the distance between two vectors + * \return The metric distance in float between two vectors with euclidean norm + * + * \param vec The other vector to measure the distance with + */ + template float Vector2::Distancef(const Vector2& vec) const { return std::sqrt(static_cast(SquaredDistance(vec))); } + /*! + * \brief Calculates the dot (scalar) product with two vectors + * \return The value of the dot product + * + * \param vec The other vector to calculate the dot product with + * + * \see AbsDotProduct, DotProduct + */ + template T Vector2::DotProduct(const Vector2& vec) const { return x*vec.x + y*vec.y; } + /*! + * \brief Calculates the length (magnitude) of the vector + * \return The length of the vector + * + * \see GetSquaredLength + */ + template T Vector2::GetLength() const { - return std::sqrt(GetSquaredLength()); + return static_cast(std::sqrt(GetSquaredLength())); } + /*! + * \brief Calculates the length (magnitude) of the vector + * \return The length in float of the vector + */ + template float Vector2::GetLengthf() const { return std::sqrt(static_cast(GetSquaredLength())); } + /*! + * \brief Gets a copy normalized of the vector + * \return A new vector which is the vector normalized + * + * \param length Optional argument to obtain the length's ratio of the vector and the unit-length + * + * \remark If this vector is (0, 0), then it returns (0, 0) and length is 0 + * + * \see Normalize + */ + template Vector2 Vector2::GetNormal(T* length) const { @@ -101,36 +213,80 @@ namespace Nz return vec; } + /*! + * \brief Calculates the squared length (magnitude) of the vector + * \return The squared length of the vector + * + * \see GetLength + */ + template T Vector2::GetSquaredLength() const { return x*x + y*y; } + /*! + * \brief Makes the vector (1, 1) + * \return A reference to this vector with components (1, 1) + * + * \see Unit + */ + template Vector2& Vector2::MakeUnit() { return Set(F(1.0), F(1.0)); } + /*! + * \brief Makes the vector (1, 0) + * \return A reference to this vector with components (1, 0) + * + * \see UnitX + */ + template Vector2& Vector2::MakeUnitX() { return Set(F(1.0), F(0.0)); } + /*! + * \brief Makes the vector (0, 1) + * \return A reference to this vector with components (0, 1) + * + * \see UnitY + */ + template Vector2& Vector2::MakeUnitY() { return Set(F(0.0), F(1.0)); } + /*! + * \brief Makes the vector (0, 0) + * \return A reference to this vector with components (0, 0) + * + * \see Zero + */ + template Vector2& Vector2::MakeZero() { return Set(F(0.0), F(0.0)); } + /*! + * \brief Sets this vector's components to the maximum of its own and other components + * \return A reference to this vector with replaced values with the corresponding max value + * + * \param vec Other vector to compare the components with + * + * \see Minimize + */ + template Vector2& Vector2::Maximize(const Vector2& vec) { @@ -143,6 +299,15 @@ namespace Nz return *this; } + /*! + * \brief Sets this vector's components to the minimum of its own and other components + * \return A reference to this vector with replaced values with the corresponding min value + * + * \param vec Other vector to compare the components with + * + * \see Maximize + */ + template Vector2& Vector2::Minimize(const Vector2& vec) { @@ -155,6 +320,17 @@ namespace Nz return *this; } + /*! + * \brief Normalizes the current vector + * \return A reference to this vector + * + * \param length Optional argument to obtain the length's ratio of the vector and the unit-length + * + * \remark If the vector is (0, 0), then it returns (0, 0) and length is 0 + * + * \see GetNormal + */ + template Vector2& Vector2::Normalize(T* length) { @@ -172,6 +348,14 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector + * \return A reference to this vector + * + * \param X X component + * \param Y Y component + */ + template Vector2& Vector2::Set(T X, T Y) { @@ -181,6 +365,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a "scale" + * \return A reference to this vector + * + * \param scale X component = Y component + */ + template Vector2& Vector2::Set(T scale) { @@ -190,6 +381,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from an array of two elements + * \return A reference to this vector + * + * \param vec[2] vec[0] is X component and vec[1] is Y component + */ + template Vector2& Vector2::Set(const T vec[2]) { @@ -198,6 +396,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from another vector + * \return A reference to this vector + * + * \param vec The other vector + */ + template Vector2& Vector2::Set(const Vector2& vec) { @@ -206,6 +411,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from another type of Vector2 + * \return A reference to this vector + * + * \param vec Vector of type U to convert its components + */ + template template Vector2& Vector2::Set(const Vector2& vec) @@ -216,6 +428,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a Vector3 + * \return A reference to this vector + * + * \param vec Vector3 where only the first two components are taken + */ + template Vector2& Vector2::Set(const Vector3& vec) { @@ -225,6 +444,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a Vector4 + * \return A reference to this vector + * + * \param vec Vector4 where only the first two components are taken + */ + template Vector2& Vector2::Set(const Vector4& vec) { @@ -234,12 +460,26 @@ namespace Nz return *this; } + /*! + * \brief Calculates the squared distance between two vectors + * \return The metric distance between two vectors with the squared euclidean norm + * + * \param vec The other vector to measure the distance with + * + * \see Distance + */ + template T Vector2::SquaredDistance(const Vector2& vec) const { return (*this - vec).GetSquaredLength(); } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Vector2(x, y)" + */ + template String Vector2::ToString() const { @@ -248,54 +488,116 @@ namespace Nz return ss << "Vector2(" << x << ", " << y << ')'; } + /*! + * \brief Converts vector to pointer to its own data + * \return A pointer to the own data + * + * \remark Access to index greather than 1 is undefined behavior + */ + template - Vector2::operator T*() + Vector2::operator T* () { return &x; } + /*! + * \brief Converts vector to const pointer to its own data + * \return A constant pointer to the own data + * + * \remark Access to index greather than 1 is undefined behavior + */ + template - Vector2::operator const T*() const + Vector2::operator const T* () const { return &x; } + /*! + * \brief Helps to represent the sign of the vector + * \return A constant reference to this vector + */ + template const Vector2& Vector2::operator+() const { return *this; } + /*! + * \brief Negates the components of the vector + * \return A constant reference to this vector with negate components + */ + template Vector2 Vector2::operator-() const { return Vector2(-x, -y); } + /*! + * \brief Adds the components of the vector with other vector + * \return A vector where components are the sum of this vector and the other one + * + * \param vec The other vector to add components with + */ + template Vector2 Vector2::operator+(const Vector2& vec) const { return Vector2(x + vec.x, y + vec.y); } + /*! + * \brief Substracts the components of the vector with other vector + * \return A vector where components are the difference of this vector and the other one + * + * \param vec The other vector to substract components with + */ + template Vector2 Vector2::operator-(const Vector2& vec) const { return Vector2(x - vec.x, y - vec.y); } + /*! + * \brief Multiplies the components of the vector with other vector + * \return A vector where components are the product of this vector and the other one + * + * \param vec The other vector to multiply components with + */ + template Vector2 Vector2::operator*(const Vector2& vec) const { return Vector2(x * vec.x, y * vec.y); } + /*! + * \brief Multiplies the components of the vector with a scalar + * \return A vector where components are the product of this vector and the scalar + * + * \param scale The scalar to multiply components with + */ + template Vector2 Vector2::operator*(T scale) const { return Vector2(x * scale, y * scale); } + /*! + * \brief Divides the components of the vector with other vector + * \return A vector where components are the quotient of this vector and the other one + * + * \param vec The other vector to divide components with + * + * \remark Produce a NazaraError if one of the vec components is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of the vec components is null + */ + template Vector2 Vector2::operator/(const Vector2& vec) const { @@ -312,6 +614,16 @@ namespace Nz return Vector2(x / vec.x, y / vec.y); } + /*! + * \brief Divides the components of the vector with a scalar + * \return A vector where components are the quotient of this vector and the scalar + * + * \param scale The scalar to divide components with + * + * \remark Produce a NazaraError if scale is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and scale is null + */ + template Vector2 Vector2::operator/(T scale) const { @@ -328,6 +640,13 @@ namespace Nz return Vector2(x / scale, y / scale); } + /*! + * \brief Adds the components of other vector to this vector + * \return A reference to this vector where components are the sum of this vector and the other one + * + * \param vec The other vector to add components with + */ + template Vector2& Vector2::operator+=(const Vector2& vec) { @@ -337,6 +656,13 @@ namespace Nz return *this; } + /*! + * \brief Substracts the components of other vector to this vector + * \return A reference to this vector where components are the difference of this vector and the other one + * + * \param vec The other vector to substract components with + */ + template Vector2& Vector2::operator-=(const Vector2& vec) { @@ -346,6 +672,13 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of other vector to this vector + * \return A reference to this vector where components are the product of this vector and the other one + * + * \param vec The other vector to multiply components with + */ + template Vector2& Vector2::operator*=(const Vector2& vec) { @@ -355,6 +688,13 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of other vector with a scalar + * \return A reference to this vector where components are the product of this vector and the scalar + * + * \param vec The other vector to multiply components with + */ + template Vector2& Vector2::operator*=(T scale) { @@ -364,6 +704,16 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of other vector to this vector + * \return A reference to this vector where components are the quotient of this vector and the other one + * + * \param vec The other vector to multiply components with + * + * \remark Produce a NazaraError if one of the vec components is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of the vec components is null + */ + template Vector2& Vector2::operator/=(const Vector2& vec) { @@ -383,6 +733,16 @@ namespace Nz return *this; } + /*! + * \brief Divides the components of other vector with a scalar + * \return A reference to this vector where components are the quotient of this vector and the scalar + * + * \param vec The other vector to divide components with + * + * \remark Produce a NazaraError if scale is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and scale is null + */ + template Vector2& Vector2::operator/=(T scale) { @@ -402,19 +762,40 @@ namespace Nz return *this; } + /*! + * \brief Compares the vector to other one + * \return true if the vectors are the same + * + * \param vec Other vector to compare with + */ + template bool Vector2::operator==(const Vector2& vec) const { return NumberEquals(x, vec.x) && - NumberEquals(y, vec.y); + NumberEquals(y, vec.y); } + /*! + * \brief Compares the vector to other one + * \return false if the vectors are the same + * + * \param vec Other vector to compare with + */ + template bool Vector2::operator!=(const Vector2& vec) const { return !operator==(vec); } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components inferior to the other ones + * + * \param vec Other vector to compare with + */ + template bool Vector2::operator<(const Vector2& vec) const { @@ -424,6 +805,13 @@ namespace Nz return x < vec.x; } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components inferior or equal to the other ones + * + * \param vec Other vector to compare with + */ + template bool Vector2::operator<=(const Vector2& vec) const { @@ -433,24 +821,95 @@ namespace Nz return x < vec.x; } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components superior to the other ones + * + * \param vec Other vector to compare with + */ + template bool Vector2::operator>(const Vector2& vec) const { return !operator<=(vec); } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components superior or equal to the other ones + * + * \param vec Other vector to compare with + */ + template bool Vector2::operator>=(const Vector2& vec) const { return !operator<(vec); } + /*! + * \brief Calculates the dot (scalar) product with two vectors + * \return The value of the dot product + * + * \param vec1 The first vector to calculate the dot product with + * \param vec2 The second vector to calculate the dot product with + * + * \see AbsDotProduct, DotProduct + */ + + template + T Vector2::DotProduct(const Vector2& vec1, const Vector2& vec2) + { + return vec1.DotProduct(vec2); + } + + /*! + * \brief Interpolates the vector to other one with a factor of interpolation + * \return A new vector which is the interpolation of two vectors + * + * \param from Initial vector + * \param to Target vector + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * + * \see Lerp + */ + template Vector2 Vector2::Lerp(const Vector2& from, const Vector2& to, T interpolation) { - return Lerp(from, to, interpolation); + Vector2 dummy; + dummy.x = Nz::Lerp(from.x, to.x, interpolation); + dummy.y = Nz::Lerp(from.y, to.y, interpolation); + + return dummy; } + /*! + * \brief Gives the normalized vector + * \return A normalized vector from the vec + * + * \param vec Vector to normalize + * + * \remark If the vector is (0, 0), then it returns (0, 0) + * + * \see GetNormal + */ + + template + Vector2 Vector2::Normalize(const Vector2& vec) + { + return vec.GetNormal(); + } + + /*! + * \brief Shorthand for the vector (1, 1) + * \return A vector with components (1, 1) + * + * \see MakeUnit + */ + template Vector2 Vector2::Unit() { @@ -460,6 +919,13 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (1, 0) + * \return A vector with components (1, 0) + * + * \see MakeUnitX + */ + template Vector2 Vector2::UnitX() { @@ -469,6 +935,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 1) + * \return A vector with components (0, 1) + * + * \see MakeUnitY + */ template Vector2 Vector2::UnitY() { @@ -478,6 +950,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 0) + * \return A vector with components (0, 0) + * + * \see MakeZero + */ template Vector2 Vector2::Zero() { @@ -487,33 +965,96 @@ namespace Nz return vector; } - template - std::ostream& operator<<(std::ostream& out, const Vector2& vec) - { - return out << vec.ToString(); - } + /*! + * \brief Serializes a Vector2 + * \return true if successfully serialized + * + * \param context Serialization context + * \param vector Input Vector2 + */ + template + bool Serialize(SerializationContext& context, const Vector2& vector) + { + if (!Serialize(context, vector.x)) + return false; - template - Vector2 operator*(T scale, const Vector2& vec) - { - return Vector2(scale * vec.x, scale * vec.y); - } + if (!Serialize(context, vector.y)) + return false; - template - Vector2 operator/(T scale, const Vector2& vec) - { - #if NAZARA_MATH_SAFE - if (NumberEquals(vec.x, F(0.0)) || NumberEquals(vec.y, F(0.0))) - { - String error("Division by zero"); + return true; + } - NazaraError(error); - throw std::domain_error(error); - } - #endif + /*! + * \brief Unserializes a Vector2 + * \return true if successfully unserialized + * + * \param context Serialization context + * \param vector Output Vector2 + */ + template + bool Unserialize(SerializationContext& context, Vector2* vector) + { + if (!Unserialize(context, &vector->x)) + return false; - return Vector2(scale/vec.x, scale/vec.y); - } + if (!Unserialize(context, &vector->y)) + return false; + + return true; + } +} + +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param vec The vector to output +*/ + +template +std::ostream& operator<<(std::ostream& out, const Nz::Vector2& vec) +{ + return out << vec.ToString(); +} + +/*! +* \brief Multiplies the components of the vector with a scalar +* \return A vector where components are the product of this vector and the scalar +* +* \param scale The scalar to multiply components with +*/ + +template +Nz::Vector2 operator*(T scale, const Nz::Vector2& vec) +{ + return Nz::Vector2(scale * vec.x, scale * vec.y); +} + +/*! +* \brief Divides the components of the vector with a scalar +* \return A vector where components are the quotient of this vector and the scalar +* +* \param scale The scalar to divide components with +* +* \remark Produce a NazaraError if scale is null with NAZARA_MATH_SAFE defined +* \throw std::domain_error if NAZARA_MATH_SAFE is defined and scale is null +*/ + +template +Nz::Vector2 operator/(T scale, const Nz::Vector2& vec) +{ + #if NAZARA_MATH_SAFE + if (Nz::NumberEquals(vec.x, F(0.0)) || Nz::NumberEquals(vec.y, F(0.0))) + { + Nz::String error("Division by zero"); + + NazaraError(error); + throw std::domain_error(error); + } + #endif + + return Nz::Vector2(scale / vec.x, scale / vec.y); } #undef F diff --git a/include/Nazara/Math/Vector3.hpp b/include/Nazara/Math/Vector3.hpp index a1302127f..a5fbad035 100644 --- a/include/Nazara/Math/Vector3.hpp +++ b/include/Nazara/Math/Vector3.hpp @@ -11,6 +11,8 @@ namespace Nz { + struct SerializationContext; + template class Vector2; template class Vector4; @@ -73,8 +75,8 @@ namespace Nz String ToString() const; - operator T*(); - operator const T*() const; + operator T* (); + operator const T* () const; const Vector3& operator+() const; Vector3 operator-() const; @@ -133,6 +135,9 @@ namespace Nz typedef Vector3 Vector3ui; typedef Vector3 Vector3i32; typedef Vector3 Vector3ui32; + + template bool Serialize(SerializationContext& context, const Vector3& vector); + template bool Unserialize(SerializationContext& context, Vector3* vector); } #include diff --git a/include/Nazara/Math/Vector3.inl b/include/Nazara/Math/Vector3.inl index b5b7ef29d..96f70f9b1 100644 --- a/include/Nazara/Math/Vector3.inl +++ b/include/Nazara/Math/Vector3.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -13,36 +14,78 @@ namespace Nz { + /*! + * \ingroup math + * \class Nz::Vector3 + * \brief Math class that represents an element of the three dimensional vector space + * + * \remark The basis is said to be "right-hand". It means that with your right hand, the thumb is X positive, the index finger Y positive and the middle finger (pointing to you) Z positive + */ + + /*! + * \brief Constructs a Vector3 object from its coordinates + * + * \param X X component + * \param Y Y component + * \param Z Z component + */ template Vector3::Vector3(T X, T Y, T Z) { Set(X, Y, Z); } + /*! + * \brief Constructs a Vector3 object from a component and a Vector2 + * + * \param X X component + * \param vec vec.X = Y component and vec.y = Z component + */ template Vector3::Vector3(T X, const Vector2& vec) { Set(X, vec); } + /*! + * \brief Constructs explicitely a Vector3 object from its "scale" + * + * \param scale X component = Y component = Z component + */ template Vector3::Vector3(T scale) { Set(scale); } + /*! + * \brief Constructs a Vector3 object from an array of three elements + * + * \param vec[3] vec[0] is X component, vec[1] is Y component and vec[2] is Z component + */ template Vector3::Vector3(const T vec[3]) { Set(vec); } + /*! + * \brief Constructs a Vector3 object from a Vector2 and a component + * + * \param vec vec.X = X component and vec.y = Y component + * \param Z Z component + */ template Vector3::Vector3(const Vector2& vec, T Z) { Set(vec, Z); } + /*! + * \brief Constructs a Vector3 object from another type of Vector3 + * + * \param vec Vector of type U to convert to type T + */ template template Vector3::Vector3(const Vector3& vec) @@ -50,18 +93,43 @@ namespace Nz Set(vec); } + /*! + * \brief Constructs a Vector3 object from a Vector4 + * + * \param vec Vector4 where only the first three components are taken + */ template Vector3::Vector3(const Vector4& vec) { Set(vec); } + /*! + * \brief Calculates the absolute dot (scalar) product with two vectors + * \return The dot product with absolutes values on each component + * + * \param vec The other vector to calculate the absolute dot product with + * + * \see DotProduct + */ template T Vector3::AbsDotProduct(const Vector3& vec) const { return std::abs(x * vec.x) + std::abs(y * vec.y) + std::abs(z * vec.z); } + /*! + * \brief Calculates the angle between two vectors in orthonormal basis + * \return The angle unit depends of NAZARA_MATH_ANGLE_RADIAN in the range 0..pi + * + * \param vec The other vector to measure the angle with + * + * \remark The vectors do not need to be normalised + * \remark Produce a NazaraError if one of the vec components is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of the vec components is null + * + * \see NormalizeAngle + */ template T Vector3::AngleBetween(const Vector3& vec) const { @@ -78,46 +146,96 @@ namespace Nz } #endif - T alpha = DotProduct(vec)/divisor; + T alpha = DotProduct(vec) / divisor; return FromRadians(std::acos(Clamp(alpha, F(-1.0), F(1.0)))); } + /*! + * \brief Calculates the cross (scalar) product with two vectors + * \return The vector of the cross product according to "right-hand" rule + * + * \param vec The other vector to calculate the cross product with + * + * \see CrossProduct + */ template Vector3 Vector3::CrossProduct(const Vector3& vec) const { return Vector3(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x); } + /*! + * \brief Calculates the distance between two vectors + * \return The metric distance between two vectors with euclidean norm + * + * \param vec The other vector to measure the distance with + * + * \see SquaredDistance + */ template T Vector3::Distance(const Vector3& vec) const { return std::sqrt(SquaredDistance(vec)); } + /*! + * \brief Calculates the distance between two vectors + * \return The metric distance in float between two vectors with euclidean norm + * + * \param vec The other vector to measure the distance with + */ template float Vector3::Distancef(const Vector3& vec) const { return std::sqrt(static_cast(SquaredDistance(vec))); } + /*! + * \brief Calculates the dot (scalar) product with two vectors + * \return The value of the dot product + * + * \param vec The other vector to calculate the dot product with + * + * \see AbsDotProduct, DotProduct + */ template T Vector3::DotProduct(const Vector3& vec) const { - return x*vec.x + y*vec.y + z*vec.z; + return x * vec.x + y * vec.y + z * vec.z; } + /*! + * \brief Calculates the length (magnitude) of the vector + * \return The length of the vector + * + * \see GetSquaredLength + */ template T Vector3::GetLength() const { return static_cast(std::sqrt(GetSquaredLength())); } + /*! + * \brief Calculates the length (magnitude) of the vector + * \return The length in float of the vector + */ template float Vector3::GetLengthf() const { return std::sqrt(static_cast(GetSquaredLength())); } + /*! + * \brief Gets a copy normalized of the vector + * \return A new vector which is the vector normalized + * + * \param length Optional argument to obtain the length's ratio of the vector and the unit-length + * + * \remark If ths vector is (0, 0, 0), then it returns (0, 0, 0) and length is 0 + * + * \see Normalize + */ template Vector3 Vector3::GetNormal(T* length) const { @@ -127,78 +245,158 @@ namespace Nz return vec; } + /*! + * \brief Calculates the squared length (magnitude) of the vector + * \return The squared length of the vector + * + * \see GetLength + */ template T Vector3::GetSquaredLength() const { return x*x + y*y + z*z; } + /*! + * \brief Makes the vector (0, 0, 1) + * \return A reference to this vector with components (0, 0, 1) + * + * \see Backward + */ template Vector3& Vector3::MakeBackward() { return Set(F(0.0), F(0.0), F(1.0)); } + /*! + * \brief Makes the vector (0, -1, 0) + * \return A reference to this vector with components (0, -1, 0) + * + * \see Down + */ template Vector3& Vector3::MakeDown() { return Set(F(0.0), F(-1.0), F(0.0)); } + /*! + * \brief Makes the vector (0, 0, -1) + * \return A reference to this vector with components (0, 0, -1) + * + * \see Forward + */ template Vector3& Vector3::MakeForward() { return Set(F(0.0), F(0.0), F(-1.0)); } + /*! + * \brief Makes the vector (-1, 0, 0) + * \return A reference to this vector with components (-1, 0, 0) + * + * \see Left + */ template Vector3& Vector3::MakeLeft() { return Set(F(-1.0), F(0.0), F(0.0)); } + /*! + * \brief Makes the vector (1, 0, 0) + * \return A reference to this vector with components (1, 0, 0) + * + * \see Right + */ template Vector3& Vector3::MakeRight() { return Set(F(1.0), F(0.0), F(0.0)); } + /*! + * \brief Makes the vector (1, 1, 1) + * \return A reference to this vector with components (1, 1, 1) + * + * \see Unit + */ template Vector3& Vector3::MakeUnit() { return Set(F(1.0), F(1.0), F(1.0)); } + /*! + * \brief Makes the vector (1, 0, 0) + * \return A reference to this vector with components (1, 0, 0) + * + * \see UnitX + */ template Vector3& Vector3::MakeUnitX() { return Set(F(1.0), F(0.0), F(0.0)); } + /*! + * \brief Makes the vector (0, 1, 0) + * \return A reference to this vector with components (0, 1, 0) + * + * \see UnitY + */ template Vector3& Vector3::MakeUnitY() { return Set(F(0.0), F(1.0), F(0.0)); } + /*! + * \brief Makes the vector (0, 0, 1) + * \return A reference to this vector with components (0, 0, 1) + * + * \see UnitZ + */ template Vector3& Vector3::MakeUnitZ() { return Set(F(0.0), F(0.0), F(1.0)); } + /*! + * \brief Makes the vector (0, 1, 0) + * \return A reference to this vector with components (0, 1, 0) + * + * \see Up + */ template Vector3& Vector3::MakeUp() { return Set(F(0.0), F(1.0), F(0.0)); } + /*! + * \brief Makes the vector (0, 0, 0) + * \return A reference to this vector with components (0, 0, 0) + * + * \see Zero + */ template Vector3& Vector3::MakeZero() { return Set(F(0.0), F(0.0), F(0.0)); } + /*! + * \brief Sets this vector's components to the maximum of its own and other components + * \return A reference to this vector with replaced values with the corresponding max value + * + * \param vec Other vector to compare the components with + * + * \see Minimize + */ template Vector3& Vector3::Maximize(const Vector3& vec) { @@ -214,6 +412,14 @@ namespace Nz return *this; } + /*! + * \brief Sets this vector's components to the minimum of its own and other components + * \return A reference to this vector with replaced values with the corresponding min value + * + * \param vec Other vector to compare the components with + * + * \see Maximize + */ template Vector3& Vector3::Minimize(const Vector3& vec) { @@ -229,6 +435,16 @@ namespace Nz return *this; } + /*! + * \brief Normalizes the current vector + * \return A reference to this vector + * + * \param length Optional argument to obtain the length's ratio of the vector and the unit-length + * + * \remark If the vector is (0, 0, 0), then it returns (0, 0, 0) and length is 0 + * + * \see GetNormal + */ template Vector3& Vector3::Normalize(T* length) { @@ -247,6 +463,14 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector + * \return A reference to this vector + * + * \param X X component + * \param Y Y component + * \param Z Z component + */ template Vector3& Vector3::Set(T X, T Y, T Z) { @@ -257,6 +481,12 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a component and a Vector2 + * + * \param X X component + * \param vec vec.X = Y component and vec.y = Z component + */ template Vector3& Vector3::Set(T X, const Vector2& vec) { @@ -267,6 +497,12 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a "scale" + * \return A reference to this vector + * + * \param scale X component = Y component = Z component + */ template Vector3& Vector3::Set(T scale) { @@ -277,6 +513,12 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from an array of three elements + * \return A reference to this vector + * + * \param vec[3] vec[0] is X component, vec[1] is Y component and vec[2] is Z component + */ template Vector3& Vector3::Set(const T vec[3]) { @@ -285,6 +527,12 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a Vector2 and a component + * + * \param vec vec.X = X component and vec.y = Y component + * \param Z Z component + */ template Vector3& Vector3::Set(const Vector2& vec, T Z) { @@ -295,6 +543,12 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from another vector + * \return A reference to this vector + * + * \param vec The other vector + */ template Vector3& Vector3::Set(const Vector3& vec) { @@ -303,6 +557,12 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from another type of Vector3 + * \return A reference to this vector + * + * \param vec Vector of type U to convert its components + */ template template Vector3& Vector3::Set(const Vector3& vec) @@ -314,6 +574,12 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a Vector4 + * \return A reference to this vector + * + * \param vec Vector4 where only the first three components are taken + */ template Vector3& Vector3::Set(const Vector4& vec) { @@ -324,12 +590,24 @@ namespace Nz return *this; } + /*! + * \brief Calculates the squared distance between two vectors + * \return The metric distance between two vectors with the squared euclidean norm + * + * \param vec The other vector to measure the distance with + * + * \see Distance + */ template T Vector3::SquaredDistance(const Vector3& vec) const { return (*this - vec).GetSquaredLength(); } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Vector3(x, y, z)" + */ template String Vector3::ToString() const { @@ -338,54 +616,107 @@ namespace Nz return ss << "Vector3(" << x << ", " << y << ", " << z <<')'; } + /*! + * \brief Converts vector to pointer to its own data + * \return A pointer to the own data + * + * \remark Access to index greather than 2 is undefined behavior + */ template - Vector3::operator T*() + Vector3::operator T* () { return &x; } + /*! + * \brief Converts vector to const pointer to its own data + * \return A constant pointer to the own data + * + * \remark Access to index greather than 2 is undefined behavior + */ template - Vector3::operator const T*() const + Vector3::operator const T* () const { return &x; } + /*! + * \brief Helps to represent the sign of the vector + * \return A constant reference to this vector + */ template const Vector3& Vector3::operator+() const { return *this; } + /*! + * \brief Negates the components of the vector + * \return A constant reference to this vector with negate components + */ template Vector3 Vector3::operator-() const { return Vector3(-x, -y, -z); } + /*! + * \brief Adds the components of the vector with other vector + * \return A vector where components are the sum of this vector and the other one + * + * \param vec The other vector to add components with + */ template Vector3 Vector3::operator+(const Vector3& vec) const { return Vector3(x + vec.x, y + vec.y, z + vec.z); } + /*! + * \brief Substracts the components of the vector with other vector + * \return A vector where components are the difference of this vector and the other one + * + * \param vec The other vector to substract components with + */ template Vector3 Vector3::operator-(const Vector3& vec) const { return Vector3(x - vec.x, y - vec.y, z - vec.z); } + /*! + * \brief Multiplies the components of the vector with other vector + * \return A vector where components are the product of this vector and the other one + * + * \param vec The other vector to multiply components with + */ template Vector3 Vector3::operator*(const Vector3& vec) const { return Vector3(x * vec.x, y * vec.y, z * vec.z); } + /*! + * \brief Multiplies the components of the vector with a scalar + * \return A vector where components are the product of this vector and the scalar + * + * \param scale The scalar to multiply components with + */ template Vector3 Vector3::operator*(T scale) const { return Vector3(x * scale, y * scale, z * scale); } + /*! + * \brief Divides the components of the vector with other vector + * \return A vector where components are the quotient of this vector and the other one + * + * \param vec The other vector to divide components with + * + * \remark Produce a NazaraError if one of the vec components is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of the vec components is null + */ template Vector3 Vector3::operator/(const Vector3& vec) const { @@ -402,6 +733,15 @@ namespace Nz return Vector3(x / vec.x, y / vec.y, z / vec.z); } + /*! + * \brief Divides the components of the vector with a scalar + * \return A vector where components are the quotient of this vector and the scalar + * + * \param scale The scalar to divide components with + * + * \remark Produce a NazaraError if scale is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and scale is null + */ template Vector3 Vector3::operator/(T scale) const { @@ -418,6 +758,12 @@ namespace Nz return Vector3(x / scale, y / scale, z / scale); } + /*! + * \brief Adds the components of other vector to this vector + * \return A reference to this vector where components are the sum of this vector and the other one + * + * \param vec The other vector to add components with + */ template Vector3& Vector3::operator+=(const Vector3& vec) { @@ -428,6 +774,12 @@ namespace Nz return *this; } + /*! + * \brief Substracts the components of other vector to this vector + * \return A reference to this vector where components are the difference of this vector and the other one + * + * \param vec The other vector to substract components with + */ template Vector3& Vector3::operator-=(const Vector3& vec) { @@ -438,6 +790,12 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of other vector to this vector + * \return A reference to this vector where components are the product of this vector and the other one + * + * \param vec The other vector to multiply components with + */ template Vector3& Vector3::operator*=(const Vector3& vec) { @@ -448,6 +806,12 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of other vector with a scalar + * \return A reference to this vector where components are the product of this vector and the scalar + * + * \param vec The other vector to multiply components with + */ template Vector3& Vector3::operator*=(T scale) { @@ -458,6 +822,15 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of other vector to this vector + * \return A reference to this vector where components are the quotient of this vector and the other one + * + * \param vec The other vector to multiply components with + * + * \remark Produce a NazaraError if one of the vec components is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of the vec components is null + */ template Vector3& Vector3::operator/=(const Vector3& vec) { @@ -476,6 +849,15 @@ namespace Nz return *this; } + /*! + * \brief Divides the components of other vector with a scalar + * \return A reference to this vector where components are the quotient of this vector and the scalar + * + * \param vec The other vector to divide components with + * + * \remark Produce a NazaraError if scale is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and scale is null + */ template Vector3& Vector3::operator/=(T scale) { @@ -494,20 +876,38 @@ namespace Nz return *this; } + /*! + * \brief Compares the vector to other one + * \return true if the vectors are the same + * + * \param vec Other vector to compare with + */ template bool Vector3::operator==(const Vector3& vec) const { return NumberEquals(x, vec.x) && - NumberEquals(y, vec.y) && - NumberEquals(z, vec.z); + NumberEquals(y, vec.y) && + NumberEquals(z, vec.z); } + /*! + * \brief Compares the vector to other one + * \return false if the vectors are the same + * + * \param vec Other vector to compare with + */ template bool Vector3::operator!=(const Vector3& vec) const { return !operator==(vec); } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components inferior to the other ones + * + * \param vec Other vector to compare with + */ template bool Vector3::operator<(const Vector3& vec) const { @@ -522,6 +922,12 @@ namespace Nz return x < vec.x; } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components inferior or equal to the other ones + * + * \param vec Other vector to compare with + */ template bool Vector3::operator<=(const Vector3& vec) const { @@ -536,30 +942,66 @@ namespace Nz return x < vec.x; } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components superior to the other ones + * + * \param vec Other vector to compare with + */ template bool Vector3::operator>(const Vector3& vec) const { return !operator<=(vec); } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components superior or equal to the other ones + * + * \param vec Other vector to compare with + */ template bool Vector3::operator>=(const Vector3& vec) const { return !operator<(vec); } + /*! + * \brief Calculates the cross product with two vectors + * \return A vector which is the cross product according to "right-hand" rule + * + * \param vec1 The first vector to calculate the cross product with + * \param vec2 The second vector to calculate the cross product with + * + * \see CrossProduct + */ template Vector3 Vector3::CrossProduct(const Vector3& vec1, const Vector3& vec2) { return vec1.CrossProduct(vec2); } + /*! + * \brief Calculates the dot (scalar) product with two vectors + * \return The value of the dot product + * + * \param vec1 The first vector to calculate the dot product with + * \param vec2 The second vector to calculate the dot product with + * + * \see AbsDotProduct, DotProduct + */ template T Vector3::DotProduct(const Vector3& vec1, const Vector3& vec2) { return vec1.DotProduct(vec2); } + /*! + * \brief Shorthand for the vector (0, 0, 1) + * \return A vector with components (0, 0, 1) + * + * \see MakeBackward + */ template Vector3 Vector3::Backward() { @@ -569,6 +1011,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, -1, 0) + * \return A vector with components (0, -1, 0) + * + * \see MakeDown + */ template T Vector3::Distance(const Vector3& vec1, const Vector3& vec2) { @@ -590,6 +1038,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 0, -1) + * \return A vector with components (0, 0, -1) + * + * \see Forward + */ template Vector3 Vector3::Forward() { @@ -599,6 +1053,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (-1, 0, 0) + * \return A vector with components (-1, 0, 0) + * + * \see MakeLeft + */ template Vector3 Vector3::Left() { @@ -608,18 +1068,51 @@ namespace Nz return vector; } + /*! + * \brief Interpolates the vector to other one with a factor of interpolation + * \return A new vector which is the interpolation of two vectors + * + * \param from Initial vector + * \param to Target vector + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * + * \see Lerp + */ template Vector3 Vector3::Lerp(const Vector3& from, const Vector3& to, T interpolation) { - return Nz::Lerp(from, to, interpolation); + Vector3 dummy; + dummy.x = Nz::Lerp(from.x, to.x, interpolation); + dummy.y = Nz::Lerp(from.y, to.y, interpolation); + dummy.z = Nz::Lerp(from.z, to.z, interpolation); + + return dummy; } + /*! + * \brief Gives the normalized vector + * \return A normalized vector from the vec + * + * \param vec Vector to normalize + * + * \remark If the vector is (0, 0, 0), then it returns (0, 0, 0) + * + * \see GetNormal + */ template Vector3 Vector3::Normalize(const Vector3& vec) { return vec.GetNormal(); } + /*! + * \brief Shorthand for the vector (1, 0, 0) + * \return A vector with components (1, 0, 0) + * + * \see MakeRight + */ template Vector3 Vector3::Right() { @@ -629,6 +1122,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (1, 1, 1) + * \return A vector with components (1, 1, 1) + * + * \see MakeUnit + */ template T Vector3::SquaredDistance(const Vector3& vec1, const Vector3& vec2) { @@ -644,6 +1143,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (1, 0, 0) + * \return A vector with components (1, 0, 0) + * + * \see MakeUnitX + */ template Vector3 Vector3::UnitX() { @@ -653,6 +1158,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 1, 0) + * \return A vector with components (0, 1, 0) + * + * \see MakeUnitY + */ template Vector3 Vector3::UnitY() { @@ -662,6 +1173,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 0, 1) + * \return A vector with components (0, 0, 1) + * + * \see MakeUnitZ + */ template Vector3 Vector3::UnitZ() { @@ -671,6 +1188,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 1, 0) + * \return A vector with components (0, 1, 0) + * + * \see MakeUp + */ template Vector3 Vector3::Up() { @@ -680,6 +1203,12 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 0, 0) + * \return A vector with components (0, 0, 0) + * + * \see MakeZero + */ template Vector3 Vector3::Zero() { @@ -689,33 +1218,100 @@ namespace Nz return vector; } - template - std::ostream& operator<<(std::ostream& out, const Vector3& vec) - { - return out << vec.ToString(); - } + /*! + * \brief Serializes a Vector3 + * \return true if successfully serialized + * + * \param context Serialization context + * \param vector Input Vector3 + */ + template + bool Serialize(SerializationContext& context, const Vector3& vector) + { + if (!Serialize(context, vector.x)) + return false; - template - Vector3 operator*(T scale, const Vector3& vec) - { - return Vector3(scale * vec.x, scale * vec.y, scale * vec.z); - } + if (!Serialize(context, vector.y)) + return false; - template - Vector3 operator/(T scale, const Vector3& vec) - { - #if NAZARA_MATH_SAFE - if (NumberEquals(vec.x, F(0.0)) || NumberEquals(vec.y, F(0.0)) || NumberEquals(vec.z, F(0.0))) - { - String error("Division by zero"); + if (!Serialize(context, vector.z)) + return false; - NazaraError(error); - throw std::domain_error(error); - } - #endif + return true; + } - return Vector3(scale / vec.x, scale / vec.y, scale / vec.z); - } + /*! + * \brief Unserializes a Vector3 + * \return true if successfully unserialized + * + * \param context Serialization context + * \param vector Output Vector3 + */ + template + bool Unserialize(SerializationContext& context, Vector3* vector) + { + if (!Unserialize(context, &vector->x)) + return false; + + if (!Unserialize(context, &vector->y)) + return false; + + if (!Unserialize(context, &vector->z)) + return false; + + return true; + } +} + +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param vec The vector to output +*/ + +template +std::ostream& operator<<(std::ostream& out, const Nz::Vector3& vec) +{ + return out << vec.ToString(); +} + +/*! +* \brief Multiplies the components of the vector with a scalar +* \return A vector where components are the product of this vector and the scalar +* +* \param scale The scalar to multiply components with +*/ + +template +Nz::Vector3 operator*(T scale, const Nz::Vector3& vec) +{ + return Nz::Vector3(scale * vec.x, scale * vec.y, scale * vec.z); +} + +/*! +* \brief Divides the components of the vector with a scalar +* \return A vector where components are the quotient of this vector and the scalar +* +* \param scale The scalar to divide components with +* +* \remark Produce a NazaraError if scale is null with NAZARA_MATH_SAFE defined +* \throw std::domain_error if NAZARA_MATH_SAFE is defined and scale is null +*/ + +template +Nz::Vector3 operator/(T scale, const Nz::Vector3& vec) +{ + #if NAZARA_MATH_SAFE + if (Nz::NumberEquals(vec.x, F(0.0)) || Nz::NumberEquals(vec.y, F(0.0)) || Nz::NumberEquals(vec.z, F(0.0))) + { + Nz::String error("Division by zero"); + + NazaraError(error); + throw std::domain_error(error); + } + #endif } #undef F diff --git a/include/Nazara/Math/Vector4.hpp b/include/Nazara/Math/Vector4.hpp index 0c8673e90..daedcad0b 100644 --- a/include/Nazara/Math/Vector4.hpp +++ b/include/Nazara/Math/Vector4.hpp @@ -11,6 +11,8 @@ namespace Nz { + struct SerializationContext; + template class Vector2; template class Vector3; @@ -60,8 +62,8 @@ namespace Nz String ToString() const; - operator T*(); - operator const T*() const; + operator T* (); + operator const T* () const; const Vector4& operator+() const; Vector4 operator-() const; @@ -87,6 +89,9 @@ namespace Nz bool operator>(const Vector4& vec) const; bool operator>=(const Vector4& vec) const; + static T DotProduct(const Vector4& vec1, const Vector4& vec2); + static Vector4 Lerp(const Vector4& from, const Vector4& to, T interpolation); + static Vector4 Normalize(const Vector4& vec); static Vector4 UnitX(); static Vector4 UnitY(); static Vector4 UnitZ(); @@ -106,6 +111,9 @@ namespace Nz typedef Vector4 Vector4ui; typedef Vector4 Vector4i32; typedef Vector4 Vector4ui32; + + template bool Serialize(SerializationContext& context, const Vector4& vector); + template bool Unserialize(SerializationContext& context, Vector4* vector); } #include diff --git a/include/Nazara/Math/Vector4.inl b/include/Nazara/Math/Vector4.inl index c16397c35..f0220ec4e 100644 --- a/include/Nazara/Math/Vector4.inl +++ b/include/Nazara/Math/Vector4.inl @@ -2,6 +2,7 @@ // This file is part of the "Nazara Engine - Mathematics module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include #include @@ -14,54 +15,126 @@ namespace Nz { + + /*! + * \ingroup math + * \class Nz::Vector4 + * \brief Math class that represents an element of the three dimensional vector space with the notion of projectivity. When the fourth component is 1, it describes an 'usual' point and when it is 0, it represents the point at infinity + */ + + /*! + * \brief Constructs a Vector4 object from its coordinates + * + * \param X X component + * \param Y Y component + * \param Z Z component + * \param W W component + */ + template Vector4::Vector4(T X, T Y, T Z, T W) { Set(X, Y, Z, W); } + /*! + * \brief Constructs a Vector4 object from two components and a Vector2 + * + * \param X X component + * \param Y Y component + * \param vec vec.X = Z component and vec.y = W component + */ + template Vector4::Vector4(T X, T Y, const Vector2& vec) { Set(X, Y, vec); } + /*! + * \brief Constructs a Vector4 object from one component, a Vector2 and one component + * + * \param X X component + * \param vec vec.X = Y component and vec.y = Z component + * \param W W component + */ + template Vector4::Vector4(T X, const Vector2& vec, T W) { Set(X, vec, W); } + /*! + * \brief Constructs a Vector4 object from one component and a Vector3 + * + * \param X X component + * \param vec vec.X = Y component, vec.y = Z component and vec.z = W component + */ + template Vector4::Vector4(T X, const Vector3& vec) { Set(X, vec); } + /*! + * \brief Constructs explicitely a Vector4 object from its "scale" + * + * \param scale X component = Y component = Z component = W component + */ + template Vector4::Vector4(T scale) { Set(scale); } + /*! + * \brief Constructs a Vector4 object from an array of four elements + * + * \param vec[4] vec[0] is X component, vec[1] is Y component, vec[2] is Z component and vec[3] is W component + */ + template Vector4::Vector4(const T vec[4]) { Set(vec); } + /*! + * \brief Constructs a Vector4 object from a Vector2 and two components + * + * \param vec vec.X = X component and vec.y = Y component + * \param Z Z component + * \param W W component + */ + template Vector4::Vector4(const Vector2& vec, T Z, T W) { Set(vec, Z, W); } + /*! + * \brief Constructs a Vector4 object from one component and a Vector3 + * + * \param vec vec.X = X component, vec.y = Y component and vec.z = Z component + * \param W W component + */ + template Vector4::Vector4(const Vector3& vec, T W) { Set(vec, W); } + /*! + * \brief Constructs a Vector4 object from another type of Vector4 + * + * \param vec Vector of type U to convert to type T + */ + template template Vector4::Vector4(const Vector4& vec) @@ -69,18 +142,45 @@ namespace Nz Set(vec); } + /*! + * \brief Calculates the absolute dot (scalar) product with two vectors + * \return The dot product with absolutes values on each component + * + * \param vec The other vector to calculate the absolute dot product with + * + * \see DotProduct + */ + template T Vector4::AbsDotProduct(const Vector4& vec) const { return std::abs(x * vec.x) + std::abs(y * vec.y) + std::abs(z * vec.z) + std::abs(w * vec.w); } + /*! + * \brief Calculates the dot (scalar) product with two vectors + * \return The value of the dot product + * + * \param vec The other vector to calculate the dot product with + * + * \see AbsDotProduct, DotProduct + */ + template T Vector4::DotProduct(const Vector4& vec) const { return x*vec.x + y*vec.y + z*vec.z + w*vec.w; } + /*! + * \brief Gets a copy normalized of the vector + * \return A new vector which is the vector normalized + * + * \param length Optional argument to obtain the length's ratio of the vector and the unit-length in this case w + * + * \see Normalize + */ + template Vector4 Vector4::GetNormal(T* length) const { @@ -90,30 +190,67 @@ namespace Nz return vec; } + /*! + * \brief Makes the vector (1, 0, 0, 1) + * \return A reference to this vector with components (1, 0, 0, 1) + * + * \see UnitX + */ + template Vector4& Vector4::MakeUnitX() { return Set(F(1.0), F(0.0), F(0.0), F(1.0)); } + /*! + * \brief Makes the vector (0, 1, 0, 1) + * \return A reference to this vector with components (0, 1, 0, 1) + * + * \see UnitY + */ + template Vector4& Vector4::MakeUnitY() { return Set(F(0.0), F(1.0), F(0.0), F(1.0)); } + /*! + * \brief Makes the vector (0, 0, 1, 1) + * \return A reference to this vector with components (0, 0, 1, 1) + * + * \see UnitZ + */ + template Vector4& Vector4::MakeUnitZ() { return Set(F(0.0), F(0.0), F(1.0), F(1.0)); } + /*! + * \brief Makes the vector (0, 0, 0, 1) + * \return A reference to this vector with components (0, 0, 0, 1) + * + * \see Zero + */ + template Vector4& Vector4::MakeZero() { - return Set(F(0.0), F(0.0), F(0.0), F(0.0)); + return Set(F(0.0), F(0.0), F(0.0), F(1.0)); } + /*! + * \brief Sets this vector's components to the maximum of its own and other components + * \return A reference to this vector with replaced values with the corresponding max value + * + * \param vec Other vector to compare the components with + * + * \see Minimize + */ + template Vector4& Vector4::Maximize(const Vector4& vec) { @@ -132,6 +269,15 @@ namespace Nz return *this; } + /*! + * \brief Sets this vector's components to the minimum of its own and other components + * \return A reference to this vector with replaced values with the corresponding min value + * + * \param vec Other vector to compare the components with + * + * \see Maximize + */ + template Vector4& Vector4::Minimize(const Vector4& vec) { @@ -150,11 +296,20 @@ namespace Nz return *this; } + /*! + * \brief Gives the normalized vector + * \return A normalized vector from the vec with w = 1 + * + * \param length Optional argument to obtain the length's ratio of the vector in this case w + * + * \see GetNormal + */ + template Vector4& Vector4::Normalize(T* length) { T invLength = F(1.0)/w; - x *= invLength; // Attention, briser cette logique casserait Frustum::Extract + x *= invLength; // Warning, change this logic will break Frustum::Extract y *= invLength; z *= invLength; @@ -166,6 +321,16 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector + * \return A reference to this vector + * + * \param X X component + * \param Y Y component + * \param Z Z component + * \param W W component + */ + template Vector4& Vector4::Set(T X, T Y, T Z, T W) { @@ -177,6 +342,15 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from two components and a Vector2 + * \return A reference to this vector + * + * \param X X component + * \param Y Y component + * \param vec vec.X = Z component and vec.y = W component + */ + template Vector4& Vector4::Set(T X, T Y, const Vector2& vec) { @@ -188,6 +362,15 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from one component, a Vector2 and one component + * \return A reference to this vector + * + * \param X X component + * \param vec vec.X = Y component and vec.y = Z component + * \param W W component + */ + template Vector4& Vector4::Set(T X, const Vector2& vec, T W) { @@ -199,6 +382,14 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from one component and a Vector3 + * \return A reference to this vector + * + * \param X X component + * \param vec vec.X = Y component, vec.y = Z component and vec.z = W component + */ + template Vector4& Vector4::Set(T X, const Vector3& vec) { @@ -210,6 +401,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a "scale" + * \return A reference to this vector + * + * \param scale X component = Y component = Z component = W component + */ + template Vector4& Vector4::Set(T scale) { @@ -221,6 +419,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from an array of four elements + * \return A reference to this vector + * + * \param vec[4] vec[0] is X component, vec[1] is Y component, vec[2] is Z component and vec[3] is W component + */ + template Vector4& Vector4::Set(const T vec[4]) { @@ -229,6 +434,14 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a Vector2 and two components + * + * \param vec vec.X = X component and vec.y = Y component + * \param Z Z component + * \param W W component + */ + template Vector4& Vector4::Set(const Vector2& vec, T Z, T W) { @@ -240,6 +453,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from a Vector3 and one components + * + * \param vec vec.X = X component, vec.y = Y component and vec.z = Z component + * \param W W component + */ + template Vector4& Vector4::Set(const Vector3& vec, T W) { @@ -251,6 +471,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from another vector + * \return A reference to this vector + * + * \param vec The other vector + */ + template Vector4& Vector4::Set(const Vector4& vec) { @@ -259,6 +486,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the vector from another type of Vector4 + * \return A reference to this vector + * + * \param vec Vector of type U to convert its components + */ + template template Vector4& Vector4::Set(const Vector4& vec) @@ -271,6 +505,11 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Vector4(x, y, z, w)" + */ + template String Vector4::ToString() const { @@ -279,54 +518,116 @@ namespace Nz return ss << "Vector4(" << x << ", " << y << ", " << z << ", " << w << ')'; } + /*! + * \brief Converts vector to pointer to its own data + * \return A pointer to the own data + * + * \remark Access to index greather than 3 is undefined behavior + */ + template - Vector4::operator T*() + Vector4::operator T* () { return &x; } + /*! + * \brief Converts vector to const pointer to its own data + * \return A constant pointer to the own data + * + * \remark Access to index greather than 3 is undefined behavior + */ + template - Vector4::operator const T*() const + Vector4::operator const T* () const { return &x; } + /*! + * \brief Helps to represent the sign of the vector + * \return A constant reference to this vector + */ + template const Vector4& Vector4::operator+() const { return *this; } + /*! + * \brief Negates the components of the vector + * \return A constant reference to this vector with negate components + */ + template Vector4 Vector4::operator-() const { return Vector4(-x, -y, -z, -w); } + /*! + * \brief Adds the components of the vector with other vector + * \return A vector where components are the sum of this vector and the other one + * + * \param vec The other vector to add components with + */ + template Vector4 Vector4::operator+(const Vector4& vec) const { return Vector4(x + vec.x, y + vec.y, z + vec.z, w + vec.w); } + /*! + * \brief Substracts the components of the vector with other vector + * \return A vector where components are the difference of this vector and the other one + * + * \param vec The other vector to substract components with + */ + template Vector4 Vector4::operator-(const Vector4& vec) const { return Vector4(x - vec.x, y - vec.y, z - vec.z, w - vec.w); } + /*! + * \brief Multiplies the components of the vector with other vector + * \return A vector where components are the product of this vector and the other one + * + * \param vec The other vector to multiply components with + */ + template Vector4 Vector4::operator*(const Vector4& vec) const { return Vector4(x * vec.x, y * vec.y, z * vec.z, w * vec.w); } + /*! + * \brief Multiplies the components of the vector with a scalar + * \return A vector where components are the product of this vector and the scalar + * + * \param scale The scalar to multiply components with + */ + template Vector4 Vector4::operator*(T scale) const { return Vector4(x * scale, y * scale, z * scale, w * scale); } + /*! + * \brief Divides the components of the vector with other vector + * \return A vector where components are the quotient of this vector and the other one + * + * \param vec The other vector to divide components with + * + * \remark Produce a NazaraError if one of the vec components is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of the vec components is null + */ + template Vector4 Vector4::operator/(const Vector4& vec) const { @@ -343,6 +644,16 @@ namespace Nz return Vector4(x / vec.x, y / vec.y, z / vec.z, w / vec.w); } + /*! + * \brief Divides the components of the vector with a scalar + * \return A vector where components are the quotient of this vector and the scalar + * + * \param scale The scalar to divide components with + * + * \remark Produce a NazaraError if scale is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and scale is null + */ + template Vector4 Vector4::operator/(T scale) const { @@ -359,6 +670,13 @@ namespace Nz return Vector4(x / scale, y / scale, z / scale, w / scale); } + /*! + * \brief Adds the components of other vector to this vector + * \return A reference to this vector where components are the sum of this vector and the other one + * + * \param vec The other vector to add components with + */ + template Vector4& Vector4::operator+=(const Vector4& vec) { @@ -370,6 +688,13 @@ namespace Nz return *this; } + /*! + * \brief Substracts the components of other vector to this vector + * \return A reference to this vector where components are the difference of this vector and the other one + * + * \param vec The other vector to substract components with + */ + template Vector4& Vector4::operator-=(const Vector4& vec) { @@ -381,6 +706,13 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of other vector to this vector + * \return A reference to this vector where components are the product of this vector and the other one + * + * \param vec The other vector to multiply components with + */ + template Vector4& Vector4::operator*=(const Vector4& vec) { @@ -392,6 +724,13 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of other vector with a scalar + * \return A reference to this vector where components are the product of this vector and the scalar + * + * \param vec The other vector to multiply components with + */ + template Vector4& Vector4::operator*=(T scale) { @@ -403,6 +742,16 @@ namespace Nz return *this; } + /*! + * \brief Multiplies the components of other vector to this vector + * \return A reference to this vector where components are the quotient of this vector and the other one + * + * \param vec The other vector to multiply components with + * + * \remark Produce a NazaraError if one of the vec components is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of the vec components is null + */ + template Vector4& Vector4::operator/=(const Vector4& vec) { @@ -424,6 +773,16 @@ namespace Nz return *this; } + /*! + * \brief Divides the components of other vector with a scalar + * \return A reference to this vector where components are the quotient of this vector and the scalar + * + * \param vec The other vector to divide components with + * + * \remark Produce a NazaraError if scale is null with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and scale is null + */ + template Vector4& Vector4::operator/=(T scale) { @@ -445,21 +804,42 @@ namespace Nz return *this; } + /*! + * \brief Compares the vector to other one + * \return true if the vectors are the same + * + * \param vec Other vector to compare with + */ + template bool Vector4::operator==(const Vector4& vec) const { return NumberEquals(x, vec.x) && - NumberEquals(y, vec.y) && - NumberEquals(z, vec.z) && - NumberEquals(w, vec.w); + NumberEquals(y, vec.y) && + NumberEquals(z, vec.z) && + NumberEquals(w, vec.w); } + /*! + * \brief Compares the vector to other one + * \return false if the vectors are the same + * + * \param vec Other vector to compare with + */ + template bool Vector4::operator!=(const Vector4& vec) const { return !operator==(vec); } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components inferior to the other ones + * + * \param vec Other vector to compare with + */ + template bool Vector4::operator<(const Vector4& vec) const { @@ -479,6 +859,13 @@ namespace Nz return x < vec.x; } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components inferior or equal to the other ones + * + * \param vec Other vector to compare with + */ + template bool Vector4::operator<=(const Vector4& vec) const { @@ -498,18 +885,79 @@ namespace Nz return x < vec.x; } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components superior to the other ones + * + * \param vec Other vector to compare with + */ + template bool Vector4::operator>(const Vector4& vec) const { return !operator<=(vec); } + /*! + * \brief Compares the vector to other one + * \return true if this vector has its first components superior or equal to the other ones + * + * \param vec Other vector to compare with + */ + template bool Vector4::operator>=(const Vector4& vec) const { return !operator<(vec); } + /*! + * \brief Interpolates the vector to other one with a factor of interpolation + * \return A new vector which is the interpolation of two vectors + * + * \param from Initial vector + * \param to Target vector + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * + * \see Lerp + */ + + template + Vector4 Vector4::Lerp(const Vector4& from, const Vector4& to, T interpolation) + { + Vector4 dummy; + dummy.x = Nz::Lerp(from.x, to.x, interpolation); + dummy.y = Nz::Lerp(from.y, to.y, interpolation); + dummy.z = Nz::Lerp(from.z, to.z, interpolation); + dummy.w = Nz::Lerp(from.w, to.w, interpolation); + + return dummy; + } + + /*! + * \brief Gives the normalized vector + * \return A normalized vector from the vec with w = 1 + * + * \param vec Vector to normalize + * + * \see GetNormal + */ + + template + Vector4 Vector4::Normalize(const Vector4& vec) + { + return vec.GetNormal(); + } + + /*! + * \brief Shorthand for the vector (1, 0, 0, 1) + * \return A vector with components (1, 0, 0, 1) + * + * \see MakeUnitX + */ + template Vector4 Vector4::UnitX() { @@ -519,6 +967,13 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 1, 0, 1) + * \return A vector with components (0, 1, 0, 1) + * + * \see MakeUnitY + */ + template Vector4 Vector4::UnitY() { @@ -528,6 +983,13 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 0, 1, 1) + * \return A vector with components (0, 0, 1, 1) + * + * \see MakeUnitZ + */ + template Vector4 Vector4::UnitZ() { @@ -537,6 +999,13 @@ namespace Nz return vector; } + /*! + * \brief Shorthand for the vector (0, 0, 0, 1) + * \return A vector with components (0, 0, 0, 1) + * + * \see MakeZero + */ + template Vector4 Vector4::Zero() { @@ -546,33 +1015,107 @@ namespace Nz return vector; } - template - std::ostream& operator<<(std::ostream& out, const Vector4& vec) - { - return out << vec.ToString(); - } + /*! + * \brief Serializes a Vector4 + * \return true if successfully serialized + * + * \param context Serialization context + * \param vector Input Vector3 + */ + template + bool Serialize(SerializationContext& context, const Vector4& vector) + { + if (!Serialize(context, vector.x)) + return false; - template - Vector4 operator*(T scale, const Vector4& vec) - { - return Vector4(scale * vec.x, scale * vec.y, scale * vec.z, scale * vec.w); - } + if (!Serialize(context, vector.y)) + return false; - template - Vector4 operator/(T scale, const Vector4& vec) - { - #if NAZARA_MATH_SAFE - if (NumberEquals(vec.x, F(0.0)) || NumberEquals(vec.y, F(0.0)) || NumberEquals(vec.z, F(0.0)) || NumberEquals(vec.w, F(0.0))) - { - String error("Division by zero"); + if (!Serialize(context, vector.z)) + return false; - NazaraError(error); - throw std::domain_error(error); - } - #endif + if (!Serialize(context, vector.w)) + return false; - return Vector4(scale / vec.x, scale / vec.y, scale / vec.z, scale / vec.w); - } + return true; + } + + /*! + * \brief Unserializes a Vector4 + * \return true if successfully unserialized + * + * \param context Serialization context + * \param vector Output Vector3 + */ + template + bool Unserialize(SerializationContext& context, Vector4* vector) + { + if (!Unserialize(context, &vector->x)) + return false; + + if (!Unserialize(context, &vector->y)) + return false; + + if (!Unserialize(context, &vector->z)) + return false; + + if (!Unserialize(context, &vector->w)) + return false; + + return true; + } +} + +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param vec The vector to output +*/ + +template +std::ostream& operator<<(std::ostream& out, const Nz::Vector4& vec) +{ + return out << vec.ToString(); +} + +/*! +* \brief Multiplies the components of the vector with a scalar +* \return A vector where components are the product of this vector and the scalar +* +* \param scale The scalar to multiply components with +*/ + + +template +Nz::Vector4 operator*(T scale, const Nz::Vector4& vec) +{ + return Nz::Vector4(scale * vec.x, scale * vec.y, scale * vec.z, scale * vec.w); +} + +/*! +* \brief Divides the components of the vector with a scalar +* \return A vector where components are the quotient of this vector and the scalar +* +* \param scale The scalar to divide components with +* +* \remark Produce a NazaraError if scale is null with NAZARA_MATH_SAFE defined +* \throw std::domain_error if NAZARA_MATH_SAFE is defined and scale is null +*/ + +template +Nz::Vector4 operator/(T scale, const Nz::Vector4& vec) +{ + #if NAZARA_MATH_SAFE + if (NumberEquals(vec.x, F(0.0)) || NumberEquals(vec.y, F(0.0)) || NumberEquals(vec.z, F(0.0)) || NumberEquals(vec.w, F(0.0))) + { + Nz::String error("Division by zero"); + + NazaraError(error); + throw std::domain_error(error); + } + #endif } #undef F diff --git a/include/Nazara/Network.hpp b/include/Nazara/Network.hpp index d5c731be7..72565a17f 100644 --- a/include/Nazara/Network.hpp +++ b/include/Nazara/Network.hpp @@ -1,4 +1,4 @@ -// This file was automatically generated on 09 Nov 2015 at 13:52:47 +// This file was automatically generated on 03 Feb 2016 at 00:06:56 /* Nazara Engine - Network module @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Network/AbstractSocket.hpp b/include/Nazara/Network/AbstractSocket.hpp index 1eab38241..f3789ebf0 100644 --- a/include/Nazara/Network/AbstractSocket.hpp +++ b/include/Nazara/Network/AbstractSocket.hpp @@ -33,7 +33,10 @@ namespace Nz inline bool IsBlockingEnabled() const; - unsigned int QueryAvailableBytes() const; + std::size_t QueryAvailableBytes() const; + + AbstractSocket& operator=(const AbstractSocket&) = delete; + AbstractSocket& operator=(AbstractSocket&& abstractSocket); // Signals: NazaraSignal(OnStateChange, const AbstractSocket* /*socket*/, SocketState /*newState*/); @@ -41,14 +44,14 @@ namespace Nz protected: AbstractSocket(SocketType type); - inline void UpdateState(SocketState newState); - virtual void OnClose(); virtual void OnOpened(); bool Open(NetProtocol protocol); void Open(SocketHandle existingHandle); + inline void UpdateState(SocketState newState); + NetProtocol m_protocol; SocketError m_lastError; SocketHandle m_handle; diff --git a/include/Nazara/Network/Enums.hpp b/include/Nazara/Network/Enums.hpp index 21c96dde4..32d9484ae 100644 --- a/include/Nazara/Network/Enums.hpp +++ b/include/Nazara/Network/Enums.hpp @@ -9,20 +9,15 @@ namespace Nz { - enum ResolveError + enum NetCode : UInt16 { - ResolveError_NoError, + NetCode_Acknowledge = 0x9A4E, + NetCode_AcknowledgeConnection = 0xC108, + NetCode_Ping = 0x96AC, + NetCode_Pong = 0x974C, + NetCode_RequestConnection = 0xF27D, - ResolveError_Internal, //< An internal error occured - ResolveError_ResourceError, //< The operating system lacks the resources to proceed (insufficient memory) - ResolveError_NonRecoverable, //< An nonrecoverable error occured - ResolveError_NotFound, //< No such host is known - ResolveError_NotInitialized, //< Nazara network has not been initialized - ResolveError_ProtocolNotSupported, //< A specified protocol is not supported by the server - ResolveError_TemporaryFailure, //< A temporary failure occured, try again - ResolveError_Unknown, //< The last operation failed with an unlisted error code - - ResolveError_Max = ResolveError_TemporaryFailure + NetCode_Invalid = 0x0000 }; enum NetProtocol @@ -35,25 +30,63 @@ namespace Nz NetProtocol_Max = NetProtocol_Unknown }; + enum PacketPriority + { + PacketPriority_High = 1, //< High-priority packet, will be sent quickly + PacketPriority_Immediate = 0, //< Immediate priority, will be sent immediately + PacketPriority_Medium = 2, //< Medium-priority packet, will be sent as regular + PacketPriority_Low = 3, //< Low-priority packet, may take some time to be sent + + PacketPriority_Lowest = PacketPriority_Low, + PacketPriority_Highest = PacketPriority_Immediate, + PacketPriority_Max = PacketPriority_Low + }; + + enum PacketReliability + { + PacketReliability_Reliable, //< Packet will be resent if lost + PacketReliability_ReliableOrdered, //< Packet will be resent if lost and will only arrive in order + PacketReliability_Unreliable, //< Packet won't be resent if lost + + PacketReliability_Max = PacketReliability_Unreliable + }; + + enum ResolveError + { + ResolveError_NoError, + + ResolveError_Internal, //< An internal error occurred + ResolveError_ResourceError, //< The operating system lacks the resources to proceed (insufficient memory) + ResolveError_NonRecoverable, //< An nonrecoverable error occurred + ResolveError_NotFound, //< No such host is known + ResolveError_NotInitialized, //< Nazara network has not been initialized + ResolveError_ProtocolNotSupported, //< A specified protocol is not supported by the server + ResolveError_TemporaryFailure, //< A temporary failure occurred, try again + ResolveError_Unknown, //< The last operation failed with an unlisted error code + + ResolveError_Max = ResolveError_Unknown + }; + enum SocketError { SocketError_NoError, - SocketError_AddressNotAvailable, //< The address is already in use (when binding/listening) - SocketError_ConnectionClosed, //< The connection has been closed - SocketError_ConnectionRefused, //< The connection attempt was refused - SocketError_DatagramSize, //< The datagram size is over the system limit - SocketError_Internal, //< The error is coming from the engine - SocketError_NetworkError, //< The network system has failed (maybe network is down) - SocketError_NotInitialized, //< Nazara network has not been initialized - SocketError_NotSupported, //< The operation is not supported (e.g. creating a bluetooth socket on a system without any bluetooth adaptater) - SocketError_ResolveError, //< The hostname couldn't be resolved (more information in ResolveError code) - SocketError_ResourceError, //< The operating system lacks the resources to proceed (e.g. memory/socket descriptor) - SocketError_UnreachableHost, //< The host is not reachable - SocketError_TimedOut, //< The operation timed out - SocketError_Unknown, //< The last operation failed with an unlisted error code - - SocketError_Max = SocketError_Unknown + SocketError_AddressNotAvailable, //< The address is already in use (when binding/listening) + SocketError_ConnectionClosed, //< The connection has been closed + SocketError_ConnectionRefused, //< The connection attempt was refused + SocketError_DatagramSize, //< The datagram size is over the system limit + SocketError_Internal, //< The error is coming from the engine + SocketError_Packet, //< The packet encoding/decoding failed, probably because of corrupted data + SocketError_NetworkError, //< The network system has failed (maybe network is down) + SocketError_NotInitialized, //< Nazara network has not been initialized + SocketError_NotSupported, //< The operation is not supported (e.g. creating a bluetooth socket on a system without any bluetooth adapter) + SocketError_ResolveError, //< The hostname couldn't be resolved (more information in ResolveError code) + SocketError_ResourceError, //< The operating system lacks the resources to proceed (e.g. memory/socket descriptor) + SocketError_TimedOut, //< The operation timed out + SocketError_Unknown, //< The last operation failed with an unlisted error code + SocketError_UnreachableHost, //< The host is not reachable + + SocketError_Max = SocketError_UnreachableHost }; enum SocketState @@ -62,9 +95,9 @@ namespace Nz SocketState_Connecting, //< The socket is currently connecting SocketState_Connected, //< The socket is currently connected SocketState_NotConnected, //< The socket is not connected (or has been disconnected) - SocketState_Resolving, //< The socket is currently resolving a hostname + SocketState_Resolving, //< The socket is currently resolving a host name - SocketState_Max = SocketState_NotConnected + SocketState_Max = SocketState_Resolving }; enum SocketType diff --git a/include/Nazara/Network/IpAddress.inl b/include/Nazara/Network/IpAddress.inl index 425fa6fee..c11c1879a 100644 --- a/include/Nazara/Network/IpAddress.inl +++ b/include/Nazara/Network/IpAddress.inl @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include @@ -211,4 +212,42 @@ namespace Nz } } +namespace std +{ + template<> + struct hash + { + size_t operator()(const Nz::IpAddress& ip) const + { + if (!ip) + return std::numeric_limits::max(); //< Returns a fixed value for invalid addresses + + // This is SDBM adapted for IP addresses, tested to generate the least collisions possible + // (It doesn't mean it cannot be improved though) + std::size_t hash = 0; + switch (ip.GetProtocol()) + { + case Nz::NetProtocol_Any: + case Nz::NetProtocol_Unknown: + return std::numeric_limits::max(); + + case Nz::NetProtocol_IPv4: + { + hash = ip.ToUInt32() + (hash << 6) + (hash << 16) - hash; + break; + } + case Nz::NetProtocol_IPv6: + { + Nz::IpAddress::IPv6 v6 = ip.ToIPv6(); + for (std::size_t i = 0; i < v6.size(); i++) + hash = v6[i] + (hash << 6) + (hash << 16) - hash; + + break; + } + } + + return ip.GetPort() + (hash << 6) + (hash << 16) - hash; + } + }; +} #include diff --git a/include/Nazara/Network/NetPacket.hpp b/include/Nazara/Network/NetPacket.hpp new file mode 100644 index 000000000..272808a33 --- /dev/null +++ b/include/Nazara/Network/NetPacket.hpp @@ -0,0 +1,75 @@ +// Copyright (C) 2015 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 + +#pragma once + +#ifndef NAZARA_NETPACKET_HPP +#define NAZARA_NETPACKET_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_NETWORK_API NetPacket : public ByteStream + { + friend class Network; + + public: + inline NetPacket(); + inline NetPacket(UInt16 netCode, std::size_t minCapacity = 0); + inline NetPacket(UInt16 netCode, const void* ptr, std::size_t size); + NetPacket(const NetPacket&) = delete; + NetPacket(NetPacket&& packet); + inline ~NetPacket(); + + inline const UInt8* GetConstData() const; + inline UInt8* GetData() const; + inline size_t GetDataSize() const; + inline UInt16 GetNetCode() const; + + virtual void OnReceive(UInt16 netCode, const void* data, std::size_t size); + virtual const void* OnSend(std::size_t* newSize) const; + + inline void Reset(); + inline void Reset(UInt16 netCode, std::size_t minCapacity = 0); + inline void Reset(UInt16 netCode, const void* ptr, std::size_t size); + + inline void Resize(std::size_t newSize); + + inline void SetNetCode(UInt16 netCode); + + NetPacket& operator=(const NetPacket&) = delete; + NetPacket& operator=(NetPacket&& packet); + + static bool DecodeHeader(const void* data, UInt16* packetSize, UInt16* netCode); + static bool EncodeHeader(void* data, UInt16 packetSize, UInt16 netCode); + + static constexpr std::size_t HeaderSize = sizeof(UInt16) + sizeof(UInt16); //< PacketSize + NetCode + + private: + void OnEmptyStream() override; + + void FreeStream(); + void InitStream(std::size_t minCapacity, UInt64 cursorPos, UInt32 openMode); + + static bool Initialize(); + static void Uninitialize(); + + std::unique_ptr m_buffer; + MemoryStream m_memoryStream; + UInt16 m_netCode; + + static std::unique_ptr s_availableBuffersMutex; + static std::vector>> s_availableBuffers; + }; +} + +#include + +#endif // NAZARA_NETPACKET_HPP diff --git a/include/Nazara/Network/NetPacket.inl b/include/Nazara/Network/NetPacket.inl new file mode 100644 index 000000000..c9bbed681 --- /dev/null +++ b/include/Nazara/Network/NetPacket.inl @@ -0,0 +1,121 @@ +// Copyright (C) 2015 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 +#include +#include + +namespace Nz +{ + inline NetPacket::NetPacket() : + m_netCode(NetCode_Invalid) + { + } + + inline NetPacket::NetPacket(UInt16 netCode, std::size_t minCapacity) + { + Reset(netCode, minCapacity); + } + + inline NetPacket::NetPacket(UInt16 netCode, const void* ptr, std::size_t size) + { + Reset(netCode, ptr, size); + } + + inline NetPacket::NetPacket(NetPacket&& packet) : + ByteStream(std::move(packet)), + m_buffer(std::move(packet.m_buffer)), + m_memoryStream(std::move(packet.m_memoryStream)), + m_netCode(packet.m_netCode) + { + ///< Redirect memory stream to the moved buffer + m_memoryStream.SetBuffer(m_buffer.get(), m_memoryStream.GetOpenMode()); + SetStream(&m_memoryStream); + } + + inline NetPacket::~NetPacket() + { + FlushBits(); //< Needs to be done here as the stream will be freed before ByteStream calls it + FreeStream(); + } + + inline const UInt8* NetPacket::GetConstData() const + { + NazaraAssert(m_buffer, "Invalid buffer"); + + return m_buffer->GetConstBuffer(); + } + + inline UInt8* NetPacket::GetData() const + { + NazaraAssert(m_buffer, "Invalid buffer"); + + return m_buffer->GetBuffer(); + } + + inline size_t NetPacket::GetDataSize() const + { + if (m_buffer) + return m_buffer->GetSize() - HeaderSize; + else + return 0; + } + + inline UInt16 NetPacket::GetNetCode() const + { + return m_netCode; + } + + inline void NetPacket::Reset() + { + FreeStream(); + } + + inline void NetPacket::Reset(UInt16 netCode, std::size_t minCapacity) + { + InitStream(HeaderSize + minCapacity, HeaderSize, OpenMode_ReadWrite); + m_netCode = netCode; + } + + inline void NetPacket::Reset(UInt16 netCode, const void* ptr, std::size_t size) + { + InitStream(HeaderSize + size, HeaderSize, OpenMode_ReadOnly); + m_buffer->Resize(HeaderSize + size); + std::memcpy(m_buffer->GetBuffer() + HeaderSize, ptr, size); + + m_netCode = netCode; + } + + inline void NetPacket::Resize(std::size_t newSize) + { + NazaraAssert(m_buffer, "Invalid buffer"); + + m_buffer->Resize(newSize); + } + + inline void NetPacket::SetNetCode(UInt16 netCode) + { + m_netCode = netCode; + } + + inline NetPacket& Nz::NetPacket::operator=(NetPacket&& packet) + { + FreeStream(); + + ByteStream::operator=(std::move(packet)); + + m_buffer = std::move(packet.m_buffer); + m_memoryStream = std::move(packet.m_memoryStream); + m_netCode = packet.m_netCode; + + ///< Redirect memory stream to the moved buffer + m_memoryStream.SetBuffer(m_buffer.get(), m_memoryStream.GetOpenMode()); + SetStream(&m_memoryStream); + + return *this; + } +} + +#include diff --git a/include/Nazara/Network/Network.hpp b/include/Nazara/Network/Network.hpp index 93b0e921f..16cf940d2 100644 --- a/include/Nazara/Network/Network.hpp +++ b/include/Nazara/Network/Network.hpp @@ -27,7 +27,7 @@ namespace Nz private: static unsigned int s_moduleReferenceCounter; - }; + }; } #endif // NAZARA_MODULENAME_HPP diff --git a/include/Nazara/Network/RUdpConnection.hpp b/include/Nazara/Network/RUdpConnection.hpp new file mode 100644 index 000000000..6166d787a --- /dev/null +++ b/include/Nazara/Network/RUdpConnection.hpp @@ -0,0 +1,166 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RUDPSERVER_HPP +#define NAZARA_RUDPSERVER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class RUdpClient; + + class NAZARA_NETWORK_API RUdpConnection + { + friend class Network; + + public: + using SequenceIndex = UInt16; + + RUdpConnection(); + RUdpConnection(const RUdpConnection&) = delete; + RUdpConnection(RUdpConnection&&) = default; + ~RUdpConnection() = default; + + inline void Close(); + + bool Connect(const IpAddress& remoteAddress); + bool Connect(const String& hostName, NetProtocol protocol = NetProtocol_Any, const String& service = "http", ResolveError* error = nullptr); + inline void Disconnect(); + + inline IpAddress GetBoundAddress() const; + inline UInt16 GetBoundPort() const; + inline SocketError GetLastError() const; + + inline bool Listen(NetProtocol protocol, UInt16 port = 64266); + bool Listen(const IpAddress& address); + + bool PollMessage(RUdpMessage* message); + + bool Send(const IpAddress& clientIp, PacketPriority priority, PacketReliability reliability, const NetPacket& packet); + + inline void SetProtocolId(UInt32 protocolId); + inline void SetTimeBeforeAck(UInt32 ms); + + inline void SimulateNetwork(double packetLoss); + + void Update(); + + RUdpConnection& operator=(const RUdpConnection&) = delete; + RUdpConnection& operator=(RUdpConnection&&) = default; + + static constexpr std::size_t MessageHeader = sizeof(UInt16) + 2 * sizeof(SequenceIndex) + sizeof(UInt32); //< Protocol ID (begin) + Sequence ID + Remote Sequence ID + Ack bitfield + static constexpr std::size_t MessageFooter = sizeof(UInt16); //< Protocol ID (end) + + // Signals: + NazaraSignal(OnConnectedToPeer, RUdpConnection* /*connection*/); + NazaraSignal(OnPeerAcknowledged, RUdpConnection* /*connection*/, const IpAddress& /*adress*/); + NazaraSignal(OnPeerConnection, RUdpConnection* /*connection*/, const IpAddress& /*adress*/); + NazaraSignal(OnPeerDisconnected, RUdpConnection* /*connection*/, const IpAddress& /*adress*/); + + private: + struct PeerData; + struct PendingAckPacket; + struct PendingPacket; + + enum PeerState + { + PeerState_Aknowledged, //< A connection request from this peer has been received, we're waiting for another packet to validate + PeerState_Connected, //< Connection is working in both-ways + PeerState_Connecting, //< A connection request has been made + PeerState_WillAck //< Connected, received one or more packets and has no packets to send, waiting before sending an empty ack packet + }; + + void DisconnectPeer(std::size_t peerIndex); + void EnqueuePacket(PeerData& peer, PacketPriority priority, PacketReliability reliability, const NetPacket& packet); + void EnqueuePacketInternal(PeerData& peer, PacketPriority priority, PacketReliability reliability, NetPacket&& data); + bool InitSocket(NetProtocol protocol); + void ProcessAcks(PeerData& peer, SequenceIndex lastAck, UInt32 ackBits); + PeerData& RegisterPeer(const IpAddress& address, PeerState state); + void OnClientRequestingConnection(const IpAddress& address, SequenceIndex sequenceId, UInt64 token); + void OnPacketLost(PeerData& peer, PendingAckPacket&& packet); + void OnPacketReceived(const IpAddress& peerIp, NetPacket&& packet); + void SendPacket(PeerData& peer, PendingPacket&& packet); + + static inline unsigned int ComputeSequenceDifference(SequenceIndex sequence, SequenceIndex sequence2); + static inline bool HasPendingPackets(PeerData& peer); + static bool Initialize(); + static inline bool IsAckMoreRecent(SequenceIndex ack, SequenceIndex ack2); + static inline bool IsReliable(PacketReliability reliability); + static void Uninitialize(); + + struct PendingPacket + { + PacketPriority priority; + PacketReliability reliability; + NetPacket data; + }; + + struct PendingAckPacket + { + PacketPriority priority; + PacketReliability reliability; + NetPacket data; + SequenceIndex sequenceId; + UInt64 timeSent; + }; + + struct PeerData //TODO: Move this to RUdpClient + { + PeerData() = default; + PeerData(PeerData&& other) = default; + PeerData& operator=(PeerData&& other) = default; + + std::array, PacketPriority_Max + 1> pendingPackets; + std::deque pendingAckQueue; + std::set receivedQueue; + std::size_t index; + PeerState state; + IpAddress address; + SequenceIndex localSequence; + SequenceIndex remoteSequence; + UInt32 roundTripTime; + UInt64 lastPacketTime; + UInt64 lastPingTime; + UInt64 stateData1; + }; + + std::bernoulli_distribution m_packetLossProbability; + std::queue m_receivedMessages; + std::size_t m_peerIterator; + std::unordered_map m_peerByIP; + std::vector m_peers; + Bitset m_activeClients; + Clock m_clock; + SocketError m_lastError; + UdpSocket m_socket; + UInt32 m_forceAckSendTime; + UInt32 m_pingInterval; + UInt32 m_protocol; + UInt32 m_timeBeforePing; + UInt32 m_timeBeforeTimeOut; + UInt64 m_currentTime; + bool m_isSimulationEnabled; + bool m_shouldAcceptConnections; + + static std::mt19937_64 s_randomGenerator; + }; +} + +#include + +#endif // NAZARA_RUDPSERVER_HPP diff --git a/include/Nazara/Network/RUdpConnection.inl b/include/Nazara/Network/RUdpConnection.inl new file mode 100644 index 000000000..b2372285d --- /dev/null +++ b/include/Nazara/Network/RUdpConnection.inl @@ -0,0 +1,139 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline void RUdpConnection::Close() + { + m_socket.Close(); + } + + inline void RUdpConnection::Disconnect() + { + Close(); + } + + inline IpAddress RUdpConnection::GetBoundAddress() const + { + return m_socket.GetBoundAddress(); + } + + inline UInt16 RUdpConnection::GetBoundPort() const + { + return m_socket.GetBoundPort(); + } + + inline SocketError RUdpConnection::GetLastError() const + { + return m_lastError; + } + + inline bool RUdpConnection::Listen(NetProtocol protocol, UInt16 port) + { + NazaraAssert(protocol != NetProtocol_Any, "Any protocol not supported for Listen"); //< TODO + NazaraAssert(protocol != NetProtocol_Unknown, "Invalid protocol"); + + IpAddress any; + switch (protocol) + { + case NetProtocol_Any: + case NetProtocol_Unknown: + NazaraInternalError("Invalid protocol Any at this point"); + return false; + + case NetProtocol_IPv4: + any = IpAddress::AnyIpV4; + break; + + case NetProtocol_IPv6: + any = IpAddress::AnyIpV6; + break; + } + + any.SetPort(port); + return Listen(any); + } + + inline void RUdpConnection::SetProtocolId(UInt32 protocolId) + { + m_protocol = protocolId; + } + + inline void RUdpConnection::SetTimeBeforeAck(UInt32 ms) + { + m_forceAckSendTime = ms * 1000; //< Store in microseconds for easier handling + } + + inline unsigned int RUdpConnection::ComputeSequenceDifference(SequenceIndex sequence, SequenceIndex sequence2) + { + unsigned int difference; + if (sequence2 > sequence) + difference = std::numeric_limits::max() - sequence2 + sequence; + else + difference = sequence - sequence2; + + return 0; + } + + inline bool RUdpConnection::HasPendingPackets(PeerData& peer) + { + for (unsigned int priority = PacketPriority_Highest; priority <= PacketPriority_Lowest; ++priority) + { + std::vector& pendingPackets = peer.pendingPackets[priority]; + if (!pendingPackets.empty()) + return true; + + pendingPackets.clear(); + } + + return false; + } + + inline bool RUdpConnection::IsAckMoreRecent(SequenceIndex ack, SequenceIndex ack2) + { + constexpr SequenceIndex maxDifference = std::numeric_limits::max() / 2; + + if (ack > ack2) + return ack - ack2 <= maxDifference; + else if (ack2 > ack) + return ack2 - ack > maxDifference; + else + return false; ///< Same ack + } + + inline bool RUdpConnection::IsReliable(PacketReliability reliability) + { + switch (reliability) + { + case PacketReliability_Reliable: + case PacketReliability_ReliableOrdered: + return true; + + case PacketReliability_Unreliable: + return false; + } + + NazaraError("PacketReliability not handled (0x" + String::Number(reliability, 16) + ')'); + return false; + } + + inline void RUdpConnection::SimulateNetwork(double packetLoss) + { + NazaraAssert(packetLoss >= 0.0 && packetLoss <= 1.0, "Packet loss must be in range [0..1]"); + + if (packetLoss > 0.0) + { + m_isSimulationEnabled = true; + m_packetLossProbability = std::bernoulli_distribution(packetLoss); + } + else + m_isSimulationEnabled = false; + } +} + +#include diff --git a/include/Nazara/Network/RUdpMessage.hpp b/include/Nazara/Network/RUdpMessage.hpp new file mode 100644 index 000000000..5d4fdb4a5 --- /dev/null +++ b/include/Nazara/Network/RUdpMessage.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RUDMESSAGE_HPP +#define NAZARA_RUDMESSAGE_HPP + +#include +#include +#include + +namespace Nz +{ + struct RUdpMessage + { + IpAddress from; + NetPacket data; + }; +} + +#endif // NAZARA_RUDMESSAGE_HPP \ No newline at end of file diff --git a/include/Nazara/Network/TcpClient.hpp b/include/Nazara/Network/TcpClient.hpp index f06a43836..fda4671f9 100644 --- a/include/Nazara/Network/TcpClient.hpp +++ b/include/Nazara/Network/TcpClient.hpp @@ -8,6 +8,7 @@ #define NAZARA_TCPCLIENT_HPP #include +#include #include #include #include @@ -15,13 +16,15 @@ namespace Nz { + class NetPacket; + class NAZARA_NETWORK_API TcpClient : public AbstractSocket, public Stream { friend class TcpServer; public: inline TcpClient(); - inline TcpClient(TcpClient&& tcpClient); + TcpClient(TcpClient&& tcpClient) = default; ~TcpClient() = default; SocketState Connect(const IpAddress& remoteAddress); @@ -43,13 +46,17 @@ namespace Nz inline bool IsKeepAliveEnabled() const; bool Receive(void* buffer, std::size_t size, std::size_t* received); + bool ReceivePacket(NetPacket* packet); bool Send(const void* buffer, std::size_t size, std::size_t* sent); + bool SendPacket(const NetPacket& packet); bool SetCursorPos(UInt64 offset) override; bool WaitForConnected(UInt64 msTimeout = 3000); + inline TcpClient& operator=(TcpClient&& tcpClient) = default; + private: void FlushStream() override; @@ -60,7 +67,16 @@ namespace Nz void Reset(SocketHandle handle, const IpAddress& peerAddress); std::size_t WriteBlock(const void* buffer, std::size_t size) override; + struct PendingPacket + { + std::size_t received = 0; + ByteArray data; + UInt16 netcode; + bool headerReceived = false; + }; + IpAddress m_peerAddress; + PendingPacket m_pendingPacket; UInt64 m_keepAliveInterval; UInt64 m_keepAliveTime; bool m_isLowDelayEnabled; @@ -70,4 +86,4 @@ namespace Nz #include -#endif // NAZARA_TCPCLIENT_HPP \ No newline at end of file +#endif // NAZARA_TCPCLIENT_HPP diff --git a/include/Nazara/Network/TcpClient.inl b/include/Nazara/Network/TcpClient.inl index 5348d2152..da292b982 100644 --- a/include/Nazara/Network/TcpClient.inl +++ b/include/Nazara/Network/TcpClient.inl @@ -17,17 +17,6 @@ namespace Nz { } - inline TcpClient::TcpClient(TcpClient&& tcpClient) : - AbstractSocket(std::move(tcpClient)), - Stream(std::move(tcpClient)), - m_peerAddress(std::move(tcpClient.m_peerAddress)), - m_keepAliveInterval(tcpClient.m_keepAliveInterval), - m_keepAliveTime(tcpClient.m_keepAliveTime), - m_isLowDelayEnabled(tcpClient.m_isLowDelayEnabled), - m_isKeepAliveEnabled(tcpClient.m_isKeepAliveEnabled) - { - } - inline void TcpClient::Disconnect() { Close(); diff --git a/include/Nazara/Network/UdpSocket.hpp b/include/Nazara/Network/UdpSocket.hpp index e6577bec7..96c391436 100644 --- a/include/Nazara/Network/UdpSocket.hpp +++ b/include/Nazara/Network/UdpSocket.hpp @@ -13,6 +13,8 @@ namespace Nz { + class NetPacket; + class NAZARA_NETWORK_API UdpSocket : public AbstractSocket { public: @@ -34,11 +36,13 @@ namespace Nz inline bool IsBroadcastingEnabled() const; - unsigned int QueryMaxDatagramSize(); + std::size_t QueryMaxDatagramSize(); bool Receive(void* buffer, std::size_t size, IpAddress* from, std::size_t* received); + bool ReceivePacket(NetPacket* packet, IpAddress* from); bool Send(const IpAddress& to, const void* buffer, std::size_t size, std::size_t* sent); + bool SendPacket(const IpAddress& to, const NetPacket& packet); private: void OnClose() override; diff --git a/include/Nazara/Physics/Geom.hpp b/include/Nazara/Physics/Geom.hpp index 3db143292..34b6c58ba 100644 --- a/include/Nazara/Physics/Geom.hpp +++ b/include/Nazara/Physics/Geom.hpp @@ -39,6 +39,9 @@ namespace Nz class NAZARA_PHYSICS_API PhysGeom : public RefCounted { + friend PhysGeomLibrary; + friend class Physics; + public: PhysGeom() = default; PhysGeom(const PhysGeom&) = delete; @@ -64,6 +67,9 @@ namespace Nz protected: virtual NewtonCollision* CreateHandle(PhysWorld* world) const = 0; + static bool Initialize(); + static void Uninitialize(); + mutable std::unordered_map m_handles; static PhysGeomLibrary::LibraryMap s_library; @@ -225,7 +231,7 @@ namespace Nz public: NullGeom(); - void ComputeInertialMatrix(Vector3f* inertia, Vector3f* center) const; + void ComputeInertialMatrix(Vector3f* inertia, Vector3f* center) const override; GeomType GetType() const override; diff --git a/include/Nazara/Renderer/OpenGL.hpp b/include/Nazara/Renderer/OpenGL.hpp index a9cf7d4b4..1af17ff99 100644 --- a/include/Nazara/Renderer/OpenGL.hpp +++ b/include/Nazara/Renderer/OpenGL.hpp @@ -153,7 +153,6 @@ namespace Nz static void OnContextChanged(const Context* newContext); static void OnContextDestruction(const Context* context); }; -} NAZARA_RENDERER_API extern PFNGLACTIVETEXTUREPROC glActiveTexture; NAZARA_RENDERER_API extern PFNGLATTACHSHADERPROC glAttachShader; @@ -337,6 +336,8 @@ NAZARA_RENDERER_API extern GLX::PFNGLXSWAPINTERVALMESAPROC NzglXSwapInter NAZARA_RENDERER_API extern GLX::PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; #endif +} + #endif // NAZARA_RENDERER_OPENGL #endif // NAZARA_OPENGL_HPP diff --git a/include/Nazara/Renderer/RenderTexture.hpp b/include/Nazara/Renderer/RenderTexture.hpp index c197f50c0..798e441ee 100644 --- a/include/Nazara/Renderer/RenderTexture.hpp +++ b/include/Nazara/Renderer/RenderTexture.hpp @@ -43,12 +43,12 @@ namespace Nz void Detach(AttachmentPoint attachmentPoint, UInt8 index); unsigned int GetHeight() const override; - RenderTargetParameters GetParameters() const; + RenderTargetParameters GetParameters() const override; Vector2ui GetSize() const; unsigned int GetWidth() const override; bool IsComplete() const; - bool IsRenderable() const; + bool IsRenderable() const override; inline bool IsValid() const; bool Lock() const; diff --git a/include/Nazara/Renderer/RenderWindow.hpp b/include/Nazara/Renderer/RenderWindow.hpp index 0e283151b..d9889d803 100644 --- a/include/Nazara/Renderer/RenderWindow.hpp +++ b/include/Nazara/Renderer/RenderWindow.hpp @@ -46,11 +46,11 @@ namespace Nz void EnableVerticalSync(bool enabled); - unsigned int GetHeight() const; - RenderTargetParameters GetParameters() const; - unsigned int GetWidth() const; + unsigned int GetHeight() const override; + RenderTargetParameters GetParameters() const override; + unsigned int GetWidth() const override; - bool IsRenderable() const; + bool IsRenderable() const override; bool IsValid() const; void SetFramerateLimit(unsigned int limit); diff --git a/include/Nazara/Renderer/Texture.hpp b/include/Nazara/Renderer/Texture.hpp index 15f3c11ec..73069aa37 100644 --- a/include/Nazara/Renderer/Texture.hpp +++ b/include/Nazara/Renderer/Texture.hpp @@ -22,7 +22,6 @@ namespace Nz { - class Texture; using TextureConstRef = ObjectRef; @@ -94,6 +93,10 @@ namespace Nz bool LoadFaceFromMemory(CubemapFace face, const void* data, std::size_t size, const ImageParams& params = ImageParams()); bool LoadFaceFromStream(CubemapFace face, Stream& stream, const ImageParams& params = ImageParams()); + // Save + bool SaveToFile(const String& filePath, const ImageParams& params = ImageParams()); + bool SaveToStream(Stream& stream, const String& format, const ImageParams& params = ImageParams()); + bool SetMipmapRange(UInt8 minLevel, UInt8 maxLevel); bool Update(const Image& image, UInt8 level = 0); diff --git a/include/Nazara/Utility/Animation.hpp b/include/Nazara/Utility/Animation.hpp index 162d86e35..e196a0f77 100644 --- a/include/Nazara/Utility/Animation.hpp +++ b/include/Nazara/Utility/Animation.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,7 @@ namespace Nz { - struct NAZARA_UTILITY_API AnimationParams + struct NAZARA_UTILITY_API AnimationParams : ResourceParameters { // La frame de fin à charger unsigned int endFrame = std::numeric_limits::max(); diff --git a/include/Nazara/Utility/Font.hpp b/include/Nazara/Utility/Font.hpp index ef801d5a2..58ff341b5 100644 --- a/include/Nazara/Utility/Font.hpp +++ b/include/Nazara/Utility/Font.hpp @@ -14,13 +14,14 @@ #include #include #include +#include #include #include #include namespace Nz { - struct NAZARA_UTILITY_API FontParams + struct NAZARA_UTILITY_API FontParams : ResourceParameters { bool IsValid() const; }; diff --git a/include/Nazara/Utility/Formats/OBJParser.hpp b/include/Nazara/Utility/Formats/OBJParser.hpp index 3decd248f..90e653f93 100644 --- a/include/Nazara/Utility/Formats/OBJParser.hpp +++ b/include/Nazara/Utility/Formats/OBJParser.hpp @@ -54,7 +54,7 @@ namespace Nz const Vector3f* GetTexCoords() const; unsigned int GetTexCoordCount() const; - bool Parse(); + bool Parse(std::size_t reservedVertexCount = 100); private: bool Advance(bool required = true); diff --git a/include/Nazara/Utility/Image.hpp b/include/Nazara/Utility/Image.hpp index ed9772a88..cefc606af 100644 --- a/include/Nazara/Utility/Image.hpp +++ b/include/Nazara/Utility/Image.hpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -25,7 +27,7 @@ namespace Nz { - struct NAZARA_UTILITY_API ImageParams + struct NAZARA_UTILITY_API ImageParams : ResourceParameters { // Le format dans lequel l'image doit être chargée (Undefined pour le format le plus proche de l'original) PixelFormatType loadFormat = PixelFormatType_Undefined; @@ -43,12 +45,14 @@ namespace Nz using ImageLoader = ResourceLoader; using ImageManager = ResourceManager; using ImageRef = ObjectRef; + using ImageSaver = ResourceSaver; class NAZARA_UTILITY_API Image : public AbstractImage, public RefCounted, public Resource { friend ImageLibrary; friend ImageLoader; friend ImageManager; + friend ImageSaver; friend class Utility; public: @@ -107,6 +111,17 @@ namespace Nz bool LoadCubemapFromMemory(const void* data, std::size_t size, const ImageParams& imageParams = ImageParams(), const CubemapParams& cubemapParams = CubemapParams()); bool LoadCubemapFromStream(Stream& stream, const ImageParams& imageParams = ImageParams(), const CubemapParams& cubemapParams = CubemapParams()); + // LoadFace + bool LoadFaceFromFile(CubemapFace face, const String& filePath, const ImageParams& params = ImageParams()); + bool LoadFaceFromMemory(CubemapFace face, const void* data, std::size_t size, const ImageParams& params = ImageParams()); + bool LoadFaceFromStream(CubemapFace face, Stream& stream, const ImageParams& params = ImageParams()); + + // Save + bool SaveToFile(const String& filePath, const ImageParams& params = ImageParams()); + bool SaveToStream(Stream& stream, const String& format, const ImageParams& params = ImageParams()); + + //TODO: SaveArray, SaveCubemap, SaveFace + void SetLevelCount(UInt8 levelCount); bool SetPixelColor(const Color& color, unsigned int x, unsigned int y = 0, unsigned int z = 0); @@ -165,6 +180,7 @@ namespace Nz static ImageLoader::LoaderList s_loaders; static ImageManager::ManagerMap s_managerMap; static ImageManager::ManagerParams s_managerParameters; + static ImageSaver::SaverList s_savers; }; } diff --git a/include/Nazara/Utility/Mesh.hpp b/include/Nazara/Utility/Mesh.hpp index 17857704a..433f50ab5 100644 --- a/include/Nazara/Utility/Mesh.hpp +++ b/include/Nazara/Utility/Mesh.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,7 @@ namespace Nz { - struct NAZARA_UTILITY_API MeshParams + struct NAZARA_UTILITY_API MeshParams : ResourceParameters { MeshParams(); // Vérifie que le storage par défaut est supporté (software autrement) diff --git a/include/Nazara/Utility/SimpleTextDrawer.hpp b/include/Nazara/Utility/SimpleTextDrawer.hpp index 22b52f293..3f87bf3bb 100644 --- a/include/Nazara/Utility/SimpleTextDrawer.hpp +++ b/include/Nazara/Utility/SimpleTextDrawer.hpp @@ -24,7 +24,11 @@ namespace Nz SimpleTextDrawer(SimpleTextDrawer&& drawer); virtual ~SimpleTextDrawer(); - const Rectui& GetBounds() const; + void AppendText(const String& str); + + void Clear(); + + const Rectui& GetBounds() const override; unsigned int GetCharacterSize() const; const Color& GetColor() const; Font* GetFont() const; @@ -48,11 +52,14 @@ namespace Nz static SimpleTextDrawer Draw(Font* font, const String& str, unsigned int characterSize, UInt32 style = TextStyle_Regular, const Color& color = Color::White); private: + void ClearGlyphs() const; void ConnectFontSlots(); void DisconnectFontSlots(); + void GenerateGlyphs(const String& text) const; void OnFontAtlasLayerChanged(const Font* font, AbstractImage* oldLayer, AbstractImage* newLayer); void OnFontInvalidated(const Font* font); void OnFontRelease(const Font* object); + void UpdateGlyphColor() const; void UpdateGlyphs() const; NazaraSlot(Font, OnFontAtlasChanged, m_atlasChangedSlot); @@ -63,9 +70,13 @@ namespace Nz mutable std::vector m_glyphs; Color m_color; FontRef m_font; + mutable Rectf m_workingBounds; mutable Rectui m_bounds; String m_text; + mutable UInt32 m_previousCharacter; UInt32 m_style; + mutable Vector2ui m_drawPos; + mutable bool m_colorUpdated; mutable bool m_glyphUpdated; unsigned int m_characterSize; }; diff --git a/include/Nazara/Utility/Window.hpp b/include/Nazara/Utility/Window.hpp index 75f066bd8..af23be7f1 100644 --- a/include/Nazara/Utility/Window.hpp +++ b/include/Nazara/Utility/Window.hpp @@ -38,14 +38,14 @@ namespace Nz friend class Utility; public: - Window(); - Window(VideoMode mode, const String& title, UInt32 style = WindowStyle_Default); - Window(WindowHandle handle); + inline Window(); + inline Window(VideoMode mode, const String& title, UInt32 style = WindowStyle_Default); + inline Window(WindowHandle handle); Window(const Window&) = delete; - Window(Window&&) = delete; ///TODO + inline Window(Window&& window) noexcept; virtual ~Window(); - void Close(); + inline void Close(); bool Create(VideoMode mode, const String& title, UInt32 style = WindowStyle_Default); bool Create(WindowHandle handle); @@ -66,9 +66,9 @@ namespace Nz bool HasFocus() const; bool IsMinimized() const; - bool IsOpen(bool checkClosed = true); - bool IsOpen() const; - bool IsValid() const; + inline bool IsOpen(bool checkClosed = true); + inline bool IsOpen() const; + inline bool IsValid() const; bool IsVisible() const; bool PollEvent(WindowEvent* event); @@ -93,7 +93,7 @@ namespace Nz bool WaitEvent(WindowEvent* event); Window& operator=(const Window&) = delete; - Window& operator=(Window&&) = delete; ///TODO + inline Window& operator=(Window&& window); protected: virtual bool OnWindowCreated(); @@ -104,7 +104,7 @@ namespace Nz private: void IgnoreNextMouseEvent(int mouseX, int mouseY) const; - void PushEvent(const WindowEvent& event); + inline void PushEvent(const WindowEvent& event); static bool Initialize(); static void Uninitialize(); @@ -122,4 +122,6 @@ namespace Nz }; } +#include + #endif // NAZARA_WINDOW_HPP diff --git a/include/Nazara/Utility/Window.inl b/include/Nazara/Utility/Window.inl new file mode 100644 index 000000000..ebf5dfbee --- /dev/null +++ b/include/Nazara/Utility/Window.inl @@ -0,0 +1,154 @@ +// Copyright (C) 2015 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 +#include + +namespace Nz +{ + /*! + * \class Nz::Window + */ + + inline Window::Window() : + #if NAZARA_UTILITY_THREADED_WINDOW + m_impl(nullptr), + m_eventListener(true), + m_waitForEvent(false) + #else + m_impl(nullptr) + #endif + { + } + + inline Window::Window(VideoMode mode, const String& title, UInt32 style) : + #if NAZARA_UTILITY_THREADED_WINDOW + m_impl(nullptr), + m_eventListener(true), + m_waitForEvent(false) + #else + m_impl(nullptr) + #endif + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + Create(mode, title, style); + } + + inline Window::Window(WindowHandle handle) : + #if NAZARA_UTILITY_THREADED_WINDOW + m_impl(nullptr), + m_eventListener(true), + m_waitForEvent(false) + #else + m_impl(nullptr) + #endif + { + ErrorFlags flags(ErrorFlag_ThrowException, true); + Create(handle); + } + + /*! + * \brief Constructs a Window object by moving another one + */ + inline Window::Window(Window&& window) noexcept : + m_impl(window.m_impl), + m_events(std::move(window.m_events)), + #if NAZARA_UTILITY_THREADED_WINDOW + m_eventCondition(std::move(window.m_eventCondition)), + m_eventMutex(std::move(window.m_eventMutex)), + m_eventConditionMutex(std::move(window.m_eventConditionMutex)), + m_eventListener(window.m_eventListener), + m_waitForEvent(window.m_waitForEvent), + #endif + m_closed(window.m_closed), + m_ownsWindow(window.m_ownsWindow) + { + window.m_impl = nullptr; + } + + inline Window::~Window() + { + Destroy(); + } + + inline void Window::Close() + { + m_closed = true; // The window will be closed at the next non-const IsOpen() call + } + + inline bool Window::IsOpen(bool checkClosed) + { + if (!m_impl) + return false; + + if (checkClosed && m_closed) + { + Destroy(); + return false; + } + + return true; + } + + inline bool Window::IsOpen() const + { + return m_impl != nullptr; + } + + inline bool Window::IsValid() const + { + return m_impl != nullptr; + } + + inline void Window::PushEvent(const WindowEvent& event) + { + #if NAZARA_UTILITY_THREADED_WINDOW + m_eventMutex.Lock(); + #endif + + m_events.push(event); + if (event.type == WindowEventType_Resized) + OnWindowResized(); + + #if NAZARA_UTILITY_THREADED_WINDOW + m_eventMutex.Unlock(); + + if (m_waitForEvent) + { + m_eventConditionMutex.Lock(); + m_eventCondition.Signal(); + m_eventConditionMutex.Unlock(); + } + #endif + } + + /*! + * \brief Moves a window to another window object + * \return A reference to the object + */ + inline Window& Window::operator=(Window&& window) + { + Destroy(); + + m_closed = window.m_closed; + m_impl = window.m_impl; + m_events = std::move(window.m_events); + m_ownsWindow = window.m_ownsWindow; + + window.m_impl = nullptr; + + #if NAZARA_UTILITY_THREADED_WINDOW + m_eventCondition = std::move(window.m_eventCondition); + m_eventMutex = std::move(window.m_eventMutex); + m_eventConditionMutex = std::move(window.m_eventConditionMutex); + m_eventListener = window.m_eventListener; + m_waitForEvent = window.m_waitForEvent; + #endif + + return *this; + } +} + +#include diff --git a/readme.md b/readme.md index 759f901e9..90a30fda5 100644 --- a/readme.md +++ b/readme.md @@ -22,13 +22,13 @@ Use the premake build system in the build directory then compile the engine for How to use ---------- -You can find tutorials on installation, compilation and use on the [official wiki](http://wiki.digitalpulsesoftware.com/index.php?title=Nazara:Tutorials) (**\*Broken link***) +You can find tutorials on installation, compilation and use on the [official wiki](https://github.com/DigitalPulseSoftware/NazaraEngine/wiki) Contribute ---------- #####Don't hesitate to contribute to Nazara Engine by:##### -- Extending the [wiki](http://wiki.digitalpulsesoftware.com/index.php?title=Nazara) (**\*Broken link***) +- Extending the [wiki](https://github.com/DigitalPulseSoftware/NazaraEngine/wiki) - Submitting a patch to GitHub - Post suggestions/bugs on the forum or the [GitHub tracker](https://github.com/DigitalPulseSoftware/NazaraEngine/issues) - [Fork the project](https://github.com/DigitalPulseSoftware/NazaraEngine/fork) on GitHub and [push your changes](https://github.com/DigitalPulseSoftware/NazaraEngine/pulls) @@ -37,12 +37,12 @@ Contribute Links ----- -[Website](http://www.digitalpulsesoftware.com) -[Wiki](http://wiki.digitalpulsesoftware.com/index.php?title=Nazara) (**\*Broken link***) -[Forum](http://forum.digitalpulsesoftware.com) +[Website](http://www.digitalpulsesoftware.net) +[Wiki](https://github.com/DigitalPulseSoftware/NazaraEngine/wiki) +[Forum](http://forum.digitalpulsesoftware.net) ###Thanks to:### - **RafBill** and **Raakz:** Finding bugs and/or testing - **Fissal "DrFisher" Hannoun**: Helping a lot in architecture design - **Alexandre "Danman" Janniaux**: Helping making the POSIX implementation -- **Gawaboumga**: Improving the engine code by merging on GitHub +- **Youri "Gawaboumga" Hubaut**: Improving the engine code by merging on GitHub diff --git a/readme_fr.md b/readme_fr.md index fd8aaaf91..fd0f42e7b 100644 --- a/readme_fr.md +++ b/readme_fr.md @@ -22,13 +22,13 @@ Utilisez le système premake pour construire le projet du moteur, dans le dossie Utilisation ----------- -Vous pouvez lire des tutoriaux sur l'installation, la compilation et l'utilisation sur le [wiki officiel](http://wiki.digitalpulsesoftware.com/index.php?title=Nazara:Tutorials) (**\*Lien brisé***) +Vous pouvez lire des tutoriaux sur l'installation, la compilation et l'utilisation sur le [wiki officiel](https://github.com/DigitalPulseSoftware/NazaraEngine/wiki) (**\*En cours de rédaction***) Contribution ---------- #####N'hésitez pas à contribuer à Nazara Engine en :##### -- Contribuant au [wiki](http://wiki.digitalpulsesoftware.com/index.php?title=Nazara) (**\*Lien brisé***) +- Contribuant au [wiki](https://github.com/DigitalPulseSoftware/NazaraEngine/wiki) (**\*Lien brisé***) - Soumettant un patch sur GitHub - Postant des suggestions/bugs sur le forum ou sur le [tracker GitHub](https://github.com/DigitalPulseSoftware/NazaraEngine/issues) - Faisant un [fork du projet](https://github.com/DigitalPulseSoftware/NazaraEngine/fork) sur GitHub et en [proposant vos changements](https://github.com/DigitalPulseSoftware/NazaraEngine/pulls) @@ -37,12 +37,12 @@ Contribution Liens ----- -[Website](http://www.digitalpulsesoftware.com) -[Wiki](http://wiki.digitalpulsesoftware.com/index.php?title=Nazara) (**\*Lien brisé***) -[Forum](http://forum.digitalpulsesoftware.com) +[Website](http://www.digitalpulsesoftware.net) +[Wiki](https://github.com/DigitalPulseSoftware/NazaraEngine/wiki) +[Forum](http://forum.digitalpulsesoftware.net) ###Remerciements:### - **RafBill** et **Raakz:** Recherche de bugs et/ou tests - **Fissal "DrFisher" Hannoun**: Aide et conseils lors de la conception de l'architecture du moteur - **Alexandre "Danman" Janniaux**: Aide sur l'implémentation POSIX -- **Gawaboumga**: Amélioration du code via le merging sur GitHub +- **Youri "Gawaboumga" Hubaut**: Amélioration du code via le merging sur GitHub diff --git a/src/Nazara/Audio/Formats/sndfileLoader.cpp b/src/Nazara/Audio/Formats/sndfileLoader.cpp index 4cf99fee7..2aca17976 100644 --- a/src/Nazara/Audio/Formats/sndfileLoader.cpp +++ b/src/Nazara/Audio/Formats/sndfileLoader.cpp @@ -180,7 +180,7 @@ namespace Nz return true; } - unsigned int Read(void* buffer, unsigned int sampleCount) + unsigned int Read(void* buffer, unsigned int sampleCount) override { // Si la musique a été demandée en mono, nous devons la convertir à la volée lors de la lecture if (m_mixToMono) diff --git a/src/Nazara/Audio/Music.cpp b/src/Nazara/Audio/Music.cpp index 8ff8e2a7f..aba84b5ff 100644 --- a/src/Nazara/Audio/Music.cpp +++ b/src/Nazara/Audio/Music.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,9 @@ namespace Nz ALenum audioFormat; std::unique_ptr stream; std::vector chunkSamples; + Mutex bufferLock; Thread thread; + UInt64 processedSamples; bool loop = false; bool streaming = false; unsigned int sampleRate; @@ -36,15 +39,9 @@ namespace Nz bool Music::Create(SoundStream* soundStream) { - Destroy(); + NazaraAssert(soundStream, "Invalid stream"); - #if NAZARA_AUDIO_SAFE - if (!soundStream) - { - NazaraError("Sound stream must be valid"); - return false; - } - #endif + Destroy(); AudioFormat format = soundStream->GetFormat(); @@ -54,6 +51,8 @@ namespace Nz m_impl->chunkSamples.resize(format * m_impl->sampleRate); // Une seconde de samples m_impl->stream.reset(soundStream); + SetPlayingOffset(0); + return true; } @@ -116,9 +115,14 @@ namespace Nz return 0; } #endif + + // Prevent music thread from enqueing new buffers while we're getting the count + Nz::LockGuard lock(m_impl->bufferLock); - ///TODO - return 0; + ALint samples = 0; + alGetSourcei(m_source, AL_SAMPLE_OFFSET, &samples); + + return (1000ULL * (samples + (m_impl->processedSamples / m_impl->stream->GetFormat()))) / m_impl->sampleRate; } UInt32 Music::GetSampleCount() const @@ -144,7 +148,7 @@ namespace Nz } #endif - return m_impl->stream->GetSampleRate(); + return m_impl->sampleRate; } SoundStatus Music::GetStatus() const @@ -209,22 +213,29 @@ namespace Nz } #endif - // Nous sommes déjà en train de jouer + // Maybe we are already playing if (m_impl->streaming) { - // Peut-être sommes-nous en pause - if (GetStatus() != SoundStatus_Playing) - alSourcePlay(m_source); + switch (GetStatus()) + { + case SoundStatus_Playing: + SetPlayingOffset(0); + break; - return; + case SoundStatus_Paused: + alSourcePlay(m_source); + break; + + default: + break; // We shouldn't be stopped + } + } + else + { + // Starting streaming's thread + m_impl->streaming = true; + m_impl->thread = Thread(&Music::MusicThread, this); } - - // Lancement du thread de streaming - m_impl->stream->Seek(0); - m_impl->streaming = true; - m_impl->thread = Thread(&Music::MusicThread, this); - - return; } void Music::SetPlayingOffset(UInt32 offset) @@ -237,7 +248,16 @@ namespace Nz } #endif - ///TODO + bool isPlaying = m_impl->streaming; + + if (isPlaying) + Stop(); + + m_impl->stream->Seek(offset); + m_impl->processedSamples = UInt64(offset) * m_impl->sampleRate * m_impl->stream->GetFormat() / 1000ULL; + + if (isPlaying) + Play(); } void Music::Stop() @@ -262,17 +282,22 @@ namespace Nz unsigned int sampleCount = m_impl->chunkSamples.size(); unsigned int sampleRead = 0; - // Lecture depuis le stream pour remplir le buffer + // Fill the buffer by reading from the stream for (;;) { sampleRead += m_impl->stream->Read(&m_impl->chunkSamples[sampleRead], sampleCount - sampleRead); - if (sampleRead < sampleCount && !m_impl->loop) - break; // Fin du stream (On ne boucle pas) + if (sampleRead < sampleCount && m_impl->loop) + { + // In case we read less than expected, assume we reached the end of the stream and seek back to the beginning + m_impl->stream->Seek(0); + continue; + } - m_impl->stream->Seek(0); // On boucle au début du stream et on remplit à nouveau + // Either we read the size we wanted, either we're not looping + break; } - // Mise à jour du buffer (envoi à OpenAL) et placement dans la file d'attente + // Update the buffer (send it to OpenAL) and queue it if we got any data if (sampleRead > 0) { alBufferData(buffer, m_impl->audioFormat, &m_impl->chunkSamples[0], sampleRead*sizeof(Int16), m_impl->sampleRate); @@ -307,18 +332,29 @@ namespace Nz break; } + Nz::LockGuard lock(m_impl->bufferLock); + // On traite les buffers lus ALint processedCount = 0; alGetSourcei(m_source, AL_BUFFERS_PROCESSED, &processedCount); - - ALuint buffer; while (processedCount--) { + ALuint buffer; alSourceUnqueueBuffers(m_source, 1, &buffer); + + ALint bits, size; + alGetBufferi(buffer, AL_BITS, &bits); + alGetBufferi(buffer, AL_SIZE, &size); + + if (bits != 0) + m_impl->processedSamples += (8 * size) / bits; + if (FillAndQueueBuffer(buffer)) break; } + lock.Unlock(); + // On retourne dormir un peu Thread::Sleep(50); } diff --git a/src/Nazara/Audio/OpenAL.cpp b/src/Nazara/Audio/OpenAL.cpp index 72a56f7a7..f527b682d 100644 --- a/src/Nazara/Audio/OpenAL.cpp +++ b/src/Nazara/Audio/OpenAL.cpp @@ -23,14 +23,14 @@ namespace Nz ALCcontext* s_context = nullptr; unsigned int s_version; - unsigned int ParseDevices(const char* deviceString, std::vector& devices) + std::size_t ParseDevices(const char* deviceString, std::vector& devices) { if (!deviceString) return 0; - unsigned int startSize = devices.size(); + std::size_t startSize = devices.size(); - unsigned int length; + std::size_t length; while ((length = std::strlen(deviceString)) > 0) { devices.push_back(String(deviceString, length)); @@ -222,7 +222,7 @@ namespace Nz return s_library.IsLoaded(); } - unsigned int OpenAL::QueryInputDevices(std::vector& devices) + std::size_t OpenAL::QueryInputDevices(std::vector& devices) { const char* deviceString = reinterpret_cast(alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER)); if (!deviceString) @@ -231,7 +231,7 @@ namespace Nz return ParseDevices(deviceString, devices); } - unsigned int OpenAL::QueryOutputDevices(std::vector& devices) + std::size_t OpenAL::QueryOutputDevices(std::vector& devices) { const char* deviceString = reinterpret_cast(alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER)); if (!deviceString) @@ -372,7 +372,6 @@ namespace Nz return entry; } -} // al OpenALDetail::LPALBUFFER3F alBuffer3f = nullptr; @@ -470,3 +469,5 @@ OpenALDetail::LPALCMAKECONTEXTCURRENT alcMakeContextCurrent = nullptr; OpenALDetail::LPALCOPENDEVICE alcOpenDevice = nullptr; OpenALDetail::LPALCPROCESSCONTEXT alcProcessContext = nullptr; OpenALDetail::LPALCSUSPENDCONTEXT alcSuspendContext = nullptr; + +} diff --git a/src/Nazara/Audio/Sound.cpp b/src/Nazara/Audio/Sound.cpp index 31ef3af27..7198a01d6 100644 --- a/src/Nazara/Audio/Sound.cpp +++ b/src/Nazara/Audio/Sound.cpp @@ -42,23 +42,17 @@ namespace Nz UInt32 Sound::GetDuration() const { - #if NAZARA_AUDIO_SAFE - if (!m_buffer) - { - NazaraError("Invalid sound buffer"); - return 0; - } - #endif + NazaraAssert(m_buffer, "Invalid sound buffer"); return m_buffer->GetDuration(); } UInt32 Sound::GetPlayingOffset() const { - ALfloat seconds = -1.f; - alGetSourcef(m_source, AL_SEC_OFFSET, &seconds); + ALint samples = 0; + alGetSourcei(m_source, AL_SAMPLE_OFFSET, &samples); - return static_cast(seconds*1000); + return static_cast(1000ULL * samples / m_buffer->GetSampleRate()); } SoundStatus Sound::GetStatus() const @@ -166,7 +160,7 @@ namespace Nz void Sound::SetPlayingOffset(UInt32 offset) { - alSourcef(m_source, AL_SEC_OFFSET, offset/1000.f); + alSourcei(m_source, AL_SAMPLE_OFFSET, static_cast(offset/1000.f * m_buffer->GetSampleRate())); } void Sound::Stop() diff --git a/src/Nazara/Audio/SoundBuffer.cpp b/src/Nazara/Audio/SoundBuffer.cpp index 10edc74f8..c01878383 100644 --- a/src/Nazara/Audio/SoundBuffer.cpp +++ b/src/Nazara/Audio/SoundBuffer.cpp @@ -105,7 +105,7 @@ namespace Nz m_impl = new SoundBufferImpl; m_impl->buffer = buffer; - m_impl->duration = (1000*sampleCount / (format * sampleRate)); + m_impl->duration = static_cast((1000ULL*sampleCount / (format * sampleRate))); m_impl->format = format; m_impl->sampleCount = sampleCount; m_impl->sampleRate = sampleRate; diff --git a/src/Nazara/Core/AbstractHash.cpp b/src/Nazara/Core/AbstractHash.cpp index f0e82bb19..419ba74e9 100644 --- a/src/Nazara/Core/AbstractHash.cpp +++ b/src/Nazara/Core/AbstractHash.cpp @@ -17,8 +17,25 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::AbstractHash + * \brief Core class that represents the behaviour of the hash classes + * + * \remark This class is abstract + */ + AbstractHash::~AbstractHash() = default; + /*! + * \brief Factory of Hash classes in function of HashType + * \return A new instance of the Hash class according to the HashType + * + * \param type Enumeration of type HashType + * + * \remark If enumeration is not defined in HashType, a NazaraInternalError is thrown and nullptr is returned + */ + std::unique_ptr AbstractHash::Get(HashType type) { NazaraAssert(type <= HashType_Max, "Hash type value out of enum"); @@ -26,34 +43,34 @@ namespace Nz switch (type) { case HashType_Fletcher16: - return std::unique_ptr(new HashFletcher16); + return std::make_unique(); case HashType_CRC32: - return std::unique_ptr(new HashCRC32); + return std::make_unique(); case HashType_MD5: - return std::unique_ptr(new HashMD5); + return std::make_unique(); case HashType_SHA1: - return std::unique_ptr(new HashSHA1); + return std::make_unique(); case HashType_SHA224: - return std::unique_ptr(new HashSHA224); + return std::make_unique(); case HashType_SHA256: - return std::unique_ptr(new HashSHA256); + return std::make_unique(); case HashType_SHA384: - return std::unique_ptr(new HashSHA384); + return std::make_unique(); case HashType_SHA512: - return std::unique_ptr(new HashSHA512); + return std::make_unique(); case HashType_Whirlpool: - return std::unique_ptr(new HashWhirlpool); + return std::make_unique(); } NazaraInternalError("Hash type not handled (0x" + String::Number(type, 16) + ')'); - return std::unique_ptr(); + return nullptr; } } diff --git a/src/Nazara/Core/AbstractLogger.cpp b/src/Nazara/Core/AbstractLogger.cpp index 1397d3592..ceac9eff8 100644 --- a/src/Nazara/Core/AbstractLogger.cpp +++ b/src/Nazara/Core/AbstractLogger.cpp @@ -11,17 +11,35 @@ namespace Nz namespace { const char* errorType[] = { - "Assert failed: ", // ErrorType_AssertFailed - "Internal error: ", // ErrorType_Internal - "Error: ", // ErrorType_Normal - "Warning: " // ErrorType_Warning + "Assert failed: ", // ErrorType_AssertFailed + "Internal error: ", // ErrorType_Internal + "Error: ", // ErrorType_Normal + "Warning: " // ErrorType_Warning }; static_assert(sizeof(errorType) / sizeof(const char*) == ErrorType_Max + 1, "Error type array is incomplete"); } + /*! + * \ingroup core + * \class Nz::AbstractLogger + * \brief Core class that represents the behaviour of the log classes + * + * \remark This class is abstract + */ + AbstractLogger::~AbstractLogger() = default; + /*! + * \brief Writes the error in StringStream + * + * \param type Enumeration of type ErrorType + * \param error String describing the error + * \param line Line number in the file + * \param file Filename + * \param function Name of the function throwing the error + */ + void AbstractLogger::WriteError(ErrorType type, const String& error, unsigned int line, const char* file, const char* function) { StringStream stream; diff --git a/src/Nazara/Core/ByteArray.cpp b/src/Nazara/Core/ByteArray.cpp index cc5734822..e505f13f2 100644 --- a/src/Nazara/Core/ByteArray.cpp +++ b/src/Nazara/Core/ByteArray.cpp @@ -9,6 +9,20 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::ByteArray + * \brief Core class that represents an array of bytes + */ + + /*! + * \brief Output operator + * \return The stream + * + * \param out The stream + * \param byteArray The ByteArray to output + */ + std::ostream& operator<<(std::ostream& out, const Nz::ByteArray& byteArray) { out << byteArray.ToHex(); diff --git a/src/Nazara/Core/ByteStream.cpp b/src/Nazara/Core/ByteStream.cpp new file mode 100644 index 000000000..cf5a045a9 --- /dev/null +++ b/src/Nazara/Core/ByteStream.cpp @@ -0,0 +1,125 @@ +// Copyright (C) 2015 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 +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup core + * \class Nz::ByteStream + * \brief Core class that represents a stream of bytes + */ + + /*! + * \brief Constructs a ByteStream object with a byte array + * + * \param byteArray Bytes to stream + * \param openMode Reading/writing mode for the stream + */ + + ByteStream::ByteStream(ByteArray* byteArray, UInt32 openMode) : + ByteStream() + { + SetStream(byteArray, openMode); + } + + /*! + * \brief Constructs a ByteStream object with a raw memory and a size + * + * \param ptr Pointer to raw memory + * \param size Size that can be read + * + * \remark If preallocated space of ptr is less than the size, the behaviour is undefined + */ + + ByteStream::ByteStream(void* ptr, Nz::UInt64 size) : + ByteStream() + { + SetStream(ptr, size); + } + + /*! + * \brief Constructs a ByteStream object with a raw memory and a size + * + * \param ptr Constant pointer to raw memory + * \param size Size that can be read + * + * \remark If preallocated space of ptr is less than the size, the behaviour is undefined + */ + + ByteStream::ByteStream(const void* ptr, Nz::UInt64 size) : + ByteStream() + { + SetStream(ptr, size); + } + + /*! + * \brief Sets this with a byte array + * + * \param byteArray Bytes to stream + * \param openMode Reading/writing mode for the stream + */ + + void ByteStream::SetStream(ByteArray* byteArray, UInt32 openMode) + { + std::unique_ptr stream(new MemoryStream(byteArray, openMode)); + + SetStream(m_ownedStream.get()); + // SetStream reset our smart pointer, set it after calling it + m_ownedStream = std::move(stream); + } + + /*! + * \brief Sets this with a raw memory and a size + * + * \param ptr Pointer to raw memory + * \param size Size that can be read + * + * \remark If preallocated space of ptr is less than the size, the behaviour is undefined + */ + + void ByteStream::SetStream(void* ptr, Nz::UInt64 size) + { + std::unique_ptr stream(new MemoryView(ptr, size)); + + SetStream(m_ownedStream.get()); + // SetStream reset our smart pointer, set it after calling it + m_ownedStream = std::move(stream); + } + + /*! + * \brief Sets this with a raw memory and a size + * + * \param ptr Constant pointer to raw memory + * \param size Size that can be read + * + * \remark If preallocated space of ptr is less than the size, the behaviour is undefined + */ + + void ByteStream::SetStream(const void* ptr, Nz::UInt64 size) + { + std::unique_ptr stream(new MemoryView(ptr, size)); + + SetStream(m_ownedStream.get()); + // SetStream reset our smart pointer, set it after calling it + m_ownedStream = std::move(stream); + } + + /*! + * \brief Signal function (meant to be virtual) + * + * \remark Produces a NazaraError + */ + + void ByteStream::OnEmptyStream() + { + NazaraError("No stream"); + } +} diff --git a/src/Nazara/Core/Clock.cpp b/src/Nazara/Core/Clock.cpp index dae4c84d5..60482b18e 100644 --- a/src/Nazara/Core/Clock.cpp +++ b/src/Nazara/Core/Clock.cpp @@ -42,17 +42,17 @@ namespace Nz } /*! + * \ingroup core * \class Nz::Clock * \brief Utility class that measure the elapsed time */ - /*! + /*! * \brief Constructs a Clock object * * \param startingValue The starting time value, in microseconds * \param paused The clock pause state */ - Clock::Clock(UInt64 startingValue, bool paused) : m_elapsedTime(startingValue), m_refTime(GetElapsedMicroseconds()), @@ -66,7 +66,6 @@ namespace Nz * * \see GetMicroseconds, GetMilliseconds */ - float Clock::GetSeconds() const { return GetMicroseconds()/1000000.f; @@ -78,7 +77,6 @@ namespace Nz * * \see GetMilliseconds, GetSeconds */ - UInt64 Clock::GetMicroseconds() const { NazaraLock(m_mutex); @@ -96,7 +94,6 @@ namespace Nz * * \see GetMicroseconds, GetSeconds */ - UInt64 Clock::GetMilliseconds() const { return GetMicroseconds()/1000; @@ -108,7 +105,6 @@ namespace Nz * * \see Pause, Unpause */ - bool Clock::IsPaused() const { NazaraLock(m_mutex); @@ -124,7 +120,6 @@ namespace Nz * * \see IsPaused, Unpause */ - void Clock::Pause() { NazaraLock(m_mutex); @@ -140,7 +135,6 @@ namespace Nz * \brief Restart the clock * Restarts the clock, putting it's time counter back to zero (as if the clock got constructed). */ - void Clock::Restart() { NazaraLock(m_mutex); @@ -158,7 +152,6 @@ namespace Nz * * \see IsPaused, Unpause */ - void Clock::Unpause() { NazaraLock(m_mutex); diff --git a/src/Nazara/Core/ConditionVariable.cpp b/src/Nazara/Core/ConditionVariable.cpp index f893c7f82..0730ea829 100644 --- a/src/Nazara/Core/ConditionVariable.cpp +++ b/src/Nazara/Core/ConditionVariable.cpp @@ -18,35 +18,102 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::ConditionVariable + * \brief Core class that represents a condition variable + * + * The ConditionVariable class is a synchronization primitive that can be used to block a thread, or multiple threads at the same time, until another thread both modifies a shared variable (the condition), and notifies the ConditionVariable + */ + + /*! + * \brief Constructs a ConditionVariable object by default + */ + ConditionVariable::ConditionVariable() { m_impl = new ConditionVariableImpl; } + /*! + * \brief Destructs the object + */ + ConditionVariable::~ConditionVariable() { delete m_impl; } + /*! + * \brief Sends a signal to one thread waiting on the condition + * + * If any threads are waiting on *this, calling Signal unblocks one of the waiting threads + * + * \see SignalAll + */ + void ConditionVariable::Signal() { m_impl->Signal(); } + /*! + * \brief Sends a signal to every threads waiting on the condition + * + * Unblocks all threads currently waiting for *this + * + * \see Signal + */ + void ConditionVariable::SignalAll() { m_impl->SignalAll(); } + /*! + * \brief Makes the thread wait on the condition + * + * Wait causes the current thread to block until the condition variable is notified or a spurious wakeup occurs + * + * \param mutex Mutex for the condition + * + * \remark Produces a NazaraAssert if mutex is invalid + */ + void ConditionVariable::Wait(Mutex* mutex) { NazaraAssert(mutex != nullptr, "Mutex must be valid"); m_impl->Wait(mutex->m_impl); } + /*! + * \brief Makes the thread wait on the condition for a certain amount of time + * + * Wait causes the current thread to block until the condition variable is notified, a specific time is reached, or a spurious wakeup occurs + * + * \param mutex Mutex for the condition + * \param timeout Time before expiration of the waiting + * + * \remark Produces a NazaraAssert if mutex is invalid + */ + bool ConditionVariable::Wait(Mutex* mutex, UInt32 timeout) { NazaraAssert(mutex != nullptr, "Mutex must be valid"); return m_impl->Wait(mutex->m_impl, timeout); } + + /*! + * \brief Moves a condition to another ConditionVariable object + * \return A reference to the object + */ + ConditionVariable& ConditionVariable::operator=(ConditionVariable&& condition) noexcept + { + delete m_impl; + + m_impl = condition.m_impl; + condition.m_impl = nullptr; + + return *this; + } } diff --git a/src/Nazara/Core/Core.cpp b/src/Nazara/Core/Core.cpp index 142ffa909..1ccb35212 100644 --- a/src/Nazara/Core/Core.cpp +++ b/src/Nazara/Core/Core.cpp @@ -13,12 +13,25 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Core + * \brief Core class that represents the module initializer of Core + */ + + /*! + * \brief Initializes the Core module + * \return true if initialization is successful + * + * \remark Produces a NazaraNotice + */ + bool Core::Initialize() { if (s_moduleReferenceCounter > 0) { s_moduleReferenceCounter++; - return true; // Déjà initialisé + return true; // Already initialized } s_moduleReferenceCounter++; @@ -29,23 +42,34 @@ namespace Nz return true; } + /*! + * \brief Checks whether the module is initialized + * \return true if module is initialized + */ + bool Core::IsInitialized() { return s_moduleReferenceCounter != 0; } + /*! + * \brief Uninitializes the Core module + * + * \remark Produces a NazaraNotice + */ + void Core::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; HardwareInfo::Uninitialize(); diff --git a/src/Nazara/Core/Directory.cpp b/src/Nazara/Core/Directory.cpp index ed8ed6bd0..b48930419 100644 --- a/src/Nazara/Core/Directory.cpp +++ b/src/Nazara/Core/Directory.cpp @@ -30,16 +30,32 @@ namespace Nz { namespace { - //FIXME: MinGW seems to dislike thread_local shared_ptr.. (using a std::string is a working hackfix) + //FIXME: MinGW seems to dislike thread_local shared_ptr.. (using a std::string is a working hackfix) thread_local std::string currentPath(DirectoryImpl::GetCurrent()); } + /*! + * \ingroup core + * \class Nz::Directory + * \brief Core class that represents a directory + */ + + /*! + * \brief Constructs a Directory object by default + */ + Directory::Directory() : m_pattern('*'), m_impl(nullptr) { } + /*! + * \brief Constructs a Directory object with a path + * + * \param dirPath Path to the directory + */ + Directory::Directory(const String& dirPath) : m_dirPath(dirPath), m_pattern('*'), @@ -47,11 +63,21 @@ namespace Nz { } + /*! + * \brief Destructs the object and calls Close + * + * \see Close + */ + Directory::~Directory() { Close(); } + /*! + * \brief Closes the directory + */ + void Directory::Close() { NazaraLock(m_mutex); @@ -64,16 +90,26 @@ namespace Nz } } + /*! + * \brief Checks whether the directory exists + * \return true if directory exists + */ + bool Directory::Exists() const { NazaraLock(m_mutex); if (IsOpen()) - return true; // Le fichier est ouvert, donc il existe + return true; // If directory is open, then it exists else return Exists(m_dirPath); } + /*! + * \brief Gets the path of the directory + * \return Path of the directory + */ + String Directory::GetPath() const { NazaraLock(m_mutex); @@ -81,6 +117,11 @@ namespace Nz return m_dirPath; } + /*! + * \brief Gets the pattern for the path of the directory + * \return Pattern for the path of the directory + */ + String Directory::GetPattern() const { NazaraLock(m_mutex); @@ -88,12 +129,19 @@ namespace Nz return m_pattern; } + /*! + * \brief Gets the result name of the directory + * \return Resulting name + * + * \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined + */ + String Directory::GetResultName() const { NazaraLock(m_mutex); #if NAZARA_CORE_SAFE - if (!m_impl) + if (!IsOpen()) { NazaraError("Directory not opened"); return String(); @@ -103,12 +151,19 @@ namespace Nz return m_impl->GetResultName(); } + /*! + * \brief Gets the result path of the directory + * \return Resulting path + * + * \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined + */ + String Directory::GetResultPath() const { NazaraLock(m_mutex); #if NAZARA_CORE_SAFE - if (!m_impl) + if (!IsOpen()) { NazaraError("Directory not opened"); return String(); @@ -118,12 +173,19 @@ namespace Nz return m_dirPath + NAZARA_DIRECTORY_SEPARATOR + m_impl->GetResultName(); } + /*! + * \brief Gets the resulting size of the directory + * \return Size of the directory + * + * \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined + */ + UInt64 Directory::GetResultSize() const { NazaraLock(m_mutex); #if NAZARA_CORE_SAFE - if (!m_impl) + if (!IsOpen()) { NazaraError("Directory not opened"); return 0; @@ -133,6 +195,11 @@ namespace Nz return m_impl->GetResultSize(); } + /*! + * \brief Checks whether the directory is open + * \return true if open + */ + bool Directory::IsOpen() const { NazaraLock(m_mutex); @@ -140,12 +207,19 @@ namespace Nz return m_impl != nullptr; } + /*! + * \brief Checks whether the directory is result + * \return true if result + * + * \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined + */ + bool Directory::IsResultDirectory() const { NazaraLock(m_mutex); #if NAZARA_CORE_SAFE - if (!m_impl) + if (!IsOpen()) { NazaraError("Directory not opened"); return false; @@ -155,12 +229,21 @@ namespace Nz return m_impl->IsResultDirectory(); } + /*! + * \brief Sets the next result in the directory + * \return true if directory has a next result + * + * \param skipDots Skips the dots in the path + * + * \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined + */ + bool Directory::NextResult(bool skipDots) { NazaraLock(m_mutex); #if NAZARA_CORE_SAFE - if (!m_impl) + if (!IsOpen()) { NazaraError("Directory not opened"); return false; @@ -185,6 +268,11 @@ namespace Nz return true; } + /*! + * \brief Opens the directory + * \return true if opening is successful + */ + bool Directory::Open() { NazaraLock(m_mutex); @@ -206,6 +294,12 @@ namespace Nz return true; } + /*! + * \brief Sets the path of the directory + * + * \param dirPath Path of the directory + */ + void Directory::SetPath(const String& dirPath) { NazaraLock(m_mutex); @@ -215,6 +309,12 @@ namespace Nz m_dirPath = File::AbsolutePath(dirPath); } + /*! + * \brief Sets the pattern of the directory + * + * \param dirPath Pattern of the directory + */ + void Directory::SetPattern(const String& pattern) { NazaraLock(m_mutex); @@ -222,6 +322,18 @@ namespace Nz m_pattern = pattern; } + /*! + * \brief Copies the first directory to a new directory path + * \return true if copy is successful + * + * \param sourcePath Path of the original directory + * \param targetPath Path of the copied directory + * + * \remark Produces a NazaraError if could not create destination directory + * \remark Produces a NazaraError if could not open origin directory + * \remark Produces a NazaraError if could not copy a file + */ + bool Directory::Copy(const String& sourcePath, const String& destPath) { if (sourcePath.IsEmpty() || destPath.IsEmpty()) @@ -262,6 +374,14 @@ namespace Nz return true; } + /*! + * \brief Creates a directory from a path + * \return true if creation is successful + * + * \param dirPath Path of the directory + * \param recursive Creates subdirectories + */ + bool Directory::Create(const String& dirPath, bool recursive) { if (dirPath.IsEmpty()) @@ -275,7 +395,7 @@ namespace Nz return false; #ifdef NAZARA_PLATFORM_WINDOWS - // Contrairement au disque (Ex: "C:"), le chemin réseau n'est pas considéré comme un dossier (Ex: "\\Laptop") + // Unlike to disk (Ex: "C:"), the netwrok path is not considered as a directory (Ex: "\\Laptop") if (path.Match("\\\\*")) { foundPos = path.Find('\\', 2); @@ -309,6 +429,13 @@ namespace Nz return DirectoryImpl::Create(File::NormalizePath(dirPath)); } + /*! + * \brief Checks whether the directory exists + * \return true if directory exists + * + * \param dirPath Path of the directory + */ + bool Directory::Exists(const String& dirPath) { if (dirPath.IsEmpty()) @@ -317,14 +444,24 @@ namespace Nz return DirectoryImpl::Exists(File::NormalizePath(dirPath)); } + /*! + * \brief Gets the current path of this directory + * \return Current path + */ + String Directory::GetCurrent() { return currentPath; } + /*! + * \brief Gets this current file relative to the engine + * \return Path to this file + */ + const char* Directory::GetCurrentFileRelativeToEngine(const char* currentFile) { - ///FIXME: Est-ce que cette méthode est au bon endroit ? + ///FIXME: Is this method in the right place ? const char* ptr = std::strstr(currentFile, "NazaraEngine/"); if (!ptr) ptr = std::strstr(currentFile, "NazaraEngine\\"); @@ -335,6 +472,14 @@ namespace Nz return ptr; } + /*! + * \brief Removes the directory + * \return true if remove is successful + * + * \param dirPath Path of the directory + * \param emptyDirectory Remove recursively + */ + bool Directory::Remove(const String& dirPath, bool emptyDirectory) { if (dirPath.IsEmpty()) @@ -344,7 +489,7 @@ namespace Nz { Directory dir(dirPath); if (!dir.Open()) - return DirectoryImpl::Remove(dirPath); // Si on n'arrive pas à ouvrir le dossier, on tente de le supprimer + return DirectoryImpl::Remove(dirPath); // If we can't open the directory, we try to delete it while (dir.NextResult(true)) { @@ -366,6 +511,13 @@ namespace Nz return DirectoryImpl::Remove(File::NormalizePath(dirPath)); } + /*! + * \brief Sets the current directory + * \return true if directory path exists + * + * \param dirPath Path of the directory + */ + bool Directory::SetCurrent(const String& dirPath) { String path = File::AbsolutePath(dirPath); @@ -376,5 +528,5 @@ namespace Nz } else return false; - } + } } diff --git a/src/Nazara/Core/DynLib.cpp b/src/Nazara/Core/DynLib.cpp index 39539e7ed..515f437b4 100644 --- a/src/Nazara/Core/DynLib.cpp +++ b/src/Nazara/Core/DynLib.cpp @@ -25,11 +25,27 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::DynLib + * \brief Core class that represents a dynamic library loader + */ + + /*! + * \brief Constructs a DynLib object by default + */ + DynLib::DynLib() : m_impl(nullptr) { } + /*! + * \brief Constructs a DynLib object by move semantic + * + * \param lib DynLib to move into this + */ + DynLib::DynLib(DynLib&& lib) : m_lastError(std::move(lib.m_lastError)), m_impl(lib.m_impl) @@ -37,11 +53,22 @@ namespace Nz lib.m_impl = nullptr; } + /*! + * \brief Destructs the object and calls Unload + * + * \see Unload + */ + DynLib::~DynLib() { Unload(); } + /*! + * \brief Gets the last error + * \return Last error + */ + String DynLib::GetLastError() const { NazaraLock(m_mutex) @@ -49,12 +76,19 @@ namespace Nz return m_lastError; } + /*! + * \brief Gets the symbol for the name + * \return Function which is the symbol of the function name + * + * \remark Produces a NazaraError if library is not loaded with NAZARA_CORE_SAFE defined + */ + DynLibFunc DynLib::GetSymbol(const String& symbol) const { NazaraLock(m_mutex) #if NAZARA_CORE_SAFE - if (!m_impl) + if (!IsLoaded()) { NazaraError("Library not opened"); return nullptr; @@ -64,11 +98,25 @@ namespace Nz return m_impl->GetSymbol(symbol, &m_lastError); } + /*! + * \brief Checks whether the library is loaded + * \return true if loaded + */ + bool DynLib::IsLoaded() const { return m_impl != nullptr; } + /*! + * \brief Loads the library with that path + * \return true if loading is successful + * + * \param libraryPath Path of the library + * + * \remark Produces a NazaraError if library is could not be loaded + */ + bool DynLib::Load(const String& libraryPath) { NazaraLock(m_mutex) @@ -87,11 +135,15 @@ namespace Nz return true; } + /*! + * \brief Unloads the library + */ + void DynLib::Unload() { NazaraLock(m_mutex) - if (m_impl) + if (IsLoaded()) { m_impl->Unload(); delete m_impl; @@ -99,6 +151,13 @@ namespace Nz } } + /*! + * \brief Moves the other lib into this + * \return A reference to this + * + * \param lib DynLib to move in this + */ + DynLib& DynLib::operator=(DynLib&& lib) { Unload(); diff --git a/src/Nazara/Core/Error.cpp b/src/Nazara/Core/Error.cpp index 805e60190..8de5e5747 100644 --- a/src/Nazara/Core/Error.cpp +++ b/src/Nazara/Core/Error.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -18,11 +19,31 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Error + * \brief Core class that represents an error + */ + + /*! + * \brief Gets the flags of the error + * \return Flag + */ + UInt32 Error::GetFlags() { return s_flags; } + /*! + * \brief Gets the last error + * \return Last error + * + * \param file Optional argument to set last error file + * \param line Optional argument to set last error line + * \param function Optional argument to set last error function + */ + String Error::GetLastError(const char** file, unsigned int* line, const char** function) { if (file) @@ -37,6 +58,11 @@ namespace Nz return s_lastError; } + /*! + * \brief Gets the last system error code + * \return "errno" + */ + unsigned int Error::GetLastSystemErrorCode() { #if defined(NAZARA_PLATFORM_WINDOWS) @@ -49,6 +75,13 @@ namespace Nz #endif } + /*! + * \brief Gets the string representation of the last system error code + * \return Message of the error + * + * \param code Code of the error + */ + String Error::GetLastSystemError(unsigned int code) { #if defined(NAZARA_PLATFORM_WINDOWS) @@ -65,7 +98,7 @@ namespace Nz String error(String::Unicode(buffer)); LocalFree(buffer); - error.Trim(); // Pour une raison inconnue, Windows met deux-trois retours à la ligne après le message + error.Trim(); // For an unknown reason, Windows put two-three line return after the message return error; #elif defined(NAZARA_PLATFORM_POSIX) @@ -77,11 +110,27 @@ namespace Nz #endif } + /*! + * \brief Sets the flags + * + * \param flags Flags for the error + */ + void Error::SetFlags(UInt32 flags) { s_flags = flags; } + /*! + * \brief Checks if the error should trigger + * + * \param type ErrorType of the error + * \param error Message of the error + * + * \remark Produces a std::abort on AssertFailed with NAZARA_CORE_EXIT_ON_ASSERT_FAILURE defined + * \remark Produces a std::runtime_error on AssertFailed or throwing exception + */ + void Error::Trigger(ErrorType type, const String& error) { if (type == ErrorType_AssertFailed || (s_flags & ErrorFlag_Silent) == 0 || (s_flags & ErrorFlag_SilentDisabled) != 0) @@ -102,8 +151,23 @@ namespace Nz throw std::runtime_error(error); } + /*! + * \brief Checks if the error should trigger + * + * \param type ErrorType of the error + * \param error Message of the error + * \param line Line of the error + * \param file File of the error + * \param function Function of the error + * + * \remark Produces a std::abort on AssertFailed with NAZARA_CORE_EXIT_ON_ASSERT_FAILURE defined + * \remark Produces a std::runtime_error on AssertFailed or throwing exception + */ + void Error::Trigger(ErrorType type, const String& error, unsigned int line, const char* file, const char* function) { + file = Nz::Directory::GetCurrentFileRelativeToEngine(file); + if (type == ErrorType_AssertFailed || (s_flags & ErrorFlag_Silent) == 0 || (s_flags & ErrorFlag_SilentDisabled) != 0) Log::WriteError(type, error, line, file, function); diff --git a/src/Nazara/Core/ErrorFlags.cpp b/src/Nazara/Core/ErrorFlags.cpp index 08c1d3e19..003e37a23 100644 --- a/src/Nazara/Core/ErrorFlags.cpp +++ b/src/Nazara/Core/ErrorFlags.cpp @@ -8,22 +8,51 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::ErrorFlags + * \brief Core class that represents flags for error + */ + + /*! + * \brief Constructs a ErrorFlags object with flags + * + * \param flags Flags for the error + * \param replace Replace the entirely the old flag if true, else do a "OR" + */ + ErrorFlags::ErrorFlags(UInt32 flags, bool replace) : m_previousFlags(Error::GetFlags()) { SetFlags(flags, replace); } + /*! + * \brief Destructs the object and sets the old flag + */ + ErrorFlags::~ErrorFlags() { Error::SetFlags(m_previousFlags); } + /*! + * \brief Gets the previous flag + * \return Previous flag + */ + UInt32 ErrorFlags::GetPreviousFlags() const { return m_previousFlags; } + /*! + * \brief Sets the flags + * + * \param flags Flags for the error + * \param replace Replace the entirely the old flag if true, else do a "OR" + */ + void ErrorFlags::SetFlags(UInt32 flags, bool replace) { if (!replace) diff --git a/src/Nazara/Core/File.cpp b/src/Nazara/Core/File.cpp index db71f2d95..c03fb7b7f 100644 --- a/src/Nazara/Core/File.cpp +++ b/src/Nazara/Core/File.cpp @@ -30,23 +30,52 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::File + * \brief Core class that represents a file + */ + + /*! + * \brief Constructs a File object by default + */ + File::File() : m_impl(nullptr) { } + /*! + * \brief Constructs a File object with a file path + * + * \param filePath Path to the file + */ + File::File(const String& filePath) : File() { SetFile(filePath); } + /*! + * \brief Constructs a File object with a file path and flags + * + * \param filePath Path to the file + * \param openMode Flag of the file + */ + File::File(const String& filePath, UInt32 openMode) : File() { Open(filePath, openMode); } + /*! + * \brief Constructs a File object by move semantic + * + * \param file File to move into this + */ + File::File(File&& file) noexcept : Stream(std::move(file)), m_filePath(std::move(file.m_filePath)), @@ -55,16 +84,33 @@ namespace Nz file.m_impl = nullptr; } + /*! + * \brief Destructs the object and calls Close + * + * \see Close + */ + File::~File() { Close(); } + /*! + * \brief Copies this file to a new file path + * \return true if copy is successful + * + * \param newFilePath Path of the new file + */ + bool File::Copy(const String& newFilePath) { return Copy(m_filePath, newFilePath); } + /*! + * \brief Closes the file + */ + void File::Close() { NazaraLock(m_mutex) @@ -79,6 +125,11 @@ namespace Nz } } + /*! + * \brief Deletes the file + * \return true if delete is successful + */ + bool File::Delete() { NazaraLock(m_mutex) @@ -88,6 +139,13 @@ namespace Nz return Delete(m_filePath); } + /*! + * \brief Checks whether the file has reached the end + * \return true if cursor is at the end of the file + * + * \remark Produces a NazaraError if file is not open with NAZARA_CORE_SAFE defined + */ + bool File::EndOfFile() const { NazaraLock(m_mutex) @@ -95,7 +153,7 @@ namespace Nz #if NAZARA_CORE_SAFE if (!IsOpen()) { - NazaraError("File not opened"); + NazaraError("File not open"); return false; } #endif @@ -103,11 +161,25 @@ namespace Nz return m_impl->EndOfFile(); } + /*! + * \brief Checks whether the file has reached the end of the stream + * \return true if cursor is at the end of the file + * + * \remark Produces a NazaraError if file is not open with NAZARA_CORE_SAFE defined + * + * \see EndOfFile + */ + bool File::EndOfStream() const { return EndOfFile(); } + /*! + * \brief Checks whether the file exists + * \return true if file exists + */ + bool File::Exists() const { NazaraLock(m_mutex) @@ -118,6 +190,11 @@ namespace Nz return Exists(m_filePath); } + /*! + * \brief Gets the creation time of the file + * \return Information about the creation time + */ + time_t File::GetCreationTime() const { NazaraLock(m_mutex) @@ -125,15 +202,27 @@ namespace Nz return GetCreationTime(m_filePath); } + /*! + * \brief Gets the position of the cursor in the file + * \return Position of the cursor + * + * \remark Produces a NazaraAssert if file is not open + */ + UInt64 File::GetCursorPos() const { NazaraLock(m_mutex) - NazaraAssert(IsOpen(), "File is not opened"); + NazaraAssert(IsOpen(), "File is not open"); return m_impl->GetCursorPos(); } + /*! + * \brief Gets the directory of the file + * \return Directory of the file + */ + String File::GetDirectory() const { NazaraLock(m_mutex) @@ -141,6 +230,11 @@ namespace Nz return m_filePath.SubStringTo(NAZARA_DIRECTORY_SEPARATOR, -1, true, true); } + /*! + * \brief Gets the name of the file + * \return Name of the file + */ + String File::GetFileName() const { NazaraLock(m_mutex) @@ -148,6 +242,11 @@ namespace Nz return m_filePath.SubStringFrom(NAZARA_DIRECTORY_SEPARATOR, -1, true); } + /*! + * \brief Gets the last time the file was accessed + * \return Information about the last access time + */ + time_t File::GetLastAccessTime() const { NazaraLock(m_mutex) @@ -155,6 +254,11 @@ namespace Nz return GetLastAccessTime(m_filePath); } + /*! + * \brief Gets the last time the file was written + * \return Information about the last writing time + */ + time_t File::GetLastWriteTime() const { NazaraLock(m_mutex) @@ -162,6 +266,11 @@ namespace Nz return GetLastWriteTime(m_filePath); } + /*! + * \brief Gets the path of the file + * \return Path of the file + */ + String File::GetPath() const { NazaraLock(m_mutex) @@ -169,6 +278,11 @@ namespace Nz return m_filePath; } + /*! + * \brief Gets the size of the file + * \return Size of the file + */ + UInt64 File::GetSize() const { NazaraLock(m_mutex) @@ -176,6 +290,11 @@ namespace Nz return GetSize(m_filePath); } + /*! + * \brief Checks whether the file is open + * \return true if open + */ + bool File::IsOpen() const { NazaraLock(m_mutex) @@ -183,22 +302,14 @@ namespace Nz return m_impl != nullptr; } - bool File::Rename(const String& newFilePath) - { - NazaraLock(m_mutex) - - bool opened = IsOpen(); - Close(); - - bool success = Rename(m_filePath, newFilePath); - if (success) - m_filePath = NormalizePath(newFilePath); - - if (opened) - Open(); - - return success; - } + /*! + * \brief Opens the file with flags + * \return true if opening is successful + * + * \param openMode Flag for file + * + * \remark Produces a NazaraError if OS error to open a file + */ bool File::Open(unsigned int openMode) { @@ -215,7 +326,7 @@ namespace Nz std::unique_ptr impl(new FileImpl(this)); if (!impl->Open(m_filePath, openMode)) { - ErrorFlags flags(ErrorFlag_Silent); // Silencieux par défaut + ErrorFlags flags(ErrorFlag_Silent); // Silent by default NazaraError("Failed to open \"" + m_filePath + "\": " + Error::GetLastSystemError()); return false; } @@ -231,6 +342,16 @@ namespace Nz return true; } + /*! + * \brief Opens the file with file path and flags + * \return true if opening is successful + * + * \param filePath Path to the file + * \param openMode Flag for file + * + * \remark Produces a NazaraError if OS error to open a file + */ + bool File::Open(const String& filePath, unsigned int openMode) { NazaraLock(m_mutex) @@ -241,24 +362,72 @@ namespace Nz return Open(openMode); } + /*! + * \brief Renames the file with a new name + * \return true if rename is successful + */ + + bool File::Rename(const String& newFilePath) + { + NazaraLock(m_mutex) + + bool open = IsOpen(); + Close(); + + bool success = Rename(m_filePath, newFilePath); + if (success) + m_filePath = NormalizePath(newFilePath); + + if (open) + Open(); + + return success; + } + + /*! + * \brief Sets the position of the cursor + * \return true if cursor is successfully positioned + * + * \param pos Position of the cursor + * \param offset Offset according to the cursor position + * + * \remark Produces a NazaraAssert if file is not open + */ + bool File::SetCursorPos(CursorPosition pos, Int64 offset) { NazaraLock(m_mutex) - NazaraAssert(IsOpen(), "File is not opened"); + NazaraAssert(IsOpen(), "File is not open"); return m_impl->SetCursorPos(pos, offset); } + /*! + * \brief Sets the position of the cursor + * \return true if cursor is successfully positioned + * + * \param offset Offset according to the cursor begin position + * + * \remark Produces a NazaraAssert if file is not open + */ + bool File::SetCursorPos(UInt64 offset) { NazaraLock(m_mutex) - NazaraAssert(IsOpen(), "File is not opened"); + NazaraAssert(IsOpen(), "File is not open"); return m_impl->SetCursorPos(CursorPosition_AtBegin, offset); } + /*! + * \brief Sets the file path + * \return true if file opening is successful + * + * \remark Produces a NazaraError if file path can not be open + */ + bool File::SetFile(const String& filePath) { NazaraLock(m_mutex) @@ -285,6 +454,13 @@ namespace Nz return true; } + /*! + * \brief Sets the file path + * \return A reference to this + * + * \remark Produces a NazaraError if file path can not be open + */ + File& File::operator=(const String& filePath) { SetFile(filePath); @@ -292,6 +468,13 @@ namespace Nz return *this; } + /*! + * \brief Moves the other file into this + * \return A reference to this + * + * \param file File to move in this + */ + File& File::operator=(File&& file) noexcept { NazaraLock(m_mutex) @@ -302,9 +485,18 @@ namespace Nz return *this; } + /*! + * \brief Gets the absolute path of the file + * \return Absolute path of the file + * + * \param filePath Path of the file + * + * \remark Produces a NazaraError if filePath is weird with NAZARA_PLATFORM_WINDOWS defined + */ + String File::AbsolutePath(const String& filePath) { - // Je n'utilise pas les fonctions de l'OS car elles ne fonctionnent que pour un chemin existant + // We don't use OS functions because they only work for existing path String path = NormalizePath(filePath); if (path.IsEmpty()) return String(); @@ -319,7 +511,7 @@ namespace Nz base = "\\\\"; start = 2; } - else if (path.StartsWith('\\')) // Spécial : '\' fait référence au disque racine + else if (path.StartsWith('\\')) // Special : '\' refering to root { String drive = Directory::GetCurrent().SubStringTo('\\'); String end = path.SubString(1, -1); @@ -351,14 +543,14 @@ namespace Nz if (path.Split(sep, NAZARA_DIRECTORY_SEPARATOR) <= 1) return path; - // Nous avons un chemin absolu, mais il nous faut un peu le nettoyer + // We have the absolute path, but we need to clean it up for (unsigned int i = 0; i < sep.size(); ++i) { if (sep[i] == '.') sep.erase(sep.begin() + i--); else if (sep[i] == "..") { - if (i > start) // Si nous ne sommes pas dans la partie protégée + if (i > start) // If we are not in the protected area sep.erase(sep.begin() + i--); sep.erase(sep.begin() + i--); @@ -377,6 +569,14 @@ namespace Nz return stream; } + /*! + * \brief Copies the first file to a new file path + * \return true if copy is successful + * + * \param sourcePath Path of the original file + * \param targetPath Path of the copied file + */ + bool File::Copy(const String& sourcePath, const String& targetPath) { if (sourcePath.IsEmpty() || targetPath.IsEmpty()) @@ -385,6 +585,13 @@ namespace Nz return FileImpl::Copy(NormalizePath(sourcePath), NormalizePath(targetPath)); } + /*! + * \brief Deletes the file + * \return true if delete is successful + * + * \param filePath Path of the file + */ + bool File::Delete(const String& filePath) { if (filePath.IsEmpty()) @@ -393,6 +600,13 @@ namespace Nz return FileImpl::Delete(NormalizePath(filePath)); } + /*! + * \brief Checks whether the file exists + * \return true if file exists + * + * \param filePath Path of the file + */ + bool File::Exists(const String& filePath) { if (filePath.IsEmpty()) @@ -401,6 +615,13 @@ namespace Nz return FileImpl::Exists(NormalizePath(filePath)); } + /*! + * \brief Gets the creation time of the file + * \return Information about the creation time + * + * \param filePath Path of the file + */ + time_t File::GetCreationTime(const String& filePath) { if (filePath.IsEmpty()) @@ -409,10 +630,23 @@ namespace Nz return FileImpl::GetCreationTime(NormalizePath(filePath)); } + /*! + * \brief Gets the directory of the file + * \return Directory of the file + * + * \param filePath Path of the file + */ + String File::GetDirectory(const String& filePath) { return filePath.SubStringTo(NAZARA_DIRECTORY_SEPARATOR, -1, true, true); } + /*! + * \brief Gets the last time the file was accessed + * \return Information about the last access time + * + * \param filePath Path of the file + */ time_t File::GetLastAccessTime(const String& filePath) { @@ -422,6 +656,13 @@ namespace Nz return FileImpl::GetLastAccessTime(NormalizePath(filePath)); } + /*! + * \brief Gets the last time the file was written + * \return Information about the last writing time + * + * \param filePath Path of the file + */ + time_t File::GetLastWriteTime(const String& filePath) { if (filePath.IsEmpty()) @@ -430,6 +671,13 @@ namespace Nz return FileImpl::GetLastWriteTime(NormalizePath(filePath)); } + /*! + * \brief Gets the size of the file + * \return Size of the file + * + * \param filePath Path of the file + */ + UInt64 File::GetSize(const String& filePath) { if (filePath.IsEmpty()) @@ -438,6 +686,13 @@ namespace Nz return FileImpl::GetSize(NormalizePath(filePath)); } + /*! + * \brief Checks whether the file path is absolute + * \return true if path is absolute + * + * \param filePath Path to test + */ + bool File::IsAbsolute(const String& filePath) { String path(filePath.Trimmed()); @@ -451,7 +706,7 @@ namespace Nz return true; else if (path.Match("\\\\*")) // Ex: \\Laptop return true; - else if (path.StartsWith('\\')) // Spécial : '\' fait référence au disque racine + else if (path.StartsWith('\\')) // Special : '\' refering to the root return true; else return false; @@ -462,6 +717,13 @@ namespace Nz #endif } + /*! + * \brief Normalizes the file path + * \return Path normalized (replacing '/' with '\\' on Windows, ...) + * + * \param filePath Path to normalize + */ + String File::NormalizePath(const String& filePath) { String path = NormalizeSeparators(filePath.Trimmed()); @@ -475,6 +737,13 @@ namespace Nz return path; } + /*! + * \brief Normalizes the path separator + * \return Path normalized (replacing '/' with '\\' on Windows, ...) + * + * \param filePath Path to normalize + */ + String File::NormalizeSeparators(const String& filePath) { String path(filePath); @@ -490,6 +759,14 @@ namespace Nz return path; } + /*! + * \brief Renames the file with a new name + * \return true if rename is successful + * + * \param sourcePath Path of the original file + * \param targetPath Path of the renamed file + */ + bool File::Rename(const String& sourcePath, const String& targetPath) { if (sourcePath.IsEmpty() || targetPath.IsEmpty()) @@ -498,6 +775,12 @@ namespace Nz return FileImpl::Rename(NormalizePath(sourcePath), NormalizePath(targetPath)); } + /*! + * \brief Flushes the stream + * + * \remark Produces a NazaraAssert if file is not open + */ + void File::FlushStream() { NazaraLock(m_mutex) @@ -507,11 +790,21 @@ namespace Nz m_impl->Flush(); } + /*! + * \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 file is not open + */ + std::size_t File::ReadBlock(void* buffer, std::size_t size) { NazaraLock(m_mutex) - NazaraAssert(IsOpen(), "File is not opened"); + NazaraAssert(IsOpen(), "File is not open"); if (size == 0) return 0; @@ -520,7 +813,7 @@ namespace Nz return m_impl->Read(buffer, size); else { - // Si nous ne devons rien lire, nous avançons simplement + // If we don't have to read, we move forward UInt64 currentPos = m_impl->GetCursorPos(); m_impl->SetCursorPos(CursorPosition_AtCurrent, size); @@ -529,11 +822,22 @@ namespace Nz } } + /*! + * \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 file is not open + * \remark Produces a NazaraAssert if buffer is nullptr + */ + std::size_t File::WriteBlock(const void* buffer, std::size_t size) { NazaraLock(m_mutex) - NazaraAssert(IsOpen(), "File is not opened"); + NazaraAssert(IsOpen(), "File is not open"); if (size == 0) return 0; @@ -543,9 +847,22 @@ namespace Nz return m_impl->Write(buffer, size); } + /*! + * \brief Appends the file to the hash + * \return true if hash is successful + * + * \param hash Hash to append data of the file + * \param originalFile Path of the file + * + * \remark Produces a NazaraAssert if hash is nullptr + * \remark Produces a NazaraError if file could not be open + * \remark Produces a NazaraError if file could not be read + */ NAZARA_CORE_API bool HashAppend(AbstractHash* hash, const File& originalFile) { + NazaraAssert(hash, "Invalid hash"); + File file(originalFile.GetPath()); if (!file.Open(OpenMode_ReadOnly)) { diff --git a/src/Nazara/Core/FileLogger.cpp b/src/Nazara/Core/FileLogger.cpp index d8f23a1b1..bac0cb099 100644 --- a/src/Nazara/Core/FileLogger.cpp +++ b/src/Nazara/Core/FileLogger.cpp @@ -12,6 +12,18 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::FileLogger + * \brief Core class that represents a file logger + */ + + /*! + * \brief Constructs a FileLogger object with a file name + * + * \param logPath Path to log + */ + FileLogger::FileLogger(const String& logPath) : m_outputFile(logPath), m_forceStdOutput(false), @@ -20,28 +32,64 @@ namespace Nz { } + /*! + * \brief Destructs the object + */ + FileLogger::~FileLogger() = default; + /*! + * \brief Enables the log of the time + * + * \param enable If true, enables the time log + */ + void FileLogger::EnableTimeLogging(bool enable) { m_timeLoggingEnabled = enable; } + /*! + * \brief Enables the replication to the stdout + * + * \param enable If true, enables the replication + */ + void FileLogger::EnableStdReplication(bool enable) { m_stdReplicationEnabled = enable; } + /*! + * \brief Checks whether or not the replication to the stdout is enabled + * \return true If replication is enabled + */ + bool FileLogger::IsStdReplicationEnabled() { return m_stdReplicationEnabled; } + /*! + * \brief Checks whether or not the logging of the time is enabled + * \return true If logging of the time is enabled + */ + bool FileLogger::IsTimeLoggingEnabled() { return m_timeLoggingEnabled; } + /*! + * \brief Writes a string in the log + * + * \param string String to log + * + * \remark Produces a NazaraError if file could not be opened + * + * \see WriteError + */ + void FileLogger::Write(const String& string) { if (m_forceStdOutput || m_stdReplicationEnabled) @@ -54,8 +102,8 @@ namespace Nz // To prevent infinite loops m_forceStdOutput = true; - CallOnExit resetOnExit([this] () - { + CallOnExit resetOnExit([this] () + { m_forceStdOutput = false; }); @@ -85,6 +133,18 @@ namespace Nz m_outputFile.Write(stream); } + /*! + * \brief Writes an error in the log + * + * \param type The error type + * \param error The error text + * \param line The line the error occurred + * \param file The file the error occurred + * \param function The function the error occurred + * + * \see Write + */ + void FileLogger::WriteError(ErrorType type, const String& error, unsigned int line, const char* file, const char* function) { if (m_forceStdOutput || m_stdReplicationEnabled) diff --git a/src/Nazara/Core/GuillotineBinPack.cpp b/src/Nazara/Core/GuillotineBinPack.cpp index 05f8736b5..e43e07572 100644 --- a/src/Nazara/Core/GuillotineBinPack.cpp +++ b/src/Nazara/Core/GuillotineBinPack.cpp @@ -16,13 +16,37 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::GuillotineBinPack + * \brief Core class that represents the "Guillotine problem", combination of the "Bin packing problem" and the "cutting stock" + */ + namespace { + /*! + * \brief Gets the score for fitting the area + * \return Score of the fitting + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreBestAreaFit(int width, int height, const Rectui& freeRectSize) { return freeRectSize.width * freeRectSize.height - width * height; } + /*! + * \brief Gets the score for fitting the area following long side + * \return Score of the fitting following long side + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreBestLongSideFit(int width, int height, const Rectui& freeRectSize) { int leftoverHoriz = std::abs(static_cast(freeRectSize.width - width)); @@ -32,6 +56,15 @@ namespace Nz return leftover; } + /*! + * \brief Gets the score for fitting the area following short side + * \return Score of the fitting following short side + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreBestShortSideFit(int width, int height, const Rectui& freeRectSize) { int leftoverHoriz = std::abs(static_cast(freeRectSize.width - width)); @@ -41,37 +74,85 @@ namespace Nz return leftover; } + /*! + * \brief Gets the worst score for fitting the area + * \return Worst score of the fitting + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreWorstAreaFit(int width, int height, const Rectui& freeRectSize) { return -ScoreBestAreaFit(width, height, freeRectSize); } + /*! + * \brief Gets the worst score for fitting the area following long side + * \return Worst score of the fitting following long side + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreWorstLongSideFit(int width, int height, const Rectui& freeRectSize) { return -ScoreBestLongSideFit(width, height, freeRectSize); } + /*! + * \brief Gets the worst score for fitting the area following short side + * \return Worst score of the fitting following short side + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreWorstShortSideFit(int width, int height, const Rectui& freeRectSize) { return -ScoreBestShortSideFit(width, height, freeRectSize); } } + /*! + * \brief Constructs a GuillotineBinPack object by default + */ + GuillotineBinPack::GuillotineBinPack() { Reset(); } + /*! + * \brief Constructs a GuillotineBinPack object with width and height + * + * \param width Width + * \param height Height + */ + GuillotineBinPack::GuillotineBinPack(unsigned int width, unsigned int height) { Reset(width, height); } + /*! + * \brief Constructs a GuillotineBinPack object with area + * + * \param size Vector2 representing the area (width, height) + */ + GuillotineBinPack::GuillotineBinPack(const Vector2ui& size) { Reset(size); } + /*! + * \brief Clears the content + */ + void GuillotineBinPack::Clear() { m_freeRectangles.clear(); @@ -80,6 +161,15 @@ namespace Nz m_usedArea = 0; } + /*! + * \brief Expands the content + * + * \param newWidth New width for the expansion + * \param newHeight New height for the expansion + * + * \see Expand + */ + void GuillotineBinPack::Expand(unsigned int newWidth, unsigned newHeight) { unsigned int oldWidth = m_width; @@ -98,52 +188,123 @@ namespace Nz while (MergeFreeRectangles()); } + /*! + * \brief Expands the content + * + * \param newSize New area for the expansion + * + * \see Expand + */ + void GuillotineBinPack::Expand(const Vector2ui& newSize) { Expand(newSize.x, newSize.y); } + /*! + * \brief Frees the rectangle + * + * \param rect Area to free + * + * \remark This method should only be called with computed rectangles by the method Insert and can produce fragmentation + */ + void GuillotineBinPack::FreeRectangle(const Rectui& rect) { - ///DOC: Cette méthode ne devrait recevoir que des rectangles calculés par la méthode Insert et peut provoquer de la fragmentation m_freeRectangles.push_back(rect); m_usedArea -= rect.width * rect.height; } + /*! + * \brief Gets the height + * \return Height of the area + */ + unsigned int GuillotineBinPack::GetHeight() const { return m_height; } + /*! + * \brief Gets percentage of occupation + * \return Percentage of the already occupied area + */ + float GuillotineBinPack::GetOccupancy() const { return static_cast(m_usedArea)/(m_width*m_height); } + /*! + * \brief Gets the size of the area + * \return Size of the area + */ + Vector2ui GuillotineBinPack::GetSize() const { return Vector2ui(m_width, m_height); } + /*! + * \brief Gets the width + * \return Width of the area + */ + unsigned int GuillotineBinPack::GetWidth() const { return m_width; } + /*! + * \brief Inserts rectangles in the area + * \return true if each rectangle could be inserted + * + * \param rects List of rectangles + * \param count Count of rectangles + * \param merge Merge possible + * \param rectChoice Heuristic to use to free + * \param splitMethod Heuristic to use to split + */ + bool GuillotineBinPack::Insert(Rectui* rects, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) { return Insert(rects, nullptr, nullptr, count, merge, rectChoice, splitMethod); } + /*! + * \brief Inserts rectangles in the area + * \return true if each rectangle could be inserted + * + * \param rects List of rectangles + * \param flipped List of flipped rectangles + * \param count Count of rectangles + * \param merge Merge possible + * \param rectChoice Heuristic to use to free + * \param splitMethod Heuristic to use to split + */ + bool GuillotineBinPack::Insert(Rectui* rects, bool* flipped, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) { return Insert(rects, flipped, nullptr, count, merge, rectChoice, splitMethod); } + /*! + * \brief Inserts rectangles in the area + * \return true if each rectangle could be inserted + * + * \param rects List of rectangles + * \param flipped List of flipped rectangles + * \param flipped List of inserted rectangles + * \param count Count of rectangles + * \param merge Merge possible + * \param rectChoice Heuristic to use to free + * \param splitMethod Heuristic to use to split + */ + bool GuillotineBinPack::Insert(Rectui* rects, bool* flipped, bool* inserted, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) { - std::vector remainingRects(count); // La position du rectangle + std::vector remainingRects(count); // Position of the rectangle for (unsigned int i = 0; i < count; ++i) remainingRects[i] = &rects[i]; @@ -214,7 +375,7 @@ namespace Nz // If we didn't manage to find any rectangle to pack, abort. if (bestScore == std::numeric_limits::max()) { - // Si nous le pouvons, on marque les rectangles n'ayant pas pu être insérés + // If we can do it, we mark the rectangle could be inserted if (inserted) { for (Rectui* rect : remainingRects) @@ -259,9 +420,13 @@ namespace Nz return true; } + /*! + * \brief Merges free rectangles together + * \return true if there was a merge (and thus if a merge is still possible) + */ + bool GuillotineBinPack::MergeFreeRectangles() { - ///DOC: Renvoie true s'il y a eu fusion (et donc si une fusion est encore possible) std::size_t oriSize = m_freeRectangles.size(); // Do a Theta(n^2) loop to see if any pair of free rectangles could me merged into one. @@ -312,6 +477,10 @@ namespace Nz return m_freeRectangles.size() < oriSize; } + /*! + * \brief Resets the area + */ + void GuillotineBinPack::Reset() { m_height = 0; @@ -320,6 +489,13 @@ namespace Nz Clear(); } + /*! + * \brief Resets the area + * + * \param width Width + * \param height Height + */ + void GuillotineBinPack::Reset(unsigned int width, unsigned int height) { m_height = height; @@ -328,11 +504,25 @@ namespace Nz Clear(); } + /*! + * \brief Resets the area + * + * \param size Size of the area + */ + void GuillotineBinPack::Reset(const Vector2ui& size) { Reset(size.x, size.y); } + /*! + * \brief Splits the free rectangle along axis + * + * \param freeRect Free rectangle to split + * \param placedRect Already placed rectangle + * \param splitHorizontal Split horizontally (or vertically) + */ + void GuillotineBinPack::SplitFreeRectAlongAxis(const Rectui& freeRect, const Rectui& placedRect, bool splitHorizontal) { // Form the two new rectangles. @@ -365,50 +555,60 @@ namespace Nz m_freeRectangles.push_back(right); } + /*! + * \brief Splits the free rectangle using the heuristic + * + * \param freeRect Free rectangle to split + * \param placedRect Already placed rectangle + * \param method Method used to split + * + * \remark Produces a NazaraError if enumeration GuillotineSplitHeuristic is invalid + */ + void GuillotineBinPack::SplitFreeRectByHeuristic(const Rectui& freeRect, const Rectui& placedRect, GuillotineSplitHeuristic method) { - // Compute the lengths of the leftover area. + // Compute the lengths of the leftover area const int w = freeRect.width - placedRect.width; const int h = freeRect.height - placedRect.height; // Placing placedRect into freeRect results in an L-shaped free area, which must be split into - // two disjoint rectangles. This can be achieved with by splitting the L-shape using a single line. - // We have two choices: horizontal or vertical. + // two disjoint rectangles. This can be achieved with by splitting the L-shape using a single line + // We have two choices: horizontal or vertical - // Use the given heuristic to decide which choice to make. + // Use the given heuristic to decide which choice to make bool splitHorizontal; switch (method) { case SplitLongerAxis: - // Split along the longer total axis. + // Split along the longer total axis splitHorizontal = (freeRect.width > freeRect.height); break; case SplitLongerLeftoverAxis: - // Split along the longer leftover axis. + // Split along the longer leftover axis splitHorizontal = (w > h); break; case SplitMaximizeArea: - // Maximize the smaller area == minimize the larger area. - // Tries to make the rectangles more even-sized. + // Maximize the smaller area == minimize the larger area + // Tries to make the rectangles more even-sized splitHorizontal = (placedRect.width * h <= w * placedRect.height); break; case SplitMinimizeArea: - // Maximize the larger area == minimize the smaller area. - // Tries to make the single bigger rectangle. + // Maximize the larger area == minimize the smaller area + // Tries to make the single bigger rectangle splitHorizontal = (placedRect.width * h > w * placedRect.height); break; case SplitShorterAxis: - // Split along the shorter total axis. + // Split along the shorter total axis splitHorizontal = (freeRect.width <= freeRect.height); break; case SplitShorterLeftoverAxis: - // Split along the shorter leftover axis. + // Split along the shorter leftover axis splitHorizontal = (w <= h); break; @@ -417,10 +617,22 @@ namespace Nz splitHorizontal = true; } - // Perform the actual split. + // Perform the actual split SplitFreeRectAlongAxis(freeRect, placedRect, splitHorizontal); } + /*! + * \brief Gets the score using heuristic + * \return Score of the heuristic + * + * \param width Width + * \param height Height + * \param freeRect Free area + * \param rectChoice Heuristic to get score + * + * \remark Produces a NazaraError if enumeration FreeRectChoiceHeuristic is invalid + */ + int GuillotineBinPack::ScoreByHeuristic(int width, int height, const Rectui& freeRect, FreeRectChoiceHeuristic rectChoice) { switch (rectChoice) diff --git a/src/Nazara/Core/HardwareInfo.cpp b/src/Nazara/Core/HardwareInfo.cpp index 1ada7a794..baef7c2f3 100644 --- a/src/Nazara/Core/HardwareInfo.cpp +++ b/src/Nazara/Core/HardwareInfo.cpp @@ -82,11 +82,32 @@ namespace Nz char s_brandString[48] = "Not initialized"; } + /*! + * \ingroup core + * \class Nz::HardwareInfo + * \brief Core class that represents the info we can get from hardware + */ + + /*! + * \brief Generates the cpuid instruction (available on x86 & x64) + * + * \param functionId Information to retrieve + * \param subFunctionId Additional code for information retrieval + * \param result Supported features of the CPU + */ + void HardwareInfo::Cpuid(UInt32 functionId, UInt32 subFunctionId, UInt32 result[4]) { return HardwareInfoImpl::Cpuid(functionId, subFunctionId, result); } + /*! + * \brief Gets the brand of the processor + * \return String of the brand + * + * \remark Produces a NazaraError if not Initialize + */ + String HardwareInfo::GetProcessorBrandString() { if (!Initialize()) @@ -95,13 +116,26 @@ namespace Nz return s_brandString; } + /*! + * \brief Gets the number of threads + * \return Number of threads available on the CPU + * + * \remark Doesn't need the initialization of HardwareInfo + */ + unsigned int HardwareInfo::GetProcessorCount() { - ///DOC: Ne nécessite pas l'initialisation de HardwareInfo pour fonctionner static unsigned int processorCount = std::max(HardwareInfoImpl::GetProcessorCount(), 1U); return processorCount; } + /*! + * \brief Gets the processor vendor + * \return ProcessorVendor containing information the vendor + * + * \remark Produces a NazaraError if not Initialize + */ + ProcessorVendor HardwareInfo::GetProcessorVendor() { if (!Initialize()) @@ -110,6 +144,13 @@ namespace Nz return s_vendorEnum; } + /*! + * \brief Gets the vendor of the processor + * \return String of the vendor + * + * \remark Produces a NazaraError if not Initialize + */ + String HardwareInfo::GetProcessorVendorName() { if (!Initialize()) @@ -118,13 +159,26 @@ namespace Nz return vendorNames[s_vendorEnum+1]; } + /*! + * \brief Gets the amount of total memory + * \return Number of total memory available + * + * \remark Doesn't need the initialization of HardwareInfo + */ + UInt64 HardwareInfo::GetTotalMemory() { - ///DOC: Ne nécessite pas l'initialisation de HardwareInfo pour fonctionner static UInt64 totalMemory = HardwareInfoImpl::GetTotalMemory(); return totalMemory; } + /*! + * \brief Checks whether the processor owns the capacity to handle certain instructions + * \return true If instructions supported + * + * \remark Produces a NazaraError if capability is a wrong enum with NAZARA_DEBUG defined + */ + bool HardwareInfo::HasCapability(ProcessorCap capability) { #ifdef NAZARA_DEBUG @@ -138,9 +192,16 @@ namespace Nz return s_capabilities[capability]; } + /*! + * \brief Initializes the HardwareInfo class + * \return true if successful + * + * \remark Produces a NazaraError if cpuid is not supported + */ + bool HardwareInfo::Initialize() { - if (s_initialized) + if (IsInitialized()) return true; if (!HardwareInfoImpl::IsCpuidSupported()) @@ -151,21 +212,21 @@ namespace Nz s_initialized = true; - UInt32 registers[4]; // Récupère les quatre registres (EAX, EBX, ECX et EDX) + UInt32 registers[4]; // To store our registers values (EAX, EBX, ECX and EDX) - // Pour plus de clarté + // Let's make it clearer UInt32& eax = registers[0]; UInt32& ebx = registers[1]; UInt32& ecx = registers[2]; UInt32& edx = registers[3]; - // Pour commencer, on va récupérer l'identifiant du constructeur ainsi que l'id de fonction maximal supporté par le CPUID + // To begin, we get the id of the constructor and the id of maximal functions supported by the CPUID HardwareInfoImpl::Cpuid(0, 0, registers); - // Attention à l'ordre : EBX, EDX, ECX + // Note the order: EBX, EDX, ECX UInt32 manufacturerId[3] = {ebx, edx, ecx}; - // Identification du concepteur + // Identification of conceptor s_vendorEnum = ProcessorVendor_Unknown; for (const VendorString& vendorString : vendorStrings) { @@ -178,7 +239,7 @@ namespace Nz if (eax >= 1) { - // Récupération de certaines capacités du processeur (ECX et EDX, fonction 1) + // Retrieval of certain capacities of the processor (ECX et EDX, function 1) HardwareInfoImpl::Cpuid(1, 0, registers); s_capabilities[ProcessorCap_AVX] = (ecx & (1U << 28)) != 0; @@ -192,53 +253,67 @@ namespace Nz s_capabilities[ProcessorCap_SSE42] = (ecx & (1U << 20)) != 0; } - // Récupération de la plus grande fonction étendue supportée (EAX, fonction 0x80000000) + // Retrieval of biggest extended function handled (EAX, function 0x80000000) HardwareInfoImpl::Cpuid(0x80000000, 0, registers); UInt32 maxSupportedExtendedFunction = eax; if (maxSupportedExtendedFunction >= 0x80000001) { - // Récupération des capacités étendues du processeur (ECX et EDX, fonction 0x80000001) + // Retrieval of extended capabilities of the processor (ECX and EDX, function 0x80000001) HardwareInfoImpl::Cpuid(0x80000001, 0, registers); - s_capabilities[ProcessorCap_x64] = (edx & (1U << 29)) != 0; // Support du 64bits, indépendant de l'OS + s_capabilities[ProcessorCap_x64] = (edx & (1U << 29)) != 0; // Support of 64bits, independent of the OS s_capabilities[ProcessorCap_FMA4] = (ecx & (1U << 16)) != 0; s_capabilities[ProcessorCap_SSE4a] = (ecx & (1U << 6)) != 0; s_capabilities[ProcessorCap_XOP] = (ecx & (1U << 11)) != 0; if (maxSupportedExtendedFunction >= 0x80000004) { - // Récupération d'une chaîne de caractère décrivant le processeur (EAX, EBX, ECX et EDX, - // fonctions de 0x80000002 à 0x80000004 compris) + // Retrieval of the string describing the processor (EAX, EBX, ECX and EDX, + // functions from 0x80000002 to 0x80000004 inclusive) char* ptr = &s_brandString[0]; for (UInt32 code = 0x80000002; code <= 0x80000004; ++code) { HardwareInfoImpl::Cpuid(code, 0, registers); - std::memcpy(ptr, ®isters[0], 4*sizeof(UInt32)); // On rajoute les 16 octets à la chaîne + std::memcpy(ptr, ®isters[0], 4*sizeof(UInt32)); // We add the 16 bytes to the string ptr += 4*sizeof(UInt32); } - // Le caractère nul faisant partie de la chaîne retournée par le CPUID, pas besoin de le rajouter + // The character '\0' is already part of the string } } return true; } + /*! + * \brief Checks whether the instruction of cpuid is supported + * \return true if it the case + */ + bool HardwareInfo::IsCpuidSupported() { return HardwareInfoImpl::IsCpuidSupported(); } + /*! + * \brief Checks whether the class HardwareInfo is initialized + * \return true if it is initialized + */ + bool HardwareInfo::IsInitialized() { return s_initialized; } + /*! + * \brief Unitializes the class HardwareInfo + */ + void HardwareInfo::Uninitialize() { - // Rien à faire + // Nothing to do s_initialized = false; } } diff --git a/src/Nazara/Core/LockGuard.cpp b/src/Nazara/Core/LockGuard.cpp deleted file mode 100644 index b59596a7c..000000000 --- a/src/Nazara/Core/LockGuard.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) 2015 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 -#include - -namespace Nz -{ - LockGuard::LockGuard(Mutex& mutex) : - m_mutex(mutex) - { - m_mutex.Lock(); - } - - LockGuard::~LockGuard() - { - m_mutex.Unlock(); - } -} diff --git a/src/Nazara/Core/Log.cpp b/src/Nazara/Core/Log.cpp index e554b7631..9e6b13353 100644 --- a/src/Nazara/Core/Log.cpp +++ b/src/Nazara/Core/Log.cpp @@ -15,21 +15,49 @@ namespace Nz StdLogger s_stdLogger; } + /*! + * \ingroup core + * \class Nz::Log + * \brief Core class that represents a logger + */ + + /*! + * \brief Enables the log + * + * \param enable If true, enables the log + */ + void Log::Enable(bool enable) { s_enabled = enable; } + /*! + * \brief Gets the logger + * \return An abstract pointer to the logger + */ + AbstractLogger* Log::GetLogger() { return s_logger; } + /*! + * \brief Checks whether or not the logging is enabled + * \return true If logging is enabled + */ + bool Log::IsEnabled() { return s_enabled; } + /*! + * \brief Sets the logger + * + * \param logger AbstractLogger to log + */ + void Log::SetLogger(AbstractLogger* logger) { if (s_logger != &s_stdLogger) @@ -40,28 +68,57 @@ namespace Nz s_logger = &s_stdLogger; } + /*! + * \brief Writes a string in the log + * + * \param string String to log + * + * \see WriteError + */ + void Log::Write(const String& string) { if (s_enabled) s_logger->Write(string); - + OnLogWrite(string); } + /*! + * \brief Writes the error in the log + * + * \param type Type of the error + * \param error Message of the error + * \param line Line of the error + * \param file File of the error + * \param function Function of the error + * + * \see Write + */ + void Log::WriteError(ErrorType type, const String& error, unsigned int line, const char* file, const char* function) { if (s_enabled) s_logger->WriteError(type, error, line, file, function); - + OnLogWriteError(type, error, line, file, function); } + /*! + * \brief Initializes the Log class + * \return true if successful + */ + bool Log::Initialize() { SetLogger(new FileLogger()); return true; } + /*! + * \brief Unitializes the Log class + */ + void Log::Uninitialize() { SetLogger(nullptr); diff --git a/src/Nazara/Core/MemoryManager.cpp b/src/Nazara/Core/MemoryManager.cpp index db58f2299..3a4883bec 100644 --- a/src/Nazara/Core/MemoryManager.cpp +++ b/src/Nazara/Core/MemoryManager.cpp @@ -15,7 +15,7 @@ #include #endif -// Le seul fichier n'ayant pas à inclure Debug.hpp +// The only file that does not need to include Debug.hpp namespace Nz { @@ -61,18 +61,44 @@ namespace Nz CRITICAL_SECTION s_mutex; #elif defined(NAZARA_PLATFORM_POSIX) pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER; + #else + #error Lack of implementation: Mutex #endif } + + /*! + * \ingroup core + * \class Nz::MemoryManager + * \brief Core class that represents a manager for the memory + */ + + /*! + * \brief Constructs a MemoryManager object by default + */ MemoryManager::MemoryManager() { } + /*! + * \brief Destructs a MemoryManager object and calls Unitialize + */ + MemoryManager::~MemoryManager() { Uninitialize(); } + /*! + * \brief Allocates memory + * \return Raw memory allocated + * + * \param size Size to allocate + * \parma multi Array or not + * \param file File of the allocation + * \param line Line of the allocation in the file + */ + void* MemoryManager::Allocate(std::size_t size, bool multi, const char* file, unsigned int line) { if (!s_initialized) @@ -84,7 +110,7 @@ namespace Nz pthread_mutex_lock(&s_mutex); #endif - Block* ptr = reinterpret_cast(std::malloc(size+sizeof(Block))); + Block* ptr = static_cast(std::malloc(size+sizeof(Block))); if (!ptr) { char timeStr[23]; @@ -147,22 +173,43 @@ namespace Nz return reinterpret_cast(ptr) + sizeof(Block); } + /*! + * \brief Enables the filling of the allocation + * + * \param allocationFilling If true, sets the rest of the allocation block to '0xFF' + */ + void MemoryManager::EnableAllocationFilling(bool allocationFilling) { s_allocationFilling = allocationFilling; } + /*! + * \brief Enables the logging of the allocation + * + * \param logAllocations If true, registers every allocation + */ + void MemoryManager::EnableAllocationLogging(bool logAllocations) { s_allocationLogging = logAllocations; } + /*! + * \brief Frees the pointer + * + * \param pointer Pointer to free + * \param multi Array or not + * + * \remark If pointer is nullptr, nothing is done + */ + void MemoryManager::Free(void* pointer, bool multi) { if (!pointer) return; - Block* ptr = reinterpret_cast(reinterpret_cast(pointer) - sizeof(Block)); + Block* ptr = reinterpret_cast(static_cast(pointer) - sizeof(Block)); if (ptr->magic != s_allocatedId) { char timeStr[23]; @@ -227,37 +274,73 @@ namespace Nz #endif } + /*! + * \brief Gets the number of allocated blocks + * \return Number of allocated blocks + */ + unsigned int MemoryManager::GetAllocatedBlockCount() { return s_allocatedBlock; } + /*! + * \brief Gets the allocated size + * \return Size of total allocation + */ + std::size_t MemoryManager::GetAllocatedSize() { return s_allocatedSize; } + /*! + * \brief Gets the number of allocations + * \return Number of allocations + */ + unsigned int MemoryManager::GetAllocationCount() { return s_allocationCount; } + /*! + * \brief Checks whether the filling of allocation is enabled + * \return true if it is filling + */ + bool MemoryManager::IsAllocationFillingEnabled() { return s_allocationFilling; } + /*! + * \brief Checks whether the logging of allocation is enabled + * \return true if it is logging + */ + bool MemoryManager::IsAllocationLoggingEnabled() { return s_allocationLogging; } + /*! + * \brief Sets the next free + * + * \param file Name of the file + * \param line Line in the file + */ + void MemoryManager::NextFree(const char* file, unsigned int line) { s_nextFreeFile = file; s_nextFreeLine = line; } + /*! + * \brief Initializes the MemoryManager + */ + void MemoryManager::Initialize() { char timeStr[23]; @@ -282,12 +365,22 @@ namespace Nz s_initialized = true; } + /*! + * \brief Gets the time + * + * \param buffer Buffer to set the time in + */ + void MemoryManager::TimeInfo(char buffer[23]) { time_t currentTime = std::time(nullptr); std::strftime(buffer, 23, "%d/%m/%Y - %H:%M:%S:", std::localtime(¤tTime)); } + /*! + * \brief Uninitializes the MemoryManager + */ + void MemoryManager::Uninitialize() { #ifdef NAZARA_PLATFORM_WINDOWS @@ -334,5 +427,5 @@ namespace Nz } std::fclose(log); -} + } } diff --git a/src/Nazara/Core/MemoryStream.cpp b/src/Nazara/Core/MemoryStream.cpp new file mode 100644 index 000000000..bb77f35f8 --- /dev/null +++ b/src/Nazara/Core/MemoryStream.cpp @@ -0,0 +1,144 @@ +// Copyright (C) 2015 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 +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup core + * \class Nz::MemoryStream + * \brief Core class that represents a stream of memory + */ + + /*! + * \brief Clears the content of the stream + */ + + void MemoryStream::Clear() + { + m_buffer->Clear(); + m_pos = 0; + } + + /*! + * \brief Checks whether the stream reached the end of the stream + * \return true if cursor is at the end of the stream + */ + + bool MemoryStream::EndOfStream() const + { + return m_pos >= m_buffer->size(); + } + + /*! + * \brief Gets the position of the cursor + * \return Position of the cursor + */ + + UInt64 MemoryStream::GetCursorPos() const + { + return m_pos; + } + + /*! + * \brief Gets the size of the raw memory + * \return Size of the memory + */ + + UInt64 MemoryStream::GetSize() const + { + return m_buffer->GetSize(); + } + + /*! + * \brief Sets the buffer for the memory stream + * + * \param byteArray Bytes to stream + * \param openMode Reading/writing mode for the stream + * + * \remark Produces a NazaraAssert if byteArray is nullptr + */ + + void MemoryStream::SetBuffer(ByteArray* byteArray, UInt32 openMode) + { + NazaraAssert(byteArray, "Invalid ByteArray"); + + m_buffer = byteArray; + m_openMode = openMode; + } + + /*! + * \brief Sets the position of the cursor + * \return true + * + * \param offset Offset according to the beginning of the stream + */ + + bool MemoryStream::SetCursorPos(UInt64 offset) + { + m_pos = offset; + + return true; + } + + /*! + * \brief Flushes the stream + */ + + void MemoryStream::FlushStream() + { + // Nothing to flush + } + + /*! + * \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 + */ + + std::size_t MemoryStream::ReadBlock(void* buffer, std::size_t size) + { + if (EndOfStream()) + return 0; + + std::size_t readSize = std::min(size, static_cast(m_buffer->GetSize() - m_pos)); + + if (buffer) + std::memcpy(buffer, m_buffer->GetConstBuffer() + m_pos, readSize); + + m_pos += readSize; + return readSize; + } + + /*! + * \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 + */ + + std::size_t MemoryStream::WriteBlock(const void* buffer, std::size_t size) + { + std::size_t endPos = static_cast(m_pos + size); + if (endPos > m_buffer->GetSize()) + m_buffer->Resize(endPos); + + NazaraAssert(buffer, "Invalid buffer"); + + std::memcpy(m_buffer->GetBuffer() + m_pos, buffer, size); + + m_pos = endPos; + return size; + } +} diff --git a/src/Nazara/Core/MemoryView.cpp b/src/Nazara/Core/MemoryView.cpp index a630241d5..e501a031f 100644 --- a/src/Nazara/Core/MemoryView.cpp +++ b/src/Nazara/Core/MemoryView.cpp @@ -9,29 +9,83 @@ namespace Nz { - MemoryView::MemoryView(const void* ptr, UInt64 size) : - Stream(StreamOption_None, OpenMode_ReadOnly), - m_ptr(reinterpret_cast(ptr)), + /*! + * \ingroup core + * \class Nz::MemoryView + * \brief Core class that represents a view of the memory behaving like a stream + */ + + /*! + * \brief Constructs a MemoryView object with a raw memory and a size + * + * \param ptr Pointer to raw memory + * \param size Size that can be read + * + * \remark If preallocated space of ptr is less than the size, the behaviour is undefined + */ + + MemoryView::MemoryView(void* ptr, UInt64 size) : + Stream(StreamOption_None, OpenMode_ReadWrite), + m_ptr(static_cast(ptr)), m_pos(0), m_size(size) { } + /*! + * \brief Constructs a MemoryView object with a raw memory and a size + * + * \param ptr Constant pointer to raw memory + * \param size Size that can be read + * + * \remark If preallocated space of ptr is less than the size, the behaviour is undefined + */ + + MemoryView::MemoryView(const void* ptr, UInt64 size) : + Stream(StreamOption_None, OpenMode_ReadOnly), + m_ptr(static_cast(const_cast(ptr))), //< Okay, right, const_cast is bad, but this pointer is still read-only + m_pos(0), + m_size(size) + { + } + + /*! + * \brief Checks whether the stream reached the end of the stream + * \return true if cursor is at the end of the stream + */ + bool MemoryView::EndOfStream() const { return m_pos >= m_size; } + /*! + * \brief Gets the position of the cursor + * \return Position of the cursor + */ + UInt64 MemoryView::GetCursorPos() const { return m_pos; } + /*! + * \brief Gets the size of the raw memory + * \return Size of the memory + */ + UInt64 MemoryView::GetSize() const { return m_size; } + /*! + * \brief Sets the position of the cursor + * \return true + * + * \param offset Offset according to the beginning of the stream + */ + bool MemoryView::SetCursorPos(UInt64 offset) { m_pos = std::min(offset, m_size); @@ -39,11 +93,23 @@ namespace Nz return true; } + /*! + * \brief Flushes the stream + */ + void MemoryView::FlushStream() { - NazaraInternalError("FlushStream has been called on a MemoryView"); + // Nothing to do } + /*! + * \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 + */ + std::size_t MemoryView::ReadBlock(void* buffer, std::size_t size) { std::size_t readSize = std::min(size, static_cast(m_size - m_pos)); @@ -55,9 +121,27 @@ namespace Nz return readSize; } + /*! + * \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 + */ + std::size_t MemoryView::WriteBlock(const void* buffer, std::size_t size) { - NazaraInternalError("WriteBlock has been called on a MemoryView"); - return 0; + std::size_t endPos = static_cast(m_pos + size); + if (endPos > m_size) + size = static_cast(m_size - m_pos); + + NazaraAssert(buffer, "Invalid buffer"); + + std::memcpy(&m_ptr[m_pos], buffer, size); + + m_pos += size; + return size; } } diff --git a/src/Nazara/Core/Mutex.cpp b/src/Nazara/Core/Mutex.cpp index 1245cd801..68c94a8cf 100644 --- a/src/Nazara/Core/Mutex.cpp +++ b/src/Nazara/Core/Mutex.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #if defined(NAZARA_PLATFORM_WINDOWS) #include @@ -16,28 +17,78 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Mutex + * \brief Core class that represents a binary semaphore, a mutex + * + * \remark The mutex is recursive, it means that a thread who owns the mutex can call the same function which needs the same mutex + */ + + /*! + * \brief Constructs a Mutex object by default + */ + Mutex::Mutex() { m_impl = new MutexImpl; } + /*! + * \brief Destructs the object + */ + Mutex::~Mutex() { delete m_impl; } + /*! + * \brief Locks the mutex + * + * If another thread has already locked the mutex, a call to lock will block execution until the lock is acquired. A thread may call lock on a recursive mutex repeatedly. Ownership will only be released after the thread makes a matching number of calls to unlock + */ + void Mutex::Lock() { + NazaraAssert(m_impl, "Cannot lock a moved mutex"); m_impl->Lock(); } + /*! + * \brief Tries to lock the mutex + * \return true if the lock was acquired successfully + */ + bool Mutex::TryLock() { + NazaraAssert(m_impl, "Cannot lock a moved mutex"); return m_impl->TryLock(); } + /*! + * \brief Unlocks the mutex + * + * Unlocks the mutex if its level of ownership is 1 (there was exactly one more call to Lock() than there were calls to Unlock() made by this thread), reduces the level of ownership by 1 otherwise + */ + void Mutex::Unlock() { + NazaraAssert(m_impl, "Cannot unlock a moved mutex"); m_impl->Unlock(); } + + /*! + * \brief Moves a mutex to another mutex object + * \return A reference to the object + */ + Mutex& Mutex::operator=(Mutex&& mutex) noexcept + { + delete m_impl; + + m_impl = mutex.m_impl; + mutex.m_impl = nullptr; + + return *this; + } } diff --git a/src/Nazara/Core/ParameterList.cpp b/src/Nazara/Core/ParameterList.cpp index 5b63218af..5edd8f976 100644 --- a/src/Nazara/Core/ParameterList.cpp +++ b/src/Nazara/Core/ParameterList.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -12,16 +13,34 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::ParameterList + * \brief Core class that represents a list of parameters + */ + + /*! + * \brief Constructs a ParameterList object by copy + */ + ParameterList::ParameterList(const ParameterList& list) { operator=(list); } + /*! + * \brief Destructs the object and clears + */ + ParameterList::~ParameterList() { Clear(); } + /*! + * \brief Clears the list of parameters + */ + void ParameterList::Clear() { for (auto it = m_parameters.begin(); it != m_parameters.end(); ++it) @@ -30,8 +49,24 @@ namespace Nz m_parameters.clear(); } + /*! + * \brief Gets a boolean parameter by name + * \return true if success + * + * \param name Name of the variable + * \param value Value to set + * + * \remark Produces a NazaraAssert if pointer is invalid + * \remark Produces a silent NazaraError if name is not a variable + * \remark Produces a silent NazaraError if value could not be convertible + */ + bool ParameterList::GetBooleanParameter(const String& name, bool* value) const { + NazaraAssert(value, "Invalid pointer"); + + ErrorFlags flags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled); + auto it = m_parameters.find(name); if (it == m_parameters.end()) { @@ -50,16 +85,16 @@ namespace Nz return true; case ParameterType_String: - { - bool converted; - if (it->second.value.stringVal.ToBool(&converted, String::CaseInsensitive)) { - *value = converted; - return true; - } + bool converted; + if (it->second.value.stringVal.ToBool(&converted, String::CaseInsensitive)) + { + *value = converted; + return true; + } - break; - } + break; + } case ParameterType_Float: case ParameterType_None: @@ -72,8 +107,24 @@ namespace Nz return false; } + /*! + * \brief Gets a float parameter by name + * \return true if success + * + * \param name Name of the variable + * \param value Value to set + * + * \remark Produces a NazaraAssert if pointer is invalid + * \remark Produces a silent NazaraError if name is not a variable + * \remark Produces a silent NazaraError if value could not be convertible + */ + bool ParameterList::GetFloatParameter(const String& name, float* value) const { + NazaraAssert(value, "Invalid pointer"); + + ErrorFlags flags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled); + auto it = m_parameters.find(name); if (it == m_parameters.end()) { @@ -92,16 +143,16 @@ namespace Nz return true; case ParameterType_String: - { - double converted; - if (it->second.value.stringVal.ToDouble(&converted)) { - *value = static_cast(converted); - return true; - } + double converted; + if (it->second.value.stringVal.ToDouble(&converted)) + { + *value = static_cast(converted); + return true; + } - break; - } + break; + } case ParameterType_Boolean: case ParameterType_None: @@ -114,8 +165,24 @@ namespace Nz return false; } + /*! + * \brief Gets a integer parameter by name + * \return true if success + * + * \param name Name of the variable + * \param value Value to set + * + * \remark Produces a NazaraAssert if pointer is invalid + * \remark Produces a silent NazaraError if name is not a variable + * \remark Produces a silent NazaraError if value could not be convertible + */ + bool ParameterList::GetIntegerParameter(const String& name, int* value) const { + NazaraAssert(value, "Invalid pointer"); + + ErrorFlags flags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled); + auto it = m_parameters.find(name); if (it == m_parameters.end()) { @@ -138,18 +205,18 @@ namespace Nz return false; case ParameterType_String: - { - long long converted; - if (it->second.value.stringVal.ToInteger(&converted)) { - if (converted <= std::numeric_limits::max() && converted >= std::numeric_limits::min()) + long long converted; + if (it->second.value.stringVal.ToInteger(&converted)) { - *value = static_cast(converted); - return true; + if (converted <= std::numeric_limits::max() && converted >= std::numeric_limits::min()) + { + *value = static_cast(converted); + return true; + } } + break; } - break; - } case ParameterType_None: case ParameterType_Pointer: @@ -161,8 +228,20 @@ namespace Nz return false; } + /*! + * \brief Gets a parameter type by name + * \return true if the parameter is present + * + * \param name Name of the variable + * \param type Pointer to a variable to hold result + * + * \remark Produces a NazaraAssert if type is invalid + */ + bool ParameterList::GetParameterType(const String& name, ParameterType* type) const { + NazaraAssert(type, "Invalid pointer"); + auto it = m_parameters.find(name); if (it == m_parameters.end()) return false; @@ -172,8 +251,24 @@ namespace Nz return true; } + /*! + * \brief Gets a pointer parameter by name + * \return true if success + * + * \param name Name of the variable + * \param value Value to set + * + * \remark Produces a NazaraAssert if pointer is invalid + * \remark Produces a silent NazaraError if name is not a variable + * \remark Produces a silent NazaraError if value could not be convertible + */ + bool ParameterList::GetPointerParameter(const String& name, void** value) const { + NazaraAssert(value, "Invalid pointer"); + + ErrorFlags flags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled); + auto it = m_parameters.find(name); if (it == m_parameters.end()) { @@ -203,8 +298,24 @@ namespace Nz return false; } + /*! + * \brief Gets a string parameter by name + * \return true if success + * + * \param name Name of the variable + * \param value Value to set + * + * \remark Produces a NazaraAssert if pointer is invalid + * \remark Produces a silent NazaraError if name is not a variable + * \remark Produces a silent NazaraError if value could not be convertible + */ + bool ParameterList::GetStringParameter(const String& name, String* value) const { + NazaraAssert(value, "Invalid pointer"); + + ErrorFlags flags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled); + auto it = m_parameters.find(name); if (it == m_parameters.end()) { @@ -247,8 +358,24 @@ namespace Nz return false; } + /*! + * \brief Gets a user parameter by name + * \return true if success + * + * \param name Name of the variable + * \param value Value to set + * + * \remark Produces a NazaraAssert if pointer is invalid + * \remark Produces a silent NazaraError if name is not a variable + * \remark Produces a silent NazaraError if value could not be convertible + */ + bool ParameterList::GetUserdataParameter(const String& name, void** value) const { + NazaraAssert(value, "Invalid pointer"); + + ErrorFlags flags(ErrorFlag_Silent | ErrorFlag_ThrowExceptionDisabled); + auto it = m_parameters.find(name); if (it == m_parameters.end()) { @@ -268,11 +395,26 @@ namespace Nz } } + /*! + * \brief Checks whether the parameter list has a parameter with that name + * \return true if found + * + * \param name Name of the parameter + */ + bool ParameterList::HasParameter(const String& name) const { return m_parameters.find(name) != m_parameters.end(); } + /*! + * \brief Removes the parameter with that name + * + * Removes the parameter with that name, if not found, nothing is done + * + * \param name Name of the parameter + */ + void ParameterList::RemoveParameter(const String& name) { auto it = m_parameters.find(name); @@ -283,103 +425,126 @@ namespace Nz } } + /*! + * \brief Sets the parameter with the name to ParameterType_None + * + * \param name Name of the parameter + */ + void ParameterList::SetParameter(const String& name) { - std::pair pair = m_parameters.insert(std::make_pair(name, Parameter())); - Parameter& parameter = pair.first->second; - - if (!pair.second) - DestroyValue(parameter); - + Parameter& parameter = CreateValue(name); parameter.type = ParameterType_None; } + /*! + * \brief Sets the parameter with the name to the value + * + * \param name Name of the parameter + * \param value Value of the parameter + */ + void ParameterList::SetParameter(const String& name, const String& value) { - std::pair pair = m_parameters.insert(std::make_pair(name, Parameter())); - Parameter& parameter = pair.first->second; - - if (!pair.second) - DestroyValue(parameter); - + Parameter& parameter = CreateValue(name); parameter.type = ParameterType_String; PlacementNew(¶meter.value.stringVal, value); } + /*! + * \brief Sets the parameter with the name to the value + * + * \param name Name of the parameter + * \param value Value of the parameter + */ + void ParameterList::SetParameter(const String& name, const char* value) { - std::pair pair = m_parameters.insert(std::make_pair(name, Parameter())); - Parameter& parameter = pair.first->second; - - if (!pair.second) - DestroyValue(parameter); - + Parameter& parameter = CreateValue(name); parameter.type = ParameterType_String; PlacementNew(¶meter.value.stringVal, value); } + /*! + * \brief Sets the parameter with the name to the value + * + * \param name Name of the parameter + * \param value Value of the parameter + */ + void ParameterList::SetParameter(const String& name, void* value) { - std::pair pair = m_parameters.insert(std::make_pair(name, Parameter())); - Parameter& parameter = pair.first->second; - - if (!pair.second) - DestroyValue(parameter); - + Parameter& parameter = CreateValue(name); parameter.type = ParameterType_Pointer; parameter.value.ptrVal = value; } + /*! + * \brief Sets the parameter with the name to the value + * + * \param name Name of the parameter + * \param value Value of the parameter + * \param destructor Destructor for dynamic variable + */ + void ParameterList::SetParameter(const String& name, void* value, Destructor destructor) { - std::pair pair = m_parameters.insert(std::make_pair(name, Parameter())); - Parameter& parameter = pair.first->second; - - if (!pair.second) - DestroyValue(parameter); - + Parameter& parameter = CreateValue(name); parameter.type = ParameterType_Userdata; parameter.value.userdataVal = new Parameter::UserdataValue(destructor, value); } + /*! + * \brief Sets the parameter with the name to the value + * + * \param name Name of the parameter + * \param value Value of the parameter + */ + void ParameterList::SetParameter(const String& name, bool value) { - std::pair pair = m_parameters.insert(std::make_pair(name, Parameter())); - Parameter& parameter = pair.first->second; - - if (!pair.second) - DestroyValue(parameter); - + Parameter& parameter = CreateValue(name); parameter.type = ParameterType_Boolean; parameter.value.boolVal = value; } + /*! + * \brief Sets the parameter with the name to the value + * + * \param name Name of the parameter + * \param value Value of the parameter + */ + void ParameterList::SetParameter(const String& name, float value) { - std::pair pair = m_parameters.insert(std::make_pair(name, Parameter())); - Parameter& parameter = pair.first->second; - - if (!pair.second) - DestroyValue(parameter); - + Parameter& parameter = CreateValue(name); parameter.type = ParameterType_Float; parameter.value.floatVal = value; } + /*! + * \brief Sets the parameter with the name to the value + * + * \param name Name of the parameter + * \param value Value of the parameter + */ + void ParameterList::SetParameter(const String& name, int value) { - std::pair pair = m_parameters.insert(std::make_pair(name, Parameter())); - Parameter& parameter = pair.first->second; - - if (!pair.second) - DestroyValue(parameter); - + Parameter& parameter = CreateValue(name); parameter.type = ParameterType_Integer; parameter.value.intVal = value; } + /*! + * \brief Assigns the content of the other parameter list to this + * \return A reference to this + * + * \param list List to assign + */ + ParameterList& ParameterList::operator=(const ParameterList& list) { Clear(); @@ -418,6 +583,30 @@ namespace Nz return *this; } + /*! + * \brief Create an uninitialized value of a set name + * + * \param name Name of the parameter + * \param value Value of the parameter + * + * \remark The previous value if any gets destroyed + */ + ParameterList::Parameter& ParameterList::CreateValue(const String& name) + { + std::pair pair = m_parameters.insert(std::make_pair(name, Parameter())); + Parameter& parameter = pair.first->second; + + if (!pair.second) + DestroyValue(parameter); + + return parameter; + } + + /*! + * \brief Destroys the value for the parameter + * + * \param parameter Parameter to destroy + */ void ParameterList::DestroyValue(Parameter& parameter) { switch (parameter.type) @@ -427,15 +616,15 @@ namespace Nz break; case ParameterType_Userdata: - { - Parameter::UserdataValue* userdata = parameter.value.userdataVal; - if (--userdata->counter == 0) { - userdata->destructor(userdata->ptr); - delete userdata; + Parameter::UserdataValue* userdata = parameter.value.userdataVal; + if (--userdata->counter == 0) + { + userdata->destructor(userdata->ptr); + delete userdata; + } + break; } - break; - } case ParameterType_Boolean: case ParameterType_Float: diff --git a/src/Nazara/Core/PluginManager.cpp b/src/Nazara/Core/PluginManager.cpp index ddfede54c..24fbafcfb 100644 --- a/src/Nazara/Core/PluginManager.cpp +++ b/src/Nazara/Core/PluginManager.cpp @@ -24,6 +24,20 @@ namespace Nz }; } + /*! + * \ingroup core + * \class Nz::PluginManager + * \brief Core class that represents a manager for plugin + */ + + /*! + * \brief Adds a directory + * + * \param directoryPath Path to the directory + * + * \remark Produces a NazaraError if not initialized + */ + void PluginManager::AddDirectory(const String& directoryPath) { if (!Initialize()) @@ -35,6 +49,11 @@ namespace Nz s_directories.insert(File::AbsolutePath(directoryPath)); } + /*! + * \brief Initializes the plugin manager + * \return true if everything is ok + */ + bool PluginManager::Initialize() { if (s_initialized) @@ -48,11 +67,36 @@ namespace Nz return true; } + /*! + * \brief Mounts the plugin + * \return true if mounting was a success + * + * \remark Produces a NazaraError if not initialized + * \remark Produces a NazaraError if plugin is not found + * \remark Produces a NazaraError if fail to load plugin + * \remark Produces a NazaraError if fail to get symbol PluginLoad + * \remark Produces a NazaraError if fail to initialize the plugin with PluginLoad + */ + bool PluginManager::Mount(Plugin plugin) { return Mount(s_pluginFiles[plugin]); } + /*! + * \brief Mounts the plugin with a path + * \return true if mounting was a success + * + * \param pluginPath Path to the plugin + * \param appendExtension Adds the extension to the path or not + * + * \remark Produces a NazaraError if not initialized + * \remark Produces a NazaraError if plugin is not found + * \remark Produces a NazaraError if fail to load plugin + * \remark Produces a NazaraError if fail to get symbol PluginLoad + * \remark Produces a NazaraError if fail to initialize the plugin with PluginLoad + */ + bool PluginManager::Mount(const String& pluginPath, bool appendExtension) { if (!Initialize()) @@ -119,6 +163,14 @@ namespace Nz return true; } + /*! + * \brief Removes a directory + * + * \param directoryPath Path to the directory + * + * \remark Produces a NazaraError if not initialized + */ + void PluginManager::RemoveDirectory(const String& directoryPath) { if (!Initialize()) @@ -130,11 +182,29 @@ namespace Nz s_directories.erase(File::AbsolutePath(directoryPath)); } + /*! + * \brief Unmounts the plugin with a path + * + * \param pluginPath Path to the plugin + * + * \remark Produces a NazaraError if not initialized + * \remark Produces a NazaraError if plugin is not loaded + */ + void PluginManager::Unmount(Plugin plugin) { Unmount(s_pluginFiles[plugin]); } + /*! + * \brief Unmounts the plugin with a path + * + * \param pluginPath Path to the plugin + * + * \remark Produces a NazaraError if not initialized + * \remark Produces a NazaraError if plugin is not loaded + */ + void PluginManager::Unmount(const String& pluginPath) { if (!Initialize()) @@ -160,6 +230,10 @@ namespace Nz s_plugins.erase(it); } + /*! + * \brief Uninitializes the plugin manager + */ + void PluginManager::Uninitialize() { if (!s_initialized) diff --git a/src/Nazara/Core/Posix/DirectoryImpl.cpp b/src/Nazara/Core/Posix/DirectoryImpl.cpp index 4abf575b3..e23c82626 100644 --- a/src/Nazara/Core/Posix/DirectoryImpl.cpp +++ b/src/Nazara/Core/Posix/DirectoryImpl.cpp @@ -49,7 +49,7 @@ namespace Nz return true; else { - if (errno != ENOENT) + if (errno == EBADF || errno == EOVERFLOW) NazaraError("Unable to get next result: " + Error::GetLastSystemError()); return false; @@ -70,9 +70,8 @@ namespace Nz bool DirectoryImpl::Create(const String& dirPath) { - mode_t permissions; // TODO: check permissions - - return mkdir(dirPath.GetConstBuffer(), permissions) != -1;; + mode_t permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; // TODO: check permissions, no right to execute but read and write for every others. + return mkdir(dirPath.GetConstBuffer(), permissions) != -1; } bool DirectoryImpl::Exists(const String& dirPath) diff --git a/src/Nazara/Core/Posix/FileImpl.cpp b/src/Nazara/Core/Posix/FileImpl.cpp index ddd8d4253..31c1ace73 100644 --- a/src/Nazara/Core/Posix/FileImpl.cpp +++ b/src/Nazara/Core/Posix/FileImpl.cpp @@ -18,8 +18,8 @@ namespace Nz void FileImpl::Close() { - if (m_fileDescriptor != -1) - close(m_fileDescriptor); + if (m_fileDescriptor != -1) + close(m_fileDescriptor); } bool FileImpl::EndOfFile() const @@ -54,24 +54,24 @@ namespace Nz int flags; mode_t permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - if (mode & OpenMode_ReadWrite) + if ((mode & OpenMode_ReadWrite) == OpenMode_ReadWrite) flags = O_CREAT | O_RDWR; - else if (mode & OpenMode_ReadOnly) + else if ((mode & OpenMode_ReadOnly) == OpenMode_ReadOnly) flags = O_RDONLY; - else if (mode & OpenMode_WriteOnly) + else if ((mode & OpenMode_WriteOnly) == OpenMode_WriteOnly) flags = O_CREAT | O_WRONLY; else return false; - + if (mode & OpenMode_Append) flags |= O_APPEND; if (mode & OpenMode_Truncate) flags |= O_TRUNC; - ///TODO: lock - // if ((mode & OpenMode_Lock) == 0) - // shareMode |= FILE_SHARE_WRITE; + ///TODO: lock + //if ((mode & OpenMode_Lock) == 0) + // shareMode |= FILE_SHARE_WRITE; m_fileDescriptor = open64(filePath.GetConstBuffer(), flags, permissions); return m_fileDescriptor != -1; diff --git a/src/Nazara/Core/Posix/SemaphoreImpl.cpp b/src/Nazara/Core/Posix/SemaphoreImpl.cpp index 4342cf1c6..eb1b7d1dd 100644 --- a/src/Nazara/Core/Posix/SemaphoreImpl.cpp +++ b/src/Nazara/Core/Posix/SemaphoreImpl.cpp @@ -13,7 +13,7 @@ namespace Nz { SemaphoreImpl::SemaphoreImpl(unsigned int count) { - if(sem_init(&m_semaphore, 0, count) != 0) + if (sem_init(&m_semaphore, 0, count) != 0) NazaraError("Failed to create semaphore: " + Error::GetLastSystemError()); } @@ -68,7 +68,7 @@ namespace Nz return true; #else - return sem_timedwait(&m_semaphore, &ti) != 0; + return sem_timedwait(&m_semaphore, &ti) != 0; #endif } } diff --git a/src/Nazara/Core/PrimitiveList.cpp b/src/Nazara/Core/PrimitiveList.cpp index a9d2fc6ee..a8d406b94 100644 --- a/src/Nazara/Core/PrimitiveList.cpp +++ b/src/Nazara/Core/PrimitiveList.cpp @@ -8,71 +8,200 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::PrimitiveList + * \brief Core class that represents a list of geometric primitives + */ + + /*! + * \brief Adds a box centered + * + * \param lengths (Width, Height, Depht) + * \param subdivision Number of subdivision for the axis + * \param transformMatrix Matrix to apply + */ + void PrimitiveList::AddBox(const Vector3f& lengths, const Vector3ui& subdivision, const Matrix4f& transformMatrix) { m_primitives.push_back(Primitive::Box(lengths, subdivision, transformMatrix)); } + /*! + * \brief Adds a box centered + * + * \param lengths (Width, Height, Depht) + * \param subdivision Number of subdivision for the axis + * \param position Position of the box + * \param rotation Rotation of the box + */ + void PrimitiveList::AddBox(const Vector3f& lengths, const Vector3ui& subdivision, const Vector3f& position, const Quaternionf& rotation) { m_primitives.push_back(Primitive::Box(lengths, subdivision, position, rotation)); } + /*! + * \brief Adds a cone, centered in (0, 0, 0) and circle in (0, -length, 0) + * + * \param length Height of the cone + * \param radius Width of the radius + * \param subdivision Number of sides for the circle + * \param transformMatrix Matrix to apply + */ + void PrimitiveList::AddCone(float length, float radius, unsigned int subdivision, const Matrix4f& transformMatrix) { m_primitives.push_back(Primitive::Cone(length, radius, subdivision, transformMatrix)); } + /*! + * \brief Adds a cone, centered in (0, 0, 0) and circle in (0, -length, 0) + * + * \param length Height of the cone + * \param radius Width of the radius + * \param subdivision Number of sides for the circle + * \param position Position of the cone + * \param rotation Rotation of the cone + */ + void PrimitiveList::AddCone(float length, float radius, unsigned int subdivision, const Vector3f& position, const Quaternionf& rotation) { m_primitives.push_back(Primitive::Cone(length, radius, subdivision, position, rotation)); } + /*! + * \brief Adds a cubic sphere, centered in (0, 0, 0) + * + * \param size Radius of the cubic sphere + * \param subdivision Number of subdivision for the box + * \param transformMatrix Matrix to apply + */ + void PrimitiveList::AddCubicSphere(float size, unsigned int subdivision, const Matrix4f& transformMatrix) { m_primitives.push_back(Primitive::CubicSphere(size, subdivision, transformMatrix)); } + /*! + * \brief Adds a cubic sphere, centered in (0, 0, 0) + * + * \param size Radius of the cubic sphere + * \param subdivision Number of subdivision for the box + * \param position Position of the cubic sphere + * \param rotation Rotation of the cubic sphere + */ + void PrimitiveList::AddCubicSphere(float size, unsigned int subdivision, const Vector3f& position, const Quaternionf& rotation) { m_primitives.push_back(Primitive::CubicSphere(size, subdivision, position, rotation)); } + /*! + * \brief Adds a icosphere, centered in (0, 0, 0) + * + * \param size Radius of the icosphere + * \param recursionLevel Number of recursion for the icosphere + * \param transformMatrix Matrix to apply + */ + void PrimitiveList::AddIcoSphere(float size, unsigned int recursionLevel, const Matrix4f& transformMatrix) { m_primitives.push_back(Primitive::IcoSphere(size, recursionLevel, transformMatrix)); } + /*! + * \brief Adds a icosphere, centered in (0, 0, 0) + * + * \param size Radius of the sphere + * \param recursionLevel Number of recursion for the icosphere + * \param position Position of the icosphere + * \param rotation Rotation of the icosphere + */ + void PrimitiveList::AddIcoSphere(float size, unsigned int recursionLevel, const Vector3f& position, const Quaternionf& rotation) { m_primitives.push_back(Primitive::IcoSphere(size, recursionLevel, position, rotation)); } + /*! + * \brief Adds a plane, centered in (0, 0, 0) + * + * \param size (Width, Depth) + * \param subdivision Number of subdivision for the axis + * \param transformMatrix Matrix to apply + */ + void PrimitiveList::AddPlane(const Vector2f& size, const Vector2ui& subdivision, const Matrix4f& transformMatrix) { m_primitives.push_back(Primitive::Plane(size, subdivision, transformMatrix)); } + /*! + * \brief Adds a plane, centered in (0, 0, 0) + * + * \param size (Width, Depth) + * \param subdivision Number of subdivision for the axis + * \param planeInfo Information for the plane + */ + void PrimitiveList::AddPlane(const Vector2f& size, const Vector2ui& subdivision, const Planef& planeInfo) { m_primitives.push_back(Primitive::Plane(size, subdivision, planeInfo)); } + /*! + * \brief Adds a plane, centered in (0, 0, 0) + * + * \param size (Width, Depth) + * \param subdivision Number of subdivision for the axis + * \param position Position of the plane + * \param rotation Rotation of the plane + */ + void PrimitiveList::AddPlane(const Vector2f& size, const Vector2ui& subdivision, const Vector3f& position, const Quaternionf& rotation) { m_primitives.push_back(Primitive::Plane(size, subdivision, position, rotation)); } + /*! + * \brief Adds a UV sphere, centered in (0, 0, 0) + * + * \param size Radius of the sphere + * \param sliceCount Number of slices + * \param stackCount Number of stacks + * \param transformMatrix Matrix to apply + */ + void PrimitiveList::AddUVSphere(float size, unsigned int sliceCount, unsigned int stackCount, const Matrix4f& transformMatrix) { m_primitives.push_back(Primitive::UVSphere(size, sliceCount, stackCount, transformMatrix)); } + /*! + * \brief Adds a UV sphere, centered in (0, 0, 0) + * + * \param size Radius of the sphere + * \param sliceCount Number of slices + * \param stackCount Number of stacks + * \param position Position of the box + * \param rotation Rotation of the box + */ + void PrimitiveList::AddUVSphere(float size, unsigned int sliceCount, unsigned int stackCount, const Vector3f& position, const Quaternionf& rotation) { m_primitives.push_back(Primitive::UVSphere(size, sliceCount, stackCount, position, rotation)); } + /*! + * \brief Gets the ith primitive + * \return A reference to the ith primitive + * + * \param i Index of the primitive + * + * \remark Produces a NazaraAssert if index is greather than the size + */ + Primitive& PrimitiveList::GetPrimitive(std::size_t i) { NazaraAssert(i < m_primitives.size(), "Primitive index out of range"); @@ -80,6 +209,15 @@ namespace Nz return m_primitives[i]; } + /*! + * \brief Gets the ith primitive + * \return A constant reference to the ith primitive + * + * \param i Index of the primitive + * + * \remark Produces a NazaraAssert if index is greather than the size + */ + const Primitive& PrimitiveList::GetPrimitive(std::size_t i) const { NazaraAssert(i < m_primitives.size(), "Primitive index out of range"); @@ -87,16 +225,39 @@ namespace Nz return m_primitives[i]; } + /*! + * \brief Gets the number of primitives + * \return Number of primitives + */ + std::size_t PrimitiveList::GetSize() const { return m_primitives.size(); } + /*! + * \brief Gets the ith primitive + * \return A reference to the ith primitive + * + * \param i Index of the primitive + * + * \remark Produces a NazaraAssert if index is greather than the size + */ + Primitive& PrimitiveList::operator()(unsigned int i) { return GetPrimitive(i); } + /*! + * \brief Gets the ith primitive + * \return A constant reference to the ith primitive + * + * \param i Index of the primitive + * + * \remark Produces a NazaraAssert if index is greather than the size + */ + const Primitive& PrimitiveList::operator()(unsigned int i) const { return GetPrimitive(i); diff --git a/src/Nazara/Core/RefCounted.cpp b/src/Nazara/Core/RefCounted.cpp index db6ea7edf..31e749515 100644 --- a/src/Nazara/Core/RefCounted.cpp +++ b/src/Nazara/Core/RefCounted.cpp @@ -16,12 +16,30 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::RefCounted + * \brief Core class that represents a reference with a counter + */ + + /*! + * \brief Constructs a RefCounted object with a persistance aspect + * + * \param persistent if false, object is destroyed when no more referenced + */ + RefCounted::RefCounted(bool persistent) : m_persistent(persistent), m_referenceCount(0) { } + /*! + * \brief Destructs the object + * + * \remark Produces a NazaraWarning if still referenced with NAZARA_CORE_SAFE defined + */ + RefCounted::~RefCounted() { #if NAZARA_CORE_SAFE @@ -30,21 +48,42 @@ namespace Nz #endif } + /*! + * \brief Adds a reference to the object + */ + void RefCounted::AddReference() const { m_referenceCount++; } + /*! + * \brief Gets the number of references to the object + * \return Number of references + */ + unsigned int RefCounted::GetReferenceCount() const { return m_referenceCount; } + /*! + * \brief Checks whether the object is persistent + * \return true if object is not destroyed when no more referenced + */ + bool RefCounted::IsPersistent() const { return m_persistent; } + /*! + * \brief Removes a reference to the object + * \return true if object is deleted because no more referenced + * + * \remark Produces a NazaraError if counter is already 0 with NAZARA_CORE_SAFE defined + */ + bool RefCounted::RemoveReference() const { #if NAZARA_CORE_SAFE @@ -65,6 +104,14 @@ namespace Nz return false; } + /*! + * \brief Sets the persistence of the object + * \return true if object is deleted because no more referenced + * + * \param persistent Sets the persistence of the object + * \param checkReferenceCount Checks if the object should be destroyed if true + */ + bool RefCounted::SetPersistent(bool persistent, bool checkReferenceCount) { m_persistent = persistent; diff --git a/src/Nazara/Core/Resource.cpp b/src/Nazara/Core/Resource.cpp index 080e4fd22..82ef768d1 100644 --- a/src/Nazara/Core/Resource.cpp +++ b/src/Nazara/Core/Resource.cpp @@ -7,13 +7,30 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Resource + * \brief Core class that represents a resource + */ + Resource::~Resource() = default; + /*! + * \brief Gets the file path associated with the resource + * \return A reference to the path + */ + const String& Resource::GetFilePath() const { return m_filePath; } + /*! + * \brief Sets the file path associated with the resource + * + * \param filePath Path to the resource + */ + void Resource::SetFilePath(const String& filePath) { m_filePath = filePath; diff --git a/src/Nazara/Core/Semaphore.cpp b/src/Nazara/Core/Semaphore.cpp index 1a9afdc95..1b2194095 100644 --- a/src/Nazara/Core/Semaphore.cpp +++ b/src/Nazara/Core/Semaphore.cpp @@ -16,31 +16,69 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Semaphore + * \brief Core class that represents a counting semaphore + */ + + /*! + * \brief Constructs a semaphore object with a count + */ + Semaphore::Semaphore(unsigned int count) { m_impl = new SemaphoreImpl(count); } + /*! + * \brief Destructs the object + */ + Semaphore::~Semaphore() { delete m_impl; } + /*! + * \brief Gets the number of count that can handle the semaphore + * \return Number of count associated with the semaphore + */ + unsigned int Semaphore::GetCount() const { return m_impl->GetCount(); } + /*! + * \brief Increments the count of the semaphore and wait if count equals zero + * + * Increments the value of semaphore variable by 1. After the increment, if the pre-increment value was negative (meaning there are processes waiting for a resource), it transfers a blocked process from the semaphore's waiting queue to the ready queue + */ + void Semaphore::Post() { m_impl->Post(); } + /*! + * \brief Decrements the count of the semaphore and wait if count equals zero + * + * If the value of semaphore variable is not negative, decrements it by 1. If the semaphore variable is now negative, the process executing Wait is blocked (i.e., added to the semaphore's queue) until the value is greater or equal to 1. Otherwise, the process continues execution, having used a unit of the resource + */ + void Semaphore::Wait() { m_impl->Wait(); } + /*! + * \brief Decrements the count of the semaphore and wait if count equals zero + * \return true if the semaphore successfully decrements before timeout + * + * If the value of semaphore variable is not negative, decrements it by 1. If the semaphore variable is now negative, the process executing Wait is blocked (i.e., added to the semaphore's queue) until the value is greater or equal to 1. Otherwise, the process continues execution, having used a unit of the resource + */ + bool Semaphore::Wait(UInt32 timeout) { return m_impl->Wait(timeout); diff --git a/src/Nazara/Core/StdLogger.cpp b/src/Nazara/Core/StdLogger.cpp index 49752db26..e38daa077 100644 --- a/src/Nazara/Core/StdLogger.cpp +++ b/src/Nazara/Core/StdLogger.cpp @@ -13,24 +13,27 @@ namespace Nz const char* errorType[] = { "Assert failed", // ErrorType_AssertFailed "Internal error", // ErrorType_Internal - "Error", // ErrorType_Normal - "Warning" // ErrorType_Warning + "Error", // ErrorType_Normal + "Warning" // ErrorType_Warning }; static_assert(sizeof(errorType) / sizeof(const char*) == ErrorType_Max + 1, "Error type array is incomplete"); } /*! + * \ingroup core * \class Nz::StdLogger - * \brief Logger writing to standard output (stdout, stderr) + * \brief Core class that represents a logger writing to standard output (stdout, stderr) */ StdLogger::~StdLogger() = default; /*! - * \brief Enable replication to standard output + * \brief Enables replication to standard output * * Does nothing, as the std logger always write to standard output + * + * \param enable Unused argument */ void StdLogger::EnableStdReplication(bool enable) @@ -40,9 +43,8 @@ namespace Nz } /*! - * \brief Get the standard output replication status - * - * Always returns true + * \brief Gets the standard output replication status + * \return Always returns true */ bool StdLogger::IsStdReplicationEnabled() @@ -51,7 +53,8 @@ namespace Nz } /*! - * Write to the console + * \brief Writes to the console + * * \param string The log to write to the console * * \see WriteError @@ -64,7 +67,8 @@ namespace Nz } /*! - * Write an error to the console + * \brief Writes an error to the console + * * \param type The error type * \param error The error text * \param line The line the error occurred diff --git a/src/Nazara/Core/Stream.cpp b/src/Nazara/Core/Stream.cpp index f8aab413e..b36b67d85 100644 --- a/src/Nazara/Core/Stream.cpp +++ b/src/Nazara/Core/Stream.cpp @@ -11,22 +11,51 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Stream + * \brief Core class that represents a stream + */ + + /*! + * \brief Destructs the object + */ + Stream::~Stream() = default; + /*! + * \brief Gets the directory of the stream + * \return Empty string (meant to be virtual) + */ + String Stream::GetDirectory() const { return String(); } + /*! + * \brief Gets the path of the stream + * \return Empty string (meant to be virtual) + */ + String Stream::GetPath() const { return String(); } + /*! + * \brief Reads characters in the stream + * \return Line containing characters + * + * \param lineSize Number of characters to read, if lineSize is 0, read as much as possible + * + * \remark Produces a NazaraWarning if cursor position could not be reset + */ + String Stream::ReadLine(unsigned int lineSize) { String line; - if (lineSize == 0) // Taille maximale indéterminée + if (lineSize == 0) // Maximal size undefined { const unsigned int bufferSize = 64; @@ -63,7 +92,7 @@ namespace Nz line.Set(lineSize, '\0'); std::size_t readSize = Read(&line[0], lineSize); std::size_t pos = line.Find('\n'); - if (pos <= readSize) // Faux uniquement si le caractère n'est pas présent (npos étant le plus grand entier) + if (pos <= readSize) // False only if the character is not available (npos being the biggest integer) { if (m_streamOptions & StreamOption_Text && pos > 0 && line[pos - 1] == '\r') line.Resize(pos); @@ -80,12 +109,26 @@ namespace Nz return line; } + /*! + * \brief Writes a ByteArray into the stream + * \return true if successful + * + * \param byteArray Bytes to write + */ + bool Stream::Write(const ByteArray& byteArray) { ByteArray::size_type size = byteArray.GetSize(); return Write(byteArray.GetConstBuffer(), size) == size; } + /*! + * \brief Writes a String into the stream + * \return true if successful + * + * \param string String to write + */ + bool Stream::Write(const String& string) { String temp(string); diff --git a/src/Nazara/Core/String.cpp b/src/Nazara/Core/String.cpp index 4d63cd87f..b94715457 100644 --- a/src/Nazara/Core/String.cpp +++ b/src/Nazara/Core/String.cpp @@ -2,7 +2,7 @@ // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Config.hpp -///TODO: Réécrire une bonne partie des algorithmes employés (Relu jusqu'à 3538) +///TODO: Rewrite most of used algorithms (Reread to to line 4638) #include #include @@ -23,7 +23,7 @@ namespace Nz { namespace Detail { - // Cet algorithme est inspiré de la documentation de Qt + // This algorithm is inspired by the documentation of Qt inline std::size_t GetNewSize(std::size_t newSize) { if (newSize < 20) @@ -76,11 +76,27 @@ namespace Nz } } + /*! + * \ingroup core + * \class Nz::String + * \brief Core class that represents a string + */ + + /*! + * \brief Constructs a String object by default + */ + String::String() : m_sharedString(GetEmptyString()) { } + /*! + * \brief Constructs a String object with a character + * + * \param character Single character + */ + String::String(char character) { if (character != '\0') @@ -92,6 +108,13 @@ namespace Nz m_sharedString = GetEmptyString(); } + /*! + * \brief Constructs a String object with multiple times the same character + * + * \param rep Number of repetitions of the character + * \param character Single character + */ + String::String(std::size_t rep, char character) { if (rep > 0) @@ -105,11 +128,26 @@ namespace Nz m_sharedString = GetEmptyString(); } + /*! + * \brief Constructs a String object with multiple times the same string + * + * \param rep Number of repetitions of the string + * \param string String to multiply + */ + String::String(std::size_t rep, const char* string) : String(rep, string, (string) ? std::strlen(string) : 0) { } + /*! + * \brief Constructs a String object with multiple times the same string + * + * \param rep Number of repetitions of the string + * \param string String to multiply + * \param length Length of the string + */ + String::String(std::size_t rep, const char* string, std::size_t length) { std::size_t totalSize = rep*length; @@ -125,16 +163,36 @@ namespace Nz m_sharedString = GetEmptyString(); } + /*! + * \brief Constructs a String object with multiple times the same string + * + * \param rep Number of repetitions of the string + * \param string String to multiply + */ + String::String(std::size_t rep, const String& string) : String(rep, string.GetConstBuffer(), string.GetSize()) { } + /*! + * \brief Constructs a String object with a "C string" + * + * \param string String to represent + */ + String::String(const char* string) : String(string, (string) ? std::strlen(string) : 0) { } + /*! + * \brief Constructs a String object with a "C string" + * + * \param string String to represent + * \param length Length of the string + */ + String::String(const char* string, std::size_t length) { if (length > 0) @@ -146,58 +204,148 @@ namespace Nz m_sharedString = GetEmptyString(); } + /*! + * \brief Constructs a String object which is a copy of another + * + * \param string String to copy + */ + String::String(const std::string& string) : String(string.c_str(), string.size()) { } + /*! + * \brief Appends the character to the string + * \return A reference to this + * + * \param character Single character + * + * \see Insert + */ + String& String::Append(char character) { return Insert(m_sharedString->size, character); } + /*! + * \brief Appends the "C string" to the string + * \return A reference to this + * + * \param string String to add + * + * \see Insert + */ + String& String::Append(const char* string) { return Insert(m_sharedString->size, string); } + /*! + * \brief Appends the "C string" to the string + * \return A reference to this + * + * \param string String to add + * \param length Size of the string + * + * \see Insert + */ + String& String::Append(const char* string, std::size_t length) { return Insert(m_sharedString->size, string, length); } + /*! + * \brief Appends the string to the string + * \return A reference to this + * + * \param string String to add + * + * \see Insert + */ + String& String::Append(const String& string) { return Insert(m_sharedString->size, string); } + /*! + * \brief Clears the content of the string + * + * \param keepBuffer Should the buffer be kept + */ + void String::Clear(bool keepBuffer) { if (keepBuffer) { EnsureOwnership(true); m_sharedString->size = 0; - m_sharedString->string.reset(); } else ReleaseString(); } + /*! + * \Brief Checks whether the string contains the character + * \return true if found in the string + * + * \param character Single character + * \param start Index to begin the research + * \param flags Flag for the look up + * + * \see Find + */ + bool String::Contains(char character, std::intmax_t start, UInt32 flags) const { return Find(character, start, flags) != npos; } + /*! + * \Brief Checks whether the string contains the "C string" + * \return true if found in the string + * + * \param string String to search + * \param start Index to begin the research + * \param flags Flag for the look up + * + * \see Find + */ + bool String::Contains(const char* string, std::intmax_t start, UInt32 flags) const { return Find(string, start, flags) != npos; } + /*! + * \Brief Checks whether the string contains the string + * \return true if found in the string + * + * \param string String to search + * \param start Index to begin the research + * \param flags Flag for the look up + * + * \see Find + */ + bool String::Contains(const String& string, std::intmax_t start, UInt32 flags) const { return Find(string, start, flags) != npos; } + /*! + * \brief Counts the number of occurrences in the string + * \return Number of occurrences + * + * \param character Single character + * \param start Index to begin the research + * \param flags Flag for the look up + */ + unsigned int String::Count(char character, std::intmax_t start, UInt32 flags) const { if (character == '\0' || m_sharedString->size == 0) @@ -235,6 +383,15 @@ namespace Nz return count; } + /*! + * \brief Counts the number of occurrences in the string + * \return Number of occurrences + * + * \param string String to count + * \param start Index to begin the research + * \param flags Flag for the look up + */ + unsigned int String::Count(const char* string, std::intmax_t start, UInt32 flags) const { if (!string || !string[0] || m_sharedString->size == 0) @@ -332,11 +489,29 @@ namespace Nz return count; } + /*! + * \brief Counts the number of occurrences in the string + * \return Number of occurrences + * + * \param string String to count + * \param start Index to begin the research + * \param flags Flag for the look up + */ + unsigned int String::Count(const String& string, std::intmax_t start, UInt32 flags) const { return Count(string.GetConstBuffer(), start, flags); } + /*! + * \brief Counts the number of occurrences of any characters in the list in the string + * \return Number of occurrences + * + * \param string String to match + * \param start Index to begin the research + * \param flags Flag for the look up + */ + unsigned int String::CountAny(const char* string, std::intmax_t start, UInt32 flags) const { if (!string || !string[0] || m_sharedString->size == 0) @@ -425,11 +600,30 @@ namespace Nz return count; } + /*! + * \brief Counts the number of occurrences of any characters in the list in the string + * \return Number of occurrences + * + * \param string String to match + * \param start Index to begin the research + * \param flags Flag for the look up + */ + unsigned int String::CountAny(const String& string, std::intmax_t start, UInt32 flags) const { return CountAny(string.GetConstBuffer(), start, flags); } + /*! + * \brief Checks whether the string ends with the character + * \return true if it the case + * + * \param character Single character + * \param flags Flag for the look up + * + * \see StartsWith + */ + bool String::EndsWith(char character, UInt32 flags) const { if (m_sharedString->size == 0) @@ -438,14 +632,35 @@ namespace Nz if (flags & CaseInsensitive) return Detail::ToLower(m_sharedString->string[m_sharedString->size-1]) == Detail::ToLower(character); else - return m_sharedString->string[m_sharedString->size-1] == character; // character == '\0' sera toujours faux + return m_sharedString->string[m_sharedString->size-1] == character; // character == '\0' will always be false } + /*! + * \brief Checks whether the string ends with the "C string" + * \return true if it the case + * + * \param string String to match + * \param flags Flag for the look up + * + * \see StartsWith + */ + bool String::EndsWith(const char* string, UInt32 flags) const { return EndsWith(string, std::strlen(string), flags); } + /*! + * \brief Checks whether the string ends with the "C string" + * \return true if it the case + * + * \param string String to match + * \param length Size of the string + * \param flags Flag for the look up + * + * \see StartsWith + */ + bool String::EndsWith(const char* string, std::size_t length, UInt32 flags) const { if (!string || !string[0] || m_sharedString->size == 0 || length > m_sharedString->size) @@ -462,11 +677,30 @@ namespace Nz return std::strcmp(&m_sharedString->string[m_sharedString->size - length], string) == 0; } + /*! + * \brief Checks whether the string ends with the string + * \return true if it the case + * + * \param string String to match + * \param flags Flag for the look up + * + * \see StartsWith + */ + bool String::EndsWith(const String& string, UInt32 flags) const { return EndsWith(string.GetConstBuffer(), string.m_sharedString->size, flags); } + /*! + * \brief Finds the first index of the character in the string + * \return Index in the string + * + * \param character Single character + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::Find(char character, std::intmax_t start, UInt32 flags) const { if (character == '\0' || m_sharedString->size == 0) @@ -502,6 +736,15 @@ namespace Nz } } + /*! + * \brief Finds the first index of the "C string" in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::Find(const char* string, std::intmax_t start, UInt32 flags) const { if (!string || !string[0] || m_sharedString->size == 0) @@ -592,11 +835,29 @@ namespace Nz return npos; } + /*! + * \brief Finds the first index of the string in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::Find(const String& string, std::intmax_t start, UInt32 flags) const { return Find(string.GetConstBuffer(), start, flags); } + /*! + * \brief Finds the first index of any characters in the list in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindAny(const char* string, std::intmax_t start, UInt32 flags) const { if (m_sharedString->size == 0 || !string || !string[0]) @@ -675,11 +936,29 @@ namespace Nz return npos; } + /*! + * \brief Finds the first index of any characters in the list in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindAny(const String& string, std::intmax_t start, UInt32 flags) const { return FindAny(string.GetConstBuffer(), start, flags); } + /*! + * \brief Finds the last index of the character in the string + * \return Index in the string + * + * \param character Single character + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindLast(char character, std::intmax_t start, UInt32 flags) const { if (character == '\0' || m_sharedString->size == 0) @@ -717,6 +996,15 @@ namespace Nz return npos; } + /*! + * \brief Finds the last index of the "C string" in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindLast(const char* string, std::intmax_t start, UInt32 flags) const { if (!string || !string[0] || m_sharedString->size == 0) @@ -729,14 +1017,14 @@ namespace Nz if (pos >= m_sharedString->size) return npos; - ///Algo 1.FindLast#3 (Taille du pattern inconnue) + ///Algo 1.FindLast#3 (Size of the pattern unknown) const char* ptr = &m_sharedString->string[pos]; if (flags & CaseInsensitive) { if (flags & HandleUtf8) { if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + utf8::unchecked::prior(ptr); // We ensure to have one pointer pointing to the begin of the character utf8::unchecked::iterator it(ptr); const char* t = string; @@ -825,6 +1113,15 @@ namespace Nz return npos; } + /*! + * \brief Finds the last index of the string in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindLast(const String& string, std::intmax_t start, UInt32 flags) const { if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) @@ -844,9 +1141,9 @@ namespace Nz { if (flags & HandleUtf8) { - ///Algo 1.FindLast#3 (Itérateur non-adapté) + ///Algo 1.FindLast#3 (Iterator non-adapted) if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + utf8::unchecked::prior(ptr); // We ensure to have one pointer pointing to the begin of the character utf8::unchecked::iterator it(ptr); const char* t = string.GetConstBuffer(); @@ -879,7 +1176,7 @@ namespace Nz } else { - ///Algo 1.FindLast#4 (Taille du pattern connue) + ///Algo 1.FindLast#4 (Size of the pattern unknown) char c = Detail::ToLower(string.m_sharedString->string[string.m_sharedString->size-1]); for (;;) { @@ -905,7 +1202,7 @@ namespace Nz } else { - ///Algo 1.FindLast#4 (Taille du pattern connue) + ///Algo 1.FindLast#4 (Size of the pattern known) for (;;) { if (*ptr == string.m_sharedString->string[string.m_sharedString->size-1]) @@ -931,6 +1228,15 @@ namespace Nz return npos; } + /*! + * \brief Finds the last index of any characters in the list in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindLastAny(const char* string, std::intmax_t start, UInt32 flags) const { if (!string || !string[0] || m_sharedString->size == 0) @@ -1017,11 +1323,29 @@ namespace Nz return npos; } + /*! + * \brief Finds the last index of any characters in the list in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindLastAny(const String& string, std::intmax_t start, UInt32 flags) const { return FindLastAny(string.GetConstBuffer(), start, flags); } + /*! + * \brief Finds the last word in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindLastWord(const char* string, std::intmax_t start, UInt32 flags) const { if (!string || !string[0] || m_sharedString->size == 0) @@ -1034,19 +1358,19 @@ namespace Nz if (pos >= m_sharedString->size) return npos; - ///Algo 2.FindLastWord#1 (Taille du pattern inconnue) + ///Algo 2.FindLastWord#1 (Size of the pattern unknown) const char* ptr = &m_sharedString->string[pos]; if (flags & HandleUtf8) { if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + utf8::unchecked::prior(ptr); // We ensure to have a pointer pointing to the beginning of the string utf8::unchecked::iterator it(ptr); if (flags & CaseInsensitive) { - const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument + const char* t = string; // utf8(::unchecked)::next affects the iterator on argument UInt32 c = Unicode::GetLowercase(utf8::unchecked::next(t)); do { @@ -1088,7 +1412,7 @@ namespace Nz } else { - const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument + const char* t = string; // utf8(::unchecked)::next affects the iterator on argument UInt32 c = utf8::unchecked::next(t); do { @@ -1138,8 +1462,12 @@ namespace Nz { if (Detail::ToLower(*ptr) == c) { - if (ptr != m_sharedString->string.get() && !std::isspace(*(ptr-1))) - continue; + if (ptr != m_sharedString->string.get()) + { + --ptr; + if (!(Unicode::GetCategory(*ptr++) & Unicode::Category_Separator)) + continue; + } const char* p = &string[1]; const char* tPtr = ptr+1; @@ -1147,7 +1475,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || std::isspace(*tPtr)) + if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator) return ptr-m_sharedString->string.get(); else break; @@ -1172,8 +1500,12 @@ namespace Nz { if (*ptr == string[0]) { - if (ptr != m_sharedString->string.get() && !std::isspace(*(ptr-1))) - continue; + if (ptr != m_sharedString->string.get()) + { + --ptr; + if (!(Unicode::GetCategory(*ptr++) & Unicode::Category_Separator)) + continue; + } const char* p = &string[1]; const char* tPtr = ptr+1; @@ -1181,7 +1513,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || std::isspace(*tPtr)) + if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator) return ptr-m_sharedString->string.get(); else break; @@ -1205,6 +1537,15 @@ namespace Nz return npos; } + /*! + * \brief Finds the last word in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindLastWord(const String& string, std::intmax_t start, UInt32 flags) const { if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) @@ -1223,13 +1564,13 @@ namespace Nz if (flags & HandleUtf8) { if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + utf8::unchecked::prior(ptr); // We ensure to have a pointer pointing to the beginning of the string utf8::unchecked::iterator it(ptr); if (flags & CaseInsensitive) { - const char* t = string.GetConstBuffer(); // utf8(::unchecked)::next affecte l'itérateur en argument + const char* t = string.GetConstBuffer(); // utf8(::unchecked)::next affects the iterator on argument UInt32 c = Unicode::GetLowercase(utf8::unchecked::next(t)); do { @@ -1271,7 +1612,7 @@ namespace Nz } else { - const char* t = string.GetConstBuffer(); // utf8(::unchecked)::next affecte l'itérateur en argument + const char* t = string.GetConstBuffer(); // utf8(::unchecked)::next affects the iterator on argument UInt32 c = utf8::unchecked::next(t); do { @@ -1314,7 +1655,7 @@ namespace Nz } else { - ///Algo 2.FindLastWord#2 (Taille du pattern connue) + ///Algo 2.FindLastWord#2 (Size of the pattern known) if (flags & CaseInsensitive) { char c = Detail::ToLower(string.m_sharedString->string[string.m_sharedString->size-1]); @@ -1322,7 +1663,8 @@ namespace Nz { if (Detail::ToLower(*ptr) == c) { - if (*(ptr+1) != '\0' && !std::isspace(*(ptr+1))) + char nextC = *(ptr + 1); + if (nextC != '\0' && (Unicode::GetCategory(nextC) & Unicode::Category_Separator_Space) == 0) continue; const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; @@ -1333,7 +1675,7 @@ namespace Nz if (p == &string.m_sharedString->string[0]) { - if (ptr == m_sharedString->string.get() || std::isspace(*(ptr-1))) + if (ptr == m_sharedString->string.get() || Unicode::GetCategory(*(ptr-1)) & Unicode::Category_Separator_Space) return ptr-m_sharedString->string.get(); else break; @@ -1352,7 +1694,8 @@ namespace Nz { if (*ptr == string.m_sharedString->string[string.m_sharedString->size-1]) { - if (*(ptr+1) != '\0' && !std::isspace(*(ptr+1))) + char nextC = *(ptr + 1); + if (nextC != '\0' && (Unicode::GetCategory(nextC) & Unicode::Category_Separator_Space) == 0) continue; const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; @@ -1363,7 +1706,7 @@ namespace Nz if (p == &string.m_sharedString->string[0]) { - if (ptr == m_sharedString->string.get() || std::isspace(*(ptr-1))) + if (ptr == m_sharedString->string.get() || Unicode::GetCategory(*(ptr - 1)) & Unicode::Category_Separator_Space) return ptr-m_sharedString->string.get(); else break; @@ -1381,6 +1724,15 @@ namespace Nz return npos; } + /*! + * \brief Finds the first word in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindWord(const char* string, std::intmax_t start, UInt32 flags) const { if (!string || !string[0] || m_sharedString->size == 0) @@ -1393,18 +1745,18 @@ namespace Nz if (pos >= m_sharedString->size) return npos; - ///Algo 3.FindWord#3 (Taille du pattern inconnue) + ///Algo 3.FindWord#3 (Size of the pattern unknown) const char* ptr = m_sharedString->string.get(); if (flags & HandleUtf8) { if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + utf8::unchecked::prior(ptr); // We ensure to have one pointer pointing to the begin of the character utf8::unchecked::iterator it(ptr); if (flags & CaseInsensitive) { - const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument + const char* t = string; // utf8(::unchecked)::next affects the iterator on argument UInt32 c = Unicode::GetLowercase(utf8::unchecked::next(t)); do @@ -1444,7 +1796,7 @@ namespace Nz } else { - const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument + const char* t = string; // utf8(::unchecked)::next affects the iterator on argument UInt32 c = Unicode::GetLowercase(utf8::unchecked::next(t)); do @@ -1492,7 +1844,7 @@ namespace Nz { if (Detail::ToLower(*ptr) == c) { - if (ptr != m_sharedString->string.get() && !std::isspace(*(ptr-1))) + if (ptr != m_sharedString->string.get() && (Unicode::GetCategory(*(ptr - 1)) & Unicode::Category_Separator) == 0) continue; const char* p = &string[1]; @@ -1501,7 +1853,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || std::isspace(*tPtr)) + if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator) return ptr - m_sharedString->string.get(); else break; @@ -1523,7 +1875,7 @@ namespace Nz { if (*ptr == string[0]) { - if (ptr != m_sharedString->string.get() && !std::isspace(*(ptr-1))) + if (ptr != m_sharedString->string.get() && (Unicode::GetCategory(*(ptr-1)) & Unicode::Category_Separator) == 0) continue; const char* p = &string[1]; @@ -1532,7 +1884,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || std::isspace(*tPtr)) + if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator) return ptr - m_sharedString->string.get(); else break; @@ -1553,6 +1905,15 @@ namespace Nz return npos; } + /*! + * \brief Finds the first word in the string + * \return Index in the string + * + * \param string String to match + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::FindWord(const String& string, std::intmax_t start, UInt32 flags) const { if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) @@ -1568,15 +1929,15 @@ namespace Nz char* ptr = m_sharedString->string.get(); if (flags & HandleUtf8) { - ///Algo 3.FindWord#3 (Itérateur trop lent pour #2) + ///Algo 3.FindWord#3 (Iterator too slow for #2) if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + utf8::unchecked::prior(ptr); // We ensure to have one pointer pointing to the begin of the character utf8::unchecked::iterator it(ptr); if (flags & CaseInsensitive) { - const char* t = string.GetConstBuffer(); // utf8(::unchecked)::next affecte l'itérateur en argument + const char* t = string.GetConstBuffer(); // utf8(::unchecked)::next affects the iterator on argument UInt32 c = Unicode::GetLowercase(utf8::unchecked::next(t)); do @@ -1616,7 +1977,7 @@ namespace Nz } else { - const char* t = string.GetConstBuffer(); // utf8(::unchecked)::next affecte l'itérateur en argument + const char* t = string.GetConstBuffer(); // utf8(::unchecked)::next affects the iterator on argument UInt32 c = Unicode::GetLowercase(utf8::unchecked::next(t)); do @@ -1657,7 +2018,7 @@ namespace Nz } else { - ///Algo 3.FindWord#2 (Taille du pattern connue) + ///Algo 3.FindWord#2 (Size of the pattern known) if (flags & CaseInsensitive) { char c = Detail::ToLower(string.m_sharedString->string[0]); @@ -1665,7 +2026,7 @@ namespace Nz { if (Detail::ToLower(*ptr) == c) { - if (ptr != m_sharedString->string.get() && !std::isspace(*(ptr-1))) + if (ptr != m_sharedString->string.get() && (Unicode::GetCategory(*(ptr-1)) & Unicode::Category_Separator_Space) == 0) continue; const char* p = &string.m_sharedString->string[1]; @@ -1674,7 +2035,7 @@ namespace Nz { if (*p == '\0') { - if (*tPtr == '\0' || std::isspace(*tPtr)) + if (*tPtr == '\0' || Unicode::GetCategory(*tPtr) & Unicode::Category_Separator_Space) return ptr - m_sharedString->string.get(); else break; @@ -1694,8 +2055,8 @@ namespace Nz { while ((ptr = std::strstr(ptr, string.GetConstBuffer())) != nullptr) { - // Si le mot est bien isolé - if ((ptr == m_sharedString->string.get() || std::isspace(*(ptr-1))) && (*(ptr+m_sharedString->size) == '\0' || std::isspace(*(ptr+m_sharedString->size)))) + // 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)) return ptr - m_sharedString->string.get(); ptr++; @@ -1706,6 +2067,11 @@ namespace Nz return npos; } + /*! + * \brief Gets the raw buffer + * \return Raw buffer + */ + char* String::GetBuffer() { EnsureOwnership(); @@ -1713,31 +2079,61 @@ namespace Nz return m_sharedString->string.get(); } + /*! + * \brief Gets the capacity of the string + * \return Capacity of the string + */ + std::size_t String::GetCapacity() const { return m_sharedString->capacity; } + /*! + * \brief Gets the raw buffer + * \return Raw buffer + */ + const char* String::GetConstBuffer() const { return m_sharedString->string.get(); } + /*! + * \brief Gets the length of the string + * \return Length of the string with UTF-8 awareness + */ + std::size_t String::GetLength() const { return utf8::distance(m_sharedString->string.get(), &m_sharedString->string[m_sharedString->size]); } + /*! + * \brief Gets the size of the string + * \return Size of the string without UTF-8 awareness + */ + std::size_t String::GetSize() const { return m_sharedString->size; } + /*! + * \brief Gets the std::string corresponding + * \return String in UTF-8 + */ + std::string String::GetUtf8String() const { return std::string(m_sharedString->string.get(), m_sharedString->size); } + /*! + * \brief Gets the std::string corresponding + * \return String in UTF-16 + */ + std::u16string String::GetUtf16String() const { if (m_sharedString->size == 0) @@ -1751,6 +2147,11 @@ namespace Nz return str; } + /*! + * \brief Gets the std::string corresponding + * \return String in UTF-32 + */ + std::u32string String::GetUtf32String() const { if (m_sharedString->size == 0) @@ -1764,6 +2165,11 @@ namespace Nz return str; } + /*! + * \brief Gets the std::wstring corresponding + * \return String in Wide + */ + std::wstring String::GetWideString() const { static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "wchar_t size is not supported"); @@ -1773,7 +2179,7 @@ namespace Nz std::wstring str; str.reserve(m_sharedString->size); - if (sizeof(wchar_t) == 4) // Je veux du static_if :( + if (sizeof(wchar_t) == 4) // I want a static_if :( utf8::utf8to32(begin(), end(), std::back_inserter(str)); else { @@ -1792,6 +2198,14 @@ namespace Nz return str; } + /*! + * \brief Gets the word until next separator + * \return Word string + * + * \param start Index to begin the search + * \param flags Flag for the look up + */ + String String::GetWord(unsigned int index, UInt32 flags) const { std::size_t startPos = GetWordPosition(index, flags); @@ -1817,7 +2231,7 @@ namespace Nz { do { - if (std::isspace(*ptr)) + if (Unicode::GetCategory(*ptr) & Unicode::Category_Separator) { endPos = static_cast(ptr - m_sharedString->string.get() - 1); break; @@ -1829,6 +2243,14 @@ namespace Nz return SubString(startPos, endPos); } + /*! + * \brief Gets the word position + * \return Position of the beginning of the word + * + * \param start Index to begin the search + * \param flags Flag for the look up + */ + std::size_t String::GetWordPosition(unsigned int index, UInt32 flags) const { if (m_sharedString->size == 0) @@ -1861,7 +2283,7 @@ namespace Nz { do { - if (std::isspace(*ptr)) + if (Unicode::GetCategory(*ptr) & Unicode::Category_Separator) inWord = false; else { @@ -1879,16 +2301,41 @@ namespace Nz return npos; } + /*! + * \brief Inserts the character into the string + * \return A reference to this + * + * \param pos Position in the string + * \param character Single character + */ + String& String::Insert(std::intmax_t pos, char character) { return Insert(pos, &character, 1); } + /*! + * \brief Inserts the "C string" into the string + * \return A reference to this + * + * \param pos Position in the string + * \param string String to add + */ + String& String::Insert(std::intmax_t pos, const char* string) { return Insert(pos, string, std::strlen(string)); } + /*! + * \brief Inserts the "C string" into the string + * \return A reference to this + * + * \param pos Position in the string + * \param string String to add + * \param length Size of the string + */ + String& String::Insert(std::intmax_t pos, const char* string, std::size_t length) { if (length == 0) @@ -1899,7 +2346,7 @@ namespace Nz std::size_t start = std::min(pos, m_sharedString->size); - // Si le buffer est déjà suffisamment grand + // If buffer is already big enough if (m_sharedString->capacity >= m_sharedString->size + length) { EnsureOwnership(); @@ -1934,21 +2381,49 @@ namespace Nz return *this; } + /*! + * \brief Inserts the string into the string + * \return A reference to this + * + * \param pos Position in the string + * \param string String to add + */ + String& String::Insert(std::intmax_t pos, const String& string) { return Insert(pos, string.GetConstBuffer(), string.m_sharedString->size); } + /*! + * \brief Checks whether the string is empty + * \return true if string is empty + */ + bool String::IsEmpty() const { return m_sharedString->size == 0; } + /*! + * \brief Checks whether the string is null + * \return true if string is null + */ + bool String::IsNull() const { return m_sharedString.get() == GetEmptyString().get(); } + /*! + * \brief Checks whether the string is a number + * \return true if string is a number + * + * \param base Base of the number + * \param flags Flag for the look up + * + * \remark Produces a NazaraError if base is not in [2, 36( with NAZARA_CORE_SAFE defined + */ + bool String::IsNumber(UInt8 base, UInt32 flags) const { #if NAZARA_CORE_SAFE @@ -2011,6 +2486,13 @@ namespace Nz return true; } + /*! + * \brief Checks whether the string matches the pattern + * \return true if string matches + * + * \param pattern Pattern to search + */ + bool String::Match(const char* pattern) const { if (m_sharedString->size == 0 || !pattern) @@ -2058,37 +2540,91 @@ namespace Nz return !*pattern; } + /*! + * \brief Checks whether the string matches the pattern + * \return true if string matches + * + * \param pattern Pattern to search + */ + bool String::Match(const String& pattern) const { return Match(pattern.m_sharedString->string.get()); } + /*! + * \brief Prepends the character to the string + * \return A reference to this + * + * \param character Single character + * + * \see Insert + */ + String& String::Prepend(char character) { return Insert(0, character); } + /*! + * \brief Prepends the "C string" to the string + * \return A reference to this + * + * \param string String to add + * + * \see Insert + */ + String& String::Prepend(const char* string) { return Insert(0, string); } + /*! + * \brief Prepends the "C string" to the string + * \return A reference to this + * + * \param string String to add + * \param length Size of the string + * + * \see Insert + */ + String& String::Prepend(const char* string, std::size_t length) { return Insert(0, string, length); } + /*! + * \brief Prepends the string to the string + * \return A reference to this + * + * \param string String to add + * + * \see Insert + */ + String& String::Prepend(const String& string) { return Insert(0, string); } + /*! + * \brief Replaces the old character by the new one + * \return Number of changes + * + * \param oldCharacter Pattern to find + * \param newCharacter Pattern to change for + * \param start Index to begin the search + * \param flags Flag for the look up + */ + unsigned int String::Replace(char oldCharacter, char newCharacter, std::intmax_t start, UInt32 flags) { if (oldCharacter == '\0' || oldCharacter == newCharacter) return 0; - if (newCharacter == '\0') // Dans ce cas, il faut passer par un algorithme plus complexe + if (newCharacter == '\0') // In this case, we must use a more advanced algorithm return Replace(String(oldCharacter), String(), start); if (start < 0) @@ -2147,11 +2683,33 @@ namespace Nz return count; } + /*! + * \brief Replaces the old "C string" by the new one + * \return Number of changes + * + * \param oldCharacter Pattern to find + * \param newCharacter Pattern to change for + * \param start Index to begin the search + * \param flags Flag for the look up + */ + unsigned int String::Replace(const char* oldString, const char* replaceString, std::intmax_t start, UInt32 flags) { return Replace(oldString, std::strlen(oldString), replaceString, std::strlen(replaceString), start, flags); } + /*! + * \brief Replaces the old "C string" by the new one + * \return Number of changes + * + * \param oldCharacter Pattern to find + * \param oldLength Length of the old string + * \param newCharacter Pattern to change for + * \param Length of the new string + * \param start Index to begin the search + * \param flags Flag for the look up + */ + unsigned int String::Replace(const char* oldString, std::size_t oldLength, const char* replaceString, std::size_t replaceLength, std::intmax_t start, UInt32 flags) { if (oldLength == 0) @@ -2169,7 +2727,7 @@ namespace Nz { bool found = false; - // Si aucun changement de taille n'est nécessaire, nous pouvons alors utiliser un algorithme bien plus rapide + // If no size change is necessary, we can thus use a quicker algorithm while ((pos = Find(oldString, pos, flags)) != npos) { if (!found) @@ -2184,10 +2742,10 @@ namespace Nz ++count; } } - else ///TODO: Algorithme de remplacement sans changement de buffer (si replaceLength < oldLength) + else ///TODO: Replacement algorithm without changing the buffer (if replaceLength < oldLength) { std::size_t newSize = m_sharedString->size + Count(oldString)*(replaceLength - oldLength); - if (newSize == m_sharedString->size) // Alors c'est que Count(oldString) == 0 + if (newSize == m_sharedString->size) // Then it's the fact that Count(oldString) == 0 return 0; auto newString = std::make_shared(newSize); @@ -2218,18 +2776,40 @@ namespace Nz return count; } + /*! + * \brief Replaces the old string by the new one + * \return Number of changes + * + * \param oldCharacter Pattern to find + * \param newCharacter Pattern to change for + * \param start Index to begin the search + * \param flags Flag for the look up + */ + unsigned int String::Replace(const String& oldString, const String& replaceString, std::intmax_t start, UInt32 flags) { return Replace(oldString.GetConstBuffer(), oldString.m_sharedString->size, replaceString.GetConstBuffer(), replaceString.m_sharedString->size, start, flags); } + /*! + * \brief Replaces the old characters in the list by the new one + * \return Number of changes + * + * \param oldCharacters Pattern to find + * \param newCharacter Pattern to change for + * \param start Index to begin the search + * \param flags Flag for the look up + * + * \remark Does not handle UTF-8 currently + */ + unsigned int String::ReplaceAny(const char* oldCharacters, char replaceCharacter, std::intmax_t start, UInt32 flags) { - ///FIXME: Ne gère pas l'UTF-8 + ///FIXME: Does not handle UTF-8 if (!oldCharacters || !oldCharacters[0]) return 0; - /*if (replaceCharacter == '\0') // Dans ce cas, il faut passer par un algorithme plus complexe + /*if (replaceCharacter == '\0') // In this case, we must use a more advance algorithm return ReplaceAny(String(oldCharacters), String(), start);*/ if (start < 0) @@ -2455,6 +3035,14 @@ namespace Nz } */ + /*! + * \brief Reserves enough memory for the buffer size + * + * \param bufferSize Size of the buffer to allocate + * + * \remark If bufferSize is smaller than the old one, nothing is done + */ + void String::Reserve(std::size_t bufferSize) { if (m_sharedString->capacity > bufferSize) @@ -2469,7 +3057,15 @@ namespace Nz m_sharedString = std::move(newString); } - String& String::Resize(std::intmax_t size, char character) + /*! + * \brief Resizes the string + * \return A reference to this + * + * \param size Target size + * \param flags Flag for the look up + */ + + String& String::Resize(std::intmax_t size, UInt32 flags) { if (size == 0) { @@ -2481,14 +3077,22 @@ namespace Nz size = std::max(m_sharedString->size + size, 0); std::size_t newSize = static_cast(size); + + if (flags & HandleUtf8 && newSize < m_sharedString->size) + { + std::size_t characterToRemove = m_sharedString->size - newSize; + + char* ptr = &m_sharedString->string[m_sharedString->size]; + for (std::size_t i = 0; i < characterToRemove; ++i) + utf8::prior(ptr, m_sharedString->string.get()); + + newSize = ptr - m_sharedString->string.get(); + } + if (m_sharedString->capacity >= newSize) { EnsureOwnership(); - // We've got the space required, just fill it up - if (character != '\0' && newSize > m_sharedString->size) - std::memset(&m_sharedString->string[m_sharedString->size], character, newSize - m_sharedString->size); - m_sharedString->size = newSize; m_sharedString->string[newSize] = '\0'; // Adds the EoS character } @@ -2497,16 +3101,21 @@ namespace Nz auto newString = std::make_shared(newSize); std::memcpy(newString->string.get(), m_sharedString->string.get(), m_sharedString->size); - if (character != '\0') - std::memset(&newString->string[m_sharedString->size], character, newSize - m_sharedString->size); - m_sharedString = std::move(newString); } return *this; } - String String::Resized(std::intmax_t size, char character) const + /*! + * \brief Resize a copy of the string + * \return A copy of what would be the string resized + * + * \param size Target size + * \param flags Flag for the look up + */ + + String String::Resized(std::intmax_t size, UInt32 flags) const { if (size < 0) size = m_sharedString->size + size; @@ -2518,19 +3127,31 @@ namespace Nz if (newSize == m_sharedString->size) return *this; + if (flags & HandleUtf8 && newSize < m_sharedString->size) + { + std::size_t characterToRemove = m_sharedString->size - newSize; + + char* ptr = &m_sharedString->string[m_sharedString->size - 1]; + for (std::size_t i = 0; i < characterToRemove; ++i) + utf8::prior(ptr, m_sharedString->string.get()); + + newSize = ptr - m_sharedString->string.get(); + } + auto sharedStr = std::make_shared(newSize); if (newSize > m_sharedString->size) - { std::memcpy(sharedStr->string.get(), m_sharedString->string.get(), m_sharedString->size); - if (character != '\0') - std::memset(&sharedStr->string[m_sharedString->size], character, newSize - m_sharedString->size); - } else std::memcpy(sharedStr->string.get(), m_sharedString->string.get(), newSize); return String(std::move(sharedStr)); } + /*! + * \brief Reverses the string + * \return A reference to this + */ + String& String::Reverse() { if (m_sharedString->size != 0) @@ -2545,6 +3166,11 @@ namespace Nz return *this; } + /*! + * \brief Reverses a copy of the string + * \return A copy of what would be the string reversed + */ + String String::Reversed() const { if (m_sharedString->size == 0) @@ -2562,6 +3188,13 @@ namespace Nz return String(std::move(sharedStr)); } + /*! + * \brief Sets the string to the character + * \return A reference to this + * + * \param character Single character + */ + String& String::Set(char character) { if (character != '\0') @@ -2571,14 +3204,12 @@ namespace Nz EnsureOwnership(true); m_sharedString->size = 1; - m_sharedString->string[0] = character; m_sharedString->string[1] = '\0'; } else - { - auto newString = std::make_shared(1); - newString->string[0] = character; - } + m_sharedString = std::make_shared(1); + + m_sharedString->string[0] = character; } else ReleaseString(); @@ -2586,6 +3217,15 @@ namespace Nz return *this; } + + /*! + * \brief Sets the string with multiple times the same character + * \return A reference to this + * + * \param rep Number of repetitions of the character + * \param character Single character + */ + String& String::Set(std::size_t rep, char character) { if (rep > 0) @@ -2609,11 +3249,28 @@ namespace Nz return *this; } + /*! + * \brief Sets the string with multiple times the same string + * \return A reference to this + * + * \param rep Number of repetitions of the string + * \param string String to multiply + */ + String& String::Set(std::size_t rep, const char* string) { return Set(rep, string, (string) ? std::strlen(string) : 0); } + /*! + * \brief Sets the string with multiple times the same string + * \return A reference to this + * + * \param rep Number of repetitions of the string + * \param string String to multiply + * \param length Length of the string + */ + String& String::Set(std::size_t rep, const char* string, std::size_t length) { std::size_t totalSize = rep*length; @@ -2639,16 +3296,39 @@ namespace Nz return *this; } + /*! + * \brief Sets the string with multiple times the same string + * \return A reference to this + * + * \param rep Number of repetitions of the string + * \param string String to multiply + */ + String& String::Set(std::size_t rep, const String& string) { return Set(rep, string.GetConstBuffer(), string.m_sharedString->size); } + /*! + * \brief Sets the string with other "C string" + * \return A reference to this + * + * \param string String to copy + */ + String& String::Set(const char* string) { return Set(string, (string) ? std::strlen(string) : 0); } + /*! + * \brief Sets the string with other "C string" + * \return A reference to this + * + * \param string String to represent + * \param length Length of the string + */ + String& String::Set(const char* string, std::size_t length) { if (length > 0) @@ -2671,11 +3351,25 @@ namespace Nz return *this; } + /*! + * \brief Sets the string with a std::string + * \return A reference to this + * + * \param string String to copy + */ + String& String::Set(const std::string& string) { return Set(string.data(), string.size()); } + /*! + * \brief Sets the string with other string + * \return A reference to this + * + * \param string String to copy + */ + String& String::Set(const String& string) { m_sharedString = string.m_sharedString; @@ -2683,6 +3377,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the string by move semantic + * \return A reference to this + * + * \param string String to move + */ + String& String::Set(String&& string) noexcept { std::swap(m_sharedString, string.m_sharedString); @@ -2690,6 +3391,13 @@ namespace Nz return *this; } + /*! + * \brief Simplifies a copy of the string + * \return A copy of what would be the string simplified + * + * \param flags Flag for the look up + */ + String String::Simplified(UInt32 flags) const { if (m_sharedString->size == 0) @@ -2727,7 +3435,7 @@ namespace Nz const char* limit = &m_sharedString->string[m_sharedString->size]; do { - if (std::isspace(*ptr)) + if (Unicode::GetCategory(*ptr) & Unicode::Category_Separator) { if (inword) { @@ -2752,11 +3460,28 @@ namespace Nz return String(std::move(newString)); } + /*! + * \brief Simplifies the string + * \return A reference to this + * + * \param flags Flag for the look up + */ + String& String::Simplify(UInt32 flags) { return Set(Simplified(flags)); } + /*! + * \brief Splits the string into others + * \return The number of splits + * + * \param result Resulting tokens + * \param separation Separation character + * \param start Index for the beginning of the search + * \param flags Flag for the look up + */ + unsigned int String::Split(std::vector& result, char separation, std::intmax_t start, UInt32 flags) const { if (separation == '\0' || m_sharedString->size == 0) @@ -2789,11 +3514,32 @@ namespace Nz return result.size(); } + /*! + * \brief Splits the string into others + * \return The number of splits + * + * \param result Resulting tokens + * \param separation Separation string + * \param start Index for the beginning of the search + * \param flags Flag for the look up + */ + unsigned int String::Split(std::vector& result, const char* separation, std::intmax_t start, UInt32 flags) const { return Split(result, separation, std::strlen(separation), start, flags); } + /*! + * \brief Splits the string into others + * \return The number of splits + * + * \param result Resulting tokens + * \param separation Separation String + * \param length Length of the string + * \param start Index for the beginning of the search + * \param flags Flag for the look up + */ + unsigned int String::Split(std::vector& result, const char* separation, std::size_t length, std::intmax_t start, UInt32 flags) const { if (m_sharedString->size == 0) @@ -2837,11 +3583,31 @@ namespace Nz return result.size()-oldSize; } + /*! + * \brief Splits the string into others + * \return The number of splits + * + * \param result Resulting tokens + * \param separation Separation string + * \param start Index for the beginning of the search + * \param flags Flag for the look up + */ + unsigned int String::Split(std::vector& result, const String& separation, std::intmax_t start, UInt32 flags) const { return Split(result, separation.m_sharedString->string.get(), separation.m_sharedString->size, start, flags); } + /*! + * \brief Splits the string into others + * \return The number of splits + * + * \param result Resulting tokens + * \param separation List of characters of separation + * \param start Index for the beginning of the search + * \param flags Flag for the look up + */ + unsigned int String::SplitAny(std::vector& result, const char* separations, std::intmax_t start, UInt32 flags) const { if (m_sharedString->size == 0) @@ -2873,11 +3639,31 @@ namespace Nz return result.size()-oldSize; } + /*! + * \brief Splits the string into others + * \return The number of splits + * + * \param result Resulting tokens + * \param separation List of characters of separation + * \param start Index for the beginning of the search + * \param flags Flag for the look up + */ + unsigned int String::SplitAny(std::vector& result, const String& separations, std::intmax_t start, UInt32 flags) const { return SplitAny(result, separations.m_sharedString->string.get(), start, flags); } + /*! + * \brief Checks whether the string begins with the character + * \return true if it the case + * + * \param character Single character + * \param flags Flag for the look up + * + * \see EndsWith + */ + bool String::StartsWith(char character, UInt32 flags) const { if (character == '\0' || m_sharedString->size == 0) @@ -2889,6 +3675,16 @@ namespace Nz return m_sharedString->string[0] == character; } + /*! + * \brief Checks whether the string begins with the "C string" + * \return true if it the case + * + * \param string String to match + * \param flags Flag for the look up + * + * \see EndsWith + */ + bool String::StartsWith(const char* string, UInt32 flags) const { if (!string || !string[0] || m_sharedString->size == 0) @@ -2949,6 +3745,16 @@ namespace Nz return false; } + /*! + * \brief Checks whether the string begins with the string + * \return true if it the case + * + * \param string String to match + * \param flags Flag for the look up + * + * \see EndsWith + */ + bool String::StartsWith(const String& string, UInt32 flags) const { if (string.m_sharedString->size == 0) @@ -2998,6 +3804,14 @@ namespace Nz return false; } + /*! + * \brief Returns a sub string of the string + * \return SubString + * + * \param startPos Index for the beginning of the search + * \param endPos Index for the end of the search + */ + String String::SubString(std::intmax_t startPos, std::intmax_t endPos) const { if (startPos < 0) @@ -3024,6 +3838,17 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Returns a sub string of the string from a character + * \return SubString + * + * \param charater Pattern to find + * \param startPos Index for the beginning of the search + * \param fromLast beginning by the end + * \param include Include the character + * \param flags Flag for the look up + */ + String String::SubStringFrom(char character, std::intmax_t startPos, bool fromLast, bool include, UInt32 flags) const { if (character == '\0') @@ -3043,11 +3868,34 @@ namespace Nz return SubString(pos + ((include) ? 0 : 1)); } + /*! + * \brief Returns a sub string of the string from a string + * \return SubString + * + * \param string Pattern to find + * \param startPos Index for the beginning of the search + * \param fromLast beginning by the end + * \param include Include the character + * \param flags Flag for the look up + */ + String String::SubStringFrom(const char* string, std::intmax_t startPos, bool fromLast, bool include, UInt32 flags) const { return SubStringFrom(string, std::strlen(string), startPos, fromLast, include, flags); } + /*! + * \brief Returns a sub string of the string from a string + * \return SubString + * + * \param string Pattern to find + * \param length Size of the string + * \param startPos Index for the beginning of the search + * \param fromLast beginning by the end + * \param include Include the character + * \param flags Flag for the look up + */ + String String::SubStringFrom(const char* string, std::size_t length, std::intmax_t startPos, bool fromLast, bool include, UInt32 flags) const { std::size_t pos; @@ -3064,11 +3912,33 @@ namespace Nz return SubString(pos + ((include) ? 0 : length)); } + /*! + * \brief Returns a sub string of the string from a string + * \return SubString + * + * \param string Pattern to find + * \param startPos Index for the beginning of the search + * \param fromLast beginning by the end + * \param include Include the character + * \param flags Flag for the look up + */ + String String::SubStringFrom(const String& string, std::intmax_t startPos, bool fromLast, bool include, UInt32 flags) const { return SubStringFrom(string.GetConstBuffer(), string.m_sharedString->size, startPos, fromLast, include, flags); } + /*! + * \brief Returns a sub string of the string up to a character + * \return SubString + * + * \param charater Pattern to find + * \param startPos Index for the beginning of the search + * \param toLast beginning by the end + * \param include Include the character + * \param flags Flag for the look up + */ + String String::SubStringTo(char character, std::intmax_t startPos, bool toLast, bool include, UInt32 flags) const { if (character == '\0') @@ -3088,11 +3958,34 @@ namespace Nz return SubString(0, pos+((include) ? 1 : 0)-1); } + /*! + * \brief Returns a sub string of the string up to a string + * \return SubString + * + * \param string Pattern to find + * \param startPos Index for the beginning of the search + * \param toLast beginning by the end + * \param include Include the character + * \param flags Flag for the look up + */ + String String::SubStringTo(const char* string, std::intmax_t startPos, bool toLast, bool include, UInt32 flags) const { return SubStringTo(string, std::strlen(string), startPos, toLast, include, flags); } + /*! + * \brief Returns a sub string of the string up to a string + * \return SubString + * + * \param string Pattern to find + * \param length Size of the string + * \param startPos Index for the beginning of the search + * \param toLast beginning by the end + * \param include Include the character + * \param flags Flag for the look up + */ + String String::SubStringTo(const char* string, std::size_t length, std::intmax_t startPos, bool toLast, bool include, UInt32 flags) const { std::size_t pos; @@ -3109,16 +4002,41 @@ namespace Nz return SubString(0, pos+((include) ? length : 0)-1); } + /*! + * \brief Returns a sub string of the string up to a string + * \return SubString + * + * \param string Pattern to find + * \param startPos Index for the beginning of the search + * \param toLast beginning by the end + * \param include Include the character + * \param flags Flag for the look up + */ + String String::SubStringTo(const String& string, std::intmax_t startPos, bool toLast, bool include, UInt32 flags) const { return SubStringTo(string.GetConstBuffer(), string.m_sharedString->size, startPos, toLast, include, flags); } + /*! + * \brief Swaps the content with the other string + * + * \param str Other string to swap with + */ + void String::Swap(String& str) { std::swap(m_sharedString, str.m_sharedString); } + /*! + * \brief Converts the string to boolean + * \return true if successful + * + * \param value Boolean to convert to + * \param flags Flag for the look up + */ + bool String::ToBool(bool* value, UInt32 flags) const { if (m_sharedString->size == 0) @@ -3139,7 +4057,7 @@ namespace Nz else { if (flags & CaseInsensitive) - word = word.ToLower(); // Les mots identifiés sont en ASCII, inutile de passer le flag unicode + word = word.ToLower(); // The identified words are in ASCII, no use of Unicode flag if (word == "true") { @@ -3158,6 +4076,14 @@ namespace Nz return true; } + /*! + * \brief Converts the string to double + * \return true if successful + * + * \param value Double to convert to + * \param flags Flag for the look up + */ + bool String::ToDouble(double* value) const { if (m_sharedString->size == 0) @@ -3169,6 +4095,14 @@ namespace Nz return true; } + /*! + * \brief Converts the string to integer + * \return true if successful + * + * \param value Integer to convert to + * \param flags Flag for the look up + */ + bool String::ToInteger(long long* value, UInt8 base) const { if (value) @@ -3182,6 +4116,13 @@ namespace Nz return IsNumber(base); } + /*! + * \brief Converts the string to lower + * \return Lower string + * + * \param flags Flag for the look up + */ + String String::ToLower(UInt32 flags) const { if (m_sharedString->size == 0) @@ -3214,6 +4155,13 @@ namespace Nz } } + /*! + * \brief Converts the string to upper + * \return Upper string + * + * \param flags Flag for the look up + */ + String String::ToUpper(UInt32 flags) const { if (m_sharedString->size == 0) @@ -3246,16 +4194,38 @@ namespace Nz } } + /*! + * \brief Trims the string + * \return A reference to this + * + * \param flags Flag for the look up + */ + String& String::Trim(UInt32 flags) { return Set(Trimmed(flags)); } + /*! + * \brief Trims the string from a character + * \return A reference to this + * + * \param character Character to suppress + * \param flags Flag for the look up + */ + String& String::Trim(char character, UInt32 flags) { return Set(Trimmed(character, flags)); } + /*! + * \brief Trims a copy of the string + * \return A copy of what would be the string trimmed + * + * \param flags Flag for the look up + */ + String String::Trimmed(UInt32 flags) const { if (m_sharedString->size == 0) @@ -3270,7 +4240,7 @@ namespace Nz utf8::unchecked::iterator it(m_sharedString->string.get()); do { - if (Unicode::GetCategory(*it) & Unicode::Category_Separator) + if ((Unicode::GetCategory(*it) & Unicode::Category_Separator) == 0) break; } while (*++it); @@ -3285,7 +4255,7 @@ namespace Nz utf8::unchecked::iterator it(&m_sharedString->string[m_sharedString->size]); while ((it--).base() != m_sharedString->string.get()) { - if (Unicode::GetCategory(*it) & Unicode::Category_Separator) + if ((Unicode::GetCategory(*it) & Unicode::Category_Separator) == 0) break; } @@ -3301,7 +4271,7 @@ namespace Nz { for (; startPos < m_sharedString->size; ++startPos) { - if (!std::isspace(m_sharedString->string[startPos])) + if ((Unicode::GetCategory(m_sharedString->string[startPos]) & Unicode::Category_Separator) == 0) break; } } @@ -3311,7 +4281,7 @@ namespace Nz { for (; endPos > 0; --endPos) { - if (!std::isspace(m_sharedString->string[endPos])) + if ((Unicode::GetCategory(m_sharedString->string[endPos]) & Unicode::Category_Separator) == 0) break; } } @@ -3320,6 +4290,14 @@ namespace Nz return SubString(startPos, endPos); } + /*! + * \brief Trims a copy of the string from a character + * \return A copy of what would be the string trimmed + * + * \param character Character to suppress + * \param flags Flag for the look up + */ + String String::Trimmed(char character, UInt32 flags) const { if (m_sharedString->size == 0) @@ -3372,31 +4350,67 @@ namespace Nz return SubString(startPos, endPos); } + /*! + * \brief Returns an iterator pointing to the beginning of the string + * \return beginning of the string + */ + char* String::begin() { return m_sharedString->string.get(); } + /*! + * \brief Returns an iterator pointing to the beginning of the string + * \return beginning of the string + */ + const char* String::begin() const { return m_sharedString->string.get(); } + /*! + * \brief Returns an iterator pointing to the end of the string + * \return End of the string + */ + char* String::end() { return &m_sharedString->string[m_sharedString->size]; } + /*! + * \brief Returns an iterator pointing to the end of the string + * \return End of the string + */ + const char* String::end() const { return &m_sharedString->string[m_sharedString->size]; } + /*! + * \brief Pushed the character to the front of the string + * + * \param c Single character + * + * \see Prepend + */ + void String::push_front(char c) { Prepend(c); } + /*! + * \brief Pushed the character to the back of the string + * + * \param c Single character + * + * \see Append + */ + void String::push_back(char c) { Append(c); @@ -3423,11 +4437,25 @@ namespace Nz } */ + /*! + * \brief Converts the string to std::string + * \return std::string representation + */ + String::operator std::string() const { return std::string(m_sharedString->string.get(), m_sharedString->size); } + /*! + * \brief Gets the ith character in the string + * \return A reference to the character + * + * \param pos Index of the character + * + * \remark If pos is greather than the size, Resize is called + */ + char& String::operator[](std::size_t pos) { EnsureOwnership(); @@ -3438,6 +4466,15 @@ namespace Nz return m_sharedString->string[pos]; } + /*! + * \brief Gets the ith character in the string + * \return The character + * + * \param pos Index of the character + * + * \remark Produces a NazaraError if pos is greather than the size + */ + char String::operator[](std::size_t pos) const { #if NAZARA_CORE_SAFE @@ -3451,31 +4488,73 @@ namespace Nz return m_sharedString->string[pos]; } + /*! + * \brief Assigns the string to the character + * \return A reference to this + * + * \param character Single character + */ + String& String::operator=(char character) { return Set(character); } + /*! + * \brief Assigns the string with other "C string" + * \return A reference to this + * + * \param string String to copy + */ + String& String::operator=(const char* string) { return Set(string); } + /*! + * \brief Assigns the string with a std::string + * \return A reference to this + * + * \param string String to copy + */ + String& String::operator=(const std::string& string) { return Set(string); } + /*! + * \brief Assigns the string with other string + * \return A reference to this + * + * \param string String to copy + */ + String& String::operator=(const String& string) { return Set(string); } + /*! + * \brief Assigns the string by move semantic + * \return A reference to this + * + * \param string String to move + */ + String& String::operator=(String&& string) noexcept { return Set(string); } + /*! + * \brief Concatenates the character to the string + * \return String which is the result of the concatenation + * + * \param character Single character + */ + String String::operator+(char character) const { if (character == '\0') @@ -3488,6 +4567,13 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Concatenates the "C string" to the string + * \return String which is the result of the concatenation + * + * \param string String to add + */ + String String::operator+(const char* string) const { if (!string || !string[0]) @@ -3507,6 +4593,13 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Concatenates the std::string to the string + * \return String which is the result of the concatenation + * + * \param string String to add + */ + String String::operator+(const std::string& string) const { if (string.empty()) @@ -3522,6 +4615,13 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Concatenates the string to the string + * \return String which is the result of the concatenation + * + * \param string String to add + */ + String String::operator+(const String& string) const { if (string.m_sharedString->size == 0) @@ -3537,26 +4637,61 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Concatenates the character to this string + * \return A reference to this + * + * \param character Single character + */ + String& String::operator+=(char character) { return Insert(m_sharedString->size, character); } + /*! + * \brief Concatenates the "C string" to this string + * \return A reference to this + * + * \param string String to add + */ + String& String::operator+=(const char* string) { return Insert(m_sharedString->size, string); } + /*! + * \brief Concatenates the std::string to this string + * \return A reference to this + * + * \param string String to add + */ + String& String::operator+=(const std::string& string) { return Insert(m_sharedString->size, string.c_str(), string.size()); } + /*! + * \brief Concatenates the string to this string + * \return A reference to this + * + * \param string String to add + */ + String& String::operator+=(const String& string) { return Insert(m_sharedString->size, string); } + /*! + * \brief Checks whether the string is equal to the character + * \return true if it is the case + * + * \param character Single character + */ + bool String::operator==(char character) const { if (m_sharedString->size == 0) @@ -3568,6 +4703,13 @@ namespace Nz return m_sharedString->string[0] == character; } + /*! + * \brief Checks whether the string is equal to the "C string" + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator==(const char* string) const { if (m_sharedString->size == 0) @@ -3579,6 +4721,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string) == 0; } + /*! + * \brief Checks whether the string is equal to the std::string + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator==(const std::string& string) const { if (m_sharedString->size == 0 || string.empty()) @@ -3590,6 +4739,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string.c_str()) == 0; } + /*! + * \brief Checks whether the string is equal to the character + * \return false if it is the case + * + * \param character Single character + */ + bool String::operator!=(char character) const { if (m_sharedString->size == 0) @@ -3604,6 +4760,13 @@ namespace Nz return m_sharedString->string[0] != character; } + /*! + * \brief Checks whether the string is equal to the "C string" + * \return false if it is the case + * + * \param string String to compare + */ + bool String::operator!=(const char* string) const { if (m_sharedString->size == 0) @@ -3615,6 +4778,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string) != 0; } + /*! + * \brief Checks whether the string is equal to the std::string + * \return false if it is the case + * + * \param string String to compare + */ + bool String::operator!=(const std::string& string) const { if (m_sharedString->size == 0 || string.empty()) @@ -3626,6 +4796,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string.c_str()) != 0; } + /*! + * \brief Checks whether the string is less than the character + * \return true if it is the case + * + * \param character Single character + */ + bool String::operator<(char character) const { if (character == '\0') @@ -3637,6 +4814,13 @@ namespace Nz return m_sharedString->string[0] < character; } + /*! + * \brief Checks whether the string is less than the "C string" + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator<(const char* string) const { if (!string || !string[0]) @@ -3648,6 +4832,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string) < 0; } + /*! + * \brief Checks whether the string is less than the std::string + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator<(const std::string& string) const { if (string.empty()) @@ -3659,6 +4850,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string.c_str()) < 0; } + /*! + * \brief Checks whether the string is less or equal than the character + * \return true if it is the case + * + * \param character Single character + */ + bool String::operator<=(char character) const { if (m_sharedString->size == 0) @@ -3670,6 +4868,13 @@ namespace Nz return m_sharedString->string[0] < character || (m_sharedString->string[0] == character && m_sharedString->size == 1); } + /*! + * \brief Checks whether the string is less or equal than the "C string" + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator<=(const char* string) const { if (m_sharedString->size == 0) @@ -3681,6 +4886,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string) <= 0; } + /*! + * \brief Checks whether the string is less or equal than the std::string + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator<=(const std::string& string) const { if (m_sharedString->size == 0) @@ -3692,6 +4904,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string.c_str()) <= 0; } + /*! + * \brief Checks whether the string is greather than the character + * \return true if it is the case + * + * \param character Single character + */ + bool String::operator>(char character) const { if (m_sharedString->size == 0) @@ -3703,6 +4922,13 @@ namespace Nz return m_sharedString->string[0] > character; } + /*! + * \brief Checks whether the string is greather than the "C string" + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator>(const char* string) const { if (m_sharedString->size == 0) @@ -3714,6 +4940,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string) > 0; } + /*! + * \brief Checks whether the string is greather than the std::string + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator>(const std::string& string) const { if (m_sharedString->size == 0) @@ -3725,6 +4958,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string.c_str()) > 0; } + /*! + * \brief Checks whether the string is greather or equal than the character + * \return true if it is the case + * + * \param character Single character + */ + bool String::operator>=(char character) const { if (character == '\0') @@ -3736,6 +4976,13 @@ namespace Nz return m_sharedString->string[0] > character || (m_sharedString->string[0] == character && m_sharedString->size == 1); } + /*! + * \brief Checks whether the string is greather or equal than the "C string" + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator>=(const char* string) const { if (!string || !string[0]) @@ -3747,6 +4994,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string) >= 0; } + /*! + * \brief Checks whether the string is greather or equal than the std::string + * \return true if it is the case + * + * \param string String to compare + */ + bool String::operator>=(const std::string& string) const { if (string.empty()) @@ -3758,6 +5012,13 @@ namespace Nz return std::strcmp(GetConstBuffer(), string.c_str()) >= 0; } + /*! + * \brief Converts the boolean to string + * \return String representation of the boolean + * + * \param boolean Boolean value + */ + String String::Boolean(bool boolean) { std::size_t size = (boolean) ? 4 : 5; @@ -3768,6 +5029,14 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Lexicographically compares the string + * \return The expected result + * + * \param first First string to use for comparison + * \parma second Second string to use for comparison + */ + int String::Compare(const String& first, const String& second) { if (first.m_sharedString->size == 0) @@ -3779,6 +5048,13 @@ namespace Nz return std::strcmp(first.GetConstBuffer(), second.GetConstBuffer()); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Float value + */ + String String::Number(float number) { std::ostringstream oss; @@ -3788,6 +5064,13 @@ namespace Nz return String(oss.str()); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Double value + */ + String String::Number(double number) { std::ostringstream oss; @@ -3797,6 +5080,13 @@ namespace Nz return String(oss.str()); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Long double value + */ + String String::Number(long double number) { std::ostringstream oss; @@ -3806,56 +5096,143 @@ namespace Nz return String(oss.str()); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Signed char value + * \param radix Base of the number + */ + String String::Number(signed char number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Unsigned char value + * \param radix Base of the number + */ + String String::Number(unsigned char number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Short value + * \param radix Base of the number + */ + String String::Number(short number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Unsigned short value + * \param radix Base of the number + */ + String String::Number(unsigned short number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Int value + * \param radix Base of the number + */ + String String::Number(int number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Unsigned int value + * \param radix Base of the number + */ + String String::Number(unsigned int number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Long value + * \param radix Base of the number + */ + String String::Number(long number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Unsigned long value + * \param radix Base of the number + */ + String String::Number(unsigned long number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Long long value + * \param radix Base of the number + */ + String String::Number(long long number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the number to string + * \return String representation of the number + * + * \param number Unsigned long long value + * \param radix Base of the number + */ + String String::Number(unsigned long long number, UInt8 radix) { return NumberToString(number, radix); } + /*! + * \brief Converts the pointer to string + * \return String representation of the pointer + * + * \param ptr Pointer to represent + */ + String String::Pointer(const void* ptr) { const std::size_t capacity = sizeof(void*)*2 + 2; @@ -3866,6 +5243,13 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Converts the unicode point to string + * \return String representation of the unicode point + * + * \param character Unicode point + */ + String String::Unicode(char32_t character) { if (character == '\0') @@ -3887,11 +5271,25 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Converts the unicode "C string" to string + * \return String representation of the unicode "C string" + * + * \param u8String String in UTF-8 + */ + String String::Unicode(const char* u8String) { return String(u8String); } + /*! + * \brief Converts the unicode "C string" to string + * \return String representation of the unicode "C string" + * + * \param u16String String in UTF-16 + */ + String String::Unicode(const char16_t* u16String) { if (!u16String || !u16String[0]) @@ -3903,7 +5301,7 @@ namespace Nz count++; while (*++ptr); - count *= 2; // On s'assure d'avoir la place suffisante + count *= 2; // We ensure to have enough place auto str = std::make_shared(count); @@ -3915,6 +5313,13 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Converts the unicode "C string" to string + * \return String representation of the unicode "C string" + * + * \param u32String String in UTF-32 + */ + String String::Unicode(const char32_t* u32String) { if (!u32String || !u32String[0]) @@ -3942,6 +5347,13 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Converts the unicode "C string" to string + * \return String representation of the unicode "C string" + * + * \param wString String in Wide + */ + String String::Unicode(const wchar_t* wString) { if (!wString || !wString[0]) @@ -3969,6 +5381,14 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Inputs the stream into the string + * \return A reference to the stream + * + * \param is Stream to get information from + * \param str String to set value + */ + std::istream& operator>>(std::istream& is, String& str) { str.Clear(); @@ -3993,6 +5413,14 @@ namespace Nz return is; } + /*! + * \brief Output operator + * \return The stream + * + * \param out The stream + * \param str The string to output + */ + std::ostream& operator<<(std::ostream& os, const String& str) { if (str.IsEmpty()) @@ -4001,6 +5429,14 @@ namespace Nz return operator<<(os, str.m_sharedString->string.get()); } + /*! + * \brief Concatenates the character to the string + * \return String which is the result of the concatenation + * + * \param character Single character + * \param string String in the right hand side + */ + String operator+(char character, const String& string) { if (character == '\0') @@ -4016,6 +5452,14 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Concatenates the "C string" to the string + * \return String which is the result of the concatenation + * + * \param string String to add + * \param string String in the right hand side + */ + String operator+(const char* string, const String& nstring) { if (!string || !string[0]) @@ -4034,6 +5478,14 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Concatenates the std::string to the string + * \return String which is the result of the concatenation + * + * \param string String to add + * \param string String in the right hand side + */ + String operator+(const std::string& string, const String& nstring) { if (string.empty()) @@ -4051,6 +5503,14 @@ namespace Nz return String(std::move(str)); } + /*! + * \brief Checks whether the first string is equal to the second string + * \return true if it is the case + * + * \param first String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator==(const String& first, const String& second) { if (first.m_sharedString->size == 0 || second.m_sharedString->size == 0) @@ -4065,11 +5525,27 @@ namespace Nz return std::strcmp(first.GetConstBuffer(), second.GetConstBuffer()) == 0; } + /*! + * \brief Checks whether the first string is equal to the second string + * \return false if it is the case + * + * \param first String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator!=(const String& first, const String& second) { return !operator==(first, second); } + /*! + * \brief Checks whether the first string is less than the second string + * \return true if it is the case + * + * \param first String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator<(const String& first, const String& second) { if (second.m_sharedString->size == 0) @@ -4081,111 +5557,285 @@ namespace Nz return std::strcmp(first.GetConstBuffer(), second.GetConstBuffer()) < 0; } + /*! + * \brief Checks whether the first string is less or equal than the second string + * \return true if it is the case + * + * \param first String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator<=(const String& first, const String& second) { return !operator<(second, first); } + /*! + * \brief Checks whether the first string is greather than the second string + * \return true if it is the case + * + * \param first String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator>(const String& first, const String& second) { return second < first; } + /*! + * \brief Checks whether the first string is greather or equal than the second string + * \return true if it is the case + * + * \param first String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator>=(const String& first, const String& second) { return !operator<(first, second); } + /*! + * \brief Checks whether the string is equal to the character + * \return true if it is the case + * + * \param character Single character in left hand side + * \param second String to compare in right hand side + */ + bool operator==(char character, const String& nstring) { return nstring == character; } + /*! + * \brief Checks whether the string is equal to the "C string" + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator==(const char* string, const String& nstring) { return nstring == string; } + /*! + * \brief Checks whether the string is equal to the std::string + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator==(const std::string& string, const String& nstring) { return nstring == string; } + /*! + * \brief Checks whether the string is equal to the character + * \return false if it is the case + * + * \param character Single character in left hand side + * \param second String to compare in right hand side + */ + bool operator!=(char character, const String& nstring) { return !operator==(character, nstring); } + /*! + * \brief Checks whether the string is equal to the "C string" + * \return false if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator!=(const char* string, const String& nstring) { return !operator==(string, nstring); } + /*! + * \brief Checks whether the string is equal to the std::string + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator!=(const std::string& string, const String& nstring) { return !operator==(string, nstring); } + /*! + * \brief Checks whether the string is less than the character + * \return true if it is the case + * + * \param character Single character in left hand side + * \param second String to compare in right hand side + */ + bool operator<(char character, const String& nstring) { return nstring > character; } + /*! + * \brief Checks whether the string is less than the "C string" + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator<(const char* string, const String& nstring) { return nstring > string; } + /*! + * \brief Checks whether the string is less than the std::string + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator<(const std::string& string, const String& nstring) { return nstring > string; } + /*! + * \brief Checks whether the string is less or equal than the character + * \return true if it is the case + * + * \param character Single character in left hand side + * \param second String to compare in right hand side + */ + bool operator<=(char character, const String& nstring) { return !operator<(nstring, String(character)); } + /*! + * \brief Checks whether the string is less or equal than the "C string" + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator<=(const char* string, const String& nstring) { return !operator<(nstring, string); } + /*! + * \brief Checks whether the string is less or equal than the std::string + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator<=(const std::string& string, const String& nstring) { return !operator<(nstring, string); } + /*! + * \brief Checks whether the string is greather than the character + * \return true if it is the case + * + * \param character Single character in left hand side + * \param second String to compare in right hand side + */ + bool operator>(char character, const String& nstring) { return nstring < character; } + /*! + * \brief Checks whether the string is greather than the "C string" + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator>(const char* string, const String& nstring) { return nstring < string; } + /*! + * \brief Checks whether the string is greather than the std::string + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator>(const std::string& string, const String& nstring) { return nstring < string; } + /*! + * \brief Checks whether the string is greather or equal than the character + * \return true if it is the case + * + * \param character Single character in left hand side + * \param second String to compare in right hand side + */ + bool operator>=(char character, const String& nstring) { return !operator<(character, nstring); } + /*! + * \brief Checks whether the string is greather or equal than the "C string" + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator>=(const char* string, const String& nstring) { return !operator<(string, nstring); } + /*! + * \brief Checks whether the string is greather or equal than the std::string + * \return true if it is the case + * + * \param string String to compare in left hand side + * \param second String to compare in right hand side + */ + bool operator>=(const std::string& string, const String& nstring) { return !operator<(string, nstring); } + /*! + * \brief Ensures the ownership of the string + * + * \param discardContent Should discard the content + */ + void String::EnsureOwnership(bool discardContent) { if (!m_sharedString) @@ -4201,12 +5851,24 @@ namespace Nz } } + /*! + * \brief Gets the empty string + * \return A reference to the empty string + */ + const std::shared_ptr& String::GetEmptyString() { static auto emptyString = std::make_shared(); return emptyString; } + /*! + * \brief Serializes a string + * \return true if successful + * + * \param context Context of serialization + * \param string String to serialize + */ bool Serialize(SerializationContext& context, const String& string) { if (!Serialize(context, string.GetSize())) @@ -4215,7 +5877,14 @@ namespace Nz return context.stream->Write(string.GetConstBuffer(), string.GetSize()) == string.GetSize(); } - bool Unserialize(UnserializationContext& context, String* string) + /*! + * \brief Unserializes a string + * \return true if successful + * + * \param context Context of unserialization + * \param string String to unserialize + */ + bool Unserialize(SerializationContext& context, String* string) { UInt32 size; if (!Unserialize(context, &size)) @@ -4230,6 +5899,14 @@ namespace Nz namespace std { + /*! + * \brief Gets the line from the input stream + * \return A reference to the stream + * + * \param is Input stream to get information from + * \param str String to set + */ + istream& getline(istream& is, Nz::String& str) { str.Clear(); @@ -4237,7 +5914,7 @@ namespace std char c; for (;;) - { + { is.get(c); if (c != '\n' && c != '\0') str += c; @@ -4248,6 +5925,15 @@ namespace std return is; } + /*! + * \brief Gets the line from the input stream + * \return A reference to the stream + * + * \param is Input stream to get information from + * \param str String to set + * \param delim Delimitor defining the end + */ + istream& getline(istream& is, Nz::String& str, char delim) { str.Clear(); @@ -4266,6 +5952,13 @@ namespace std return is; } + /*! + * \brief Swaps two strings, specialisation of std + * + * \param lhs First string + * \param rhs Second string + */ + void swap(Nz::String& lhs, Nz::String& rhs) { lhs.Swap(rhs); diff --git a/src/Nazara/Core/StringStream.cpp b/src/Nazara/Core/StringStream.cpp index 2c2a8db24..ebd509c65 100644 --- a/src/Nazara/Core/StringStream.cpp +++ b/src/Nazara/Core/StringStream.cpp @@ -7,17 +7,38 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::StringStream + * \brief Core class that represents a stream of strings + */ + + /*! + * \brief Constructs a StringStream object by default + */ + StringStream::StringStream() : m_bufferSize(0) { } + /*! + * \brief Constructs a StringStream object with a string + * + * \param str First value of the stream + */ + StringStream::StringStream(const String& str) : m_bufferSize(str.GetSize()) { m_strings.push_back(str); } + /*! + * \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; @@ -29,6 +50,13 @@ namespace Nz return string; } + /*! + * \brief Adds the representation of the boolean + * \return A reference to this + * + * \param boolean Boolean value + */ + StringStream& StringStream::operator<<(bool boolean) { m_strings.push_back(String::Boolean(boolean)); @@ -37,6 +65,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the short + * \return A reference to this + * + * \param number Short value + */ + StringStream& StringStream::operator<<(short number) { m_strings.push_back(String::Number(number)); @@ -45,6 +80,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the unsigned short + * \return A reference to this + * + * \param number Short value + */ + StringStream& StringStream::operator<<(unsigned short number) { m_strings.push_back(String::Number(number)); @@ -53,6 +95,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the int + * \return A reference to this + * + * \param number Int value + */ + StringStream& StringStream::operator<<(int number) { m_strings.push_back(String::Number(number)); @@ -61,6 +110,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the unsigned int + * \return A reference to this + * + * \param number Int value + */ + StringStream& StringStream::operator<<(unsigned int number) { m_strings.push_back(String::Number(number)); @@ -69,6 +125,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the long + * \return A reference to this + * + * \param number Long value + */ + StringStream& StringStream::operator<<(long number) { m_strings.push_back(String::Number(number)); @@ -77,6 +140,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the unsigned long + * \return A reference to this + * + * \param number Long value + */ + StringStream& StringStream::operator<<(unsigned long number) { m_strings.push_back(String::Number(number)); @@ -85,6 +155,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the long long + * \return A reference to this + * + * \param number Long long value + */ + StringStream& StringStream::operator<<(long long number) { m_strings.push_back(String::Number(number)); @@ -93,6 +170,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the unsigned long long + * \return A reference to this + * + * \param number Long long value + */ + StringStream& StringStream::operator<<(unsigned long long number) { m_strings.push_back(String::Number(number)); @@ -101,6 +185,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the float + * \return A reference to this + * + * \param number Float value + */ + StringStream& StringStream::operator<<(float number) { m_strings.push_back(String::Number(number)); @@ -109,6 +200,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the double + * \return A reference to this + * + * \param number Double value + */ + StringStream& StringStream::operator<<(double number) { m_strings.push_back(String::Number(number)); @@ -117,6 +215,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the long double + * \return A reference to this + * + * \param number Long double value + */ + StringStream& StringStream::operator<<(long double number) { m_strings.push_back(String::Number(number)); @@ -125,6 +230,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the char + * \return A reference to this + * + * \param character Char value + */ + StringStream& StringStream::operator<<(char character) { m_strings.push_back(String(character)); @@ -133,6 +245,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the unsigned char + * \return A reference to this + * + * \param character Char value + */ + StringStream& StringStream::operator<<(unsigned char character) { m_strings.push_back(String(static_cast(character))); @@ -141,6 +260,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the const char* + * \return A reference to this + * + * \param string String value + */ + StringStream& StringStream::operator<<(const char* string) { m_strings.push_back(string); @@ -149,6 +275,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the std::string + * \return A reference to this + * + * \param string String value + */ + StringStream& StringStream::operator<<(const std::string& string) { m_strings.push_back(string); @@ -157,6 +290,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the Nz::String + * \return A reference to this + * + * \param string String value + */ + StringStream& StringStream::operator<<(const String& string) { m_strings.push_back(string); @@ -165,6 +305,13 @@ namespace Nz return *this; } + /*! + * \brief Adds the representation of the pointer + * \return A reference to this + * + * \param ptr Pointer value + */ + StringStream& StringStream::operator<<(const void* ptr) { m_strings.push_back(String::Pointer(ptr)); @@ -173,6 +320,11 @@ namespace Nz return *this; } + /*! + * \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/TaskScheduler.cpp b/src/Nazara/Core/TaskScheduler.cpp index 27d071987..a29436407 100644 --- a/src/Nazara/Core/TaskScheduler.cpp +++ b/src/Nazara/Core/TaskScheduler.cpp @@ -24,16 +24,40 @@ namespace Nz unsigned int s_workerCount = 0; } + /*! + * \ingroup core + * \class Nz::TaskScheduler + * \brief Core class that represents a pool of threads + * + * \remark Initialized should be called first + */ + + /*! + * \brief Gets the number of threads + * \return Number of threads, if none, the number of simulatenous threads on the processor is returned + */ + unsigned int TaskScheduler::GetWorkerCount() { return (s_workerCount > 0) ? s_workerCount : HardwareInfo::GetProcessorCount(); } + /*! + * \brief Initializes the TaskScheduler class + * \return true if everything is ok + */ + bool TaskScheduler::Initialize() { return TaskSchedulerImpl::Initialize(GetWorkerCount()); } + /*! + * \brief Runs the pending works + * + * \remark Produce a NazaraError if the class is not initialized + */ + void TaskScheduler::Run() { if (!Initialize()) @@ -49,6 +73,14 @@ namespace Nz } } + /*! + * \brief Sets the number of workers + * + * \param workerCount Number of simulatnous threads handling the tasks + * + * \remark Produce a NazaraError if the class is not initialized and NAZARA_CORE_SAFE is defined + */ + void TaskScheduler::SetWorkerCount(unsigned int workerCount) { #ifdef NAZARA_CORE_SAFE @@ -62,12 +94,22 @@ namespace Nz s_workerCount = workerCount; } + /*! + * \brief Uninitializes the TaskScheduler class + */ + void TaskScheduler::Uninitialize() { if (TaskSchedulerImpl::IsInitialized()) TaskSchedulerImpl::Uninitialize(); } + /*! + * \brief Waits for tasks to be done + * + * \remark Produce a NazaraError if the class is not initialized + */ + void TaskScheduler::WaitForTasks() { if (!Initialize()) @@ -79,6 +121,15 @@ namespace Nz TaskSchedulerImpl::WaitForTasks(); } + /*! + * \brief Adds a task on the pending list + * + * \param taskFunctor Functor represeting a task to be done + * + * \remark Produce a NazaraError if the class is not initialized + * \remark A task containing a call on this class is undefined behaviour + */ + void TaskScheduler::AddTaskFunctor(Functor* taskFunctor) { if (!Initialize()) diff --git a/src/Nazara/Core/Thread.cpp b/src/Nazara/Core/Thread.cpp index 105067baf..0c4cd8008 100644 --- a/src/Nazara/Core/Thread.cpp +++ b/src/Nazara/Core/Thread.cpp @@ -21,17 +21,37 @@ namespace Nz { + /*! + * \ingroup core + * \class Nz::Thread + * \brief Core class that represents a thread + */ + + /*! + * \brief Constructs a Thread object by default + */ + Thread::Thread() : m_impl(nullptr) { } + /*! + * \brief Constructs a Thread object by move semantic + * + * \param other Thread to move into this + */ + Thread::Thread(Thread&& other) noexcept : m_impl(other.m_impl) { other.m_impl = nullptr; } + /*! + * \brief Waits that the thread ends and then destroys this + */ + Thread::~Thread() { if (m_impl) @@ -42,6 +62,10 @@ namespace Nz } } + /*! + * \brief Detaches the thread + */ + void Thread::Detach() { if (m_impl) @@ -52,16 +76,32 @@ namespace Nz } } + /*! + * \brief Gets the id of the thread + * \return The identifiant of the thread (PID) + */ + Thread::Id Thread::GetId() const { return Thread::Id(m_impl); } + /*! + * \brief Checks whether the thread can be joined + * \return true if well formed and not detached + */ + bool Thread::IsJoinable() const { return m_impl != nullptr; } + /*! + * \brief Waits that the thread ends + * + * \remark Produce a NazaraError if no functor was assigned and NAZARA_CORE_SAFE is defined + */ + void Thread::Join() { #if NAZARA_CORE_SAFE @@ -77,6 +117,16 @@ namespace Nz m_impl = nullptr; } + /*! + * \brief Moves the other thread into this + * \return A reference to this + * + * \param thread Thread to move in this + * + * \remark Produce a NazaraError if no functor was assigned and NAZARA_CORE_SAFE is defined + * \remark And call std::terminate if no functor was assigned and NAZARA_CORE_SAFE is defined + */ + Thread& Thread::operator=(Thread&& thread) { #if NAZARA_CORE_SAFE @@ -91,16 +141,33 @@ namespace Nz return *this; } + /*! + * \brief Gets the number of simulatenous threads that can run on the same cpu + * \return The number of simulatenous threads + */ + unsigned int Thread::HardwareConcurrency() { return HardwareInfo::GetProcessorCount(); } + /*! + * \brief Makes sleep this thread + * + * \param milliseconds The number of milliseconds to sleep + */ + void Thread::Sleep(UInt32 milliseconds) { ThreadImpl::Sleep(milliseconds); } + /*! + * \brief Creates the implementation of this thread + * + * \param functor The task the thread will represent + */ + void Thread::CreateImpl(Functor* functor) { m_impl = new ThreadImpl(functor); @@ -108,41 +175,103 @@ namespace Nz /*********************************Thread::Id********************************/ + /*! + * \brief Constructs a Thread object with a thread implementation + * + * \param thread Thread implementation assigned to the thread + */ + Thread::Id::Id(ThreadImpl* thread) : m_id(thread) { } + /*! + * \brief Compares two Thread::Id + * \return true if the two thread ids are the same + * + * \param lhs First id + * \param rhs Second id + */ + bool operator==(const Thread::Id& lhs, const Thread::Id& rhs) { return lhs.m_id == rhs.m_id; } + /*! + * \brief Compares two Thread::Id + * \return false if the two thread ids are the same + * + * \param lhs First id + * \param rhs Second id + */ + bool operator!=(const Thread::Id& lhs, const Thread::Id& rhs) { return lhs.m_id != rhs.m_id; } + /*! + * \brief Compares two Thread::Id + * \return true if the first thread id is inferior to the second one + * + * \param lhs First id + * \param rhs Second id + */ + bool operator<(const Thread::Id& lhs, const Thread::Id& rhs) { return lhs.m_id < rhs.m_id; } + /*! + * \brief Compares two Thread::Id + * \return true if the first thread id is inferior or equal to the second one + * + * \param lhs First id + * \param rhs Second id + */ + bool operator<=(const Thread::Id& lhs, const Thread::Id& rhs) { return lhs.m_id <= rhs.m_id; } + /*! + * \brief Compares two Thread::Id + * \return true if the first thread id is superior to the second one + * + * \param lhs First id + * \param rhs Second id + */ + bool operator>(const Thread::Id& lhs, const Thread::Id& rhs) { return lhs.m_id > rhs.m_id; } + /*! + * \brief Compares two Thread::Id + * \return true if the first thread id is superior or equal to the second one + * + * \param lhs First id + * \param rhs Second id + */ + bool operator>=(const Thread::Id& lhs, const Thread::Id& rhs) { return lhs.m_id >= rhs.m_id; } + /*! + * \brief Output operator + * \return The stream + * + * \param out The stream + * \param id The thread id to output + */ + std::ostream& operator<<(std::ostream& o, const Nz::Thread::Id& id) { o << id.m_id; diff --git a/src/Nazara/Core/Unicode.cpp b/src/Nazara/Core/Unicode.cpp index c5b0d6917..0f6dd8452 100644 --- a/src/Nazara/Core/Unicode.cpp +++ b/src/Nazara/Core/Unicode.cpp @@ -11,20 +11,33 @@ namespace Nz { struct Character { - UInt16 category; // Le type du caractère - UInt8 direction; // Le sens de lecure du caractère - UInt32 lowerCase; // Le caractère correspondant en minuscule - UInt32 titleCase; // Le caractère correspondant en titre - UInt32 upperCase; // Le caractère correspondant en majuscule + UInt16 category; // The type of the character + UInt8 direction; // The reading way of the character + UInt32 lowerCase; // The corresponding lower character + UInt32 titleCase; // The corresponding title character + UInt32 upperCase; // The corresponding upper character }; } #include -#else // Implémentation supportant la table ASCII +#else // Implementation handling ASCII table namespace Nz { + /*! + * \ingroup core + * \class Nz::Unicode + * \brief Core class that represents a Unicode character + */ + + /*! + * \brief Gets the category of the character + * \return Unicode category + * + * \param character Character to get assignated category + */ + Unicode::Category Unicode::GetCategory(char32_t character) { switch (character) @@ -188,6 +201,13 @@ namespace Nz return Category_NoCategory; } + /*! + * \brief Gets the direction of reading of the character + * \return Unicode direction + * + * \param character Character to get assignated direction + */ + Unicode::Direction Unicode::GetDirection(char32_t character) { switch (character) @@ -347,6 +367,15 @@ namespace Nz return Direction_Boundary_Neutral; } + /*! + * \brief Gets the lower case of the character + * \return Unicode lower + * + * \param character Character to get assignated lower case + * + * \remark Only handling ASCII + */ + char32_t Unicode::GetLowercase(char32_t character) { if (character >= 'A' && character <= 'Z') @@ -355,11 +384,29 @@ namespace Nz return character; } + /*! + * \brief Gets the title case of the character + * \return Unicode title + * + * \param character Character to get assignated title case + * + * \remark Only handling ASCII + */ + char32_t Unicode::GetTitlecase(char32_t character) { return GetUppercase(character); } + /*! + * \brief Gets the upper case of the character + * \return Unicode upper + * + * \param character Character to get assignated upper case + * + * \remark Only handling ASCII + */ + char32_t Unicode::GetUppercase(char32_t character) { if (character >= 'a' && character <= 'z') diff --git a/src/Nazara/Core/Win32/TaskSchedulerImpl.cpp b/src/Nazara/Core/Win32/TaskSchedulerImpl.cpp index c93859d68..5b5d7153d 100644 --- a/src/Nazara/Core/Win32/TaskSchedulerImpl.cpp +++ b/src/Nazara/Core/Win32/TaskSchedulerImpl.cpp @@ -195,7 +195,7 @@ namespace Nz unsigned int __stdcall TaskSchedulerImpl::WorkerProc(void* userdata) { - unsigned int workerID = *reinterpret_cast(userdata); + unsigned int workerID = *static_cast(userdata); SetEvent(s_doneEvents[workerID]); Worker& worker = s_workers[workerID]; diff --git a/src/Nazara/Graphics/DeferredRenderTechnique.cpp b/src/Nazara/Graphics/DeferredRenderTechnique.cpp index 63342363c..88335d92b 100644 --- a/src/Nazara/Graphics/DeferredRenderTechnique.cpp +++ b/src/Nazara/Graphics/DeferredRenderTechnique.cpp @@ -120,7 +120,7 @@ namespace Nz for (unsigned int i = 0; i < 3; ++i) m_GBuffer[i] = Texture::New(); - + try { ErrorFlags errFlags(ErrorFlag_ThrowException); @@ -204,6 +204,11 @@ namespace Nz DeferredRenderTechnique::~DeferredRenderTechnique() = default; + void DeferredRenderTechnique::Clear(const SceneData& sceneData) const + { + NazaraUnused(sceneData); + } + bool DeferredRenderTechnique::Draw(const SceneData& sceneData) const { NazaraAssert(sceneData.viewer, "Invalid viewer"); diff --git a/src/Nazara/Graphics/Formats/MeshLoader.cpp b/src/Nazara/Graphics/Formats/MeshLoader.cpp index bce371e51..b915c6f2e 100644 --- a/src/Nazara/Graphics/Formats/MeshLoader.cpp +++ b/src/Nazara/Graphics/Formats/MeshLoader.cpp @@ -17,7 +17,10 @@ namespace Nz Ternary CheckStatic(Stream& stream, const ModelParameters& parameters) { NazaraUnused(stream); - NazaraUnused(parameters); + + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeMeshLoader", &skip) && skip) + return Ternary_False; return Ternary_Unknown; } @@ -66,7 +69,10 @@ namespace Nz Ternary CheckAnimated(Stream& stream, const SkeletalModelParameters& parameters) { NazaraUnused(stream); - NazaraUnused(parameters); + + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeAnimatedMeshLoader", &skip) && skip) + return Ternary_False; return Ternary_Unknown; } diff --git a/src/Nazara/Graphics/Formats/OBJLoader.cpp b/src/Nazara/Graphics/Formats/OBJLoader.cpp index b295811d4..09ad743d7 100644 --- a/src/Nazara/Graphics/Formats/OBJLoader.cpp +++ b/src/Nazara/Graphics/Formats/OBJLoader.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,10 @@ namespace Nz Ternary Check(Stream& stream, const ModelParameters& parameters) { NazaraUnused(stream); - NazaraUnused(parameters); + + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeOBJLoader", &skip) && skip) + return Ternary_False; return Ternary_Unknown; } @@ -129,8 +133,12 @@ namespace Nz bool Load(Model* model, Stream& stream, const ModelParameters& parameters) { + int reservedVertexCount; + if (!parameters.custom.GetIntegerParameter("NativeOBJLoader_VertexCount", &reservedVertexCount)) + reservedVertexCount = 100; + OBJParser parser(stream); - if (!parser.Parse()) + if (!parser.Parse(reservedVertexCount)) { NazaraError("OBJ parser failed"); return false; diff --git a/src/Nazara/Graphics/Formats/TextureLoader.cpp b/src/Nazara/Graphics/Formats/TextureLoader.cpp index 2f8dc2910..7530726fb 100644 --- a/src/Nazara/Graphics/Formats/TextureLoader.cpp +++ b/src/Nazara/Graphics/Formats/TextureLoader.cpp @@ -15,7 +15,10 @@ namespace Nz Ternary Check(Stream& stream, const MaterialParams& parameters) { NazaraUnused(stream); - NazaraUnused(parameters); + + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeTextureLoader", &skip) && skip) + return Ternary_False; return Ternary_Unknown; } diff --git a/src/Nazara/Graphics/ForwardRenderQueue.cpp b/src/Nazara/Graphics/ForwardRenderQueue.cpp index 75af497a3..683230706 100644 --- a/src/Nazara/Graphics/ForwardRenderQueue.cpp +++ b/src/Nazara/Graphics/ForwardRenderQueue.cpp @@ -502,16 +502,17 @@ namespace Nz layers.clear(); else { - for (auto it = layers.begin(); it != layers.end(); ++it) + for (auto it = layers.begin(); it != layers.end();) { Layer& layer = it->second; if (layer.clearCount++ >= 100) - it = layers.erase(it); + layers.erase(it++); else { layer.otherDrawables.clear(); layer.transparentModels.clear(); layer.transparentModelData.clear(); + ++it; } } } diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index c50791d8f..2ec95aee7 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -49,18 +49,21 @@ namespace Nz m_spriteBuffer.Reset(VertexDeclaration::Get(VertexLayout_XYZ_Color_UV), &m_vertexBuffer); } - bool ForwardRenderTechnique::Draw(const SceneData& sceneData) const + void ForwardRenderTechnique::Clear(const SceneData& sceneData) const { - NazaraAssert(sceneData.viewer, "Invalid viewer"); - - m_renderQueue.Sort(sceneData.viewer); - Renderer::Enable(RendererParameter_DepthBuffer, true); Renderer::Enable(RendererParameter_DepthWrite, true); Renderer::Clear(RendererBuffer_Depth); if (sceneData.background) sceneData.background->Draw(sceneData.viewer); + } + + bool ForwardRenderTechnique::Draw(const SceneData& sceneData) const + { + NazaraAssert(sceneData.viewer, "Invalid viewer"); + + m_renderQueue.Sort(sceneData.viewer); for (auto& pair : m_renderQueue.layers) { @@ -275,7 +278,7 @@ namespace Nz { // On ouvre le buffer en écriture BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(vertexMapper.GetPointer()); + VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); unsigned int spriteCount = 0; unsigned int maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount()/4); @@ -414,7 +417,7 @@ namespace Nz billboardCount -= renderedBillboardCount; BufferMapper vertexMapper(m_billboardPointBuffer, BufferAccess_DiscardAndWrite, 0, renderedBillboardCount*4); - BillboardPoint* vertices = reinterpret_cast(vertexMapper.GetPointer()); + BillboardPoint* vertices = static_cast(vertexMapper.GetPointer()); for (unsigned int i = 0; i < renderedBillboardCount; ++i) { diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index f4b6b0469..9af9d96cd 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +91,12 @@ namespace Nz return false; } + if (!Sprite::Initialize()) + { + NazaraError("Failed to initialize sprites"); + return false; + } + // Loaders Loaders::RegisterOBJ(); @@ -189,6 +196,7 @@ namespace Nz ParticleController::Uninitialize(); Material::Uninitialize(); SkyboxBackground::Uninitialize(); + Sprite::Uninitialize(); NazaraNotice("Uninitialized: Graphics module"); diff --git a/src/Nazara/Graphics/Material.cpp b/src/Nazara/Graphics/Material.cpp index 97a41a261..26c7e5717 100644 --- a/src/Nazara/Graphics/Material.cpp +++ b/src/Nazara/Graphics/Material.cpp @@ -208,7 +208,7 @@ namespace Nz flags & ShaderFlags_TextureOverlay); list.SetParameter("TRANSFORM", m_transformEnabled); - list.SetParameter("FLAG_BILLBOARD", static_cast(flags & ShaderFlags_Billboard)); + 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)); diff --git a/src/Nazara/Graphics/SkyboxBackground.cpp b/src/Nazara/Graphics/SkyboxBackground.cpp index 1e8678c20..a70a9dd2d 100644 --- a/src/Nazara/Graphics/SkyboxBackground.cpp +++ b/src/Nazara/Graphics/SkyboxBackground.cpp @@ -22,9 +22,11 @@ namespace Nz static VertexBufferRef s_vertexBuffer; } - SkyboxBackground::SkyboxBackground(TextureRef cubemapTexture) + SkyboxBackground::SkyboxBackground(TextureRef cubemapTexture) : + m_movementOffset(Vector3f::Zero()), + m_movementScale(0.f) { - m_sampler.SetWrapMode(SamplerWrap_Clamp); // Nécessaire pour ne pas voir les côtés + m_sampler.SetWrapMode(SamplerWrap_Clamp); // We don't want to see any beam SetTexture(std::move(cubemapTexture)); } @@ -34,9 +36,24 @@ namespace Nz Matrix4f skyboxMatrix(viewer->GetViewMatrix()); skyboxMatrix.SetTranslation(Vector3f::Zero()); + float zNear = viewer->GetZNear(); + + constexpr float movementLimit = 0.05f; + + Vector3f offset = (viewer->GetEyePosition() - m_movementOffset) * -m_movementScale; + offset.x = Clamp(offset.x, -movementLimit, movementLimit); + offset.y = Clamp(offset.y, -movementLimit, movementLimit); + offset.z = Clamp(offset.z, -movementLimit, movementLimit); + offset *= zNear; + + Matrix4f world; + world.MakeIdentity(); + world.SetScale(Vector3f(zNear)); + world.SetTranslation(offset); + Renderer::SetIndexBuffer(s_indexBuffer); Renderer::SetMatrix(MatrixType_View, skyboxMatrix); - Renderer::SetMatrix(MatrixType_World, Matrix4f::Scale(Vector3f(viewer->GetZNear()))); + Renderer::SetMatrix(MatrixType_World, world); Renderer::SetRenderStates(s_renderStates); Renderer::SetShader(s_shader); Renderer::SetTexture(0, m_texture); diff --git a/src/Nazara/Graphics/Sprite.cpp b/src/Nazara/Graphics/Sprite.cpp index 970254294..fd4ab758f 100644 --- a/src/Nazara/Graphics/Sprite.cpp +++ b/src/Nazara/Graphics/Sprite.cpp @@ -51,5 +51,21 @@ namespace Nz *texCoordPtr++ = m_textureCoords.GetCorner(RectCorner_RightBottom); } + bool Sprite::Initialize() + { + if (!SpriteLibrary::Initialize()) + { + NazaraError("Failed to initialise library"); + return false; + } + + return true; + } + + void Sprite::Uninitialize() + { + SpriteLibrary::Uninitialize(); + } + SpriteLibrary::LibraryMap Sprite::s_library; } diff --git a/src/Nazara/Graphics/TextSprite.cpp b/src/Nazara/Graphics/TextSprite.cpp index 13e4b3e49..6d0e41f52 100644 --- a/src/Nazara/Graphics/TextSprite.cpp +++ b/src/Nazara/Graphics/TextSprite.cpp @@ -33,13 +33,16 @@ namespace Nz void TextSprite::Update(const AbstractTextDrawer& drawer) { - m_atlases.clear(); - CallOnExit clearOnFail([this]() { Clear(); }); + // Mark every atlas as unused... + for (auto& pair : m_atlases) + 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) { @@ -47,19 +50,38 @@ namespace Nz const AbstractAtlas* atlas = font->GetAtlas().get(); NazaraAssert(atlas->GetStorage() & DataStorage_Hardware, "Font uses a non-hardware atlas which cannot be used by text sprites"); - if (m_atlases.find(atlas) == m_atlases.end()) + auto it = m_atlases.find(atlas); + if (it == m_atlases.end()) { - AtlasSlots& atlasSlots = m_atlases[atlas]; + it = m_atlases.insert(std::make_pair(atlas, AtlasSlots())).first; + AtlasSlots& atlasSlots = it->second; atlasSlots.clearSlot.Connect(atlas->OnAtlasCleared, this, &TextSprite::OnAtlasInvalidated); atlasSlots.layerChangeSlot.Connect(atlas->OnAtlasLayerChange, this, &TextSprite::OnAtlasLayerChange); atlasSlots.releaseSlot.Connect(atlas->OnAtlasRelease, this, &TextSprite::OnAtlasInvalidated); } + + it->second.used = true; + } + + // Remove unused atlas slots + auto atlasIt = m_atlases.begin(); + while (atlasIt != m_atlases.end()) + { + if (!atlasIt->second.used) + m_atlases.erase(atlasIt++); + else + ++atlasIt; } unsigned int glyphCount = drawer.GetGlyphCount(); m_localVertices.resize(glyphCount * 4); + // Reset glyph count for every texture to zero + for (auto& pair : m_renderInfos) + pair.second.count = 0; + + // Count glyph count for each texture Texture* lastTexture = nullptr; unsigned int* count = nullptr; for (unsigned int i = 0; i < glyphCount; ++i) @@ -69,25 +91,36 @@ namespace Nz Texture* texture = static_cast(glyph.atlas); if (lastTexture != texture) { - auto pair = m_renderInfos.insert(std::make_pair(texture, RenderIndices{0U, 0U})); + auto it = m_renderInfos.find(texture); + if (it == m_renderInfos.end()) + it = m_renderInfos.insert(std::make_pair(texture, RenderIndices{0U, 0U})).first; - count = &pair.first->second.count; + count = &it->second.count; lastTexture = texture; } (*count)++; } - // Attribution des indices + // Attributes indices and reinitialize glyph count to zero to use it as a counter in the next loop + // This is because the 1st glyph can use texture A, the 2nd glyph can use texture B and the 3th glyph C can use texture A again + // so we need a counter to know where to write informations + // also remove unused render infos unsigned int index = 0; - for (auto& pair : m_renderInfos) + auto infoIt = m_renderInfos.begin(); + while (infoIt != m_renderInfos.end()) { - RenderIndices& indices = pair.second; + RenderIndices& indices = infoIt->second; + if (indices.count == 0) + m_renderInfos.erase(infoIt++); //< No glyph uses this texture, remove from indices + else + { + indices.first = index; - indices.first = index; - - index += indices.count; - indices.count = 0; // On réinitialise count à zéro (on va s'en servir comme compteur dans la boucle suivante) + index += indices.count; + indices.count = 0; + ++infoIt; + } } lastTexture = nullptr; @@ -99,11 +132,11 @@ namespace Nz Texture* texture = static_cast(glyph.atlas); if (lastTexture != texture) { - indices = &m_renderInfos[texture]; // On a changé de texture, on ajuste le pointeur + indices = &m_renderInfos[texture]; //< We changed texture, adjust the pointer lastTexture = texture; } - // On commence par transformer les coordonnées entières en flottantes: + // 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; @@ -114,10 +147,11 @@ namespace Nz uvRect.width *= invWidth; uvRect.height *= invHeight; - static RectCorner normalCorners[4] = {RectCorner_LeftTop, RectCorner_RightTop, RectCorner_LeftBottom, RectCorner_RightBottom}; - static RectCorner flippedCorners[4] = {RectCorner_LeftBottom, RectCorner_LeftTop, RectCorner_RightBottom, RectCorner_RightTop}; + // Our glyph may be flipped in the atlas, to render it correctly we need to change the uv coordinates accordingly + const RectCorner normalCorners[4] = {RectCorner_LeftTop, RectCorner_RightTop, RectCorner_LeftBottom, RectCorner_RightBottom}; + const RectCorner flippedCorners[4] = {RectCorner_LeftBottom, RectCorner_LeftTop, RectCorner_RightBottom, RectCorner_RightTop}; - // Affectation des positions, couleurs, coordonnées de textures + // Set the position, color and UV of our vertices for (unsigned int j = 0; j < 4; ++j) { // Remember that indices->count is a counter here, not a count value @@ -126,7 +160,7 @@ namespace Nz m_localVertices[indices->count*4 + j].uv.Set(uvRect.GetCorner((glyph.flipped) ? flippedCorners[j] : normalCorners[j])); } - // Et on passe au prochain sommet + // Increment the counter, go to next glyph indices->count++; } diff --git a/src/Nazara/Lua/LuaInstance.cpp b/src/Nazara/Lua/LuaInstance.cpp index dcaad2e26..8e437d759 100644 --- a/src/Nazara/Lua/LuaInstance.cpp +++ b/src/Nazara/Lua/LuaInstance.cpp @@ -279,7 +279,7 @@ namespace Nz return (lua_compare(m_state, index1, index2, s_comparisons[comparison]) != 0); } - void LuaInstance::Compute(LuaOperation operation) + void LuaInstance::Compute(LuaOperation operation) const { #ifdef NAZARA_DEBUG if (operation > LuaOperation_Max) @@ -292,7 +292,7 @@ namespace Nz lua_arith(m_state, s_operations[operation]); } - void LuaInstance::Concatenate(int count) + void LuaInstance::Concatenate(int count) const { lua_concat(m_state, count); } @@ -366,12 +366,12 @@ namespace Nz return stream.ToString(); } - void LuaInstance::Error(const char* message) + void LuaInstance::Error(const char* message) const { luaL_error(m_state, message); } - void LuaInstance::Error(const String& message) + void LuaInstance::Error(const String& message) const { luaL_error(m_state, message.GetConstBuffer()); } @@ -443,14 +443,14 @@ namespace Nz return lua_absindex(m_state, index); } - LuaType LuaInstance::GetField(const char* fieldName, int index) const + LuaType LuaInstance::GetField(const char* fieldName, int tableIndex) const { - return FromLuaType(lua_getfield(m_state, index, fieldName)); + return FromLuaType(lua_getfield(m_state, tableIndex, fieldName)); } - LuaType LuaInstance::GetField(const String& fieldName, int index) const + LuaType LuaInstance::GetField(const String& fieldName, int tableIndex) const { - return FromLuaType(lua_getfield(m_state, index, fieldName.GetConstBuffer())); + return FromLuaType(lua_getfield(m_state, tableIndex, fieldName.GetConstBuffer())); } LuaType LuaInstance::GetGlobal(const char* name) const @@ -531,7 +531,7 @@ namespace Nz return lua_typename(m_state, s_types[type]); } - void LuaInstance::Insert(int index) + void LuaInstance::Insert(int index) const { lua_insert(m_state, index); } @@ -596,7 +596,7 @@ namespace Nz return luaL_len(m_state, index); } - void LuaInstance::MoveTo(LuaInstance* instance, int n) + void LuaInstance::MoveTo(LuaInstance* instance, int n) const { lua_xmove(m_state, instance->m_state, n); } @@ -611,105 +611,105 @@ namespace Nz return luaL_newmetatable(m_state, str.GetConstBuffer()) != 0; } - bool LuaInstance::Next(int index) + bool LuaInstance::Next(int index) const { return lua_next(m_state, index) != 0; } - void LuaInstance::Pop(unsigned int n) + void LuaInstance::Pop(unsigned int n) const { lua_pop(m_state, static_cast(n)); } - void LuaInstance::PushBoolean(bool value) + void LuaInstance::PushBoolean(bool value) const { lua_pushboolean(m_state, (value) ? 1 : 0); } - void LuaInstance::PushCFunction(LuaCFunction func, unsigned int upvalueCount) + void LuaInstance::PushCFunction(LuaCFunction func, unsigned int upvalueCount) const { lua_pushcclosure(m_state, func, upvalueCount); } - void LuaInstance::PushFunction(LuaFunction func) + void LuaInstance::PushFunction(LuaFunction func) const { - LuaFunction* luaFunc = reinterpret_cast(lua_newuserdata(m_state, sizeof(LuaFunction))); + LuaFunction* luaFunc = static_cast(lua_newuserdata(m_state, sizeof(LuaFunction))); PlacementNew(luaFunc, std::move(func)); lua_pushcclosure(m_state, ProxyFunc, 1); } - void LuaInstance::PushInteger(long long value) + void LuaInstance::PushInteger(long long value) const { lua_pushinteger(m_state, value); } - void LuaInstance::PushLightUserdata(void* value) + void LuaInstance::PushLightUserdata(void* value) const { lua_pushlightuserdata(m_state, value); } - void LuaInstance::PushMetatable(const char* str) + void LuaInstance::PushMetatable(const char* str) const { luaL_getmetatable(m_state, str); } - void LuaInstance::PushMetatable(const String& str) + void LuaInstance::PushMetatable(const String& str) const { luaL_getmetatable(m_state, str.GetConstBuffer()); } - void LuaInstance::PushNil() + void LuaInstance::PushNil() const { lua_pushnil(m_state); } - void LuaInstance::PushNumber(double value) + void LuaInstance::PushNumber(double value) const { lua_pushnumber(m_state, value); } - void LuaInstance::PushReference(int ref) + void LuaInstance::PushReference(int ref) const { lua_rawgeti(m_state, LUA_REGISTRYINDEX, ref); } - void LuaInstance::PushString(const char* str) + void LuaInstance::PushString(const char* str) const { lua_pushstring(m_state, str); } - void LuaInstance::PushString(const char* str, unsigned int size) + void LuaInstance::PushString(const char* str, unsigned int size) const { lua_pushlstring(m_state, str, size); } - void LuaInstance::PushString(const String& str) + void LuaInstance::PushString(const String& str) const { lua_pushlstring(m_state, str.GetConstBuffer(), str.GetSize()); } - void LuaInstance::PushTable(unsigned int sequenceElementCount, unsigned int arrayElementCount) + void LuaInstance::PushTable(unsigned int sequenceElementCount, unsigned int arrayElementCount) const { lua_createtable(m_state, sequenceElementCount, arrayElementCount); } - void* LuaInstance::PushUserdata(unsigned int size) + void* LuaInstance::PushUserdata(unsigned int size) const { return lua_newuserdata(m_state, size); } - void LuaInstance::PushValue(int index) + void LuaInstance::PushValue(int index) const { lua_pushvalue(m_state, index); } - void LuaInstance::Remove(int index) + void LuaInstance::Remove(int index) const { lua_remove(m_state, index); } - void LuaInstance::Replace(int index) + void LuaInstance::Replace(int index) const { lua_replace(m_state, index); } @@ -734,17 +734,17 @@ namespace Nz lua_setglobal(m_state, name.GetConstBuffer()); } - void LuaInstance::SetMetatable(const char* tname) + void LuaInstance::SetMetatable(const char* tname) const { luaL_setmetatable(m_state, tname); } - void LuaInstance::SetMetatable(const String& tname) + void LuaInstance::SetMetatable(const String& tname) const { luaL_setmetatable(m_state, tname.GetConstBuffer()); } - void LuaInstance::SetMetatable(int index) + void LuaInstance::SetMetatable(int index) const { lua_setmetatable(m_state, index); } @@ -754,7 +754,7 @@ namespace Nz m_memoryLimit = memoryLimit; } - void LuaInstance::SetTable(int index) + void LuaInstance::SetTable(int index) const { lua_settable(m_state, index); } diff --git a/src/Nazara/Network/AbstractSocket.cpp b/src/Nazara/Network/AbstractSocket.cpp index d86f93501..e46a5906a 100644 --- a/src/Nazara/Network/AbstractSocket.cpp +++ b/src/Nazara/Network/AbstractSocket.cpp @@ -9,6 +9,8 @@ #if defined(NAZARA_PLATFORM_WINDOWS) #include +#elif defined(NAZARA_PLATFORM_POSIX) +#include #else #error Missing implementation: Socket #endif @@ -16,6 +18,7 @@ namespace Nz { AbstractSocket::AbstractSocket(SocketType type) : + m_lastError(SocketError_NoError), m_handle(SocketImpl::InvalidHandle), m_state(SocketState_NotConnected), m_type(type), @@ -61,7 +64,7 @@ namespace Nz } } - unsigned int AbstractSocket::QueryAvailableBytes() const + std::size_t AbstractSocket::QueryAvailableBytes() const { if (m_handle == SocketImpl::InvalidHandle) return 0; @@ -105,4 +108,20 @@ namespace Nz m_handle = handle; OnOpened(); } -} \ No newline at end of file + + AbstractSocket& AbstractSocket::operator=(AbstractSocket&& abstractSocket) + { + Close(); + + m_handle = abstractSocket.m_handle; + m_protocol = abstractSocket.m_protocol; + m_isBlockingEnabled = abstractSocket.m_isBlockingEnabled; + m_lastError = abstractSocket.m_lastError; + m_state = abstractSocket.m_state; + m_type = abstractSocket.m_type; + + abstractSocket.m_handle = SocketImpl::InvalidHandle; + + return *this; + } +} diff --git a/src/Nazara/Network/IpAddress.cpp b/src/Nazara/Network/IpAddress.cpp index b44412aca..d40e2a581 100644 --- a/src/Nazara/Network/IpAddress.cpp +++ b/src/Nazara/Network/IpAddress.cpp @@ -12,6 +12,8 @@ #if defined(NAZARA_PLATFORM_WINDOWS) #include +#elif defined(NAZARA_PLATFORM_POSIX) +#include #else #error Missing implementation: Network #endif diff --git a/src/Nazara/Network/NetPacket.cpp b/src/Nazara/Network/NetPacket.cpp new file mode 100644 index 000000000..7a1c0d7f5 --- /dev/null +++ b/src/Nazara/Network/NetPacket.cpp @@ -0,0 +1,109 @@ +// Copyright (C) 2015 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 + +namespace Nz +{ + void NetPacket::OnReceive(UInt16 netCode, const void* data, std::size_t size) + { + Reset(netCode, data, size); + } + + const void* NetPacket::OnSend(std::size_t* newSize) const + { + NazaraAssert(newSize, "Invalid size pointer"); + NazaraAssert(m_netCode != NetCode_Invalid, "Invalid NetCode"); + + std::size_t size = m_buffer->GetSize(); + if (!EncodeHeader(m_buffer->GetBuffer(), static_cast(size), m_netCode)) + { + NazaraError("Failed to encode packet header"); + return nullptr; + } + + *newSize = size; + return m_buffer->GetBuffer(); + } + + bool NetPacket::DecodeHeader(const void* data, UInt16* packetSize, UInt16* netCode) + { + MemoryView stream(data, HeaderSize); + + SerializationContext context; + context.stream = &stream; + + return Unserialize(context, packetSize) && Unserialize(context, netCode); + } + + bool NetPacket::EncodeHeader(void* data, UInt16 packetSize, UInt16 netCode) + { + MemoryView stream(data, HeaderSize); + + SerializationContext context; + context.stream = &stream; + + return Serialize(context, packetSize) && Serialize(context, netCode); + } + + void NetPacket::OnEmptyStream() + { + Reset(0); + } + + void NetPacket::FreeStream() + { + if (!m_buffer) + return; + + std::size_t size = m_buffer->GetSize(); + + Nz::LockGuard lock(*s_availableBuffersMutex); + s_availableBuffers.emplace_back(std::make_pair(size, std::move(m_buffer))); + } + + 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"); + + { + Nz::LockGuard lock(*s_availableBuffersMutex); + + FreeStream(); //< In case it wasn't released yet + + if (!s_availableBuffers.empty()) + { + m_buffer = std::move(s_availableBuffers.back().second); + s_availableBuffers.pop_back(); + } + } + + if (!m_buffer) + m_buffer = std::make_unique(); + + m_buffer->Resize(static_cast(cursorPos)); + + m_memoryStream.SetBuffer(m_buffer.get(), openMode); + m_memoryStream.SetCursorPos(cursorPos); + SetStream(&m_memoryStream); + } + + bool NetPacket::Initialize() + { + s_availableBuffersMutex = std::make_unique(); + return true; + } + + void NetPacket::Uninitialize() + { + s_availableBuffers.clear(); + s_availableBuffersMutex.reset(); + } + + std::unique_ptr NetPacket::s_availableBuffersMutex; + std::vector>> NetPacket::s_availableBuffers; +} diff --git a/src/Nazara/Network/Network.cpp b/src/Nazara/Network/Network.cpp index ffe4842ca..41868b79b 100644 --- a/src/Nazara/Network/Network.cpp +++ b/src/Nazara/Network/Network.cpp @@ -8,10 +8,14 @@ #include #include #include +#include +#include #include #if defined(NAZARA_PLATFORM_WINDOWS) #include +#elif defined(NAZARA_PLATFORM_POSIX) +#include #else #error Missing implementation: Network #endif @@ -46,6 +50,18 @@ namespace Nz return false; } + if (!NetPacket::Initialize()) + { + NazaraError("Failed to initialize packets"); + return false; + } + + if (!RUdpConnection::Initialize()) + { + NazaraError("Failed to initialize RUdp"); + return false; + } + onExit.Reset(); NazaraNotice("Initialized: Network module"); @@ -71,6 +87,8 @@ namespace Nz s_moduleReferenceCounter = 0; // Uninitialize module here + RUdpConnection::Uninitialize(); + NetPacket::Uninitialize(); SocketImpl::Uninitialize(); NazaraNotice("Uninitialized: Network module"); diff --git a/src/Nazara/Network/Posix/IpAddressImpl.cpp b/src/Nazara/Network/Posix/IpAddressImpl.cpp new file mode 100644 index 000000000..1e4f6a89e --- /dev/null +++ b/src/Nazara/Network/Posix/IpAddressImpl.cpp @@ -0,0 +1,309 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace Detail + { + using addrinfoImpl = addrinfo; + + int GetAddressInfo(const String& hostname, const String& service, const addrinfoImpl* hints, addrinfoImpl** results) + { + return getaddrinfo(hostname.GetConstBuffer(), service.GetConstBuffer(), hints, results); + } + + int GetHostnameInfo(sockaddr* socketAddress, socklen_t socketLen, String* hostname, String* service, int flags) + { + std::array hostnameBuffer; + std::array serviceBuffer; + + int result = getnameinfo(socketAddress, socketLen, hostnameBuffer.data(), hostnameBuffer.size(), serviceBuffer.data(), serviceBuffer.size(), flags); + if (result == 0) + { + if (hostname) + hostname->Set(hostnameBuffer.data()); + + if (service) + service->Set(serviceBuffer.data()); + } + + return result; + } + + void FreeAddressInfo(addrinfoImpl* results) + { + freeaddrinfo(results); + } + + IpAddress::IPv4 convertSockaddrToIPv4(const in_addr& addr) + { + union byteToInt + { + UInt8 b[sizeof(uint32_t)]; + uint32_t i; + }; + + byteToInt hostOrder; + hostOrder.i = ntohl(addr.s_addr); + + return { hostOrder.b[3], hostOrder.b[2], hostOrder.b[1], hostOrder.b[0] }; + } + + IpAddress::IPv6 convertSockaddr6ToIPv6(const in6_addr& addr) + { + union byteToInt + { + UInt8 b[sizeof(uint32_t)]; + uint32_t i; + }; + + IpAddress::IPv6 ipv6Addr; + + for (auto i = 0; i < 4; ++i) + { + byteToInt hostOrder; + hostOrder.i = 0; + std::copy(addr.s6_addr + 4 * i, addr.s6_addr + 4 * (i + 1), hostOrder.b); + ipv6Addr[2 * i] = (hostOrder.b[3] << 8) + hostOrder.b[2]; + ipv6Addr[2 * i + 1] = (hostOrder.b[1] << 8) + hostOrder.b[0]; + } + + return ipv6Addr; + } + } + + IpAddress IpAddressImpl::FromAddrinfo(const addrinfo* info) + { + switch (info->ai_family) + { + case AF_INET: + { + sockaddr_in* ipv4 = reinterpret_cast(info->ai_addr); + + return FromSockAddr(ipv4); + } + + case AF_INET6: + { + sockaddr_in6* ipv6 = reinterpret_cast(info->ai_addr); + + return FromSockAddr(ipv6); + } + } + + return IpAddress::Invalid; + } + + IpAddress IpAddressImpl::FromSockAddr(const sockaddr* address) + { + switch (address->sa_family) + { + case AF_INET: + return FromSockAddr(reinterpret_cast(address)); + + case AF_INET6: + return FromSockAddr(reinterpret_cast(address)); + } + + return IpAddress::Invalid; + } + + IpAddress IpAddressImpl::FromSockAddr(const sockaddr_in* addressv4) + { + IpAddress::IPv4 ip4Address = Detail::convertSockaddrToIPv4(addressv4->sin_addr); + return IpAddress(ip4Address, ntohs(addressv4->sin_port)); + } + + IpAddress IpAddressImpl::FromSockAddr(const sockaddr_in6* addressv6) + { + IpAddress::IPv6 ip6Address = Detail::convertSockaddr6ToIPv6(addressv6->sin6_addr); + return IpAddress(ip6Address, ntohs(addressv6->sin6_port)); + } + + bool IpAddressImpl::ResolveAddress(const IpAddress& ipAddress, String* hostname, String* service, ResolveError* error) + { + SockAddrBuffer socketAddress; + socklen_t socketAddressLen = ToSockAddr(ipAddress, socketAddress.data()); + + if (Detail::GetHostnameInfo(reinterpret_cast(socketAddress.data()), socketAddressLen, hostname, service, NI_NUMERICSERV) != 0) + { + if (error) + *error = TranslateEAIErrorToResolveError(errno); + + return false; + } + + if (error) + *error = ResolveError_NoError; + + return true; + } + + std::vector IpAddressImpl::ResolveHostname(NetProtocol procol, const String& hostname, const String& service, ResolveError* error) + { + std::vector results; + + Detail::addrinfoImpl hints; + std::memset(&hints, 0, sizeof(Detail::addrinfoImpl)); + hints.ai_family = SocketImpl::TranslateNetProtocolToAF(procol); + hints.ai_flags = AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; + + Detail::addrinfoImpl* servinfo; + if (Detail::GetAddressInfo(hostname, service, &hints, &servinfo) != 0) + { + if (error) + *error = TranslateEAIErrorToResolveError(errno); + + return results; + } + + CallOnExit onExit([servinfo]() + { + Detail::FreeAddressInfo(servinfo); + }); + + // loop through all the results and connect to the first we can + for (Detail::addrinfoImpl* p = servinfo; p != nullptr; p = p->ai_next) + { + HostnameInfo result; + result.address = FromAddrinfo(p); + result.canonicalName = String::Unicode(p->ai_canonname); + result.protocol = TranslatePFToNetProtocol(p->ai_family); + result.socketType = TranslateSockToNetProtocol(p->ai_socktype); + + results.push_back(result); + } + + if (error) + *error = ResolveError_NoError; + + return results; + } + + socklen_t IpAddressImpl::ToSockAddr(const IpAddress& ipAddress, void* buffer) + { + if (ipAddress.IsValid()) + { + switch (ipAddress.GetProtocol()) + { + case NetProtocol_IPv4: + { + sockaddr_in* socketAddress = reinterpret_cast(buffer); + + std::memset(socketAddress, 0, sizeof(sockaddr_in)); + socketAddress->sin_family = AF_INET; + socketAddress->sin_port = htons(ipAddress.GetPort()); + socketAddress->sin_addr.s_addr = htonl(ipAddress.ToUInt32()); + + return sizeof(sockaddr_in); + } + + case NetProtocol_IPv6: + { + sockaddr_in6* socketAddress = reinterpret_cast(buffer); + + std::memset(socketAddress, 0, sizeof(sockaddr_in6)); + socketAddress->sin6_family = AF_INET6; + socketAddress->sin6_port = htons(ipAddress.GetPort()); + + IpAddress::IPv6 address = ipAddress.ToIPv6(); + for (unsigned int i = 0; i < 8; ++i) + { + UInt16 networkOrder = htons(address[i]); + socketAddress->sin6_addr.s6_addr[2 * i] = networkOrder / 256; + socketAddress->sin6_addr.s6_addr[2 * i + 1] = networkOrder % 256; + } + + return sizeof(sockaddr_in6); + } + + default: + NazaraInternalError("Unhandled ip protocol (0x" + String::Number(ipAddress.GetProtocol()) + ')'); + break; + } + } + + NazaraError("Invalid ip address"); + return 0; + } + + NetProtocol IpAddressImpl::TranslatePFToNetProtocol(int family) + { + switch (family) + { + case PF_INET: + return NetProtocol_IPv4; + + case PF_INET6: + return NetProtocol_IPv6; + + default: + return NetProtocol_Unknown; + } + } + + SocketType IpAddressImpl::TranslateSockToNetProtocol(int socketType) + { + switch (socketType) + { + case SOCK_STREAM: + return SocketType_TCP; + + case SOCK_DGRAM: + return SocketType_UDP; + + case SOCK_RAW: + return SocketType_Raw; + + default: + return SocketType_Unknown; + } + } + + ResolveError IpAddressImpl::TranslateEAIErrorToResolveError(int error) + { + // http://man7.org/linux/man-pages/man3/gai_strerror.3.html + switch (error) + { + case 0: + return ResolveError_NoError; + + // Engine error + case EAI_BADFLAGS: + case EAI_SYSTEM: + return ResolveError_Internal; + + case EAI_FAMILY: + case EAI_SERVICE: + case EAI_SOCKTYPE: + return ResolveError_ProtocolNotSupported; + + case EAI_NONAME: + return ResolveError_NotFound; + + case EAI_FAIL: + return ResolveError_NonRecoverable; + + case EAI_NODATA: + return ResolveError_NotInitialized; + + case EAI_MEMORY: + return ResolveError_ResourceError; + + case EAI_AGAIN: + return ResolveError_TemporaryFailure; + } + + NazaraWarning("Unhandled EAI error: " + Error::GetLastSystemError(error) + " (" + String::Number(error) + ") as " + gai_strerror(error)); + return ResolveError_Unknown; + } +} diff --git a/src/Nazara/Network/Posix/IpAddressImpl.hpp b/src/Nazara/Network/Posix/IpAddressImpl.hpp new file mode 100644 index 000000000..6ca919e78 --- /dev/null +++ b/src/Nazara/Network/Posix/IpAddressImpl.hpp @@ -0,0 +1,32 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + class IpAddressImpl + { + public: + using SockAddrBuffer = std::array; + + IpAddressImpl() = delete; + ~IpAddressImpl() = delete; + + static IpAddress FromAddrinfo(const addrinfo* info); + static IpAddress FromSockAddr(const sockaddr* address); + static IpAddress FromSockAddr(const sockaddr_in* addressv4); + static IpAddress FromSockAddr(const sockaddr_in6* addressv6); + + static bool ResolveAddress(const IpAddress& ipAddress, String* hostname, String* service, ResolveError* error); + static std::vector ResolveHostname(NetProtocol procol, const String& hostname, const String& service, ResolveError* error); + + static socklen_t ToSockAddr(const IpAddress& ipAddress, void* buffer); + static NetProtocol TranslatePFToNetProtocol(int family); + static SocketType TranslateSockToNetProtocol(int socketType); + static ResolveError TranslateEAIErrorToResolveError(int error); + }; +} diff --git a/src/Nazara/Network/Posix/SocketImpl.cpp b/src/Nazara/Network/Posix/SocketImpl.cpp new file mode 100644 index 000000000..c2cf3a6f8 --- /dev/null +++ b/src/Nazara/Network/Posix/SocketImpl.cpp @@ -0,0 +1,746 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + constexpr int SOCKET_ERROR = -1; + + SocketHandle SocketImpl::Accept(SocketHandle handle, IpAddress* address, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + IpAddressImpl::SockAddrBuffer nameBuffer; + socklen_t bufferLength = sizeof(sockaddr_in); + + SocketHandle newClient = accept(handle, reinterpret_cast(&nameBuffer), &bufferLength); + if (newClient != InvalidHandle) + { + if (address) + *address = IpAddressImpl::FromSockAddr(reinterpret_cast(&nameBuffer)); + + if (error) + *error = SocketError_NoError; + } + else + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + } + + return newClient; + } + + SocketState SocketImpl::Bind(SocketHandle handle, const IpAddress& address, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + NazaraAssert(address.IsValid(), "Invalid address"); + + IpAddressImpl::SockAddrBuffer nameBuffer; + int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data()); + + if (bind(handle, reinterpret_cast(&nameBuffer), bufferLength) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return SocketState_NotConnected; + } + + if (error) + *error = SocketError_NoError; + + return SocketState_Bound; + } + + SocketHandle SocketImpl::Create(NetProtocol protocol, SocketType type, SocketError* error) + { + NazaraAssert(protocol != NetProtocol_Any, "Any protocol is not supported for socket creation"); + NazaraAssert(type <= SocketType_Max, "Type has value out of enum"); + + SocketHandle handle = socket(TranslateNetProtocolToAF(protocol), TranslateSocketTypeToSock(type), 0); + if (handle == InvalidHandle && error != nullptr) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return handle; + } + + void SocketImpl::Close(SocketHandle handle) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + if (close(handle) == SOCKET_ERROR) + NazaraWarning("Failed to close socket: " + Error::GetLastSystemError(GetLastErrorCode())); + } + + void SocketImpl::ClearErrorCode(SocketHandle handle) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + if (GetLastError(handle, nullptr) < 0) + NazaraWarning("Failed to clear socket error code: " + Error::GetLastSystemError(GetLastErrorCode())); + } + + SocketState SocketImpl::Connect(SocketHandle handle, const IpAddress& address, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + NazaraAssert(address.IsValid(), "Invalid address"); + + IpAddressImpl::SockAddrBuffer nameBuffer; + int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data()); + + if (error) + *error = SocketError_NoError; + + // Clear socket error status + ClearErrorCode(handle); + + if (connect(handle, reinterpret_cast(nameBuffer.data()), bufferLength) == SOCKET_ERROR) + { + int errorCode = GetLastErrorCode(); + switch (errorCode) //< Check for "normal errors" first + { + case EALREADY: + case EINPROGRESS: + return SocketState_Connecting; + + case EISCONN: + return SocketState_Connected; + } + + if (error) + { + if (errorCode == EADDRNOTAVAIL) + *error = SocketError_ConnectionRefused; //< ConnectionRefused seems more legit than AddressNotAvailable in connect case + else + *error = TranslateErrnoToResolveError(errorCode); + } + + return SocketState_NotConnected; + } + + return SocketState_Connected; + } + + SocketState SocketImpl::Connect(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error) + { + SocketState state = Connect(handle, address, error); + if (state == SocketState_Connecting) + { + // http://developerweb.net/viewtopic.php?id=3196 + fd_set localSet; + FD_ZERO(&localSet); + FD_SET(handle, &localSet); + + timeval tv; + tv.tv_sec = static_cast(msTimeout / 1000ULL); + tv.tv_usec = static_cast((msTimeout % 1000ULL) * 1000ULL); + + int ret = select(0, nullptr, &localSet, &localSet, (msTimeout > 0) ? &tv : nullptr); + if (ret == SOCKET_ERROR) + { + int code = GetLastErrorCode(handle, error); + if (code < 0) //< GetLastErrorCode() failed + return SocketState_NotConnected; + + if (code) + { + if (error) + *error = TranslateErrnoToResolveError(code); + + return SocketState_NotConnected; + } + } + else if (ret == 0) + { + if (error) + *error = SocketError_TimedOut; + + return SocketState_NotConnected; + } + else + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return SocketState_NotConnected; + } + + if (error) + *error = SocketError_NoError; + + state = SocketState_Connected; + } + + return state; + } + + bool SocketImpl::Initialize() + { + return true; + } + + SocketError SocketImpl::GetLastError(SocketHandle handle, SocketError* error) + { + int code = GetLastErrorCode(handle, error); + if (code < 0) + return SocketError_Internal; + + return TranslateErrnoToResolveError(code); + } + + int SocketImpl::GetLastErrorCode() + { + return errno; + } + + int SocketImpl::GetLastErrorCode(SocketHandle handle, SocketError* error) + { + int code; + unsigned int codeLength = sizeof(code); + + if (getsockopt(handle, SOL_SOCKET, SO_ERROR, &code, &codeLength) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return -1; + } + + if (error) + *error = SocketError_NoError; + + return code; + } + + SocketState SocketImpl::Listen(SocketHandle handle, const IpAddress& address, unsigned int queueSize, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + NazaraAssert(address.IsValid(), "Invalid address"); + + IpAddressImpl::SockAddrBuffer nameBuffer; + int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data()); + + if (bind(handle, reinterpret_cast(&nameBuffer), bufferLength) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return SocketState_NotConnected; + } + + if (listen(handle, queueSize) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return SocketState_NotConnected; + } + + if (error) + *error = SocketError_NoError; + + return SocketState_Bound; + } + + std::size_t SocketImpl::QueryAvailableBytes(SocketHandle handle, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + u_long availableBytes; + if (ioctl(handle, FIONREAD, &availableBytes) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return 0; + } + + if (error) + *error = SocketError_NoError; + + return availableBytes; + } + + bool SocketImpl::QueryBroadcasting(SocketHandle handle, SocketError* error) + { + bool code; + unsigned int codeLength = sizeof(code); + + if (getsockopt(handle, SOL_SOCKET, SO_BROADCAST, &code, &codeLength) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; + } + + if (error) + *error = SocketError_NoError; + + return code; + } + + bool SocketImpl::QueryKeepAlive(SocketHandle handle, SocketError* error) + { + bool code; + unsigned int codeLength = sizeof(code); + + if (getsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &code, &codeLength) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; + } + + if (error) + *error = SocketError_NoError; + + return code; + } + + std::size_t SocketImpl::QueryMaxDatagramSize(SocketHandle handle, SocketError* error) + { + unsigned int code; + unsigned int codeLength = sizeof(code); + + if (getsockopt(handle, IPPROTO_IP, IP_MTU, &code, &codeLength) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return -1; + } + + if (error) + *error = SocketError_NoError; + + return code; + } + + bool SocketImpl::QueryNoDelay(SocketHandle handle, SocketError* error) + { + bool code; + unsigned int codeLength = sizeof(code); + + if (getsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &code, &codeLength) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; + } + + if (error) + *error = SocketError_NoError; + + return code; + } + + IpAddress SocketImpl::QueryPeerAddress(SocketHandle handle, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + IpAddressImpl::SockAddrBuffer nameBuffer; + socklen_t bufferLength = sizeof(sockaddr_in); + + if (getpeername(handle, reinterpret_cast(nameBuffer.data()), &bufferLength) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return IpAddress(); + } + + if (error) + *error = SocketError_NoError; + + return IpAddressImpl::FromSockAddr(reinterpret_cast(nameBuffer.data())); + } + + IpAddress SocketImpl::QuerySocketAddress(SocketHandle handle, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + IpAddressImpl::SockAddrBuffer nameBuffer; + socklen_t bufferLength = sizeof(sockaddr_in); + + if (getsockname(handle, reinterpret_cast(nameBuffer.data()), &bufferLength) == SOCKET_ERROR) + { + if (error) + { + int errorCode = GetLastErrorCode(); + if (errorCode == EINVAL) + *error = SocketError_NoError; + else + *error = TranslateErrnoToResolveError(errorCode); + } + + return IpAddress(); + } + + if (error) + *error = SocketError_NoError; + + return IpAddressImpl::FromSockAddr(reinterpret_cast(nameBuffer.data())); + } + + + bool SocketImpl::Receive(SocketHandle handle, void* buffer, int length, int* read, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + NazaraAssert(buffer && length > 0, "Invalid buffer"); + + int byteRead = recv(handle, reinterpret_cast(buffer), length, 0); + if (byteRead == SOCKET_ERROR) + { + int errorCode = GetLastErrorCode(); + switch (errorCode) + { + case EWOULDBLOCK: + { + // If we have no data and are not blocking, return true with 0 byte read + byteRead = 0; + break; + } + + default: + { + if (error) + *error = TranslateErrnoToResolveError(errorCode); + + return false; //< Error + } + } + } + else if (byteRead == 0) + { + if (error) + *error = SocketError_ConnectionClosed; + + return false; //< Connection has been closed + } + + if (read) + *read = byteRead; + + if (error) + *error = SocketError_NoError; + + return true; + } + + bool SocketImpl::ReceiveFrom(SocketHandle handle, void* buffer, int length, IpAddress* from, int* read, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + NazaraAssert(buffer && length > 0, "Invalid buffer"); + + IpAddressImpl::SockAddrBuffer nameBuffer; + socklen_t bufferLength = sizeof(sockaddr_in); + + IpAddress senderIp; + + int byteRead = recvfrom(handle, buffer, length, 0, reinterpret_cast(&nameBuffer), &bufferLength); + if (byteRead == SOCKET_ERROR) + { + int errorCode = GetLastErrorCode(); + switch (errorCode) + { + case EWOULDBLOCK: + { + // If we have no data and are not blocking, return true with 0 byte read + byteRead = 0; + senderIp = IpAddress::Invalid; + break; + } + + default: + { + if (error) + *error = TranslateErrnoToResolveError(errorCode); + + return false; //< Error + } + } + } + else if (byteRead == 0) + { + if (error) + *error = SocketError_ConnectionClosed; + + return false; //< Connection closed + } + else // else we received something + senderIp = IpAddressImpl::FromSockAddr(reinterpret_cast(&nameBuffer)); + + if (from) + *from = senderIp; + + if (read) + *read = byteRead; + + if (error) + *error = SocketError_NoError; + + return true; + } + + bool SocketImpl::Send(SocketHandle handle, const void* buffer, int length, int* sent, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + NazaraAssert(buffer && length > 0, "Invalid buffer"); + + int byteSent = send(handle, reinterpret_cast(buffer), length, 0); + if (byteSent == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; //< Error + } + + if (sent) + *sent = byteSent; + + if (error) + *error = SocketError_NoError; + + return true; + } + + bool SocketImpl::SendTo(SocketHandle handle, const void* buffer, int length, const IpAddress& to, int* sent, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + NazaraAssert(buffer && length > 0, "Invalid buffer"); + + IpAddressImpl::SockAddrBuffer nameBuffer; + int bufferLength = IpAddressImpl::ToSockAddr(to, nameBuffer.data()); + + int byteSent = sendto(handle, reinterpret_cast(buffer), length, 0, reinterpret_cast(nameBuffer.data()), bufferLength); + if (byteSent == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; //< Error + } + + if (sent) + *sent = byteSent; + + if (error) + *error = SocketError_NoError; + + return true; + } + + bool SocketImpl::SetBlocking(SocketHandle handle, bool blocking, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + u_long block = (blocking) ? 0 : 1; + if (ioctl(handle, FIONBIO, &block) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; //< Error + } + + if (error) + *error = SocketError_NoError; + + return true; + } + + bool SocketImpl::SetBroadcasting(SocketHandle handle, bool broadcasting, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + bool option = broadcasting; + if (setsockopt(handle, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&option), sizeof(option)) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; //< Error + } + + if (error) + *error = SocketError_NoError; + + return true; + } + + bool SocketImpl::SetKeepAlive(SocketHandle handle, bool enabled, UInt64 msTime, UInt64 msInterval, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + int keepAlive = enabled ? 1 : 0; + int keepIdle = msTime / 1000; // Linux works with seconds. + int keepInterval = msInterval / 1000; // Linux works with seconds. + + if (setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &keepAlive , sizeof(keepAlive)) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; //< Error + } + + if (setsockopt(handle, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(keepIdle)) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; //< Error + } + + if (setsockopt(handle, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(keepInterval)) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; //< Error + } + + if (error) + *error = SocketError_NoError; + + return true; + } + + bool SocketImpl::SetNoDelay(SocketHandle handle, bool nodelay, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + int option = nodelay ? 1 : 0; + if (setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option)) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; //< Error + } + + if (error) + *error = SocketError_NoError; + + return true; + } + + SocketError SocketImpl::TranslateErrnoToResolveError(int error) + { + switch (error) + { + case 0: + return SocketError_NoError; + + // Engine error + case EACCES: + case EBADF: + case EINVAL: + case EFAULT: + case ENOTSOCK: + case EPROTOTYPE: + return SocketError_Internal; + + case EADDRNOTAVAIL: + case EADDRINUSE: + return SocketError_AddressNotAvailable; + + case EAFNOSUPPORT: + case EPFNOSUPPORT: + case EOPNOTSUPP: + case EPROTONOSUPPORT: + case ESOCKTNOSUPPORT: + return SocketError_NotSupported; + + // Those are not errors and should have been handled before the call + case EALREADY: + case EISCONN: + case EWOULDBLOCK: + return SocketError_Internal; + + case ECONNREFUSED: + return SocketError_ConnectionRefused; + + case EMSGSIZE: + return SocketError_DatagramSize; + + case EMFILE: + case ENOBUFS: + case ENOMEM: + return SocketError_ResourceError; + + case ENOTCONN: + case ESHUTDOWN: + return SocketError_ConnectionClosed; + + case EHOSTUNREACH: + return SocketError_UnreachableHost; + + case ENETDOWN: + case ENETUNREACH: + return SocketError_NetworkError; + + case ENODATA: + return SocketError_NotInitialized; + + case ETIMEDOUT: + return SocketError_TimedOut; + } + + NazaraWarning("Unhandled POSIX error: " + Error::GetLastSystemError(error) + " (" + String::Number(error) + ')'); + return SocketError_Unknown; + } + + int SocketImpl::TranslateNetProtocolToAF(NetProtocol protocol) + { + NazaraAssert(protocol <= NetProtocol_Max, "Protocol has value out of enum"); + + static int addressFamily[] = { + AF_UNSPEC, //< NetProtocol_Any + AF_INET, //< NetProtocol_IPv4 + AF_INET6, //< NetProtocol_IPv6 + -1 //< NetProtocol_Unknown + }; + static_assert(sizeof(addressFamily) / sizeof(int) == NetProtocol_Max + 1, "Address family array is incomplete"); + + return addressFamily[protocol]; + } + + int SocketImpl::TranslateSocketTypeToSock(SocketType type) + { + NazaraAssert(type <= SocketType_Max, "Socket type has value out of enum"); + + static int socketType[] = { + SOCK_RAW, //< SocketType_Raw + SOCK_STREAM, //< SocketType_TCP + SOCK_DGRAM, //< SocketType_UDP + -1 //< SocketType_Unknown + }; + static_assert(sizeof(socketType) / sizeof(int) == SocketType_Max + 1, "Socket type array is incomplete"); + + return socketType[type]; + } + + void SocketImpl::Uninitialize() + { + } + + SocketHandle SocketImpl::InvalidHandle = -1; + SocketImpl::socketID SocketImpl::s_socket; +} diff --git a/src/Nazara/Network/Posix/SocketImpl.hpp b/src/Nazara/Network/Posix/SocketImpl.hpp new file mode 100644 index 000000000..8896ce597 --- /dev/null +++ b/src/Nazara/Network/Posix/SocketImpl.hpp @@ -0,0 +1,68 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Network module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + class SocketImpl + { + public: + SocketImpl() = delete; + ~SocketImpl() = delete; + + static SocketHandle Accept(SocketHandle handle, IpAddress* address, SocketError* error); + + static SocketState Bind(SocketHandle handle, const IpAddress& address, SocketError* error); + + static SocketHandle Create(NetProtocol protocol, SocketType type, SocketError* error); + + static void ClearErrorCode(SocketHandle handle); + static void Close(SocketHandle handle); + + static SocketState Connect(SocketHandle handle, const IpAddress& address, SocketError* error); + static SocketState Connect(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error); + + static bool Initialize(); + + static SocketError GetLastError(SocketHandle handle, SocketError* error = nullptr); + static int GetLastErrorCode(); + static int GetLastErrorCode(SocketHandle handle, SocketError* error = nullptr); + + static SocketState Listen(SocketHandle handle, const IpAddress& address, unsigned int queueSize, SocketError* error); + + static std::size_t QueryAvailableBytes(SocketHandle handle, SocketError* error = nullptr); + static bool QueryBroadcasting(SocketHandle handle, SocketError* error = nullptr); + static bool QueryKeepAlive(SocketHandle handle, SocketError* error = nullptr); + static std::size_t QueryMaxDatagramSize(SocketHandle handle, SocketError* error = nullptr); + static bool QueryNoDelay(SocketHandle handle, SocketError* error = nullptr); + static IpAddress QueryPeerAddress(SocketHandle handle, SocketError* error = nullptr); + static IpAddress QuerySocketAddress(SocketHandle handle, SocketError* error = nullptr); + + static bool Receive(SocketHandle handle, void* buffer, int length, int* read, SocketError* error); + static bool ReceiveFrom(SocketHandle handle, void* buffer, int length, IpAddress* from, int* read, SocketError* error); + + static bool Send(SocketHandle handle, const void* buffer, int length, int* sent, SocketError* error); + static bool SendTo(SocketHandle handle, const void* buffer, int length, const IpAddress& to, int* sent, SocketError* error); + + static bool SetBlocking(SocketHandle handle, bool blocking, SocketError* error = nullptr); + static bool SetBroadcasting(SocketHandle handle, bool broadcasting, SocketError* error = nullptr); + static bool SetKeepAlive(SocketHandle handle, bool enabled, UInt64 msTime, UInt64 msInterval, SocketError* error = nullptr); + static bool SetNoDelay(SocketHandle handle, bool nodelay, SocketError* error = nullptr); + + static SocketError TranslateErrnoToResolveError(int error); + static int TranslateNetProtocolToAF(NetProtocol protocol); + static int TranslateSocketTypeToSock(SocketType type); + + static void Uninitialize(); + + static SocketHandle InvalidHandle; + + private: + using socketID = int; + static socketID s_socket; + }; +} diff --git a/src/Nazara/Network/RUdpConnection.cpp b/src/Nazara/Network/RUdpConnection.cpp new file mode 100644 index 000000000..451d08f1e --- /dev/null +++ b/src/Nazara/Network/RUdpConnection.cpp @@ -0,0 +1,489 @@ +// Copyright (C) 2015 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 + +namespace Nz +{ + RUdpConnection::RUdpConnection() : + m_peerIterator(0), + m_forceAckSendTime(10'000), //< 10ms + m_pingInterval(1'000'000), //< 1s + m_protocol(0x4E4E6574), //< "NNet" + m_timeBeforePing(500'000), //< 0.5s + m_timeBeforeTimeOut(10'000'000), //< 10s + m_currentTime(0), + m_isSimulationEnabled(false), + m_shouldAcceptConnections(true) + { + } + + bool RUdpConnection::Connect(const IpAddress& remoteAddress) + { + 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"); + + PeerData& client = RegisterPeer(remoteAddress, PeerState_Connecting); + client.stateData1 = s_randomGenerator(); + + NetPacket connectionRequestPacket(NetCode_RequestConnection); + connectionRequestPacket << client.stateData1; + + EnqueuePacket(client, PacketPriority_Immediate, PacketReliability_Reliable, connectionRequestPacket); + return true; + } + + bool RUdpConnection::Connect(const String& hostName, NetProtocol protocol, const String& service, ResolveError* error) + { + std::vector results = IpAddress::ResolveHostname(protocol, hostName, service, error); + if (results.empty()) + { + m_lastError = SocketError_ResolveError; + return false; + } + + IpAddress hostnameAddress; + for (const HostnameInfo& result : results) + { + if (!result.address) + continue; + + if (result.socketType != SocketType_UDP) + continue; + + hostnameAddress = result.address; + break; //< Take first valid address + } + + return Connect(hostnameAddress); + } + + bool RUdpConnection::Listen(const IpAddress& address) + { + if (!InitSocket(address.GetProtocol())) + return false; + + return m_socket.Bind(address) == SocketState_Bound; + } + + bool RUdpConnection::PollMessage(RUdpMessage* message) + { + if (m_receivedMessages.empty()) + return false; + + *message = std::move(m_receivedMessages.front()); + m_receivedMessages.pop(); + return true; + } + + bool RUdpConnection::Send(const IpAddress& peerIp, PacketPriority priority, PacketReliability reliability, const NetPacket& packet) + { + auto it = m_peerByIP.find(peerIp); + if (it == m_peerByIP.end()) + return false; /// Silently fail (probably a disconnected client) + + EnqueuePacket(m_peers[it->second], priority, reliability, packet); + return true; + } + + void RUdpConnection::Update() + { + m_currentTime = m_clock.GetMicroseconds(); + + NetPacket receivedPacket; + IpAddress senderIp; + while (m_socket.ReceivePacket(&receivedPacket, &senderIp)) + OnPacketReceived(senderIp, std::move(receivedPacket)); + + //for (unsigned int i = m_activeClients.FindFirst(); i != m_activeClients.npos; i = m_activeClients.FindNext(i)) + //{ + // PeerData& clientData = m_peers[i]; + + CallOnExit resetIterator([this] () { m_peerIterator = m_peers.size(); }); + + for (m_peerIterator = 0; m_peerIterator < m_peers.size(); ++m_peerIterator) + { + PeerData& peer = m_peers[m_peerIterator]; + + UInt32 timeSinceLastPacket = static_cast(m_currentTime - peer.lastPacketTime); + if (timeSinceLastPacket > m_timeBeforeTimeOut) + { + DisconnectPeer(peer.index); + continue; + } + else if (timeSinceLastPacket > m_timeBeforePing) + { + if (m_currentTime - peer.lastPingTime > m_pingInterval) + { + NetPacket pingPacket(NetCode_Ping); + EnqueuePacket(peer, PacketPriority_Low, PacketReliability_Unreliable, pingPacket); + } + } + + if (peer.state == PeerState_WillAck && m_currentTime - peer.stateData1 > m_forceAckSendTime) + { + NetPacket acknowledgePacket(NetCode_Acknowledge); + EnqueuePacket(peer, PacketPriority_Low, PacketReliability_Reliable, acknowledgePacket); + } + + for (unsigned int priority = PacketPriority_Highest; priority <= PacketPriority_Lowest; ++priority) + { + std::vector& pendingPackets = peer.pendingPackets[priority]; + for (PendingPacket& packetData : pendingPackets) + SendPacket(peer, std::move(packetData)); + + pendingPackets.clear(); + } + + auto it = peer.pendingAckQueue.begin(); + while (it != peer.pendingAckQueue.end()) + { + if (m_currentTime - it->timeSent > 2 * peer.roundTripTime) + { + OnPacketLost(peer, std::move(*it)); + it = peer.pendingAckQueue.erase(it); + } + else + ++it; + } + } + //m_activeClients.Reset(); + } + + void RUdpConnection::DisconnectPeer(std::size_t peerIndex) + { + PeerData& peer = m_peers[peerIndex]; + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": " + peer.address.ToString() + " has been disconnected due to time-out"); + + OnPeerDisconnected(this, peer.address); + + // Remove from IP lookup table + m_peerByIP.erase(peer.address); + + // Can we safely "remove" this slot? + if (m_peerIterator >= m_peers.size() - 1 || peerIndex > m_peerIterator) + { + // Yes we can + PeerData& newSlot = m_peers[peerIndex]; + newSlot = std::move(m_peers.back()); + newSlot.index = peerIndex; //< Update the moved slot index before resizing (in case it's the last one) + } + else + { + // Nope, let's be tricky + PeerData& current = m_peers[m_peerIterator]; + PeerData& newSlot = m_peers[peerIndex]; + + newSlot = std::move(current); + newSlot.index = peerIndex; //< Update the moved slot index + + current = std::move(m_peers.back()); + current.index = m_peerIterator; //< Update the moved slot index + + --m_peerIterator; + } + + // Pop the last entry (from where we moved our slot) + m_peers.pop_back(); + } + + void RUdpConnection::EnqueuePacket(PeerData& peer, PacketPriority priority, PacketReliability reliability, const NetPacket& packet) + { + UInt16 protocolBegin = static_cast(m_protocol & 0xFFFF); + UInt16 protocolEnd = static_cast((m_protocol & 0xFFFF0000) >> 16); + + NetPacket data(packet.GetNetCode(), MessageHeader + packet.GetDataSize() + MessageFooter); + data << protocolBegin; + + data.GetStream()->SetCursorPos(NetPacket::HeaderSize + MessageHeader); + data.Write(packet.GetConstData() + NetPacket::HeaderSize, packet.GetDataSize()); + + data << protocolEnd; + EnqueuePacketInternal(peer, priority, reliability, std::move(data)); + } + + void RUdpConnection::EnqueuePacketInternal(PeerData& peer, PacketPriority priority, PacketReliability reliability, NetPacket&& data) + { + PendingPacket pendingPacket; + pendingPacket.data = std::move(data); + pendingPacket.priority = priority; + pendingPacket.reliability = reliability; + + peer.pendingPackets[priority].emplace_back(std::move(pendingPacket)); + m_activeClients.UnboundedSet(peer.index); + } + + bool RUdpConnection::InitSocket(NetProtocol protocol) + { + CallOnExit updateLastError([this] + { + m_lastError = m_socket.GetLastError(); + }); + + if (!m_socket.Create(protocol)) + return false; + + m_socket.EnableBlocking(false); + return true; + } + + void RUdpConnection::ProcessAcks(PeerData& peer, SequenceIndex lastAck, UInt32 ackBits) + { + auto it = peer.pendingAckQueue.begin(); + while (it != peer.pendingAckQueue.end()) + { + bool acked = false; + if (lastAck == it->sequenceId) + acked = true; + else if (!IsAckMoreRecent(it->sequenceId, lastAck)) + { + unsigned int difference = ComputeSequenceDifference(lastAck, it->sequenceId); + if (difference <= 32) + acked = (ackBits >> (difference - 1)) & 1; + } + + if (acked) + { + it = peer.pendingAckQueue.erase(it); + } + else + ++it; + } + } + + RUdpConnection::PeerData& RUdpConnection::RegisterPeer(const IpAddress& address, PeerState state) + { + PeerData data; + data.address = address; + data.localSequence = 0; + data.remoteSequence = 0; + data.index = m_peers.size(); + data.lastPacketTime = m_currentTime; + data.lastPingTime = m_currentTime; + data.roundTripTime = 1000000; ///< Okay that's quite a lot + data.state = state; + + m_activeClients.UnboundedSet(data.index); + m_peerByIP[address] = data.index; + + m_peers.emplace_back(std::move(data)); + return m_peers.back(); + } + + void RUdpConnection::OnClientRequestingConnection(const IpAddress& address, SequenceIndex sequenceId, UInt64 token) + { + // Call hook to check if client should be accepted or not + OnPeerConnection(this, address); + + PeerData& client = RegisterPeer(address, PeerState_Aknowledged); + client.remoteSequence = sequenceId; + + /// Acknowledge connection + NetPacket connectionAcceptedPacket(NetCode_AcknowledgeConnection); + //connectionAcceptedPacket << address; + connectionAcceptedPacket << ~token; + + EnqueuePacket(client, PacketPriority_Immediate, PacketReliability_Reliable, connectionAcceptedPacket); + } + + void RUdpConnection::OnPacketLost(PeerData& peer, PendingAckPacket&& packet) + { + //NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Lost packet " + String::Number(packet.sequenceId)); + + if (IsReliable(packet.reliability)) + EnqueuePacketInternal(peer, packet.priority, packet.reliability, std::move(packet.data)); + } + + void RUdpConnection::OnPacketReceived(const IpAddress& peerIp, NetPacket&& packet) + { + UInt16 protocolBegin; + UInt16 protocolEnd; + SequenceIndex sequenceId; + SequenceIndex lastAck; + UInt32 ackBits; + + packet.GetStream()->SetCursorPos(packet.GetSize() - MessageFooter); + packet >> protocolEnd; + + packet.GetStream()->SetCursorPos(NetPacket::HeaderSize); + packet >> protocolBegin; + + UInt32 protocolId = static_cast(protocolEnd) << 16 | protocolBegin; + if (protocolId != m_protocol) + return; ///< Ignore + + packet >> sequenceId >> lastAck >> ackBits; + + auto it = m_peerByIP.find(peerIp); + if (it == m_peerByIP.end()) + { + switch (packet.GetNetCode()) + { + case NetCode_RequestConnection: + { + UInt64 token; + packet >> token; + + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_RequestConnection from " + peerIp.ToString() + ": " + String::Number(token)); + if (!m_shouldAcceptConnections) + return; //< Ignore + + OnClientRequestingConnection(peerIp, sequenceId, token); + break; + } + + default: + return; //< Ignore + } + } + else + { + PeerData& peer = m_peers[it->second]; + peer.lastPacketTime = m_currentTime; + + if (peer.receivedQueue.find(sequenceId) != peer.receivedQueue.end()) + return; //< Ignore + + if (m_isSimulationEnabled && m_packetLossProbability(s_randomGenerator)) + { + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Lost packet " + String::Number(sequenceId) + " from " + peerIp.ToString() + " for simulation purpose"); + return; + } + + ///< Receiving a packet from an acknowledged client means the connection works in both ways + if (peer.state == PeerState_Aknowledged && packet.GetNetCode() != NetCode_RequestConnection) + { + peer.state = PeerState_Connected; + OnPeerAcknowledged(this, peerIp); + } + + if (IsAckMoreRecent(sequenceId, peer.remoteSequence)) + peer.remoteSequence = sequenceId; + + ProcessAcks(peer, lastAck, ackBits); + + peer.receivedQueue.insert(sequenceId); + + switch (packet.GetNetCode()) + { + case NetCode_Acknowledge: + return; //< Do not switch to will ack mode (to prevent infinite replies, just let's ping/pong do that) + + case NetCode_AcknowledgeConnection: + { + if (peer.state == PeerState_Connected) + break; + + IpAddress externalAddress; + UInt64 token; + packet /*>> externalAddress*/ >> token; + + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_AcknowledgeConnection from " + peerIp.ToString() + ": " + String::Number(token)); + if (token == ~peer.stateData1) + { + peer.state = PeerState_Connected; + OnConnectedToPeer(this); + } + else + { + NazaraNotice("Received wrong token (" + String::Number(token) + " instead of " + String::Number(~peer.stateData1) + ") from client " + peer.address); + return; //< Ignore + } + + break; + } + + case NetCode_RequestConnection: + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_RequestConnection from " + peerIp.ToString()); + return; //< Ignore + + case NetCode_Ping: + { + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_Ping from " + peerIp.ToString()); + + NetPacket pongPacket(NetCode_Pong); + EnqueuePacket(peer, PacketPriority_Low, PacketReliability_Unreliable, pongPacket); + break; + } + + case NetCode_Pong: + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received NetCode_Pong from " + peerIp.ToString()); + break; + + default: + { + NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Received 0x" + String::Number(packet.GetNetCode(), 16) + " from " + peerIp.ToString()); + RUdpMessage receivedMessage; + receivedMessage.from = peerIp; + receivedMessage.data = std::move(packet); + + m_receivedMessages.emplace(std::move(receivedMessage)); + break; + } + } + + if (!HasPendingPackets(peer)) + { + peer.state = PeerState_WillAck; + peer.stateData1 = m_currentTime; + } + } + } + + void RUdpConnection::SendPacket(PeerData& peer, PendingPacket&& packet) + { + if (peer.state == PeerState_WillAck) + peer.state = PeerState_Connected; + + SequenceIndex remoteSequence = peer.remoteSequence; + + UInt32 previousAcks = 0; + for (SequenceIndex ack : peer.receivedQueue) + { + if (ack == remoteSequence) + continue; + + unsigned int difference = ComputeSequenceDifference(remoteSequence, ack); + if (difference <= 32U) + previousAcks |= (1U << (difference - 1)); + } + + SequenceIndex sequenceId = ++peer.localSequence; + + packet.data.GetStream()->SetCursorPos(NetPacket::HeaderSize + sizeof(UInt16)); ///< Protocol begin has already been filled + packet.data << sequenceId; + packet.data << remoteSequence; + packet.data << previousAcks; + + m_socket.SendPacket(peer.address, packet.data); + + PendingAckPacket pendingAckPacket; + pendingAckPacket.data = std::move(packet.data); + pendingAckPacket.priority = packet.priority; + pendingAckPacket.reliability = packet.reliability; + pendingAckPacket.sequenceId = sequenceId; + pendingAckPacket.timeSent = m_currentTime; + + peer.pendingAckQueue.emplace_back(std::move(pendingAckPacket)); + } + + bool RUdpConnection::Initialize() + { + std::random_device device; + s_randomGenerator.seed(device()); + + return true; + } + + void RUdpConnection::Uninitialize() + { + } + + std::mt19937_64 RUdpConnection::s_randomGenerator; +} diff --git a/src/Nazara/Network/TcpClient.cpp b/src/Nazara/Network/TcpClient.cpp index b91dfaffd..a134cf870 100644 --- a/src/Nazara/Network/TcpClient.cpp +++ b/src/Nazara/Network/TcpClient.cpp @@ -6,14 +6,18 @@ #include #include #include -#include +#include #if defined(NAZARA_PLATFORM_WINDOWS) #include +#elif defined(NAZARA_PLATFORM_POSIX) +#include #else #error Missing implementation: Socket #endif +#include + namespace Nz { SocketState TcpClient::Connect(const IpAddress& remoteAddress) @@ -21,6 +25,7 @@ namespace Nz NazaraAssert(remoteAddress.IsValid(), "Invalid remote address"); NazaraAssert(remoteAddress.GetPort() != 0, "Remote address has no port"); + Disconnect(); Open(remoteAddress.GetProtocol()); CallOnExit restoreBlocking; @@ -42,8 +47,6 @@ namespace Nz SocketState TcpClient::Connect(const String& hostName, NetProtocol protocol, const String& service, ResolveError* error) { - Disconnect(); - UpdateState(SocketState_Resolving); std::vector results = IpAddress::ResolveHostname(protocol, hostName, service, error); if (results.empty()) @@ -139,6 +142,66 @@ namespace Nz return true; } + 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 + NazaraAssert(packet, "Invalid packet"); + + if (!m_pendingPacket.headerReceived) + { + m_pendingPacket.data.Resize(NetPacket::HeaderSize); + + std::size_t received; + if (!Receive(&m_pendingPacket.data[m_pendingPacket.received], NetPacket::HeaderSize - m_pendingPacket.received, &received)) + return false; + + m_pendingPacket.received += received; + + NazaraAssert(m_pendingPacket.received <= NetPacket::HeaderSize, "Received more data than header size"); + if (m_pendingPacket.received >= NetPacket::HeaderSize) + { + UInt16 size; + if (!NetPacket::DecodeHeader(m_pendingPacket.data.GetConstBuffer(), &size, &m_pendingPacket.netcode)) + { + m_lastError = SocketError_Packet; + NazaraWarning("Invalid header data"); + return false; + } + + m_pendingPacket.data.Resize(size - NetPacket::HeaderSize); + m_pendingPacket.headerReceived = true; + m_pendingPacket.received = 0; + } + } + + // We may have just received the header now + if (m_pendingPacket.headerReceived) + { + UInt16 packetSize = static_cast(m_pendingPacket.data.GetSize()); //< Total packet size + + std::size_t received; + if (!Receive(&m_pendingPacket.data[m_pendingPacket.received], packetSize - m_pendingPacket.received, &received)) + return false; + + m_pendingPacket.received += received; + + NazaraAssert(m_pendingPacket.received <= packetSize, "Received more data than packet size"); + if (m_pendingPacket.received >= packetSize) + { + // Okay we received the whole packet, copy it + packet->Reset(m_pendingPacket.netcode, m_pendingPacket.data.GetConstBuffer(), m_pendingPacket.data.GetSize()); + + // And reset every state + m_pendingPacket.data.Clear(); + m_pendingPacket.headerReceived = false; + m_pendingPacket.received = 0; + return true; + } + } + + return false; + } + bool TcpClient::Send(const void* buffer, std::size_t size, std::size_t* sent) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -158,7 +221,7 @@ namespace Nz { int sendSize = static_cast(std::min(size - totalByteSent, std::numeric_limits::max())); //< Handle very large send int sentSize; - if (!SocketImpl::Send(m_handle, reinterpret_cast(buffer) + totalByteSent, sendSize, &sentSize, &m_lastError)) + if (!SocketImpl::Send(m_handle, static_cast(buffer) + totalByteSent, sendSize, &sentSize, &m_lastError)) { switch (m_lastError) { @@ -181,6 +244,20 @@ namespace Nz return true; } + bool TcpClient::SendPacket(const NetPacket& packet) + { + std::size_t size = 0; + const UInt8* ptr = static_cast(packet.OnSend(&size)); + if (!ptr) + { + m_lastError = SocketError_Packet; + NazaraError("Failed to prepare packet"); + return false; + } + + return Send(ptr, size, nullptr); + } + bool TcpClient::SetCursorPos(UInt64 offset) { NazaraError("SetCursorPos() cannot be used on sequential streams"); @@ -213,7 +290,7 @@ namespace Nz } SocketState newState = SocketImpl::Connect(m_handle, m_peerAddress, msTimeout, &m_lastError); - NazaraAssert(newState != SocketState_Connecting, "Invalid internal return"); + NazaraAssert(newState != SocketState_Connecting, "Invalid internal return"); //< Connect cannot return Connecting is a timeout was specified // Prevent valid peer address in non-connected state if (newState == SocketState_NotConnected) diff --git a/src/Nazara/Network/TcpServer.cpp b/src/Nazara/Network/TcpServer.cpp index 096603b8f..b5995b6f5 100644 --- a/src/Nazara/Network/TcpServer.cpp +++ b/src/Nazara/Network/TcpServer.cpp @@ -11,6 +11,8 @@ #if defined(NAZARA_PLATFORM_WINDOWS) #include +#elif defined(NAZARA_PLATFORM_POSIX) +#include #else #error Missing implementation: Socket #endif @@ -60,4 +62,4 @@ namespace Nz m_boundAddress = IpAddress::Invalid; } -} \ No newline at end of file +} diff --git a/src/Nazara/Network/UdpSocket.cpp b/src/Nazara/Network/UdpSocket.cpp index fb813b3d6..a233881ea 100644 --- a/src/Nazara/Network/UdpSocket.cpp +++ b/src/Nazara/Network/UdpSocket.cpp @@ -3,14 +3,18 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include -#include +#include #if defined(NAZARA_PLATFORM_WINDOWS) #include +#elif defined(NAZARA_PLATFORM_POSIX) +#include #else #error Missing implementation: Socket #endif +#include + namespace Nz { SocketState UdpSocket::Bind(const IpAddress& address) @@ -37,7 +41,7 @@ namespace Nz } } - unsigned int UdpSocket::QueryMaxDatagramSize() + std::size_t UdpSocket::QueryMaxDatagramSize() { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created"); @@ -58,6 +62,41 @@ namespace Nz return true; } + bool UdpSocket::ReceivePacket(NetPacket* packet, IpAddress* from) + { + // 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()); + packet->Resize(std::numeric_limits::max()); + + std::size_t received; + if (!Receive(packet->GetData(), static_cast(packet->GetSize()), from, &received)) + return false; + + if (received == 0) + return false; //< No datagram received + + Nz::UInt16 netCode; + Nz::UInt16 packetSize; + if (!NetPacket::DecodeHeader(packet->GetConstData(), &packetSize, &netCode)) + { + m_lastError = SocketError_Packet; + NazaraWarning("Invalid header data"); + return false; + } + + if (packetSize != received) + { + m_lastError = SocketError_Packet; + NazaraWarning("Invalid packet size (packet size is " + String::Number(packetSize) + " bytes, received " + Nz::String::Number(received) + " bytes)"); + return false; + } + + packet->Resize(received); + packet->SetNetCode(netCode); + return true; + } + bool UdpSocket::Send(const IpAddress& to, const void* buffer, std::size_t size, std::size_t* sent) { NazaraAssert(to.IsValid(), "Invalid ip address"); @@ -74,6 +113,20 @@ namespace Nz return true; } + bool UdpSocket::SendPacket(const IpAddress& to, const NetPacket& packet) + { + std::size_t size = 0; + const UInt8* ptr = static_cast(packet.OnSend(&size)); + if (!ptr) + { + m_lastError = SocketError_Packet; + NazaraError("Failed to prepare packet"); + return false; + } + + return Send(to, ptr, size, nullptr); + } + void UdpSocket::OnClose() { AbstractSocket::OnClose(); diff --git a/src/Nazara/Network/Win32/IpAddressImpl.cpp b/src/Nazara/Network/Win32/IpAddressImpl.cpp index b23ceae04..9f3918574 100644 --- a/src/Nazara/Network/Win32/IpAddressImpl.cpp +++ b/src/Nazara/Network/Win32/IpAddressImpl.cpp @@ -191,7 +191,6 @@ namespace Nz Detail::FreeAddressInfo(servinfo); }); - // loop through all the results and connect to the first we can for (Detail::addrinfoImpl* p = servinfo; p != nullptr; p = p->ai_next) { HostnameInfo result; diff --git a/src/Nazara/Network/Win32/SocketImpl.cpp b/src/Nazara/Network/Win32/SocketImpl.cpp index 7d83ce99d..35d4b2371 100644 --- a/src/Nazara/Network/Win32/SocketImpl.cpp +++ b/src/Nazara/Network/Win32/SocketImpl.cpp @@ -256,7 +256,7 @@ namespace Nz return SocketState_Bound; } - unsigned int SocketImpl::QueryAvailableBytes(SocketHandle handle, SocketError* error) + std::size_t SocketImpl::QueryAvailableBytes(SocketHandle handle, SocketError* error) { NazaraAssert(handle != InvalidHandle, "Invalid handle"); @@ -405,7 +405,7 @@ namespace Nz NazaraAssert(handle != InvalidHandle, "Invalid handle"); NazaraAssert(buffer && length > 0, "Invalid buffer"); - int byteRead = recv(handle, reinterpret_cast(buffer), length, 0); + int byteRead = recv(handle, static_cast(buffer), length, 0); if (byteRead == SOCKET_ERROR) { int errorCode = WSAGetLastError(); @@ -666,6 +666,12 @@ namespace Nz case WSAECONNREFUSED: return SocketError_ConnectionRefused; + case WSAECONNABORTED: + case WSAECONNRESET: + case WSAENOTCONN: + case WSAESHUTDOWN: + return SocketError_ConnectionClosed; + case WSAEMSGSIZE: return SocketError_DatagramSize; @@ -674,10 +680,6 @@ namespace Nz case WSA_NOT_ENOUGH_MEMORY: return SocketError_ResourceError; - case WSAENOTCONN: - case WSAESHUTDOWN: - return SocketError_ConnectionClosed; - case WSAEHOSTUNREACH: return SocketError_UnreachableHost; diff --git a/src/Nazara/Network/Win32/SocketImpl.hpp b/src/Nazara/Network/Win32/SocketImpl.hpp index 8f6b1f72d..dc0d77e73 100644 --- a/src/Nazara/Network/Win32/SocketImpl.hpp +++ b/src/Nazara/Network/Win32/SocketImpl.hpp @@ -35,10 +35,10 @@ namespace Nz static SocketState Listen(SocketHandle handle, const IpAddress& address, unsigned queueSize, SocketError* error); - static unsigned int QueryAvailableBytes(SocketHandle handle, SocketError* error = nullptr); + static std::size_t QueryAvailableBytes(SocketHandle handle, SocketError* error = nullptr); static bool QueryBroadcasting(SocketHandle handle, SocketError* error = nullptr); static bool QueryKeepAlive(SocketHandle handle, SocketError* error = nullptr); - static unsigned int QueryMaxDatagramSize(SocketHandle handle, SocketError* error = nullptr); + static std::size_t QueryMaxDatagramSize(SocketHandle handle, SocketError* error = nullptr); static bool QueryNoDelay(SocketHandle handle, SocketError* error = nullptr); static IpAddress QueryPeerAddress(SocketHandle handle, SocketError* error = nullptr); static IpAddress QuerySocketAddress(SocketHandle handle, SocketError* error = nullptr); diff --git a/src/Nazara/Physics/Geom.cpp b/src/Nazara/Physics/Geom.cpp index abe1b6e03..e814b8618 100644 --- a/src/Nazara/Physics/Geom.cpp +++ b/src/Nazara/Physics/Geom.cpp @@ -148,6 +148,22 @@ namespace Nz return CreateGeomFromPrimitive(list.GetPrimitive(0)); } + bool PhysGeom::Initialize() + { + if (!PhysGeomLibrary::Initialize()) + { + NazaraError("Failed to initialise library"); + return false; + } + + return true; + } + + void PhysGeom::Uninitialize() + { + PhysGeomLibrary::Uninitialize(); + } + PhysGeomLibrary::LibraryMap PhysGeom::s_library; /********************************** BoxGeom **********************************/ diff --git a/src/Nazara/Physics/Physics.cpp b/src/Nazara/Physics/Physics.cpp index 3c53ff9ad..584c334ab 100644 --- a/src/Nazara/Physics/Physics.cpp +++ b/src/Nazara/Physics/Physics.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,11 @@ namespace Nz s_moduleReferenceCounter++; // Initialisation du module + if (!PhysGeom::Initialize()) + { + NazaraError("Failed to initialize geoms"); + return false; + } NazaraNotice("Initialized: Physics module"); return true; @@ -57,6 +63,8 @@ namespace Nz } // Libération du module + PhysGeom::Uninitialize(); + s_moduleReferenceCounter = 0; NazaraNotice("Uninitialized: Physics module"); diff --git a/src/Nazara/Renderer/DebugDrawer.cpp b/src/Nazara/Renderer/DebugDrawer.cpp index 03b8b63a8..5623e6e8d 100644 --- a/src/Nazara/Renderer/DebugDrawer.cpp +++ b/src/Nazara/Renderer/DebugDrawer.cpp @@ -67,7 +67,7 @@ namespace Nz } BufferMapper mapper(s_vertexBuffer, BufferAccess_DiscardAndWrite, 0, 24); - VertexStruct_XYZ* vertex = reinterpret_cast(mapper.GetPointer()); + VertexStruct_XYZ* vertex = static_cast(mapper.GetPointer()); Vector3f max, min; max = box.GetMaximum(); @@ -158,7 +158,7 @@ namespace Nz } BufferMapper mapper(s_vertexBuffer, BufferAccess_DiscardAndWrite, 0, 24); - VertexStruct_XYZ* vertex = reinterpret_cast(mapper.GetPointer()); + VertexStruct_XYZ* vertex = static_cast(mapper.GetPointer()); vertex->position.Set(frustum.GetCorner(BoxCorner_NearLeftBottom)); vertex++; @@ -240,7 +240,7 @@ namespace Nz } BufferMapper mapper(s_vertexBuffer, BufferAccess_DiscardAndWrite, 0, 24); - VertexStruct_XYZ* vertex = reinterpret_cast(mapper.GetPointer()); + VertexStruct_XYZ* vertex = static_cast(mapper.GetPointer()); vertex->position.Set(orientedBox.GetCorner(BoxCorner_NearLeftBottom)); vertex++; @@ -329,7 +329,7 @@ namespace Nz } BufferMapper mapper(s_vertexBuffer, BufferAccess_DiscardAndWrite, 0, jointCount*2); - VertexStruct_XYZ* vertex = reinterpret_cast(mapper.GetPointer()); + VertexStruct_XYZ* vertex = static_cast(mapper.GetPointer()); unsigned int vertexCount = 0; for (unsigned int i = 0; i < jointCount; ++i) @@ -407,8 +407,8 @@ namespace Nz BufferMapper inputMapper(subMesh->GetVertexBuffer(), BufferAccess_ReadOnly); BufferMapper outputMapper(s_vertexBuffer, BufferAccess_DiscardAndWrite, 0, vertexCount); - MeshVertex* inputVertex = reinterpret_cast(inputMapper.GetPointer()); - VertexStruct_XYZ* outputVertex = reinterpret_cast(outputMapper.GetPointer()); + MeshVertex* inputVertex = static_cast(inputMapper.GetPointer()); + VertexStruct_XYZ* outputVertex = static_cast(outputMapper.GetPointer()); for (unsigned int i = 0; i < normalCount; ++i) { @@ -449,7 +449,7 @@ namespace Nz transformMatrix.SetTranslation(origin); BufferMapper mapper(s_vertexBuffer, BufferAccess_DiscardAndWrite, 0, 16); - VertexStruct_XYZ* vertex = reinterpret_cast(mapper.GetPointer()); + VertexStruct_XYZ* vertex = static_cast(mapper.GetPointer()); // On calcule le reste des points Vector3f base(Vector3f::Forward()*length); @@ -575,8 +575,8 @@ namespace Nz BufferMapper inputMapper(subMesh->GetVertexBuffer(), BufferAccess_ReadOnly); BufferMapper outputMapper(s_vertexBuffer, BufferAccess_DiscardAndWrite, 0, vertexCount); - MeshVertex* inputVertex = reinterpret_cast(inputMapper.GetPointer()); - VertexStruct_XYZ* outputVertex = reinterpret_cast(outputMapper.GetPointer()); + MeshVertex* inputVertex = static_cast(inputMapper.GetPointer()); + VertexStruct_XYZ* outputVertex = static_cast(outputMapper.GetPointer()); for (unsigned int i = 0; i < normalCount; ++i) { @@ -622,8 +622,8 @@ namespace Nz BufferMapper inputMapper(subMesh->GetVertexBuffer(), BufferAccess_ReadOnly); BufferMapper outputMapper(s_vertexBuffer, BufferAccess_DiscardAndWrite, 0, vertexCount); - MeshVertex* inputVertex = reinterpret_cast(inputMapper.GetPointer()); - VertexStruct_XYZ* outputVertex = reinterpret_cast(outputMapper.GetPointer()); + MeshVertex* inputVertex = static_cast(inputMapper.GetPointer()); + VertexStruct_XYZ* outputVertex = static_cast(outputMapper.GetPointer()); for (unsigned int i = 0; i < tangentCount; ++i) { diff --git a/src/Nazara/Renderer/GpuQuery.cpp b/src/Nazara/Renderer/GpuQuery.cpp index 6c3d404ce..4e7ff228f 100644 --- a/src/Nazara/Renderer/GpuQuery.cpp +++ b/src/Nazara/Renderer/GpuQuery.cpp @@ -19,7 +19,7 @@ namespace Nz Context::EnsureContext(); m_id = 0; - glGenQueries(1, reinterpret_cast(&m_id)); + glGenQueries(1, static_cast(&m_id)); #ifdef NAZARA_DEBUG if (!m_id) diff --git a/src/Nazara/Renderer/OpenGL.cpp b/src/Nazara/Renderer/OpenGL.cpp index 14e10e00a..2575d86c2 100644 --- a/src/Nazara/Renderer/OpenGL.cpp +++ b/src/Nazara/Renderer/OpenGL.cpp @@ -2099,7 +2099,6 @@ namespace Nz }; static_assert(VertexComponent_Max + 1 == 16, "Attribute index array is incomplete"); -} PFNGLACTIVETEXTUREPROC glActiveTexture = nullptr; PFNGLATTACHSHADERPROC glAttachShader = nullptr; @@ -2283,3 +2282,5 @@ GLX::PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = nullptr; GLX::PFNGLXSWAPINTERVALMESAPROC NzglXSwapIntervalMESA = nullptr; GLX::PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr; #endif + +} diff --git a/src/Nazara/Renderer/Renderer.cpp b/src/Nazara/Renderer/Renderer.cpp index fb965c1a7..1093f1fd1 100644 --- a/src/Nazara/Renderer/Renderer.cpp +++ b/src/Nazara/Renderer/Renderer.cpp @@ -623,14 +623,14 @@ namespace Nz s_updateFlags = Update_Matrices | Update_Shader | Update_VAO; s_vertexBuffer = nullptr; - s_fullscreenQuadBuffer.Reset(VertexDeclaration::Get(VertexLayout_XY), 4, DataStorage_Hardware, BufferUsage_Static); + s_fullscreenQuadBuffer.Reset(VertexDeclaration::Get(VertexLayout_XY_UV), 4, DataStorage_Hardware, BufferUsage_Static); - float vertices[4 * 2] = + float vertices[4 * 2 * 2] = { - -1.f, -1.f, - 1.f, -1.f, - -1.f, 1.f, - 1.f, 1.f, + -1.f, -1.f, 0.f, 1.f, + 1.f, -1.f, 1.f, 1.f, + -1.f, 1.f, 0.f, 0.f, + 1.f, 1.f, 1.f, 0.f }; if (!s_fullscreenQuadBuffer.Fill(vertices, 0, 4)) diff --git a/src/Nazara/Renderer/Texture.cpp b/src/Nazara/Renderer/Texture.cpp index 67a11120d..08fd7ebfb 100644 --- a/src/Nazara/Renderer/Texture.cpp +++ b/src/Nazara/Renderer/Texture.cpp @@ -825,6 +825,30 @@ namespace Nz return Update(image, Rectui(0, 0, faceSize, faceSize), face); } + bool Texture::SaveToFile(const String& filePath, const ImageParams& params) + { + Image image; + if (!Download(&image)) + { + NazaraError("Failed to download texture"); + return false; + } + + return image.SaveToFile(filePath, params); + } + + bool Texture::SaveToStream(Stream& stream, const String& format, const ImageParams& params) + { + Image image; + if (!Download(&image)) + { + NazaraError("Failed to download texture"); + return false; + } + + return image.SaveToStream(stream, format, params); + } + bool Texture::SetMipmapRange(UInt8 minLevel, UInt8 maxLevel) { #if NAZARA_RENDERER_SAFE diff --git a/src/Nazara/Utility/AlgorithmUtility.cpp b/src/Nazara/Utility/AlgorithmUtility.cpp index d22021cd2..b23542993 100644 --- a/src/Nazara/Utility/AlgorithmUtility.cpp +++ b/src/Nazara/Utility/AlgorithmUtility.cpp @@ -104,8 +104,11 @@ namespace Nz // Et maintenant on affine la sphère for (unsigned int i = 0; i < recursionLevel; ++i) { - for (Vector3ui& triangle : triangles) + std::size_t triangleCount = triangles.size(); + for (std::size_t i = 0; i < triangleCount; ++i) { + Vector3ui& triangle = triangles[i]; + unsigned int a = GetMiddleVertex(triangle.x, triangle.y); unsigned int b = GetMiddleVertex(triangle.y, triangle.z); unsigned int c = GetMiddleVertex(triangle.z, triangle.x); @@ -636,7 +639,7 @@ namespace Nz if (vertexCount > 0) { aabb.Set(positionPtr->x, positionPtr->y, positionPtr->z, 0.f, 0.f, 0.f); - --positionPtr; + ++positionPtr; for (unsigned int i = 1; i < vertexCount; ++i) aabb.ExtendTo(*positionPtr++); diff --git a/src/Nazara/Utility/Formats/DDSLoader.cpp b/src/Nazara/Utility/Formats/DDSLoader.cpp index b38ed4470..8f08abcf2 100644 --- a/src/Nazara/Utility/Formats/DDSLoader.cpp +++ b/src/Nazara/Utility/Formats/DDSLoader.cpp @@ -22,7 +22,9 @@ namespace Nz Ternary Check(Stream& stream, const ImageParams& parameters) { - NazaraUnused(parameters); + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeDDSLoader", &skip) && skip) + return Ternary_False; UInt32 magic; if (stream.Read(&magic, sizeof(UInt32)) == sizeof(UInt32)) diff --git a/src/Nazara/Utility/Formats/FreeTypeLoader.cpp b/src/Nazara/Utility/Formats/FreeTypeLoader.cpp index 2f5c2ef8f..aed76d608 100644 --- a/src/Nazara/Utility/Formats/FreeTypeLoader.cpp +++ b/src/Nazara/Utility/Formats/FreeTypeLoader.cpp @@ -348,7 +348,9 @@ namespace Nz Ternary Check(Stream& stream, const FontParams& parameters) { - NazaraUnused(parameters); + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeFreeTypeLoader", &skip) && skip) + return Ternary_False; FreeTypeStream face; face.SetStream(stream); diff --git a/src/Nazara/Utility/Formats/MD2Loader.cpp b/src/Nazara/Utility/Formats/MD2Loader.cpp index ea685cc6f..184adfe17 100644 --- a/src/Nazara/Utility/Formats/MD2Loader.cpp +++ b/src/Nazara/Utility/Formats/MD2Loader.cpp @@ -28,7 +28,9 @@ namespace Nz Ternary Check(Stream& stream, const MeshParams& parameters) { - NazaraUnused(parameters); + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeMD2Loader", &skip) && skip) + return Ternary_False; UInt32 magic[2]; if (stream.Read(&magic[0], 2*sizeof(UInt32)) == 2*sizeof(UInt32)) @@ -113,7 +115,7 @@ namespace Nz stream.Read(&triangles[0], header.num_tris*sizeof(MD2_Triangle)); BufferMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite); - UInt16* index = reinterpret_cast(indexMapper.GetPointer()); + UInt16* index = static_cast(indexMapper.GetPointer()); for (unsigned int i = 0; i < header.num_tris; ++i) { @@ -188,7 +190,7 @@ namespace Nz translate *= s; BufferMapper vertexMapper(vertexBuffer, BufferAccess_DiscardAndWrite); - MeshVertex* vertex = reinterpret_cast(vertexMapper.GetPointer()); + MeshVertex* vertex = static_cast(vertexMapper.GetPointer()); /// Chargement des coordonnées de texture const unsigned int indexFix[3] = {0, 2, 1}; // Pour respécifier les indices dans le bon ordre diff --git a/src/Nazara/Utility/Formats/MD5AnimLoader.cpp b/src/Nazara/Utility/Formats/MD5AnimLoader.cpp index 1950037c1..d079dc426 100644 --- a/src/Nazara/Utility/Formats/MD5AnimLoader.cpp +++ b/src/Nazara/Utility/Formats/MD5AnimLoader.cpp @@ -17,7 +17,9 @@ namespace Nz Ternary Check(Stream& stream, const AnimationParams& parameters) { - NazaraUnused(parameters); + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeMD5AnimLoader", &skip) && skip) + return Ternary_False; MD5AnimParser parser(stream); return parser.Check(); diff --git a/src/Nazara/Utility/Formats/MD5MeshLoader.cpp b/src/Nazara/Utility/Formats/MD5MeshLoader.cpp index 9bf16f055..629452161 100644 --- a/src/Nazara/Utility/Formats/MD5MeshLoader.cpp +++ b/src/Nazara/Utility/Formats/MD5MeshLoader.cpp @@ -22,7 +22,9 @@ namespace Nz Ternary Check(Stream& stream, const MeshParams& parameters) { - NazaraUnused(parameters); + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeMD5MeshLoader", &skip) && skip) + return Ternary_False; MD5MeshParser parser(stream); return parser.Check(); @@ -247,7 +249,7 @@ namespace Nz VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage); BufferMapper vertexMapper(vertexBuffer, BufferAccess_WriteOnly); - MeshVertex* vertex = reinterpret_cast(vertexMapper.GetPointer()); + MeshVertex* vertex = static_cast(vertexMapper.GetPointer()); for (const MD5MeshParser::Vertex& md5Vertex : md5Mesh.vertices) { // Skinning MD5 (Formule d'Id Tech) diff --git a/src/Nazara/Utility/Formats/OBJParser.cpp b/src/Nazara/Utility/Formats/OBJParser.cpp index 64132f586..c0daf1f84 100644 --- a/src/Nazara/Utility/Formats/OBJParser.cpp +++ b/src/Nazara/Utility/Formats/OBJParser.cpp @@ -82,7 +82,7 @@ namespace Nz return m_texCoords.size(); } - bool OBJParser::Parse() + bool OBJParser::Parse(std::size_t reservedVertexCount) { String matName, meshName; matName = meshName = "default"; @@ -95,10 +95,10 @@ namespace Nz m_positions.clear(); m_texCoords.clear(); - // Beaucoup de meshs font plus de 100 sommets, préparons le terrain - m_normals.reserve(100); - m_positions.reserve(100); - m_texCoords.reserve(100); + // Reserve some space for incoming vertices + m_normals.reserve(reservedVertexCount); + m_positions.reserve(reservedVertexCount); + m_texCoords.reserve(reservedVertexCount); // On va regrouper les meshs par nom et par matériau using FaceVec = std::vector; diff --git a/src/Nazara/Utility/Formats/PCXLoader.cpp b/src/Nazara/Utility/Formats/PCXLoader.cpp index c337409bf..1dc7622bd 100644 --- a/src/Nazara/Utility/Formats/PCXLoader.cpp +++ b/src/Nazara/Utility/Formats/PCXLoader.cpp @@ -47,7 +47,9 @@ namespace Nz Ternary Check(Stream& stream, const ImageParams& parameters) { - NazaraUnused(parameters); + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativePCXLoader", &skip) && skip) + return Ternary_False; UInt8 manufacturer; if (stream.Read(&manufacturer, 1) == 1) diff --git a/src/Nazara/Utility/Formats/STBLoader.cpp b/src/Nazara/Utility/Formats/STBLoader.cpp index 75d185e82..3ca5b3708 100644 --- a/src/Nazara/Utility/Formats/STBLoader.cpp +++ b/src/Nazara/Utility/Formats/STBLoader.cpp @@ -3,7 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include -#include +#include #include #include #include @@ -17,6 +17,12 @@ namespace Nz { namespace { + int Eof(void* userdata) + { + Stream* stream = static_cast(userdata); + return stream->GetCursorPos() >= stream->GetSize(); + } + int Read(void* userdata, char* data, int size) { Stream* stream = static_cast(userdata); @@ -29,12 +35,6 @@ namespace Nz stream->SetCursorPos(static_cast(stream->GetCursorPos()) + static_cast(size)); } - int Eof(void* userdata) - { - Stream* stream = static_cast(userdata); - return stream->GetCursorPos() >= stream->GetSize(); - } - static stbi_io_callbacks callbacks = {Read, Skip, Eof}; bool IsSupported(const String& extension) @@ -45,7 +45,9 @@ namespace Nz Ternary Check(Stream& stream, const ImageParams& parameters) { - NazaraUnused(parameters); + bool skip; + if (parameters.custom.GetBooleanParameter("SkipNativeSTBLoader", &skip) && skip) + return Ternary_False; int width, height, bpp; if (stbi_info_from_callbacks(&callbacks, &stream, &width, &height, &bpp)) @@ -87,12 +89,12 @@ namespace Nz namespace Loaders { - void RegisterSTB() + void RegisterSTBLoader() { ImageLoader::RegisterLoader(IsSupported, Check, Load); } - void UnregisterSTB() + void UnregisterSTBLoader() { ImageLoader::UnregisterLoader(IsSupported, Check, Load); } diff --git a/src/Nazara/Utility/Formats/STBLoader.hpp b/src/Nazara/Utility/Formats/STBLoader.hpp index 4d8917ef5..03e08c72f 100644 --- a/src/Nazara/Utility/Formats/STBLoader.hpp +++ b/src/Nazara/Utility/Formats/STBLoader.hpp @@ -4,8 +4,8 @@ #pragma once -#ifndef NAZARA_LOADERS_STB_HPP -#define NAZARA_LOADERS_STB_HPP +#ifndef NAZARA_FORMATS_STBLOADER_HPP +#define NAZARA_FORMATS_STBLOADER_HPP #include @@ -13,9 +13,9 @@ namespace Nz { namespace Loaders { - void RegisterSTB(); - void UnregisterSTB(); + void RegisterSTBLoader(); + void UnregisterSTBLoader(); } } -#endif // NAZARA_LOADERS_STB_HPP +#endif // NAZARA_FORMATS_STBLOADER_HPP diff --git a/src/Nazara/Utility/Formats/STBSaver.cpp b/src/Nazara/Utility/Formats/STBSaver.cpp new file mode 100644 index 000000000..4166de86f --- /dev/null +++ b/src/Nazara/Utility/Formats/STBSaver.cpp @@ -0,0 +1,242 @@ +// Copyright (C) 2015 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 + +namespace Nz +{ + namespace + { + using FormatHandler = bool(*)(const Image& image, const ImageParams& parameters, Stream& stream); + + std::map s_formatHandlers; + + int ConvertToFloatFormat(Image& image) + { + switch (image.GetFormat()) + { + case PixelFormatType_R32F: + return 1; + + case PixelFormatType_RG32F: + return 2; + + case PixelFormatType_RGB32F: + return 3; + + case PixelFormatType_RGBA32F: + return 4; + + default: + { + if (PixelFormat::HasAlpha(image.GetFormat())) + { + if (!image.Convert(PixelFormatType_RGBA32F)) + break; + + return 4; + } + else + { + if (!image.Convert(PixelFormatType_RGB32F)) + break; + + return 3; + } + } + } + + return 0; + } + + int ConvertToIntegerFormat(Image& image) + { + switch (image.GetFormat()) + { + case PixelFormatType_L8: + case PixelFormatType_R8: + return 1; + + case PixelFormatType_LA8: + case PixelFormatType_RG8: + return 2; + + case PixelFormatType_RGB8: + return 3; + + case PixelFormatType_RGBA8: + return 4; + + default: + { + if (PixelFormat::HasAlpha(image.GetFormat())) + { + if (!image.Convert(PixelFormatType_RGBA8)) + break; + + return 4; + } + else + { + if (!image.Convert(PixelFormatType_RGB8)) + break; + + return 3; + } + } + } + + return 0; + } + + void WriteToStream(void* userdata, void* data, int size) + { + Stream* stream = static_cast(userdata); + if (stream->Write(data, size) != size) + throw std::runtime_error("Failed to write to stream"); + } + + bool FormatQuerier(const String& extension) + { + return s_formatHandlers.find(extension) != s_formatHandlers.end(); + } + + bool SaveToStream(const Image& image, const String& format, Stream& stream, const ImageParams& parameters) + { + NazaraUnused(parameters); + + if (!image.IsValid()) + { + NazaraError("Invalid image"); + return false; + } + + ImageType type = image.GetType(); + if (type != ImageType_1D && type != ImageType_2D) + { + NazaraError("Image type 0x" + String::Number(type, 16) + " is not "); + return false; + } + + auto it = s_formatHandlers.find(format); + NazaraAssert(it != s_formatHandlers.end(), "Invalid handler"); + + const FormatHandler& handler = it->second; + try + { + return handler(image, parameters, stream); + } + catch (const std::exception& e) + { + NazaraError(e.what()); + return false; + } + } + + bool SaveBMP(const Image& image, const ImageParams& parameters, Stream& stream) + { + Image tempImage(image); //< We're using COW here to prevent Image copy unless required + + int componentCount = ConvertToIntegerFormat(tempImage); + if (componentCount == 0) + { + NazaraError("Failed to convert image to suitable format"); + return false; + } + + if (!stbi_write_bmp_to_func(&WriteToStream, &stream, tempImage.GetWidth(), tempImage.GetHeight(), componentCount, tempImage.GetConstPixels())) + { + NazaraError("Failed to write BMP to stream"); + return false; + } + + return true; + } + + bool SaveHDR(const Image& image, const ImageParams& parameters, Stream& stream) + { + Image tempImage(image); //< We're using COW here to prevent Image copy unless required + + int componentCount = ConvertToFloatFormat(tempImage); + if (componentCount == 0) + { + NazaraError("Failed to convert image to suitable format"); + return false; + } + + if (!stbi_write_hdr_to_func(&WriteToStream, &stream, tempImage.GetWidth(), tempImage.GetHeight(), componentCount, reinterpret_cast(tempImage.GetConstPixels()))) + { + NazaraError("Failed to write HDR to stream"); + return false; + } + + return true; + } + + bool SavePNG(const Image& image, const ImageParams& parameters, Stream& stream) + { + Image tempImage(image); //< We're using COW here to prevent Image copy unless required + + int componentCount = ConvertToIntegerFormat(tempImage); + if (componentCount == 0) + { + NazaraError("Failed to convert image to suitable format"); + return false; + } + + if (!stbi_write_png_to_func(&WriteToStream, &stream, tempImage.GetWidth(), tempImage.GetHeight(), componentCount, tempImage.GetConstPixels(), 0)) + { + NazaraError("Failed to write PNG to stream"); + return false; + } + + return true; + } + + bool SaveTGA(const Image& image, const ImageParams& parameters, Stream& stream) + { + Image tempImage(image); //< We're using COW here to prevent Image copy unless required + + int componentCount = ConvertToIntegerFormat(tempImage); + if (componentCount == 0) + { + NazaraError("Failed to convert image to suitable format"); + return false; + } + + if (!stbi_write_tga_to_func(&WriteToStream, &stream, tempImage.GetWidth(), tempImage.GetHeight(), componentCount, tempImage.GetConstPixels())) + { + NazaraError("Failed to write TGA to stream"); + return false; + } + + return true; + } + } + + namespace Loaders + { + void RegisterSTBSaver() + { + s_formatHandlers["bmp"] = &SaveBMP; + s_formatHandlers["hdr"] = &SaveHDR; + s_formatHandlers["png"] = &SavePNG; + s_formatHandlers["tga"] = &SaveTGA; + + ImageSaver::RegisterSaver(FormatQuerier, SaveToStream); + } + + void UnregisterSTBSaver() + { + ImageSaver::UnregisterSaver(FormatQuerier, SaveToStream); + s_formatHandlers.clear(); + } + } +} diff --git a/src/Nazara/Utility/Formats/STBSaver.hpp b/src/Nazara/Utility/Formats/STBSaver.hpp new file mode 100644 index 000000000..9a511c07e --- /dev/null +++ b/src/Nazara/Utility/Formats/STBSaver.hpp @@ -0,0 +1,21 @@ +// Copyright (C) 2015 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 + +#pragma once + +#ifndef NAZARA_FORMATS_STBSAVER_HPP +#define NAZARA_FORMATS_STBSAVER_HPP + +#include + +namespace Nz +{ + namespace Loaders + { + void RegisterSTBSaver(); + void UnregisterSTBSaver(); + } +} + +#endif // NAZARA_FORMATS_STBSAVER_HPP diff --git a/src/Nazara/Utility/Image.cpp b/src/Nazara/Utility/Image.cpp index 8af7d7923..0965ed06e 100644 --- a/src/Nazara/Utility/Image.cpp +++ b/src/Nazara/Utility/Image.cpp @@ -1066,6 +1066,100 @@ namespace Nz return LoadCubemapFromImage(image, cubemapParams); } + bool Image::LoadFaceFromFile(CubemapFace face, const String& filePath, const ImageParams& params) + { + NazaraAssert(IsValid() && IsCubemap(), "Texture must be a valid cubemap"); + + Image image; + if (!image.LoadFromFile(filePath, params)) + { + NazaraError("Failed to load image"); + return false; + } + + if (!image.Convert(GetFormat())) + { + NazaraError("Failed to convert image to texture format"); + return false; + } + + unsigned int faceSize = GetWidth(); + if (image.GetWidth() != faceSize || image.GetHeight() != faceSize) + { + NazaraError("Image size must match texture face size"); + return false; + } + + Copy(image, Rectui(0, 0, faceSize, faceSize), Vector3ui(0, 0, face)); + return true; + } + + bool Image::LoadFaceFromMemory(CubemapFace face, const void* data, std::size_t size, const ImageParams& params) + { + NazaraAssert(IsValid() && IsCubemap(), "Texture must be a valid cubemap"); + + Image image; + if (!image.LoadFromMemory(data, size, params)) + { + NazaraError("Failed to load image"); + return false; + } + + if (!image.Convert(GetFormat())) + { + NazaraError("Failed to convert image to texture format"); + return false; + } + + unsigned int faceSize = GetWidth(); + if (image.GetWidth() != faceSize || image.GetHeight() != faceSize) + { + NazaraError("Image size must match texture face size"); + return false; + } + + Copy(image, Rectui(0, 0, faceSize, faceSize), Vector3ui(0, 0, face)); + return true; + } + + bool Image::LoadFaceFromStream(CubemapFace face, Stream& stream, const ImageParams& params) + { + NazaraAssert(IsValid() && IsCubemap(), "Texture must be a valid cubemap"); + + Image image; + if (!image.LoadFromStream(stream, params)) + { + NazaraError("Failed to load image"); + return false; + } + + if (!image.Convert(GetFormat())) + { + NazaraError("Failed to convert image to texture format"); + return false; + } + + unsigned int faceSize = GetWidth(); + if (image.GetWidth() != faceSize || image.GetHeight() != faceSize) + { + NazaraError("Image size must match texture face size"); + return false; + } + + Copy(image, Rectui(0, 0, faceSize, faceSize), Vector3ui(0, 0, face)); + return true; + } + + bool Image::SaveToFile(const String& filePath, const ImageParams& params) + { + return ImageSaver::SaveToFile(*this, filePath, params); + } + + bool Image::SaveToStream(Stream& stream, const String& format, const ImageParams& params) + { + return ImageSaver::SaveToStream(*this, stream, format, params); + } + void Image::SetLevelCount(UInt8 levelCount) { #if NAZARA_UTILITY_SAFE @@ -1168,11 +1262,11 @@ namespace Nz EnsureOwnership(); Copy(m_sharedImage->levels[level].get(), pixels, PixelFormat::GetBytesPerPixel(m_sharedImage->format), - GetLevelSize(m_sharedImage->width, level), - GetLevelSize(m_sharedImage->height, level), - GetLevelSize(m_sharedImage->depth, level), - 0, 0, - srcWidth, srcHeight); + GetLevelSize(m_sharedImage->width, level), + GetLevelSize(m_sharedImage->height, level), + GetLevelSize(m_sharedImage->depth, level), + 0, 0, + srcWidth, srcHeight); return true; } @@ -1377,4 +1471,5 @@ namespace Nz ImageLoader::LoaderList Image::s_loaders; ImageManager::ManagerMap Image::s_managerMap; ImageManager::ManagerParams Image::s_managerParameters; + ImageSaver::SaverList Image::s_savers; } diff --git a/src/Nazara/Utility/IndexMapper.cpp b/src/Nazara/Utility/IndexMapper.cpp index e95f9b3cd..adf80a764 100644 --- a/src/Nazara/Utility/IndexMapper.cpp +++ b/src/Nazara/Utility/IndexMapper.cpp @@ -14,25 +14,25 @@ namespace Nz { UInt32 Getter16(const void* buffer, unsigned int i) { - const UInt16* ptr = reinterpret_cast(buffer); + const UInt16* ptr = static_cast(buffer); return ptr[i]; } UInt32 Getter32(const void* buffer, unsigned int i) { - const UInt32* ptr = reinterpret_cast(buffer); + const UInt32* ptr = static_cast(buffer); return ptr[i]; } void Setter16(void* buffer, unsigned int i, UInt32 value) { - UInt16* ptr = reinterpret_cast(buffer); + UInt16* ptr = static_cast(buffer); ptr[i] = static_cast(value); } void Setter32(void* buffer, unsigned int i, UInt32 value) { - UInt32* ptr = reinterpret_cast(buffer); + UInt32* ptr = static_cast(buffer); ptr[i] = value; } diff --git a/src/Nazara/Utility/SimpleTextDrawer.cpp b/src/Nazara/Utility/SimpleTextDrawer.cpp index 6f7393dd9..cc2130c05 100644 --- a/src/Nazara/Utility/SimpleTextDrawer.cpp +++ b/src/Nazara/Utility/SimpleTextDrawer.cpp @@ -11,7 +11,9 @@ namespace Nz SimpleTextDrawer::SimpleTextDrawer() : m_color(Color::White), m_style(TextStyle_Regular), - m_glyphUpdated(false) + m_colorUpdated(true), + m_glyphUpdated(true), + m_characterSize(24) { SetFont(Font::GetDefault()); } @@ -20,6 +22,7 @@ namespace Nz m_color(drawer.m_color), m_text(drawer.m_text), m_style(drawer.m_style), + m_colorUpdated(false), m_glyphUpdated(false), m_characterSize(drawer.m_characterSize) { @@ -31,9 +34,21 @@ namespace Nz operator=(std::move(drawer)); } - SimpleTextDrawer::~SimpleTextDrawer() = default; + void SimpleTextDrawer::AppendText(const String& str) + { + m_text.Append(str); + if (m_glyphUpdated) + GenerateGlyphs(str); + } + + void SimpleTextDrawer::Clear() + { + m_text.Clear(true); + ClearGlyphs(); + } + const Rectui& SimpleTextDrawer::GetBounds() const { if (!m_glyphUpdated) @@ -59,13 +74,7 @@ namespace Nz Font* SimpleTextDrawer::GetFont(unsigned int index) const { - #if NAZARA_UTILITY_SAFE - if (index > 0) - { - NazaraError("Font index out of range (" + String::Number(index) + " >= 1)"); - return nullptr; - } - #endif + NazaraAssert(index == 0, "Font index out of range"); return m_font; } @@ -79,6 +88,8 @@ namespace Nz { if (!m_glyphUpdated) UpdateGlyphs(); + else if (!m_colorUpdated) + UpdateGlyphColor(); return m_glyphs[index]; } @@ -112,7 +123,7 @@ namespace Nz { m_color = color; - m_glyphUpdated = false; + m_colorUpdated = false; } void SimpleTextDrawer::SetFont(Font* font) @@ -151,6 +162,7 @@ namespace Nz m_style = drawer.m_style; m_text = drawer.m_text; + m_colorUpdated = false; m_glyphUpdated = false; SetFont(drawer.m_font); @@ -162,6 +174,7 @@ namespace Nz DisconnectFontSlots(); m_bounds = std::move(drawer.m_bounds); + m_colorUpdated = std::move(drawer.m_colorUpdated); m_characterSize = std::move(drawer.m_characterSize); m_color = std::move(drawer.m_color); m_glyphs = std::move(drawer.m_glyphs); @@ -199,6 +212,17 @@ namespace Nz return drawer; } + void SimpleTextDrawer::ClearGlyphs() const + { + m_bounds.MakeZero(); + m_colorUpdated = true; + m_drawPos.Set(0, m_characterSize); //< Our draw "cursor" + m_glyphs.clear(); + m_glyphUpdated = true; + m_previousCharacter = 0; + m_workingBounds.MakeZero(); //< Compute bounds as float to speedup bounds computation (as casting between floats and integers is costly) + } + void SimpleTextDrawer::ConnectFontSlots() { m_atlasChangedSlot.Connect(m_font->OnFontAtlasChanged, this, &SimpleTextDrawer::OnFontInvalidated); @@ -215,6 +239,109 @@ namespace Nz m_glyphCacheClearedSlot.Disconnect(); } + void SimpleTextDrawer::GenerateGlyphs(const String& text) const + { + if (text.IsEmpty()) + return; + + ///TODO: Allow iteration on Unicode characters without allocating any buffer + std::u32string characters = text.GetUtf32String(); + if (characters.empty()) + { + NazaraError("Invalid character set"); + return; + } + + const Font::SizeInfo& sizeInfo = m_font->GetSizeInfo(m_characterSize); + + m_glyphs.reserve(m_glyphs.size() + characters.size()); + for (char32_t character : characters) + { + if (m_previousCharacter != 0) + m_drawPos.x += m_font->GetKerning(m_characterSize, m_previousCharacter, character); + + m_previousCharacter = character; + + bool whitespace = true; + switch (character) + { + case ' ': + m_drawPos.x += sizeInfo.spaceAdvance; + break; + + case '\n': + m_drawPos.x = 0; + m_drawPos.y += sizeInfo.lineHeight; + break; + + case '\t': + m_drawPos.x += sizeInfo.spaceAdvance * 4; + break; + + default: + whitespace = false; + break; + } + + if (whitespace) + continue; // White spaces are blanks and invisible, move the draw position and skip the rest + + const Font::Glyph& fontGlyph = m_font->GetGlyph(m_characterSize, m_style, character); + if (!fontGlyph.valid) + continue; // Glyph failed to load, just skip it (can't do much) + + Glyph glyph; + glyph.atlas = m_font->GetAtlas()->GetLayer(fontGlyph.layerIndex); + glyph.atlasRect = fontGlyph.atlasRect; + glyph.color = m_color; + glyph.flipped = fontGlyph.flipped; + + int advance = fontGlyph.advance; + + Rectf bounds(fontGlyph.aabb); + bounds.x += m_drawPos.x; + bounds.y += m_drawPos.y; + + if (fontGlyph.requireFauxBold) + { + // Let's simulate bold by enlarging the glyph (not a neat idea, but should work) + Vector2f center = bounds.GetCenter(); + + // Enlarge by 10% + bounds.width *= 1.1f; + bounds.height *= 1.1f; + + // Replace it at the correct height + Vector2f offset(bounds.GetCenter() - center); + bounds.y -= offset.y; + + // Adjust advance (+10%) + advance += advance / 10; + } + + // We "lean" the glyph to simulate italics style + float italic = (fontGlyph.requireFauxItalic) ? 0.208f : 0.f; + float italicTop = italic * bounds.y; + float italicBottom = italic * bounds.GetMaximum().y; + + glyph.corners[0].Set(bounds.x - italicTop, bounds.y); + glyph.corners[1].Set(bounds.x + bounds.width - italicTop, bounds.y); + glyph.corners[2].Set(bounds.x - italicBottom, bounds.y + bounds.height); + glyph.corners[3].Set(bounds.x + bounds.width - italicBottom, bounds.y + bounds.height); + + if (!m_workingBounds.IsValid()) + m_workingBounds.Set(glyph.corners[0]); + + for (unsigned int i = 0; i < 4; ++i) + m_workingBounds.ExtendTo(glyph.corners[i]); + + m_drawPos.x += advance; + m_glyphs.push_back(glyph); + } + + m_bounds.Set(Rectf(std::floor(m_workingBounds.x), std::floor(m_workingBounds.y), std::ceil(m_workingBounds.width), std::ceil(m_workingBounds.height))); + } + void SimpleTextDrawer::OnFontAtlasLayerChanged(const Font* font, AbstractImage* oldLayer, AbstractImage* newLayer) { NazaraUnused(font); @@ -228,7 +355,7 @@ namespace Nz #endif // Update atlas layer pointer - // Note: This can happend while updating + // Note: This can happen while updating for (Glyph& glyph : m_glyphs) { if (glyph.atlas == oldLayer) @@ -267,128 +394,19 @@ namespace Nz SetFont(nullptr); } + void SimpleTextDrawer::UpdateGlyphColor() const + { + for (Glyph& glyph : m_glyphs) + glyph.color = m_color; + + m_colorUpdated = true; + } + void SimpleTextDrawer::UpdateGlyphs() const { - m_bounds.MakeZero(); - m_glyphs.clear(); - m_glyphUpdated = true; + NazaraAssert(m_font && m_font->IsValid(), "Invalid font"); - #if NAZARA_UTILITY_SAFE - if (!m_font || !m_font->IsValid()) - { - NazaraError("Invalid font"); - return; - } - #endif - - if (m_text.IsEmpty()) - return; - - ///TODO: Itération UTF-8 => UTF-32 sans allocation de buffer (Exposer utf8cpp ?) - std::u32string characters = m_text.GetUtf32String(); - if (characters.empty()) - { - NazaraError("Invalid character set"); - return; - } - - const Font::SizeInfo& sizeInfo = m_font->GetSizeInfo(m_characterSize); - - // "Curseur" de dessin - Vector2ui drawPos(0, m_characterSize); - - // On calcule les bornes en flottants pour accélérer les calculs (il est coûteux de changer de type trop souvent) - bool firstGlyph = true; - Rectf textBounds = Rectf::Zero(); - UInt32 previousCharacter = 0; - - m_glyphs.reserve(characters.size()); - for (char32_t character : characters) - { - if (previousCharacter != 0) - drawPos.x += m_font->GetKerning(m_characterSize, previousCharacter, character); - - previousCharacter = character; - - bool whitespace = true; - switch (character) - { - case ' ': - drawPos.x += sizeInfo.spaceAdvance; - break; - - case '\n': - drawPos.x = 0; - drawPos.y += sizeInfo.lineHeight; - break; - - case '\t': - drawPos.x += sizeInfo.spaceAdvance*4; - break; - - default: - whitespace = false; - break; - } - - if (whitespace) - continue; // Inutile d'avoir un glyphe pour un espace blanc - - const Font::Glyph& fontGlyph = m_font->GetGlyph(m_characterSize, m_style, character); - if (!fontGlyph.valid) - continue; // Le glyphe n'a pas été correctement chargé, que pouvons-nous faire d'autre que le passer - - Glyph glyph; - glyph.atlas = m_font->GetAtlas()->GetLayer(fontGlyph.layerIndex); - glyph.atlasRect = fontGlyph.atlasRect; - glyph.color = m_color; - glyph.flipped = fontGlyph.flipped; - - int advance = fontGlyph.advance; - - Rectf bounds(fontGlyph.aabb); - bounds.x += drawPos.x; - bounds.y += drawPos.y; - - if (fontGlyph.requireFauxBold) - { - // On va agrandir le glyphe pour simuler le gras (idée moisie, mais idée quand même) - Vector2f center = bounds.GetCenter(); - - bounds.width *= 1.1f; - bounds.height *= 1.1f; - - // On le replace à la bonne hauteur - Vector2f offset(bounds.GetCenter() - center); - bounds.y -= offset.y; - - // On ajuste l'espacement - advance += advance/10; - } - - // On "penche" le glyphe pour obtenir un semblant d'italique - float italic = (fontGlyph.requireFauxItalic) ? 0.208f : 0.f; - float italicTop = italic * bounds.y; - float italicBottom = italic * bounds.GetMaximum().y; - - glyph.corners[0].Set(bounds.x - italicTop, bounds.y); - glyph.corners[1].Set(bounds.x + bounds.width - italicTop, bounds.y); - glyph.corners[2].Set(bounds.x - italicBottom, bounds.y + bounds.height); - glyph.corners[3].Set(bounds.x + bounds.width - italicBottom, bounds.y + bounds.height); - - if (firstGlyph) - { - textBounds.Set(glyph.corners[0]); - firstGlyph = false; - } - - for (unsigned int i = 0; i < 4; ++i) - textBounds.ExtendTo(glyph.corners[i]); - - drawPos.x += advance; - m_glyphs.push_back(glyph); - } - - m_bounds.Set(Rectf(std::floor(textBounds.x), std::floor(textBounds.y), std::ceil(textBounds.width), std::ceil(textBounds.height))); + ClearGlyphs(); + GenerateGlyphs(m_text); } } diff --git a/src/Nazara/Utility/Utility.cpp b/src/Nazara/Utility/Utility.cpp index cc4687197..17a1bd049 100644 --- a/src/Nazara/Utility/Utility.cpp +++ b/src/Nazara/Utility/Utility.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include namespace Nz @@ -112,7 +113,8 @@ namespace Nz Loaders::RegisterFreeType(); // Image - Loaders::RegisterSTB(); // Loader générique (STB) + Loaders::RegisterSTBLoader(); // Generic loader (STB) + Loaders::RegisterSTBSaver(); // Generic saver (STB) /// Loaders spécialisés // Animation @@ -155,7 +157,8 @@ namespace Nz Loaders::UnregisterMD5Anim(); Loaders::UnregisterMD5Mesh(); Loaders::UnregisterPCX(); - Loaders::UnregisterSTB(); + Loaders::UnregisterSTBLoader(); + Loaders::UnregisterSTBSaver(); Window::Uninitialize(); VertexDeclaration::Uninitialize(); diff --git a/src/Nazara/Utility/Win32/InputImpl.cpp b/src/Nazara/Utility/Win32/InputImpl.cpp index 443e38855..ba604deb8 100644 --- a/src/Nazara/Utility/Win32/InputImpl.cpp +++ b/src/Nazara/Utility/Win32/InputImpl.cpp @@ -213,7 +213,7 @@ namespace Nz Vector2i EventImpl::GetMousePosition(const Window& relativeTo) { - HWND handle = reinterpret_cast(relativeTo.GetHandle()); + HWND handle = static_cast(relativeTo.GetHandle()); if (handle) { POINT pos; @@ -283,7 +283,7 @@ namespace Nz void EventImpl::SetMousePosition(int x, int y, const Window& relativeTo) { - HWND handle = reinterpret_cast(relativeTo.GetHandle()); + HWND handle = static_cast(relativeTo.GetHandle()); if (handle) { POINT pos = {x, y}; diff --git a/src/Nazara/Utility/Win32/WindowImpl.cpp b/src/Nazara/Utility/Win32/WindowImpl.cpp index e910e8acc..2bf9c9d02 100644 --- a/src/Nazara/Utility/Win32/WindowImpl.cpp +++ b/src/Nazara/Utility/Win32/WindowImpl.cpp @@ -193,7 +193,7 @@ namespace Nz bool WindowImpl::Create(WindowHandle handle) { - m_handle = reinterpret_cast(handle); + m_handle = static_cast(handle); if (!m_handle || !IsWindow(m_handle)) { @@ -342,7 +342,7 @@ namespace Nz #endif if (cursor != WindowCursor_None) - m_cursor = reinterpret_cast(LoadImage(nullptr, windowsCursors[cursor], IMAGE_CURSOR, 0, 0, LR_SHARED)); + m_cursor = static_cast(LoadImage(nullptr, windowsCursors[cursor], IMAGE_CURSOR, 0, 0, LR_SHARED)); else m_cursor = nullptr; @@ -1125,7 +1125,7 @@ namespace Nz WindowImpl* me; if (message == WM_CREATE) { - me = reinterpret_cast(reinterpret_cast(lParam)->lpCreateParams); + me = static_cast(reinterpret_cast(lParam)->lpCreateParams); SetWindowLongPtr(window, GWL_USERDATA, reinterpret_cast(me)); } else diff --git a/src/Nazara/Utility/Window.cpp b/src/Nazara/Utility/Window.cpp index fec1615af..e246144d2 100644 --- a/src/Nazara/Utility/Window.cpp +++ b/src/Nazara/Utility/Window.cpp @@ -28,53 +28,6 @@ namespace Nz Window* fullscreenWindow = nullptr; } - Window::Window() : - #if NAZARA_UTILITY_THREADED_WINDOW - m_impl(nullptr), - m_eventListener(true), - m_waitForEvent(false) - #else - m_impl(nullptr) - #endif - { - } - - Window::Window(VideoMode mode, const String& title, UInt32 style) : - #if NAZARA_UTILITY_THREADED_WINDOW - m_impl(nullptr), - m_eventListener(true), - m_waitForEvent(false) - #else - m_impl(nullptr) - #endif - { - ErrorFlags flags(ErrorFlag_ThrowException, true); - Create(mode, title, style); - } - - Window::Window(WindowHandle handle) : - #if NAZARA_UTILITY_THREADED_WINDOW - m_impl(nullptr), - m_eventListener(true), - m_waitForEvent(false) - #else - m_impl(nullptr) - #endif - { - ErrorFlags flags(ErrorFlag_ThrowException, true); - Create(handle); - } - - Window::~Window() - { - Destroy(); - } - - void Window::Close() - { - m_closed = true; // On retarde la fermeture jusqu'au prochain IsOpen - } - bool Window::Create(VideoMode mode, const String& title, UInt32 style) { // Si la fenêtre est déjà ouverte, nous conservons sa position @@ -317,27 +270,6 @@ namespace Nz return m_impl->HasFocus(); } - bool Window::IsOpen(bool checkClosed) - { - if (m_impl) - { - if (m_closed && checkClosed) - { - Destroy(); - return false; - } - else - return true; - } - else - return false; - } - - bool Window::IsOpen() const - { - return m_impl != nullptr; - } - bool Window::IsMinimized() const { #if NAZARA_UTILITY_SAFE @@ -351,11 +283,6 @@ namespace Nz return m_impl->IsMinimized(); } - bool Window::IsValid() const - { - return m_impl != nullptr; - } - bool Window::IsVisible() const { #if NAZARA_UTILITY_SAFE @@ -710,28 +637,6 @@ namespace Nz m_impl->IgnoreNextMouseEvent(mouseX, mouseY); } - void Window::PushEvent(const WindowEvent& event) - { - #if NAZARA_UTILITY_THREADED_WINDOW - m_eventMutex.Lock(); - #endif - - m_events.push(event); - if (event.type == WindowEventType_Resized) - OnWindowResized(); - - #if NAZARA_UTILITY_THREADED_WINDOW - m_eventMutex.Unlock(); - - if (m_waitForEvent) - { - m_eventConditionMutex.Lock(); - m_eventCondition.Signal(); - m_eventConditionMutex.Unlock(); - } - #endif - } - bool Window::Initialize() { return WindowImpl::Initialize(); diff --git a/src/Nazara/Utility/X11/Display.cpp b/src/Nazara/Utility/X11/Display.cpp index d61543dfd..63fbb65ac 100644 --- a/src/Nazara/Utility/X11/Display.cpp +++ b/src/Nazara/Utility/X11/Display.cpp @@ -136,7 +136,6 @@ namespace Nz OpenEWMHConnection(sharedConnection); } - NazaraNotice("Initialized: Utility module"); return true; } @@ -208,8 +207,6 @@ namespace Nz xcb_disconnect(sharedConnection); } - - NazaraNotice("Uninitialized: Display module"); } xcb_window_t X11::XCBDefaultRootWindow(xcb_connection_t* connection) diff --git a/src/Nazara/Utility/X11/Display.hpp b/src/Nazara/Utility/X11/Display.hpp index 95177e2d2..88f80f2ee 100644 --- a/src/Nazara/Utility/X11/Display.hpp +++ b/src/Nazara/Utility/X11/Display.hpp @@ -17,7 +17,7 @@ namespace Nz { class String; - class X11 + class NAZARA_UTILITY_API X11 { public: X11() = delete; diff --git a/tests/Engine/Core/AbstractHash.cpp b/tests/Engine/Core/AbstractHash.cpp new file mode 100644 index 000000000..f5ee57d30 --- /dev/null +++ b/tests/Engine/Core/AbstractHash.cpp @@ -0,0 +1,27 @@ +#include +#include + +#include + +#include + +SCENARIO("AbstractHash", "[CORE][ABSTRACTHASH]") +{ + GIVEN("The hash SHA512") + { + std::unique_ptr SHA512 = Nz::AbstractHash::Get(Nz::HashType_SHA512); + SHA512->Begin(); + + WHEN("We introduce data") + { + std::array array{ 0, 1, 2, 3 }; + SHA512->Append(array.data(), array.size()); + + THEN("We ask for the bytearray") + { + Nz::ByteArray byteArray = SHA512->End(); + REQUIRE(byteArray.GetSize() == SHA512->GetDigestLength()); + } + } + } +} diff --git a/tests/Engine/Core/AlgorithmCore.cpp b/tests/Engine/Core/AlgorithmCore.cpp new file mode 100644 index 000000000..8aff5cb24 --- /dev/null +++ b/tests/Engine/Core/AlgorithmCore.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include + +#include + +TEST_CASE("Apply", "[CORE][ALGORITHM]") +{ + SECTION("Apply lambda to two vector2") + { + Nz::Vector2 vector = Nz::Vector2::Unit(); + auto lambda = [](const Nz::Vector2& vec1, const Nz::Vector2& vec2) + { + return vec1 + vec2; + }; + + Nz::Vector2 result = Nz::Apply(lambda, std::make_tuple(vector, vector)); + + REQUIRE(result == (Nz::Vector2::Unit() * 2)); + } + + SECTION("Apply member function to vector2") + { + Nz::Vector2 vector = Nz::Vector2::Unit(); + + int result = Nz::Apply(vector, &Nz::Vector2::Distance, std::make_tuple(vector)); + + REQUIRE(result == 0); + } +} + +TEST_CASE("ComputeHash", "[CORE][ALGORITHM]") +{ + SECTION("Compute hash of '0'") + { + auto result = Nz::ComputeHash(Nz::HashType_SHA512, "1234"); + REQUIRE(result.ToHex().ToUpper() == "D404559F602EAB6FD602AC7680DACBFAADD13630335E951F097AF3900E9DE176B6DB28512F2E000B9D04FBA5133E8B1C6E8DF59DB3A8AB9D60BE4B97CC9E81DB"); + } +} diff --git a/tests/Engine/Core/Bitset.cpp b/tests/Engine/Core/Bitset.cpp new file mode 100644 index 000000000..76c35c886 --- /dev/null +++ b/tests/Engine/Core/Bitset.cpp @@ -0,0 +1,117 @@ +#include +#include + +#include + +SCENARIO("Bitset", "[CORE][BITSET]") +{ + GIVEN("Allocate and constructor") + { + Nz::Bitset<> bitset(3); + + THEN("Capacity is 3 and size is 3") + { + REQUIRE(bitset.GetSize() == 3); + REQUIRE(bitset.GetCapacity() >= 3); + } + } + + GIVEN("Iterator and default constructor") + { + Nz::String anotherDataString("0101"); + Nz::Bitset<> defaultByte; + Nz::Bitset<> anotherData(anotherDataString.GetConstBuffer()); + + WHEN("We assign 'anotherData'") + { + defaultByte = anotherDataString; + REQUIRE(anotherData == defaultByte); + REQUIRE(defaultByte.GetSize() == 4); + REQUIRE(defaultByte.GetCapacity() >= 4); + REQUIRE(anotherData.GetSize() == 4); + REQUIRE(anotherData.GetCapacity() >= 4); + } + } + + GIVEN("Copy and Move constructor") + { + Nz::Bitset<> originalArray(3, true); + + WHEN("We copy") + { + Nz::Bitset<> copyBitset(originalArray); + + THEN("We get a copy") + { + REQUIRE(copyBitset == originalArray); + + AND_WHEN("We modify one") + { + for (auto i = 0; i < copyBitset.GetSize(); ++i) + copyBitset[i] = false; + + THEN("They are no more equal") + { + REQUIRE(copyBitset != originalArray); + REQUIRE(copyBitset == Nz::Bitset<>(3, false)); + } + } + } + } + + WHEN("We move") + { + Nz::Bitset<> moveBitset(std::move(originalArray)); + + THEN("These results are expected") + { + REQUIRE(moveBitset == Nz::Bitset<>(3, true)); + REQUIRE(originalArray.GetCapacity() == 0); + } + } + } + + GIVEN("Three bitsets") + { + Nz::Bitset<> first("01001"); + Nz::Bitset<> second("10110"); + Nz::Bitset<> third; + + WHEN("We swap first and third, then second and third and finally third and first") + { + Nz::Bitset<> oldFirst(first); + Nz::Bitset<> oldSecond(second); + + first.Swap(third); + std::swap(second, third); + third.Swap(first); + + THEN("First and second have been swapped and third is still empty.") + { + REQUIRE(oldFirst == second); + REQUIRE(oldSecond == first); + REQUIRE(third.GetSize() == 0); + } + } + } + + GIVEN("Two bitsets") + { + Nz::Bitset<> first("01001"); + Nz::Bitset<> second("10111"); + + WHEN("We perform operators") + { + Nz::Bitset<> andBitset = first & second; + Nz::Bitset<> orBitset = first | second; + Nz::Bitset<> xorBitset = first ^ second; + + THEN("They should operate as logical operators") + { + REQUIRE(andBitset == Nz::Bitset<>("00001")); + REQUIRE(orBitset == Nz::Bitset<>("11111")); + REQUIRE(xorBitset == Nz::Bitset<>("11110")); + } + } + } +} diff --git a/tests/Engine/Core/File.cpp b/tests/Engine/Core/File.cpp index c00af74d1..2a87c8495 100644 --- a/tests/Engine/Core/File.cpp +++ b/tests/Engine/Core/File.cpp @@ -9,7 +9,7 @@ SCENARIO("File", "[CORE][FILE]") { Nz::File file("Test File.txt", Nz::OpenMode_ReadWrite); REQUIRE(file.GetDirectory() == Nz::Directory::GetCurrent() + NAZARA_DIRECTORY_SEPARATOR); - CHECK(file.IsOpen()); + REQUIRE(file.IsOpen()); THEN("We are allowed to write 3 times 'Test String'") { @@ -50,4 +50,38 @@ SCENARIO("File", "[CORE][FILE]") } } } + + GIVEN("The test file") + { + REQUIRE(Nz::File::Exists("resources/Engine/Core/FileTest.txt")); + + Nz::File fileTest("resources/Engine/Core/FileTest.txt", Nz::OpenMode_ReadOnly | Nz::OpenMode_Text); + + WHEN("We read the first line of the file") + { + REQUIRE(fileTest.IsOpen()); + Nz::String content = fileTest.ReadLine(); + + THEN("The content must be 'Test'") + { + REQUIRE(content == "Test"); + } + } + } + + GIVEN("Nothing") + { + WHEN("We get the absolute path of something containing relative path") + { + Nz::String containingDot = "/resources/Spaceship/./spaceship.mtl"; + Nz::String containingDoubleDot = "/resources/Spaceship/../Spaceship/spaceship.mtl"; + + THEN("The relative positioning should disappear") + { + Nz::String containingNoMoreDot = "/resources/Spaceship/spaceship.mtl"; + REQUIRE(Nz::File::AbsolutePath(containingDot) == containingNoMoreDot); + REQUIRE(Nz::File::AbsolutePath(containingDoubleDot) == containingNoMoreDot); + } + } + } } diff --git a/tests/Engine/Core/MemoryPool.cpp b/tests/Engine/Core/MemoryPool.cpp new file mode 100644 index 000000000..6a3c230c4 --- /dev/null +++ b/tests/Engine/Core/MemoryPool.cpp @@ -0,0 +1,28 @@ +#include +#include + +#include + +SCENARIO("MemoryPool", "[CORE][MEMORYPOOL]") +{ + GIVEN("A MemoryPool to contain one Nz::Vector2") + { + Nz::MemoryPool memoryPool(sizeof(Nz::Vector2), 1, false); + + WHEN("We construct a Nz::Vector2") + { + Nz::Vector2* vector2 = memoryPool.New>(1, 2); + + THEN("Memory is available") + { + vector2->x = 3; + REQUIRE(*vector2 == Nz::Vector2(3, 2)); + } + + THEN("We can destroy the vector2") + { + memoryPool.Delete(vector2); + } + } + } +} diff --git a/tests/Engine/Core/ObjectRef.cpp b/tests/Engine/Core/ObjectRef.cpp new file mode 100644 index 000000000..e44e5f1ff --- /dev/null +++ b/tests/Engine/Core/ObjectRef.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include + +SCENARIO("ObjectRef", "[CORE][OBJECTREF]") +{ + GIVEN("A ObjectRef") + { + Nz::ObjectRef objectRef; + + WHEN("We have two objectRef handling the same object") + { + Nz::Font font; + + objectRef = &font; + Nz::ObjectRef otherRef(&font); + + THEN("Pointers the same") + { + REQUIRE(objectRef.IsValid()); + REQUIRE(otherRef.IsValid()); + } + + objectRef.Reset(nullptr); + } + + WHEN("We assign it to a simple font") + { + Nz::Font font; + + THEN("Release suppress the reference to the object") + { + objectRef.Reset(&font); + objectRef.Release(); + + REQUIRE(!objectRef.IsValid()); + } + } + } +} diff --git a/tests/Engine/Core/ParameterList.cpp b/tests/Engine/Core/ParameterList.cpp new file mode 100644 index 000000000..ef2729833 --- /dev/null +++ b/tests/Engine/Core/ParameterList.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include + +SCENARIO("ParameterList", "[CORE][PARAMETERLIST]") +{ + GIVEN("An empty ParameterList") + { + Nz::ParameterList parameterList; + + WHEN("We add String 'string'") + { + Nz::String string("string"); + parameterList.SetParameter("string", string); + + THEN("We can get it back") + { + Nz::String newString; + REQUIRE(parameterList.GetStringParameter("string", &newString)); + REQUIRE(newString == string); + } + } + + WHEN("We add Float '3.f'") + { + float fl = 3.f; + parameterList.SetParameter("float", fl); + + THEN("We can get it back") + { + float newFl; + REQUIRE(parameterList.GetFloatParameter("float", &newFl)); + REQUIRE(newFl == fl); + } + } + + WHEN("We add Pointer to stack value") + { + int stackValue = 3; + void* ptrToStackValue = &stackValue; // Ugly conversion + parameterList.SetParameter("ptr", ptrToStackValue); + + THEN("We can get it back") + { + void* newPtrToStackValue = nullptr; + REQUIRE(parameterList.GetPointerParameter("ptr", &newPtrToStackValue)); + REQUIRE(newPtrToStackValue == ptrToStackValue); + } + } + } +} diff --git a/tests/Engine/Core/PrimitiveList.cpp b/tests/Engine/Core/PrimitiveList.cpp new file mode 100644 index 000000000..e0098a75e --- /dev/null +++ b/tests/Engine/Core/PrimitiveList.cpp @@ -0,0 +1,44 @@ +#include +#include + +SCENARIO("PrimitiveList", "[CORE][PRIMITIVELIST]") +{ + GIVEN("An empty PrimitiveList") + { + Nz::PrimitiveList primitiveList; + + WHEN("We add two primitives") + { + float size = 1.f; + unsigned int subdivision = 1; + unsigned int recursionLevel = 1; + Nz::Matrix4f identity = Nz::Matrix4f::Identity(); + + primitiveList.AddCubicSphere(size, subdivision, identity); + primitiveList.AddBox(Nz::Vector3f(size), Nz::Vector3ui(subdivision), identity); + primitiveList.AddIcoSphere(size, subdivision, identity); + + THEN("There must be two items") + { + REQUIRE(primitiveList.GetSize() == 3); + } + + THEN("The first one is the cubic sphere") + { + REQUIRE(primitiveList(0).type == Nz::PrimitiveType_Sphere); + REQUIRE(primitiveList(0).sphere.type == Nz::SphereType_Cubic); + } + + THEN("The second one is the box") + { + REQUIRE(primitiveList(1).type == Nz::PrimitiveType_Box); + } + + THEN("The third one is the ico sphere") + { + REQUIRE(primitiveList(2).type == Nz::PrimitiveType_Sphere); + REQUIRE(primitiveList(2).sphere.type == Nz::SphereType_Ico); + } + } + } +} diff --git a/tests/Engine/Core/RefCounted.cpp b/tests/Engine/Core/RefCounted.cpp new file mode 100644 index 000000000..0201f0beb --- /dev/null +++ b/tests/Engine/Core/RefCounted.cpp @@ -0,0 +1,29 @@ +#include +#include + +SCENARIO("RefCounted", "[CORE][REFCOUNTED]") +{ + GIVEN("A refcounted persistent") + { + Nz::RefCounted refCounted; + REQUIRE(refCounted.IsPersistent() == true); + + WHEN("We add a reference to this persistent object") + { + THEN("Number of references should be one") + { + refCounted.AddReference(); + REQUIRE(refCounted.GetReferenceCount() == 1); + REQUIRE(refCounted.RemoveReference() == false); + } + + AND_THEN("We suppress the reference, object is still alive") + { + refCounted.AddReference(); + REQUIRE(refCounted.IsPersistent()); + REQUIRE(refCounted.RemoveReference() == false); + REQUIRE(refCounted.GetReferenceCount() == 0); + } + } + } +} diff --git a/tests/Engine/Core/Serialization.cpp b/tests/Engine/Core/Serialization.cpp new file mode 100644 index 000000000..a4d8ff75e --- /dev/null +++ b/tests/Engine/Core/Serialization.cpp @@ -0,0 +1,262 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include + +SCENARIO("Serialization", "[CORE][SERIALIZATION]") +{ + GIVEN("A context of serialization") + { + std::array datas; // The array must be bigger than any of the serializable classes + Nz::MemoryView stream(datas.data(), datas.size()); + + Nz::SerializationContext context; + context.stream = &stream; + + WHEN("We serialize basic types") + { + THEN("Arithmetical types") + { + context.stream->SetCursorPos(0); + REQUIRE(Serialize(context, 3)); + int value = 0; + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &value)); + REQUIRE(value == 3); + } + + THEN("Boolean type") + { + context.stream->SetCursorPos(0); + REQUIRE(Serialize(context, true)); + context.stream->SetCursorPos(0); + bool value = false; + REQUIRE(Unserialize(context, &value)); + REQUIRE(value == true); + } + } + + WHEN("We serialize mathematical classes") + { + THEN("BoudingVolume") + { + context.stream->SetCursorPos(0); + Nz::BoundingVolumef nullVolume = Nz::BoundingVolumef::Null(); + Nz::BoundingVolumef copy(nullVolume); + REQUIRE(Serialize(context, nullVolume)); + nullVolume = Nz::BoundingVolumef::Infinite(); + REQUIRE(nullVolume != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &nullVolume)); + REQUIRE(nullVolume == copy); + } + + THEN("Box") + { + context.stream->SetCursorPos(0); + Nz::Boxf zeroBox = Nz::Boxf::Zero(); + Nz::Boxf copy(zeroBox); + REQUIRE(Serialize(context, zeroBox)); + zeroBox = Nz::Boxf(1, 1, 1, 1, 1, 1); + REQUIRE(zeroBox != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &zeroBox)); + REQUIRE(zeroBox == copy); + } + + THEN("EulerAngles") + { + context.stream->SetCursorPos(0); + Nz::EulerAnglesf zeroEuler = Nz::EulerAnglesf::Zero(); + Nz::EulerAnglesf copy(zeroEuler); + REQUIRE(Serialize(context, zeroEuler)); + zeroEuler = Nz::EulerAnglesf(10, 24, 6); // Random values + REQUIRE(zeroEuler != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &zeroEuler)); + REQUIRE(zeroEuler == copy); + } + + THEN("Frustum") + { + context.stream->SetCursorPos(0); + Nz::Frustumf frustum; + frustum.Build(10, 10, 10, 100, Nz::Vector3f::UnitX(), Nz::Vector3f::UnitZ()); // Random values + Nz::Frustumf copy(frustum); + REQUIRE(Serialize(context, frustum)); + frustum.Build(50, 40, 20, 100, Nz::Vector3f::UnitX(), Nz::Vector3f::UnitZ()); + for (unsigned int i = 0; i <= Nz::BoxCorner_Max; ++i) + REQUIRE(frustum.GetCorner(static_cast(i)) != copy.GetCorner(static_cast(i))); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &frustum)); + for (unsigned int i = 0; i <= Nz::BoxCorner_Max; ++i) + REQUIRE(frustum.GetCorner(static_cast(i)) == copy.GetCorner(static_cast(i))); + } + + THEN("Matrix4") + { + context.stream->SetCursorPos(0); + Nz::Matrix4f zeroMatrix = Nz::Matrix4f::Zero(); + Nz::Matrix4f copy(zeroMatrix); + REQUIRE(Serialize(context, zeroMatrix)); + zeroMatrix = Nz::Matrix4f::Identity(); // Random values + REQUIRE(zeroMatrix != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &zeroMatrix)); + REQUIRE(zeroMatrix == copy); + } + + THEN("OrientedBox") + { + context.stream->SetCursorPos(0); + Nz::OrientedBoxf zeroOBB = Nz::OrientedBoxf::Zero(); + Nz::OrientedBoxf copy(zeroOBB); + REQUIRE(Serialize(context, zeroOBB)); + zeroOBB = Nz::OrientedBoxf(1, 1, 1, 1, 1, 1); // Random values + REQUIRE(zeroOBB != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &zeroOBB)); + REQUIRE(zeroOBB == copy); + } + + THEN("Plane") + { + context.stream->SetCursorPos(0); + Nz::Planef planeXY = Nz::Planef::XY(); + Nz::Planef copy(planeXY); + REQUIRE(Serialize(context, planeXY)); + planeXY = Nz::Planef::YZ(); + REQUIRE(planeXY != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &planeXY)); + REQUIRE(planeXY == copy); + } + + THEN("Quaternion") + { + context.stream->SetCursorPos(0); + Nz::Quaternionf quaternionIdentity = Nz::Quaternionf::Identity(); + Nz::Quaternionf copy(quaternionIdentity); + REQUIRE(Serialize(context, quaternionIdentity)); + quaternionIdentity = Nz::Quaternionf::Zero(); + REQUIRE(quaternionIdentity != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &quaternionIdentity)); + REQUIRE(quaternionIdentity == copy); + } + + THEN("Ray") + { + context.stream->SetCursorPos(0); + Nz::Rayf axisX = Nz::Rayf::AxisX(); + Nz::Rayf copy(axisX); + REQUIRE(Serialize(context, axisX)); + axisX = Nz::Rayf::AxisY(); + REQUIRE(axisX != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &axisX)); + REQUIRE(axisX == copy); + } + + THEN("Rect") + { + context.stream->SetCursorPos(0); + Nz::Rectf zeroRect = Nz::Rectf::Zero(); + Nz::Rectf copy(zeroRect); + REQUIRE(Serialize(context, zeroRect)); + zeroRect = Nz::Rectf(1, 1, 1, 1); // Random values + REQUIRE(zeroRect != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &zeroRect)); + REQUIRE(zeroRect == copy); + } + + THEN("Sphere") + { + context.stream->SetCursorPos(0); + Nz::Spheref zeroSphere = Nz::Spheref::Zero(); + Nz::Spheref copy(zeroSphere); + REQUIRE(Serialize(context, zeroSphere)); + zeroSphere = Nz::Spheref::Unit(); + REQUIRE(zeroSphere != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &zeroSphere)); + REQUIRE(zeroSphere == copy); + } + + THEN("Vector2") + { + context.stream->SetCursorPos(0); + Nz::Vector2f unitX = Nz::Vector2f::UnitX(); + Nz::Vector2f copy(unitX); + REQUIRE(Serialize(context, unitX)); + unitX = Nz::Vector2f::UnitY(); + REQUIRE(unitX != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &unitX)); + REQUIRE(unitX == copy); + } + + THEN("Vector3") + { + context.stream->SetCursorPos(0); + Nz::Vector3f unitX = Nz::Vector3f::UnitX(); + Nz::Vector3f copy(unitX); + REQUIRE(Serialize(context, unitX)); + unitX = Nz::Vector3f::UnitY(); + REQUIRE(unitX != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &unitX)); + REQUIRE(unitX == copy); + } + + THEN("Vector4") + { + context.stream->SetCursorPos(0); + Nz::Vector4f unitX = Nz::Vector4f::UnitX(); + Nz::Vector4f copy(unitX); + REQUIRE(Serialize(context, unitX)); + unitX = Nz::Vector4f::UnitY(); + REQUIRE(unitX != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &unitX)); + REQUIRE(unitX == copy); + } + } + + WHEN("We serialize core classes") + { + THEN("Color") + { + context.stream->SetCursorPos(0); + Nz::Color red = Nz::Color::Red; + Nz::Color copy(red); + REQUIRE(Serialize(context, red)); + red = Nz::Color::Black; + REQUIRE(red != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &red)); + REQUIRE(red == copy); + } + + THEN("String") + { + context.stream->SetCursorPos(0); + Nz::String string = "string"; + Nz::String copy(string); + REQUIRE(Serialize(context, string)); + string = "another"; + REQUIRE(string != copy); + context.stream->SetCursorPos(0); + REQUIRE(Unserialize(context, &string)); + REQUIRE(string == copy); + } + } + } +} diff --git a/tests/Engine/Core/Signal.cpp b/tests/Engine/Core/Signal.cpp new file mode 100644 index 000000000..c5582d866 --- /dev/null +++ b/tests/Engine/Core/Signal.cpp @@ -0,0 +1,48 @@ +#include +#include + +struct Incrementer +{ + void increment(int* inc) + { + *inc += 1; + } +}; + +void increment(int* inc) +{ + *inc += 1; +} + +SCENARIO("Signal", "[CORE][SIGNAL]") +{ + GIVEN("A signal") + { + Nz::Signal signal; + + WHEN("We connection different callbacks") + { + auto connection = signal.Connect(increment); + signal.Connect([](int* inc){ *inc += 1; }); + Incrementer incrementer; + signal.Connect(incrementer, &Incrementer::increment); + + THEN("The call of signal with inc = 0 must return 3") + { + int inc = 0; + signal(&inc); + REQUIRE(inc == 3); + } + + AND_THEN("When we disconnect one function, there should be only two listeners") + { + connection.Disconnect(); + REQUIRE(!connection.IsConnected()); + + int inc = 0; + signal(&inc); + REQUIRE(inc == 2); + } + } + } +} diff --git a/tests/Engine/Core/SparsePtr.cpp b/tests/Engine/Core/SparsePtr.cpp new file mode 100644 index 000000000..e727d0807 --- /dev/null +++ b/tests/Engine/Core/SparsePtr.cpp @@ -0,0 +1,47 @@ +#include +#include + +#include + +SCENARIO("SparsePtr", "[CORE][SPARSEPTR]") +{ + GIVEN("A sparse pointer pointing to an array with a stride of 2") + { + std::array arrays{0, 1, 2, 3, 4}; + Nz::SparsePtr sparsePtr(arrays.data(), 2 * sizeof(int)); + + WHEN("We use operators") + { + THEN("Operator[] with 2 should be 4") + { + REQUIRE(4 == sparsePtr[2]); + } + + THEN("Operator++ and Operator-- should be opposite") + { + ++sparsePtr; + REQUIRE(2 == *sparsePtr); + auto old = sparsePtr++; + REQUIRE(2 == *old); + REQUIRE(4 == *sparsePtr); + + --sparsePtr; + REQUIRE(2 == *sparsePtr); + auto oldMinus = sparsePtr--; + REQUIRE(2 == *oldMinus); + REQUIRE(0 == *sparsePtr); + } + + THEN("Operator+ and operator-") + { + auto offsetTwo = sparsePtr + 2; + REQUIRE(4 == *offsetTwo); + + auto offsetZero = offsetTwo - 2; + REQUIRE(0 == *offsetZero); + + REQUIRE((offsetTwo - offsetZero) == 2); + } + } + } +} diff --git a/tests/Engine/Core/String.cpp b/tests/Engine/Core/String.cpp index a563d0b7f..eac5eaef6 100644 --- a/tests/Engine/Core/String.cpp +++ b/tests/Engine/Core/String.cpp @@ -82,6 +82,21 @@ SCENARIO("String", "[CORE][STRING]") } } + GIVEN("One character string") + { + Nz::String characterString; + + WHEN("We set the string to one character") + { + characterString.Set('/'); + + THEN("The string must contain it") + { + REQUIRE(characterString == '/'); + } + } + } + /* TODO GIVEN("One unicode string") { diff --git a/tests/Engine/Math/Algorithm.cpp b/tests/Engine/Math/AlgorithmMath.cpp similarity index 66% rename from tests/Engine/Math/Algorithm.cpp rename to tests/Engine/Math/AlgorithmMath.cpp index 38080c576..01413d902 100644 --- a/tests/Engine/Math/Algorithm.cpp +++ b/tests/Engine/Math/AlgorithmMath.cpp @@ -1,7 +1,7 @@ #include #include -TEST_CASE("Approach", "[MATH][ALGORITHM]" ) +TEST_CASE("Approach", "[MATH][ALGORITHM]") { SECTION("Approach 8 with 5 by 2") { @@ -19,7 +19,7 @@ TEST_CASE("Approach", "[MATH][ALGORITHM]" ) } } -TEST_CASE("Clamp", "[ALGORITHM]" ) +TEST_CASE("Clamp", "[MATH][ALGORITHM]") { SECTION("Clamp 8 between 5 and 10") { @@ -37,7 +37,20 @@ TEST_CASE("Clamp", "[ALGORITHM]" ) } } -TEST_CASE("DegreeToRadian", "[ALGORITHM]" ) +TEST_CASE("CountBits", "[MATH][ALGORITHM]") +{ + SECTION("Number 10 has 2 bits set to 1") + { + REQUIRE(Nz::CountBits(10) == 2); + } + + SECTION("Number 0 has 0 bit set to 1") + { + REQUIRE(Nz::CountBits(0) == 0); + } +} + +TEST_CASE("DegreeToRadian", "[MATH][ALGORITHM]") { SECTION("Convert 45.f degree to radian") { @@ -45,8 +58,13 @@ TEST_CASE("DegreeToRadian", "[ALGORITHM]" ) } } -TEST_CASE("GetNearestPowerOfTwo", "[ALGORITHM]" ) +TEST_CASE("GetNearestPowerOfTwo", "[MATH][ALGORITHM]") { + SECTION("Nearest power of two of 0 = 1") + { + REQUIRE(Nz::GetNearestPowerOfTwo(0) == 1); + } + SECTION("Nearest power of two of 16 = 16") { REQUIRE(Nz::GetNearestPowerOfTwo(16) == 16); @@ -58,7 +76,7 @@ TEST_CASE("GetNearestPowerOfTwo", "[ALGORITHM]" ) } } -TEST_CASE("GetNumberLength", "[ALGORITHM]" ) +TEST_CASE("GetNumberLength", "[MATH][ALGORITHM]") { SECTION("GetNumberLength of -127 signed char") { @@ -115,7 +133,48 @@ TEST_CASE("GetNumberLength", "[ALGORITHM]" ) } } -TEST_CASE("IntegralPow", "[ALGORITHM]" ) +TEST_CASE("IntegralLog2", "[MATH][ALGORITHM]") +{ + SECTION("According to implementation, log in base 2 of 0 = 0") + { + REQUIRE(Nz::IntegralLog2(0) == 0); + } + + SECTION("Log in base 2 of 1 = 0") + { + REQUIRE(Nz::IntegralLog2(1) == 0); + } + + SECTION("Log in base 2 of 4 = 2") + { + REQUIRE(Nz::IntegralLog2(4) == 2); + } + + SECTION("Log in base 2 of 5 = 2") + { + REQUIRE(Nz::IntegralLog2(5) == 2); + } +} + +TEST_CASE("IntegralLog2Pot", "[MATH][ALGORITHM]") +{ + SECTION("According to implementation, log in base 2 of 0 = 0") + { + REQUIRE(Nz::IntegralLog2Pot(0) == 0); + } + + SECTION("Log in base 2 of 1 = 0") + { + REQUIRE(Nz::IntegralLog2Pot(1) == 0); + } + + SECTION("Log in base 2 of 4 = 2") + { + REQUIRE(Nz::IntegralLog2Pot(4) == 2); + } +} + +TEST_CASE("IntegralPow", "[MATH][ALGORITHM]") { SECTION("2 to power 4") { @@ -123,7 +182,7 @@ TEST_CASE("IntegralPow", "[ALGORITHM]" ) } } -TEST_CASE("Lerp", "[ALGORITHM]" ) +TEST_CASE("Lerp", "[MATH][ALGORITHM]") { SECTION("Lerp 2 to 6 with 0.5") { @@ -131,7 +190,7 @@ TEST_CASE("Lerp", "[ALGORITHM]" ) } } -TEST_CASE("MultiplyAdd", "[ALGORITHM]" ) +TEST_CASE("MultiplyAdd", "[MATH][ALGORITHM]") { SECTION("2 * 3 + 1") { @@ -139,7 +198,7 @@ TEST_CASE("MultiplyAdd", "[ALGORITHM]" ) } } -TEST_CASE("NumberEquals", "[ALGORITHM]" ) +TEST_CASE("NumberEquals", "[MATH][ALGORITHM]") { SECTION("2.35 and 2.351 should be the same at 0.01") { @@ -152,7 +211,7 @@ TEST_CASE("NumberEquals", "[ALGORITHM]" ) } } -TEST_CASE("NumberToString", "[ALGORITHM]" ) +TEST_CASE("NumberToString", "[MATH][ALGORITHM]") { SECTION("235 to string") { @@ -170,7 +229,7 @@ TEST_CASE("NumberToString", "[ALGORITHM]" ) } } -TEST_CASE("RadianToDegree", "[ALGORITHM]" ) +TEST_CASE("RadianToDegree", "[MATH][ALGORITHM]") { SECTION("PI / 4 to degree") { @@ -178,7 +237,7 @@ TEST_CASE("RadianToDegree", "[ALGORITHM]" ) } } -TEST_CASE("StringToNumber", "[ALGORITHM]" ) +TEST_CASE("StringToNumber", "[MATH][ALGORITHM]") { SECTION("235 in string") { diff --git a/tests/Engine/Math/BoundingVolume.cpp b/tests/Engine/Math/BoundingVolume.cpp index 05af375bc..9fd552863 100644 --- a/tests/Engine/Math/BoundingVolume.cpp +++ b/tests/Engine/Math/BoundingVolume.cpp @@ -99,5 +99,20 @@ SCENARIO("BoundingVolume", "[MATH][BOUNDINGVOLUME]") REQUIRE(firstCenterAndUnit.aabb != secondCenterAndUnit.aabb); } } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::BoundingVolumef nullBoundingVolume = Nz::BoundingVolumef(Nz::Vector3f::Zero(), Nz::Vector3f::Zero()); + Nz::BoundingVolumef centerAndUnit = firstCenterAndUnit; + nullBoundingVolume.Update(Nz::Matrix4f::Identity()); + centerAndUnit.Update(Nz::Matrix4f::Identity()); + Nz::BoundingVolumef result(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f); + result.Update(Nz::Matrix4f::Identity()); + + REQUIRE(Nz::BoundingVolumef::Lerp(nullBoundingVolume, centerAndUnit, 0.5f) == result); + } + } } } diff --git a/tests/Engine/Math/Box.cpp b/tests/Engine/Math/Box.cpp index e39e0654c..7d9b42a51 100644 --- a/tests/Engine/Math/Box.cpp +++ b/tests/Engine/Math/Box.cpp @@ -64,6 +64,18 @@ SCENARIO("Box", "[MATH][BOX]") REQUIRE(tmp == firstCenterAndUnit); } } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::Boxf nullBox = Nz::Boxf::Zero(); + Nz::Boxf centerAndUnit = firstCenterAndUnit; + Nz::Boxf result(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f); + + REQUIRE(Nz::Boxf::Lerp(nullBox, centerAndUnit, 0.5f) == result); + } + } } GIVEN("Two wrong box (negative width, height and depth") diff --git a/tests/Engine/Math/EulerAngles.cpp b/tests/Engine/Math/EulerAngles.cpp index a44efb361..6a6f7f77e 100644 --- a/tests/Engine/Math/EulerAngles.cpp +++ b/tests/Engine/Math/EulerAngles.cpp @@ -15,8 +15,8 @@ SCENARIO("EulerAngles", "[MATH][EULERANGLES]") WHEN("We do some operations") { - Nz::EulerAnglesf euler90(90.f, 90.f, 90.f); - Nz::EulerAnglesf euler270(270.f, 270.f, 270.f); + Nz::EulerAnglesf euler90(Nz::FromDegrees(90.f), Nz::FromDegrees(90.f), Nz::FromDegrees(90.f)); + Nz::EulerAnglesf euler270(Nz::FromDegrees(270.f), Nz::FromDegrees(270.f), Nz::FromDegrees(270.f)); Nz::EulerAnglesf euler360 = euler90 + euler270; euler360.Normalize(); @@ -42,6 +42,27 @@ SCENARIO("EulerAngles", "[MATH][EULERANGLES]") } } + GIVEN("Three rotation of 90 on each axis") + { + Nz::EulerAnglesf euler90P(Nz::FromDegrees(90.f), 0.f, 0.f); + Nz::EulerAnglesf euler90Y(0.f, Nz::FromDegrees(90.f), 0.f); + Nz::EulerAnglesf euler90R(0.f, 0.f, Nz::FromDegrees(90.f)); + + WHEN("We transform the axis") + { + THEN("This is supposed to be left-handed") + { + Nz::Vector3f rotation90P = euler90P.ToQuaternion() * Nz::Vector3f::UnitY(); + Nz::Vector3f rotation90Y = euler90Y.ToQuaternion() * Nz::Vector3f::UnitZ(); + Nz::Vector3f rotation90R = euler90R.ToQuaternion() * Nz::Vector3f::UnitX(); + + REQUIRE(rotation90P == Nz::Vector3f::UnitZ()); + REQUIRE(rotation90Y == Nz::Vector3f::UnitX()); + REQUIRE(rotation90R == Nz::Vector3f::UnitY()); + } + } + } + GIVEN("Euler angles with rotation 45 on each axis") { WHEN("We convert to quaternion") diff --git a/tests/Engine/Math/Frustum.cpp b/tests/Engine/Math/Frustum.cpp index 98715940d..d9fedebdd 100644 --- a/tests/Engine/Math/Frustum.cpp +++ b/tests/Engine/Math/Frustum.cpp @@ -78,5 +78,18 @@ SCENARIO("Frustum", "[MATH][FRUSTUM]") CHECK(frustum.Contains(&tmp, 1)); } } + + WHEN("We test for edge cases") + { + THEN("Implementation defined these") + { + Nz::BoundingVolumef nullVolume = Nz::BoundingVolumef::Null(); + CHECK(!frustum.Contains(nullVolume)); + Nz::BoundingVolumef infiniteVolume = Nz::BoundingVolumef::Infinite(); + CHECK(frustum.Contains(infiniteVolume)); + REQUIRE(frustum.Intersect(nullVolume) == Nz::IntersectionSide_Outside); + REQUIRE(frustum.Intersect(infiniteVolume) == Nz::IntersectionSide_Intersecting); + } + } } } diff --git a/tests/Engine/Math/Matrix4.cpp b/tests/Engine/Math/Matrix4.cpp index 24a56658b..eec10aacd 100644 --- a/tests/Engine/Math/Matrix4.cpp +++ b/tests/Engine/Math/Matrix4.cpp @@ -37,19 +37,29 @@ SCENARIO("Matrix4", "[MATH][Matrix4]") REQUIRE(firstIdentity.Inverse() == secondIdentity.InverseAffine()); } } + + WHEN("We transpose one of this matrix") + { + THEN("Identity transposed is the same than identity") + { + Nz::Matrix4f transposedIdentity; + firstIdentity.GetTransposed(&transposedIdentity); + REQUIRE(firstIdentity == transposedIdentity); + } + } } GIVEN("Two different matrix") { Nz::Matrix4f matrix1(1.0f, 0.0f, 0.0f, 0.0f, - 7.0f, 2.0f, 0.0f, 0.0f, - 1.0f, 5.0f, 3.0f, 0.0f, - 8.0f, 9.0f, 2.0f, 4.0f); + 7.0f, 2.0f, 0.0f, 0.0f, + 1.0f, 5.0f, 3.0f, 0.0f, + 8.0f, 9.0f, 2.0f, 4.0f); Nz::Matrix4f matrix2(1.0f, 1.0f, 2.0f, -1.0f, - -2.0f, -1.0f, -2.0f, 2.0f, - 4.0f, 2.0f, 5.0f, -4.0f, - 5.0f, -3.0f, -7.0f, -6.0f); + -2.0f, -1.0f, -2.0f, 2.0f, + 4.0f, 2.0f, 5.0f, -4.0f, + 5.0f, -3.0f, -7.0f, -6.0f); WHEN("We ask for determinant") { @@ -92,9 +102,9 @@ SCENARIO("Matrix4", "[MATH][Matrix4]") { transformedMatrix.MakeTransform(Nz::Vector3f::Zero(), Nz::EulerAnglesf(Nz::FromDegrees(45.f), 0.f, 0.f).ToQuaternion()); Nz::Matrix4f rotation45X(1.f, 0.f, 0.f, 0.f, - 0.f, std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f, - 0.f, -std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f, - 0.f, 0.f, 0.f, 1.f); + 0.f, std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f, + 0.f, -std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f, + 0.f, 0.f, 0.f, 1.f); REQUIRE(transformedMatrix == rotation45X); transformedMatrix.MakeTransform(Nz::Vector3f::Unit(), Nz::EulerAnglesf(Nz::FromDegrees(45.f), 0.f, 0.f).ToQuaternion()); @@ -106,9 +116,9 @@ SCENARIO("Matrix4", "[MATH][Matrix4]") { transformedMatrix.MakeTransform(Nz::Vector3f::Zero(), Nz::EulerAnglesf(0.f, Nz::FromDegrees(45.f), 0.f).ToQuaternion()); Nz::Matrix4f rotation45Y(std::sqrt(2.f) / 2.f, 0.f, -std::sqrt(2.f) / 2.f, 0.f, - 0.f, 1.f, 0.f, 0.f, - std::sqrt(2.f) / 2.f, 0.f, std::sqrt(2.f) / 2.f, 0.f, - 0.f, 0.f, 0.f, 1.f); + 0.f, 1.f, 0.f, 0.f, + std::sqrt(2.f) / 2.f, 0.f, std::sqrt(2.f) / 2.f, 0.f, + 0.f, 0.f, 0.f, 1.f); REQUIRE(transformedMatrix == rotation45Y); transformedMatrix.MakeTransform(Nz::Vector3f::Unit(), Nz::EulerAnglesf(0.f, Nz::FromDegrees(45.f), 0.f).ToQuaternion()); @@ -120,9 +130,9 @@ SCENARIO("Matrix4", "[MATH][Matrix4]") { transformedMatrix.MakeTransform(Nz::Vector3f::Zero(), Nz::EulerAnglesf(0.f, 0.f, Nz::FromDegrees(45.f)).ToQuaternion()); Nz::Matrix4f rotation45Z( std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f, 0.f, - -std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - 0.f, 0.f, 0.f, 1.f); + -std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f); REQUIRE(transformedMatrix == rotation45Z); transformedMatrix.MakeTransform(Nz::Vector3f::Unit(), Nz::EulerAnglesf(Nz::EulerAnglesf(0.f, 0.f, Nz::FromDegrees(45.f)).ToQuaternion())); diff --git a/tests/Engine/Math/OrientedBox.cpp b/tests/Engine/Math/OrientedBox.cpp index bd11f58ef..7a1035ee2 100644 --- a/tests/Engine/Math/OrientedBox.cpp +++ b/tests/Engine/Math/OrientedBox.cpp @@ -43,5 +43,20 @@ SCENARIO("OrientedBox", "[MATH][ORIENTEDBOX]") } } } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::OrientedBoxf nullOrientedBox = Nz::OrientedBoxf::Zero(); + Nz::OrientedBoxf centerAndUnit = firstCenterAndUnit; + nullOrientedBox.Update(Nz::Matrix4f::Identity()); + centerAndUnit.Update(Nz::Matrix4f::Identity()); + Nz::OrientedBoxf result(Nz::Vector3f::Zero(), Nz::Vector3f::Unit() * 0.5f); + result.Update(Nz::Matrix4f::Identity()); + + REQUIRE(Nz::OrientedBoxf::Lerp(nullOrientedBox, centerAndUnit, 0.5f) == result); + } + } } } diff --git a/tests/Engine/Math/Plane.cpp b/tests/Engine/Math/Plane.cpp index 1cab6f385..44377bf67 100644 --- a/tests/Engine/Math/Plane.cpp +++ b/tests/Engine/Math/Plane.cpp @@ -49,6 +49,18 @@ SCENARIO("Plane", "[MATH][PLANE]") REQUIRE(Nz::Planef(-Nz::Vector3f::UnitY(), -1000.f).Distance(Nz::Vector3f::UnitY() * 1500.f) == Approx(-500.f)); } } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::Planef planeXY = Nz::Planef::XY(); + Nz::Planef planeXZ = Nz::Planef::XZ(); + Nz::Vector3f result = Nz::Vector3f(0.f, 1.f, 1.f) * 0.5f; + result.Normalize(); + REQUIRE(Nz::Planef::Lerp(planeXY, planeXZ, 0.5f) == Nz::Planef(result, 0.f)); + } + } } GIVEN("The plane XZ, distance 1 with 3 points (0, 1, 0), (1, 1, 1), (-1, 1, 0)") diff --git a/tests/Engine/Math/Quaternion.cpp b/tests/Engine/Math/Quaternion.cpp index 65ab201bf..7ebf6ede5 100644 --- a/tests/Engine/Math/Quaternion.cpp +++ b/tests/Engine/Math/Quaternion.cpp @@ -32,6 +32,22 @@ SCENARIO("Quaternion", "[MATH][QUATERNION]") REQUIRE((firstQuaternion * Nz::Vector3f::UnitZ()) == -Nz::Vector3f::UnitZ()); } } + + WHEN("We invert or normalize Zero quaternion") + { + Nz::Quaternionf zero = Nz::Quaternionf::Zero(); + + THEN("It's meant not to be changed") + { + Nz::Quaternionf inverted = zero.GetInverse(); + float tmp = -1.f; + Nz::Quaternionf normalized = zero.GetNormal(&tmp); + + REQUIRE(inverted == zero); + REQUIRE(normalized == zero); + REQUIRE(tmp == Approx(0.f)); + } + } } GIVEN("The four unit quaternions") @@ -154,5 +170,18 @@ SCENARIO("Quaternion", "[MATH][QUATERNION]") REQUIRE(quaternionC.z == Approx(unitZ225.z)); } } + + WHEN("We get the rotation between two vectors") + { + /*TODO + * Nz::Quaternionf rotationBetweenXY = Nz::Quaternionf::RotationBetween(Nz::Vector3f::UnitX(), Nz::Vector3f::UnitY()); + + THEN("The rotation in left-handed is 270 degree on z") + { + Nz::Quaternionf rotation270Z(Nz::FromDegrees(270.f), Nz::Vector3f::UnitZ()); + Nz::Quaternionf rotation90Z(Nz::FromDegrees(90.f), Nz::Vector3f::UnitZ()); + REQUIRE(rotation90Z == rotationBetweenXY); + }*/ + } } } diff --git a/tests/Engine/Math/Ray.cpp b/tests/Engine/Math/Ray.cpp index df77a34ac..286a358c2 100644 --- a/tests/Engine/Math/Ray.cpp +++ b/tests/Engine/Math/Ray.cpp @@ -86,10 +86,37 @@ SCENARIO("Ray", "[MATH][RAY]") Nz::BoundingVolumef nullVolume(Nz::Extend_Null); CHECK(!ray.Intersect(nullVolume)); + float tmpClosest = -1.f; + float tmpFurthest = -1.f; Nz::BoundingVolumef infiniteVolume(Nz::Extend_Infinite); - CHECK(ray.Intersect(infiniteVolume)); + CHECK(ray.Intersect(infiniteVolume, &tmpClosest, &tmpFurthest)); + CHECK(tmpClosest == Approx(0.f)); + CHECK(tmpFurthest == std::numeric_limits::infinity()); } + THEN("For the triangle collision's") + { + Nz::Vector3f firstPoint(0.f, 1.f, 1.f); + Nz::Vector3f secondPoint(-1.f, 1.f, -1.f); + Nz::Vector3f thidPoint(1.f, 1.f, -1.f); + float tmpHit = -1.f; + + CHECK(ray.Intersect(firstPoint, secondPoint, thidPoint, &tmpHit)); + REQUIRE(ray.GetPoint(tmpHit) == Nz::Vector3f::UnitY()); + + Nz::Vector3f offset = Nz::Vector3f(10.f, 0.f, 10.f); + CHECK(!ray.Intersect(firstPoint + offset, secondPoint + offset, thidPoint + offset, &tmpHit)); + } + } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::Rayf AxisX = Nz::Rayf::AxisX(); + Nz::Rayf AxisY = Nz::Rayf::AxisY(); + REQUIRE(Nz::Rayf::Lerp(AxisX, AxisY, 0.5f) == (Nz::Rayf(Nz::Vector3f::Zero(), Nz::Vector3f(0.5f, 0.5f, 0.f)))); + } } } } diff --git a/tests/Engine/Math/Rect.cpp b/tests/Engine/Math/Rect.cpp index 7a1035f79..b119d76c9 100644 --- a/tests/Engine/Math/Rect.cpp +++ b/tests/Engine/Math/Rect.cpp @@ -52,5 +52,17 @@ SCENARIO("Rect", "[MATH][RECT]") } } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::Rectf nullRect = Nz::Rectf::Zero(); + Nz::Rectf centerAndUnit = firstCenterAndUnit; + Nz::Rectf result(Nz::Vector2f::Zero(), Nz::Vector2f::Unit() * 0.5f); + + REQUIRE(Nz::Rectf::Lerp(nullRect, centerAndUnit, 0.5f) == result); + } + } } } diff --git a/tests/Engine/Math/Sphere.cpp b/tests/Engine/Math/Sphere.cpp index 0f0e509ca..1820acd35 100644 --- a/tests/Engine/Math/Sphere.cpp +++ b/tests/Engine/Math/Sphere.cpp @@ -59,5 +59,45 @@ SCENARIO("Sphere", "[MATH][SPHERE]") REQUIRE(centerUnitBox.GetSquaredBoundingSphere() == Nz::Spheref(Nz::Vector3f::Zero(), 0.75f)); } } + + WHEN("We ask for positive and negative vertex") + { + Nz::Vector3f positiveVector = Nz::Vector3f::UnitY(); + + THEN("Positive vertex should be the same with centered and unit sphere") + { + REQUIRE(positiveVector == firstCenterAndUnit.GetPositiveVertex(positiveVector)); + } + + AND_THEN("Negative vertex should be the opposite") + { + REQUIRE(-positiveVector == firstCenterAndUnit.GetNegativeVertex(positiveVector)); + } + } + + WHEN("We extend the unit sphere to one point") + { + Nz::Vector3f point = Nz::Vector3f::UnitY() * 2.f; + + firstCenterAndUnit.ExtendTo(point); + + THEN("Sphere must contain it and distance should be good") + { + CHECK(firstCenterAndUnit.Contains(point)); + REQUIRE(firstCenterAndUnit.Distance(point) == 2.f); + } + } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::Spheref nullRect = Nz::Spheref::Zero(); + Nz::Spheref centerAndUnit = firstCenterAndUnit; + Nz::Spheref result(Nz::Vector3f::Zero(), 0.5f); + + REQUIRE(Nz::Spheref::Lerp(nullRect, centerAndUnit, 0.5f) == result); + } + } } } diff --git a/tests/Engine/Math/Vector2.cpp b/tests/Engine/Math/Vector2.cpp index 17ae62e72..45ca1468c 100644 --- a/tests/Engine/Math/Vector2.cpp +++ b/tests/Engine/Math/Vector2.cpp @@ -15,6 +15,7 @@ SCENARIO("Vector2", "[MATH][VECTOR2]") THEN("They are the same") { REQUIRE(firstUnit == secondUnit); + REQUIRE(firstUnit <= secondUnit); } } @@ -22,11 +23,13 @@ SCENARIO("Vector2", "[MATH][VECTOR2]") { Nz::Vector2f tmp(-1.f, 1.f); - THEN("These results are expected") + THEN("These are perpendicular") { REQUIRE(firstUnit.AbsDotProduct(tmp) == Approx(2.f)); REQUIRE(firstUnit.DotProduct(tmp) == Approx(0.f)); - REQUIRE(firstUnit.AngleBetween(tmp) == Approx(90.f)); + REQUIRE(firstUnit.AngleBetween(tmp) == Approx(Nz::FromDegrees(90.f))); + Nz::Vector2f negativeUnitX = -Nz::Vector2f::UnitX(); + REQUIRE(negativeUnitX.AngleBetween(negativeUnitX + Nz::Vector2f(0, 0.0000001f)) == Approx(Nz::FromDegrees(360.f))); } } @@ -45,5 +48,49 @@ SCENARIO("Vector2", "[MATH][VECTOR2]") REQUIRE(firstUnit.GetLength() == Approx(std::sqrt(2.f))); } } + + WHEN("We nomalize the vectors") + { + float ratio = 0.f; + THEN("For normal cases should be normal") + { + Nz::Vector2f normalized = firstUnit.GetNormal(&ratio); + REQUIRE(normalized == (Nz::Vector2f::Unit() / std::sqrt(2.f))); + REQUIRE(ratio == Approx(std::sqrt(2.f))); + } + + THEN("For null vector") + { + Nz::Vector2f zero = Nz::Vector2f::Zero(); + REQUIRE(zero.GetNormal(&ratio) == Nz::Vector2f::Zero()); + REQUIRE(ratio == Approx(0.f)); + } + } + + WHEN("We try to maximize and minimize") + { + Nz::Vector2f maximize(2.f, 1.f); + Nz::Vector2f minimize(1.f, 2.f); + + THEN("The minimised and maximised should be (1, 1) and (2, 2)") + { + Nz::Vector2f maximized = maximize; + Nz::Vector2f minimized = minimize; + REQUIRE(minimized.Minimize(maximized) == Nz::Vector2f::Unit()); + REQUIRE(maximize.Maximize(minimize) == (2.f * Nz::Vector2f::Unit())); + } + + } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::Vector2f zero = Nz::Vector2f::Zero(); + Nz::Vector2f unit = Nz::Vector2f::Unit(); + REQUIRE(Nz::Vector2f::Lerp(zero, unit, 0.5f) == (Nz::Vector2f::Unit() * 0.5f)); + } + } + } } diff --git a/tests/Engine/Math/Vector3.cpp b/tests/Engine/Math/Vector3.cpp index a61016e82..84c6a01ae 100644 --- a/tests/Engine/Math/Vector3.cpp +++ b/tests/Engine/Math/Vector3.cpp @@ -26,7 +26,8 @@ SCENARIO("Vector3", "[MATH][VECTOR3]") { REQUIRE(firstUnit.AbsDotProduct(tmp) == Approx(2.f)); REQUIRE(firstUnit.DotProduct(tmp) == Approx(0.f)); - REQUIRE(firstUnit.AngleBetween(tmp) == Approx(90.f)); + REQUIRE(firstUnit.AngleBetween(tmp) == Approx(Nz::FromDegrees(90.f))); + REQUIRE(firstUnit.AngleBetween(-firstUnit) == Approx(Nz::FromDegrees(180.f))); } } @@ -52,5 +53,48 @@ SCENARIO("Vector3", "[MATH][VECTOR3]") REQUIRE(firstUnit.GetLength() == Approx(std::sqrt(3.f))); } } + + WHEN("We nomalize the vectors") + { + float ratio = 0.f; + THEN("For normal cases should be normal") + { + Nz::Vector3f normalized = firstUnit.GetNormal(&ratio); + REQUIRE(normalized == (Nz::Vector3f::Unit() / std::sqrt(3.f))); + REQUIRE(ratio == Approx(std::sqrt(3.f))); + } + + THEN("For null vector") + { + Nz::Vector3f zero = Nz::Vector3f::Zero(); + REQUIRE(zero.GetNormal(&ratio) == Nz::Vector3f::Zero()); + REQUIRE(ratio == Approx(0.f)); + } + } + + WHEN("We try to maximize and minimize") + { + Nz::Vector3f maximize(2.f, 1.f, 2.f); + Nz::Vector3f minimize(1.f, 2.f, 1.f); + + THEN("The minimised and maximised should be (1, 1, 1) and (2, 2, 2)") + { + Nz::Vector3f maximized = maximize; + Nz::Vector3f minimized = minimize; + REQUIRE(minimized.Minimize(maximized) == Nz::Vector3f::Unit()); + REQUIRE(maximize.Maximize(minimize) == (2.f * Nz::Vector3f::Unit())); + } + + } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::Vector3f zero = Nz::Vector3f::Zero(); + Nz::Vector3f unit = Nz::Vector3f::Unit(); + REQUIRE(Nz::Vector3f::Lerp(zero, unit, 0.5f) == (Nz::Vector3f::Unit() * 0.5f)); + } + } } } diff --git a/tests/Engine/Math/Vector4.cpp b/tests/Engine/Math/Vector4.cpp index cfe91eab7..85c321ac1 100644 --- a/tests/Engine/Math/Vector4.cpp +++ b/tests/Engine/Math/Vector4.cpp @@ -39,5 +39,15 @@ SCENARIO("Vector4", "[MATH][VECTOR4]") REQUIRE(tmp.Normalize() == Nz::Vector4f(Nz::Vector3f::Unit() * (1.f / 3.f), 1.f)); } } + + WHEN("We try to lerp") + { + THEN("Compilation should be fine") + { + Nz::Vector4f zero = Nz::Vector4f::Zero(); + Nz::Vector4f unitX = Nz::Vector4f::UnitX(); + REQUIRE(Nz::Vector4f::Lerp(zero, unitX, 0.5f) == Nz::Vector4f(Nz::Vector3f::UnitX() * 0.5f, 1.f)); + } + } } } diff --git a/tests/resources/Engine/Core/FileTest.txt b/tests/resources/Engine/Core/FileTest.txt new file mode 100644 index 000000000..345e6aef7 --- /dev/null +++ b/tests/resources/Engine/Core/FileTest.txt @@ -0,0 +1 @@ +Test