Merge branch 'enet_wip_nothing_to_see_here' of https://github.com/DigitalPulseSoftware/NazaraEngine into enet_wip_nothing_to_see_here

This commit is contained in:
Lynix 2017-05-19 16:31:31 +02:00
commit 53f865c42c
79 changed files with 1367 additions and 714 deletions

View File

@ -23,7 +23,6 @@ namespace Ndk
using Factory = std::function<BaseComponent*()>; using Factory = std::function<BaseComponent*()>;
BaseComponent(ComponentIndex componentIndex); BaseComponent(ComponentIndex componentIndex);
BaseComponent(const BaseComponent&) = default;
BaseComponent(BaseComponent&&) = default; BaseComponent(BaseComponent&&) = default;
virtual ~BaseComponent(); virtual ~BaseComponent();
@ -34,10 +33,12 @@ namespace Ndk
inline static ComponentIndex GetMaxComponentIndex(); inline static ComponentIndex GetMaxComponentIndex();
BaseComponent& operator=(const BaseComponent&) = default; BaseComponent& operator=(const BaseComponent&) = delete;
BaseComponent& operator=(BaseComponent&&) = default; BaseComponent& operator=(BaseComponent&&) = default;
protected: protected:
BaseComponent(const BaseComponent&) = default;
ComponentIndex m_componentIndex; ComponentIndex m_componentIndex;
EntityHandle m_entity; EntityHandle m_entity;

View File

@ -9,6 +9,7 @@
#include <Nazara/Core/Bitset.hpp> #include <Nazara/Core/Bitset.hpp>
#include <NDK/Entity.hpp> #include <NDK/Entity.hpp>
#include <NDK/EntityList.hpp>
#include <vector> #include <vector>
namespace Ndk namespace Ndk
@ -33,7 +34,7 @@ namespace Ndk
bool Filters(const Entity* entity) const; bool Filters(const Entity* entity) const;
inline const std::vector<EntityHandle>& GetEntities() const; inline const EntityList& GetEntities() const;
inline SystemIndex GetIndex() const; inline SystemIndex GetIndex() const;
inline int GetUpdateOrder() const; inline int GetUpdateOrder() const;
inline float GetUpdateRate() const; inline float GetUpdateRate() const;
@ -84,12 +85,11 @@ namespace Ndk
static inline bool Initialize(); static inline bool Initialize();
static inline void Uninitialize(); static inline void Uninitialize();
std::vector<EntityHandle> m_entities;
Nz::Bitset<Nz::UInt64> m_entityBits;
Nz::Bitset<> m_excludedComponents; Nz::Bitset<> m_excludedComponents;
mutable Nz::Bitset<> m_filterResult; mutable Nz::Bitset<> m_filterResult;
Nz::Bitset<> m_requiredAnyComponents; Nz::Bitset<> m_requiredAnyComponents;
Nz::Bitset<> m_requiredComponents; Nz::Bitset<> m_requiredComponents;
EntityList m_entities;
SystemIndex m_systemIndex; SystemIndex m_systemIndex;
World* m_world; World* m_world;
bool m_updateEnabled; bool m_updateEnabled;

View File

@ -56,7 +56,7 @@ namespace Ndk
* \return A constant reference to the list of entities * \return A constant reference to the list of entities
*/ */
inline const std::vector<EntityHandle>& BaseSystem::GetEntities() const inline const EntityList& BaseSystem::GetEntities() const
{ {
return m_entities; return m_entities;
} }
@ -121,10 +121,7 @@ namespace Ndk
inline bool BaseSystem::HasEntity(const Entity* entity) const inline bool BaseSystem::HasEntity(const Entity* entity) const
{ {
if (!entity) return m_entities.Has(entity);
return false;
return m_entityBits.UnboundedTest(entity->GetId());
} }
/*! /*!
@ -288,9 +285,7 @@ namespace Ndk
{ {
NazaraAssert(entity, "Invalid entity"); NazaraAssert(entity, "Invalid entity");
m_entities.emplace_back(entity); m_entities.Insert(entity);
m_entityBits.UnboundedSet(entity->GetId(), true);
entity->RegisterSystem(m_systemIndex); entity->RegisterSystem(m_systemIndex);
OnEntityAdded(entity); OnEntityAdded(entity);
@ -308,14 +303,7 @@ namespace Ndk
{ {
NazaraAssert(entity, "Invalid entity"); NazaraAssert(entity, "Invalid entity");
auto it = std::find(m_entities.begin(), m_entities.end(), *entity); m_entities.Remove(entity);
NazaraAssert(it != m_entities.end(), "Entity is not part of this system");
// To avoid moving a lot of handles, we swap and pop
std::swap(*it, m_entities.back());
m_entities.pop_back(); // We get it out of the vector
m_entityBits.Reset(entity->GetId());
entity->UnregisterSystem(m_systemIndex); entity->UnregisterSystem(m_systemIndex);
OnEntityRemoved(entity); // And we alert our callback OnEntityRemoved(entity); // And we alert our callback
@ -360,7 +348,7 @@ namespace Ndk
} }
/*! /*!
* \brief Uninitializes the BaseSystem * \brief Uninitialize the BaseSystem
*/ */
inline void BaseSystem::Uninitialize() inline void BaseSystem::Uninitialize()

View File

@ -44,7 +44,7 @@ namespace Ndk
const Nz::Frustumf& GetFrustum() const override; const Nz::Frustumf& GetFrustum() const override;
inline unsigned int GetLayer() const; inline unsigned int GetLayer() const;
const Nz::Matrix4f& GetProjectionMatrix() const override; const Nz::Matrix4f& GetProjectionMatrix() const override;
inline Nz::ProjectionType GetProjectionType() const; Nz::ProjectionType GetProjectionType() const override;
inline const Nz::Vector2f& GetSize() const; inline const Nz::Vector2f& GetSize() const;
const Nz::RenderTarget* GetTarget() const override; const Nz::RenderTarget* GetTarget() const override;
inline const Nz::Rectf& GetTargetRegion() const; inline const Nz::Rectf& GetTargetRegion() const;

View File

@ -117,16 +117,6 @@ namespace Ndk
return m_layer; return m_layer;
} }
/*!
* \brief Gets the projection type of the camera
* \return Projection type of the camera
*/
inline Nz::ProjectionType CameraComponent::GetProjectionType() const
{
return m_projectionType;
}
/*! /*!
* \brief Gets the size of the camera * \brief Gets the size of the camera
* \return Size of the camera * \return Size of the camera

View File

@ -51,6 +51,9 @@ namespace Ndk
inline void RemoveFromCullingList(GraphicsComponentCullingList* cullingList) const; inline void RemoveFromCullingList(GraphicsComponentCullingList* cullingList) const;
inline void UpdateLocalMatrix(const Nz::InstancedRenderable* instancedRenderable, const Nz::Matrix4f& localMatrix);
inline void UpdateRenderOrder(const Nz::InstancedRenderable* instancedRenderable, int renderOrder);
static ComponentIndex componentIndex; static ComponentIndex componentIndex;
private: private:

View File

@ -142,6 +142,32 @@ namespace Ndk
} }
} }
inline void GraphicsComponent::UpdateLocalMatrix(const Nz::InstancedRenderable* instancedRenderable, const Nz::Matrix4f& localMatrix)
{
for (auto& renderable : m_renderables)
{
if (renderable.renderable == instancedRenderable)
{
renderable.data.localMatrix = localMatrix;
InvalidateBoundingVolume();
break;
}
}
}
inline void GraphicsComponent::UpdateRenderOrder(const Nz::InstancedRenderable* instancedRenderable, int renderOrder)
{
for (auto& renderable : m_renderables)
{
if (renderable.renderable == instancedRenderable)
{
renderable.data.renderOrder = renderOrder;
break;
}
}
}
/*! /*!
* \brief Invalidates the bounding volume * \brief Invalidates the bounding volume
*/ */

View File

@ -17,14 +17,17 @@
namespace Ndk namespace Ndk
{ {
class BaseComponent; class BaseComponent;
class BaseSystem;
class Entity; class Entity;
class EntityList;
class World; class World;
using EntityHandle = Nz::ObjectHandle<Entity>; using EntityHandle = Nz::ObjectHandle<Entity>;
class NDK_API Entity : public Nz::HandledObject<Entity> class NDK_API Entity : public Nz::HandledObject<Entity>
{ {
friend class BaseSystem; friend BaseSystem;
friend EntityList;
friend World; friend World;
public: public:
@ -78,13 +81,16 @@ namespace Ndk
inline Nz::Bitset<>& GetRemovedComponentBits(); inline Nz::Bitset<>& GetRemovedComponentBits();
inline void RegisterEntityList(EntityList* list);
inline void RegisterSystem(SystemIndex index); inline void RegisterSystem(SystemIndex index);
inline void SetWorld(World* world) noexcept; inline void SetWorld(World* world) noexcept;
inline void UnregisterEntityList(EntityList* list);
inline void UnregisterSystem(SystemIndex index); inline void UnregisterSystem(SystemIndex index);
std::vector<std::unique_ptr<BaseComponent>> m_components; std::vector<std::unique_ptr<BaseComponent>> m_components;
std::vector<EntityList*> m_containedInLists;
Nz::Bitset<> m_componentBits; Nz::Bitset<> m_componentBits;
Nz::Bitset<> m_removedComponentBits; Nz::Bitset<> m_removedComponentBits;
Nz::Bitset<> m_systemBits; Nz::Bitset<> m_systemBits;

View File

@ -6,6 +6,7 @@
#include <Nazara/Core/Error.hpp> #include <Nazara/Core/Error.hpp>
#include <Nazara/Core/StringStream.hpp> #include <Nazara/Core/StringStream.hpp>
#include <algorithm> #include <algorithm>
#include <cassert>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -257,11 +258,10 @@ namespace Ndk
return m_removedComponentBits; return m_removedComponentBits;
} }
/*! inline void Entity::RegisterEntityList(EntityList* list)
* \brief Registers a system for the entity {
* m_containedInLists.push_back(list);
* \param index Index of the system }
*/
inline void Entity::RegisterSystem(SystemIndex index) inline void Entity::RegisterSystem(SystemIndex index)
{ {
@ -289,6 +289,16 @@ namespace Ndk
* \param index Index of the system * \param index Index of the system
*/ */
inline void Entity::UnregisterEntityList(EntityList* list)
{
auto it = std::find(m_containedInLists.begin(), m_containedInLists.end(), list);
assert(it != m_containedInLists.end());
// Swap and pop idiom
*it = m_containedInLists.back();
m_containedInLists.pop_back();
}
inline void Entity::UnregisterSystem(SystemIndex index) inline void Entity::UnregisterSystem(SystemIndex index)
{ {
m_systemBits.UnboundedReset(index); m_systemBits.UnboundedReset(index);

View File

@ -15,46 +15,68 @@ namespace Ndk
{ {
class NDK_API EntityList class NDK_API EntityList
{ {
public: friend Entity;
using Container = std::vector<EntityHandle>;
EntityList() = default; public:
~EntityList() = default; class iterator;
friend iterator;
using size_type = std::size_t;
inline EntityList();
inline EntityList(const EntityList& entityList);
inline EntityList(EntityList&& entityList) noexcept;
inline ~EntityList();
inline void Clear(); inline void Clear();
inline bool Has(const Entity* entity); inline bool Has(const Entity* entity) const;
inline bool Has(EntityId entity); inline bool Has(EntityId entity) const;
inline void Insert(Entity* entity); inline void Insert(Entity* entity);
inline void Remove(Entity* entity); inline void Remove(Entity* entity);
// STL API // STL API
inline Container::iterator begin(); inline iterator begin() const;
inline Container::const_iterator begin() const;
inline Container::const_iterator cbegin() const;
inline Container::const_iterator cend() const;
inline Container::const_reverse_iterator crbegin() const;
inline Container::const_reverse_iterator crend() const;
inline bool empty() const; inline bool empty() const;
inline iterator end() const;
inline size_type size() const;
inline Container::iterator end(); inline EntityList& operator=(const EntityList& entityList);
inline Container::const_iterator end() const; inline EntityList& operator=(EntityList&& entityList) noexcept;
inline Container::reverse_iterator rbegin();
inline Container::const_reverse_iterator rbegin() const;
inline Container::reverse_iterator rend();
inline Container::const_reverse_iterator rend() const;
inline Container::size_type size() const;
private: private:
std::vector<EntityHandle> m_entities; inline std::size_t FindNext(std::size_t currentId) const;
inline World* GetWorld() const;
inline void NotifyEntityDestruction(const Entity* entity);
Nz::Bitset<Nz::UInt64> m_entityBits; Nz::Bitset<Nz::UInt64> m_entityBits;
World* m_world;
};
class NDK_API EntityList::iterator : public std::iterator<std::forward_iterator_tag, const EntityHandle>
{
friend EntityList;
public:
inline iterator(const iterator& iterator);
const EntityHandle& operator*() const;
inline iterator& operator=(const iterator& iterator);
inline iterator& operator++();
inline iterator operator++(int);
friend inline bool operator==(const iterator& lhs, const iterator& rhs);
friend inline bool operator!=(const iterator& lhs, const iterator& rhs);
friend inline void swap(iterator& lhs, iterator& rhs);
private:
inline iterator(const EntityList* world, std::size_t nextId);
std::size_t m_nextEntityId;
const EntityList* m_list;
}; };
} }

View File

@ -2,6 +2,7 @@
// This file is part of the "Nazara Development Kit" // This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp // For conditions of distribution and use, see copyright notice in Prerequesites.hpp
#include <NDK/EntityList.hpp>
#include <Nazara/Core/Error.hpp> #include <Nazara/Core/Error.hpp>
#include <algorithm> #include <algorithm>
@ -14,24 +15,71 @@ namespace Ndk
*/ */
/*! /*!
* \brief Clears the set from every entities * \brief Construct a new entity list
*/ */
inline EntityList::EntityList() :
inline void EntityList::Clear() m_world(nullptr)
{ {
m_entities.clear();
m_entityBits.Clear();
} }
/*! /*!
* \brief Checks whether or not the set contains the entity * \brief Construct a new entity list by copying another one
*/
inline EntityList::EntityList(const EntityList& entityList) :
m_entityBits(entityList.m_entityBits),
m_world(entityList.m_world)
{
for (const Ndk::EntityHandle& entity : *this)
entity->RegisterEntityList(this);
}
/*!
* \brief Construct a new entity list by moving a list into this one
*/
inline EntityList::EntityList(EntityList&& entityList) noexcept :
m_entityBits(std::move(entityList.m_entityBits)),
m_world(entityList.m_world)
{
for (const Ndk::EntityHandle& entity : *this)
{
entity->UnregisterEntityList(&entityList);
entity->RegisterEntityList(this);
}
}
inline EntityList::~EntityList()
{
for (const Ndk::EntityHandle& entity : *this)
entity->UnregisterEntityList(this);
}
/*!
* \brief Clears the set from every entities
*
* \remark This resets the implicit world member, allowing you to insert entities from a different world than previously
*/
inline void EntityList::Clear()
{
for (const Ndk::EntityHandle& entity : *this)
entity->UnregisterEntityList(this);
m_entityBits.Clear();
m_world = nullptr;
}
/*!
* \brief Checks whether or not the EntityList contains the entity
* \return true If it is the case * \return true If it is the case
* *
* \param entity Pointer to the entity * \param entity Pointer to the entity
*
* \remark If the Insert function was called since the EntityList construction (or last call to Clear), the entity passed by parameter must belong to the same world as the previously inserted entities.
*/ */
inline bool EntityList::Has(const Entity* entity) const
inline bool EntityList::Has(const Entity* entity)
{ {
NazaraAssert(!m_world || !entity || entity->GetWorld() == m_world, "Incompatible world");
return entity && entity->IsValid() && Has(entity->GetId()); return entity && entity->IsValid() && Has(entity->GetId());
} }
@ -41,8 +89,7 @@ namespace Ndk
* *
* \param id Identifier of the entity * \param id Identifier of the entity
*/ */
inline bool EntityList::Has(EntityId entity) const
inline bool EntityList::Has(EntityId entity)
{ {
return m_entityBits.UnboundedTest(entity); return m_entityBits.UnboundedTest(entity);
} }
@ -50,17 +97,23 @@ namespace Ndk
/*! /*!
* \brief Inserts the entity into the set * \brief Inserts the entity into the set
* *
* \param entity Pointer to the entity * Marks an entity as present in this entity list, it must belongs to the same world as others entities contained in this list.
*
* \param entity Valid pointer to an entity
* *
* \remark If entity is already contained, no action is performed * \remark If entity is already contained, no action is performed
* \remark If any entity has been inserted since construction (or last Clear call), the entity must belong to the same world as the previously inserted entities
*/ */
inline void EntityList::Insert(Entity* entity) inline void EntityList::Insert(Entity* entity)
{ {
NazaraAssert(entity, "Invalid entity");
if (!Has(entity)) if (!Has(entity))
{ {
m_entities.emplace_back(entity); entity->RegisterEntityList(this);
m_entityBits.UnboundedSet(entity->GetId(), true); m_entityBits.UnboundedSet(entity->GetId(), true);
m_world = entity->GetWorld();
} }
} }
@ -70,89 +123,138 @@ namespace Ndk
* \param entity Pointer to the entity * \param entity Pointer to the entity
* *
* \remark If entity is not contained, no action is performed * \remark If entity is not contained, no action is performed
* \remark This function never resets the implicit world member, even if it empties the list. Use the Clear method if you want to reset it.
*
* \see Clear
*/ */
inline void EntityList::Remove(Entity* entity) inline void EntityList::Remove(Entity* entity)
{ {
if (Has(entity)) if (Has(entity))
{ {
auto it = std::find(m_entities.begin(), m_entities.end(), *entity); m_entityBits.Reset(entity->GetId());
NazaraAssert(it != m_entities.end(), "Entity should be part of the vector");
std::swap(*it, m_entities.back()); entity->UnregisterEntityList(this);
m_entities.pop_back(); // We get it out of the vector
m_entityBits.UnboundedSet(entity->GetId(), false);
} }
} }
// Nz::Interface STD // STL Interface
inline EntityList::Container::iterator EntityList::begin() inline EntityList::iterator EntityList::begin() const
{ {
return m_entities.begin(); return EntityList::iterator(this, m_entityBits.FindFirst());
}
inline EntityList::Container::const_iterator EntityList::begin() const
{
return m_entities.begin();
}
inline EntityList::Container::const_iterator EntityList::cbegin() const
{
return m_entities.cbegin();
}
inline EntityList::Container::const_iterator EntityList::cend() const
{
return m_entities.cend();
}
inline EntityList::Container::const_reverse_iterator EntityList::crbegin() const
{
return m_entities.crbegin();
}
inline EntityList::Container::const_reverse_iterator EntityList::crend() const
{
return m_entities.crend();
} }
inline bool EntityList::empty() const inline bool EntityList::empty() const
{ {
return m_entities.empty(); return !m_entityBits.TestAny();
} }
inline EntityList::Container::iterator EntityList::end() inline EntityList::iterator EntityList::end() const
{ {
return m_entities.end(); return EntityList::iterator(this, m_entityBits.npos);
} }
inline EntityList::Container::const_iterator EntityList::end() const inline EntityList::size_type EntityList::size() const
{ {
return m_entities.end(); return m_entityBits.Count();
} }
inline EntityList::Container::reverse_iterator EntityList::rbegin() inline EntityList& EntityList::operator=(const EntityList& entityList)
{ {
return m_entities.rbegin(); m_entityBits = entityList.m_entityBits;
m_world = entityList.m_world;
for (const Ndk::EntityHandle& entity : *this)
entity->RegisterEntityList(this);
return *this;
} }
inline EntityList::Container::const_reverse_iterator EntityList::rbegin() const inline EntityList& EntityList::operator=(EntityList&& entityList) noexcept
{ {
return m_entities.rbegin(); m_entityBits = std::move(entityList.m_entityBits);
m_world = entityList.m_world;
for (const Ndk::EntityHandle& entity : *this)
{
entity->UnregisterEntityList(&entityList);
entity->RegisterEntityList(this);
}
return *this;
} }
inline EntityList::Container::reverse_iterator EntityList::rend() inline std::size_t EntityList::FindNext(std::size_t currentId) const
{ {
return m_entities.rend(); return m_entityBits.FindNext(currentId);
} }
inline EntityList::Container::const_reverse_iterator EntityList::rend() const inline World* EntityList::GetWorld() const
{ {
return m_entities.rend(); return m_world;
} }
inline EntityList::Container::size_type EntityList::size() const inline void EntityList::NotifyEntityDestruction(const Entity* entity)
{ {
return m_entities.size(); assert(Has(entity));
m_entityBits.Reset(entity->GetId());
}
inline EntityList::iterator::iterator(const EntityList* list, std::size_t nextId) :
m_nextEntityId(nextId),
m_list(list)
{
}
inline EntityList::iterator::iterator(const iterator& iterator) :
m_nextEntityId(iterator.m_nextEntityId),
m_list(iterator.m_list)
{
}
inline EntityList::iterator& EntityList::iterator::operator=(const iterator& iterator)
{
m_nextEntityId = iterator.m_nextEntityId;
m_list = iterator.m_list;
return *this;
}
inline EntityList::iterator& EntityList::iterator::operator++()
{
m_nextEntityId = m_list->FindNext(m_nextEntityId);
return *this;
}
inline EntityList::iterator EntityList::iterator::operator++(int)
{
std::size_t previousId = m_nextEntityId;
m_nextEntityId = m_list->FindNext(m_nextEntityId);
return iterator(m_list, previousId);
}
inline bool operator==(const EntityList::iterator& lhs, const EntityList::iterator& rhs)
{
NazaraAssert(lhs.m_list == rhs.m_list, "Cannot compare iterator coming from different lists");
return lhs.m_nextEntityId == rhs.m_nextEntityId;
}
inline bool operator!=(const EntityList::iterator& lhs, const EntityList::iterator& rhs)
{
return !operator==(lhs, rhs);
}
inline void swap(EntityList::iterator& lhs, EntityList::iterator& rhs)
{
NazaraAssert(lhs.m_list == rhs.m_list, "Cannot compare iterator coming from different lists");
using std::swap;
swap(lhs.m_nextEntityId, rhs.m_nextEntityId);
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright (C) 2017 Jérôme Leclercq // Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit" // This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp // For conditions of distribution and use, see copyright notice in Prerequesites.hpp
@ -10,6 +10,7 @@
#include <NDK/Prerequesites.hpp> #include <NDK/Prerequesites.hpp>
#include <NDK/State.hpp> #include <NDK/State.hpp>
#include <memory> #include <memory>
#include <vector>
namespace Ndk namespace Ndk
{ {
@ -25,14 +26,21 @@ namespace Ndk
inline const std::shared_ptr<State>& GetCurrentState() const; inline const std::shared_ptr<State>& GetCurrentState() const;
inline bool IsTopState(const State* state) const;
inline std::shared_ptr<State> PopState();
inline bool PopStatesUntil(std::shared_ptr<State> state);
inline void PushState(std::shared_ptr<State> state);
inline void SetState(std::shared_ptr<State> state);
inline bool Update(float elapsedTime); inline bool Update(float elapsedTime);
inline StateMachine& operator=(StateMachine&& fsm) = default; inline StateMachine& operator=(StateMachine&& fsm) = default;
StateMachine& operator=(const StateMachine&) = delete; StateMachine& operator=(const StateMachine&) = delete;
private: private:
std::shared_ptr<State> m_currentState; std::vector<std::shared_ptr<State>> m_states;
std::shared_ptr<State> m_nextState;
}; };
} }

View File

@ -3,7 +3,6 @@
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp // For conditions of distribution and use, see copyright notice in Prerequesites.hpp
#include <Nazara/Core/Error.hpp> #include <Nazara/Core/Error.hpp>
#include <NDK/StateMachine.hpp>
#include <utility> #include <utility>
namespace Ndk namespace Ndk
@ -11,7 +10,7 @@ namespace Ndk
/*! /*!
* \ingroup NDK * \ingroup NDK
* \class Ndk::StateMachine * \class Ndk::StateMachine
* \brief NDK class that represents a state machine, to represent the multiple states of your program * \brief NDK class that represents a state machine, to represent the multiple states of your program as a stack
*/ */
/*! /*!
@ -23,61 +22,153 @@ namespace Ndk
* \remark Produces a NazaraAssert if nullptr is given * \remark Produces a NazaraAssert if nullptr is given
*/ */
inline StateMachine::StateMachine(std::shared_ptr<State> originalState) : inline StateMachine::StateMachine(std::shared_ptr<State> originalState)
m_currentState(std::move(originalState))
{ {
NazaraAssert(m_currentState, "StateMachine must have a state to begin with"); NazaraAssert(originalState, "StateMachine must have a state to begin with");
m_currentState->Enter(*this); PushState(std::move(originalState));
} }
/*! /*!
* \brief Destructs the object * \brief Destructs the object
* *
* \remark Calls "Leave" on the state * \remark Calls "Leave" on all the states
*/ */
inline StateMachine::~StateMachine() inline StateMachine::~StateMachine()
{ {
m_currentState->Leave(*this); for (std::shared_ptr<State>& state : m_states)
state->Leave(*this);
} }
/*! /*!
* \brief Changes the current state of the machine * \brief Replaces the current state on the top of the machine
* *
* \param state Next state to represent * \param state State to replace the top one if it is nullptr, no action is performed
*/ */
inline void StateMachine::ChangeState(std::shared_ptr<State> state) inline void StateMachine::ChangeState(std::shared_ptr<State> state)
{ {
m_nextState = std::move(state); if (state)
{
PopState();
PushState(std::move(state));
}
} }
/*! /*!
* \brief Gets the current state of the machine * \brief Gets the current state on the top of the machine
* \return A constant reference to the state * \return A constant reference to the state
*
* \remark The stack is supposed to be non empty, otherwise it is undefined behaviour
*
* \see PopStatesUntil
*/ */
inline const std::shared_ptr<State>& StateMachine::GetCurrentState() const inline const std::shared_ptr<State>& StateMachine::GetCurrentState() const
{ {
return m_currentState; return m_states.back();
} }
/*! /*!
* \brief Updates the state * \brief Checks whether the state is on the top of the machine
* \return True if update is successful * \return true If it is the case
*
* \param state State to compare the top with
*/
inline bool StateMachine::IsTopState(const State* state) const
{
if (m_states.empty())
return false;
return m_states.back().get() == state;
}
/*!
* \brief Pops the state on the top of the machine
* \return Old state on the top, nullptr if stack was empty
*
* \remark This method can completely empty the stack
*/
inline std::shared_ptr<State> StateMachine::PopState()
{
if (m_states.empty())
return nullptr;
m_states.back()->Leave(*this);
std::shared_ptr<State> oldTopState = std::move(m_states.back());
m_states.pop_back();
return oldTopState;
}
/*!
* \brief Pops all the states of the machine until a specific one is reached
* \return true If that specific state is on top, false if stack is empty
*
* \param state State to find on the stack if it is nullptr, no action is performed
*
* \remark This method can completely empty the stack
*/
inline bool StateMachine::PopStatesUntil(std::shared_ptr<State> state)
{
if (!state)
return false;
while (!m_states.empty() && !IsTopState(state.get()))
PopState();
return !m_states.empty();
}
/*!
* \brief Pushes a new state on the top of the machine
*
* \param state Next state to represent if it is nullptr, it performs no action
*
* \remark Produces a NazaraAssert if the same state is pushed two times on the stack
*/
inline void StateMachine::PushState(std::shared_ptr<State> state)
{
if (state)
{
NazaraAssert(std::find(m_states.begin(), m_states.end(), state) == m_states.end(), "The same state was pushed two times");
m_states.push_back(std::move(state));
m_states.back()->Enter(*this);
}
}
/*!
* \brief Pops every states of the machine to put a new one
*
* \param state State to reset the stack with if it is nullptr, no action is performed
*/
inline void StateMachine::SetState(std::shared_ptr<State> state)
{
if (state)
{
while (!m_states.empty())
PopState();
PushState(std::move(state));
}
}
/*!
* \brief Updates all the states
* \return true If update is successful for everyone of them
* *
* \param elapsedTime Delta time used for the update * \param elapsedTime Delta time used for the update
*/ */
inline bool StateMachine::Update(float elapsedTime) inline bool StateMachine::Update(float elapsedTime)
{ {
if (m_nextState) return std::all_of(m_states.begin(), m_states.end(), [=](std::shared_ptr<State>& state) {
{ return state->Update(*this, elapsedTime);
m_currentState->Leave(*this); });
m_currentState = std::move(m_nextState);
m_currentState->Enter(*this);
}
return m_currentState->Update(*this, elapsedTime);
} }
} }

View File

@ -57,7 +57,7 @@ namespace Ndk
std::unique_ptr<Nz::AbstractRenderTechnique> m_renderTechnique; std::unique_ptr<Nz::AbstractRenderTechnique> m_renderTechnique;
std::vector<GraphicsComponentCullingList::VolumeEntry> m_volumeEntries; std::vector<GraphicsComponentCullingList::VolumeEntry> m_volumeEntries;
EntityList m_cameras; std::vector<EntityHandle> m_cameras;
EntityList m_drawables; EntityList m_drawables;
EntityList m_directionalLights; EntityList m_directionalLights;
EntityList m_lights; EntityList m_lights;

View File

@ -10,6 +10,7 @@
#include <Nazara/Core/Bitset.hpp> #include <Nazara/Core/Bitset.hpp>
#include <Nazara/Core/HandledObject.hpp> #include <Nazara/Core/HandledObject.hpp>
#include <NDK/Entity.hpp> #include <NDK/Entity.hpp>
#include <NDK/EntityList.hpp>
#include <NDK/System.hpp> #include <NDK/System.hpp>
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
@ -28,7 +29,7 @@ namespace Ndk
friend Entity; friend Entity;
public: public:
using EntityList = std::vector<EntityHandle>; using EntityVector = std::vector<EntityHandle>;
inline World(bool addDefaultSystems = true); inline World(bool addDefaultSystems = true);
World(const World&) = delete; World(const World&) = delete;
@ -41,21 +42,21 @@ namespace Ndk
template<typename SystemType, typename... Args> SystemType& AddSystem(Args&&... args); template<typename SystemType, typename... Args> SystemType& AddSystem(Args&&... args);
const EntityHandle& CreateEntity(); const EntityHandle& CreateEntity();
inline EntityList CreateEntities(unsigned int count); inline EntityVector CreateEntities(unsigned int count);
void Clear() noexcept; void Clear() noexcept;
const EntityHandle& CloneEntity(EntityId id); const EntityHandle& CloneEntity(EntityId id);
const EntityHandle& GetEntity(EntityId id); inline const EntityHandle& GetEntity(EntityId id);
inline const EntityList& GetEntities(); inline const EntityList& GetEntities() const;
inline BaseSystem& GetSystem(SystemIndex index); inline BaseSystem& GetSystem(SystemIndex index);
template<typename SystemType> SystemType& GetSystem(); template<typename SystemType> SystemType& GetSystem();
inline bool HasSystem(SystemIndex index) const; inline bool HasSystem(SystemIndex index) const;
template<typename SystemType> bool HasSystem() const; template<typename SystemType> bool HasSystem() const;
void KillEntity(Entity* entity); inline void KillEntity(Entity* entity);
inline void KillEntities(const EntityList& list); inline void KillEntities(const EntityVector& list);
inline bool IsEntityValid(const Entity* entity) const; inline bool IsEntityValid(const Entity* entity) const;
inline bool IsEntityIdValid(EntityId id) const; inline bool IsEntityIdValid(EntityId id) const;
@ -79,19 +80,22 @@ namespace Ndk
struct EntityBlock struct EntityBlock
{ {
EntityBlock(Entity&& e) : EntityBlock(Entity&& e) :
entity(std::move(e)) entity(std::move(e)),
handle(&entity)
{ {
} }
EntityBlock(EntityBlock&& block) = default; EntityBlock(EntityBlock&& block) = default;
Entity entity; Entity entity;
std::size_t aliveIndex; EntityHandle handle;
}; };
std::vector<std::unique_ptr<BaseSystem>> m_systems; std::vector<std::unique_ptr<BaseSystem>> m_systems;
std::vector<BaseSystem*> m_orderedSystems; std::vector<BaseSystem*> m_orderedSystems;
std::vector<EntityBlock> m_entities; std::vector<EntityBlock> m_entities;
std::vector<EntityBlock*> m_entityBlocks;
std::vector<std::unique_ptr<EntityBlock>> m_waitingEntities;
std::vector<EntityId> m_freeIdList; std::vector<EntityId> m_freeIdList;
EntityList m_aliveEntities; EntityList m_aliveEntities;
Nz::Bitset<Nz::UInt64> m_dirtyEntities; Nz::Bitset<Nz::UInt64> m_dirtyEntities;

View File

@ -82,9 +82,9 @@ namespace Ndk
* \param count Number of entities to create * \param count Number of entities to create
*/ */
inline World::EntityList World::CreateEntities(unsigned int count) inline World::EntityVector World::CreateEntities(unsigned int count)
{ {
EntityList list; EntityVector list;
list.reserve(count); list.reserve(count);
for (unsigned int i = 0; i < count; ++i) for (unsigned int i = 0; i < count; ++i)
@ -98,7 +98,7 @@ namespace Ndk
* \return A constant reference to the entities * \return A constant reference to the entities
*/ */
inline const World::EntityList& World::GetEntities() inline const EntityList& World::GetEntities() const
{ {
return m_aliveEntities; return m_aliveEntities;
} }
@ -164,18 +164,53 @@ namespace Ndk
return HasSystem(index); return HasSystem(index);
} }
/*!
* \brief Marks an entity for deletion
*
* \param Pointer to the entity
*
* \remark If the entity pointer is invalid, nothing is done
* \remark For safety, entities are not killed until the next world update
*/
inline void World::KillEntity(Entity* entity)
{
if (IsEntityValid(entity))
m_killedEntities.UnboundedSet(entity->GetId(), true);
}
/*! /*!
* \brief Kills a set of entities * \brief Kills a set of entities
* *
* This function has the same effect as calling KillEntity for every entity contained in the vector
*
* \param list Set of entities to kill * \param list Set of entities to kill
*/ */
inline void World::KillEntities(const EntityVector& list)
inline void World::KillEntities(const EntityList& list)
{ {
for (const EntityHandle& entity : list) for (const EntityHandle& entity : list)
KillEntity(entity); KillEntity(entity);
} }
/*!
* \brief Gets an entity
* \return A constant reference to a handle of the entity
*
* \param id Identifier of the entity
*
* \remark Handle referenced by this function can move in memory when updating the world, do not keep a reference to a handle from a world update to another
* \remark If an invalid identifier is provided, an error got triggered and an invalid handle is returned
*/
inline const EntityHandle& World::GetEntity(EntityId id)
{
if (IsEntityIdValid(id))
return m_entityBlocks[id]->handle;
else
{
NazaraError("Invalid ID");
return EntityHandle::InvalidHandle;
}
}
/*! /*!
* \brief Checks whether or not an entity is valid * \brief Checks whether or not an entity is valid
* \return true If it is the case * \return true If it is the case
@ -197,7 +232,7 @@ namespace Ndk
inline bool World::IsEntityIdValid(EntityId id) const inline bool World::IsEntityIdValid(EntityId id) const
{ {
return id < m_entities.size() && m_entities[id].entity.IsValid(); return id < m_entityBlocks.size() && m_entityBlocks[id]->entity.IsValid();
} }
/*! /*!
@ -266,10 +301,12 @@ namespace Ndk
{ {
m_aliveEntities = std::move(world.m_aliveEntities); m_aliveEntities = std::move(world.m_aliveEntities);
m_dirtyEntities = std::move(world.m_dirtyEntities); m_dirtyEntities = std::move(world.m_dirtyEntities);
m_entityBlocks = std::move(world.m_entityBlocks);
m_freeIdList = std::move(world.m_freeIdList); m_freeIdList = std::move(world.m_freeIdList);
m_killedEntities = std::move(world.m_killedEntities); m_killedEntities = std::move(world.m_killedEntities);
m_orderedSystems = std::move(world.m_orderedSystems); m_orderedSystems = std::move(world.m_orderedSystems);
m_orderedSystemsUpdated = world.m_orderedSystemsUpdated; m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
m_waitingEntities = std::move(world.m_waitingEntities);
m_entities = std::move(world.m_entities); m_entities = std::move(world.m_entities);
for (EntityBlock& block : m_entities) for (EntityBlock& block : m_entities)
@ -284,7 +321,7 @@ namespace Ndk
inline void World::Invalidate() inline void World::Invalidate()
{ {
m_dirtyEntities.Resize(m_entities.size(), false); m_dirtyEntities.Resize(m_entityBlocks.size(), false);
m_dirtyEntities.Set(true); // Activation of all bits m_dirtyEntities.Set(true); // Activation of all bits
} }

View File

@ -93,6 +93,17 @@ namespace Ndk
return m_projectionMatrix; return m_projectionMatrix;
} }
/*!
* \brief Gets the projection type of the camera
* \return Projection type of the camera
*/
Nz::ProjectionType CameraComponent::GetProjectionType() const
{
return m_projectionType;
}
/*! /*!
* \brief Gets the target of the camera * \brief Gets the target of the camera
* \return A constant reference to the render target of the camera * \return A constant reference to the render target of the camera

View File

@ -23,7 +23,9 @@ namespace Ndk
Entity::Entity(Entity&& entity) : Entity::Entity(Entity&& entity) :
HandledObject(std::move(entity)), HandledObject(std::move(entity)),
m_components(std::move(entity.m_components)), m_components(std::move(entity.m_components)),
m_containedInLists(std::move(entity.m_containedInLists)),
m_componentBits(std::move(entity.m_componentBits)), m_componentBits(std::move(entity.m_componentBits)),
m_removedComponentBits(std::move(entity.m_removedComponentBits)),
m_systemBits(std::move(entity.m_systemBits)), m_systemBits(std::move(entity.m_systemBits)),
m_id(entity.m_id), m_id(entity.m_id),
m_world(entity.m_world), m_world(entity.m_world),
@ -175,9 +177,15 @@ namespace Ndk
m_components.clear(); m_components.clear();
m_componentBits.Reset(); m_componentBits.Reset();
// And then free every handle // Free every handle
UnregisterAllHandles(); UnregisterAllHandles();
// Remove from every list
for (EntityList* list : m_containedInLists)
list->NotifyEntityDestruction(this);
m_containedInLists.clear();
m_valid = false; m_valid = false;
} }

View File

@ -0,0 +1,14 @@
// Copyright (C) 2017 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 <NDK/EntityList.hpp>
#include <NDK/World.hpp>
namespace Ndk
{
const EntityHandle& EntityList::iterator::operator*() const
{
return m_list->GetWorld()->GetEntity(static_cast<EntityId>(m_nextEntityId));
}
}

View File

@ -48,12 +48,14 @@ namespace Ndk
{ {
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
m_cameras.Remove(entity); for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it)
m_directionalLights.Remove(entity); {
m_drawables.Remove(entity); if (it->GetObject() == entity)
m_lights.Remove(entity); {
m_particleGroups.Remove(entity); m_cameras.erase(it);
m_pointSpotLights.Remove(entity); break;
}
}
if (entity->HasComponent<GraphicsComponent>()) if (entity->HasComponent<GraphicsComponent>())
{ {
@ -74,14 +76,23 @@ namespace Ndk
if (entity->HasComponent<CameraComponent>() && entity->HasComponent<NodeComponent>()) if (entity->HasComponent<CameraComponent>() && entity->HasComponent<NodeComponent>())
{ {
m_cameras.Insert(entity); m_cameras.emplace_back(entity);
std::sort(m_cameras.begin(), m_cameras.end(), [](const EntityHandle& handle1, const EntityHandle& handle2) std::sort(m_cameras.begin(), m_cameras.end(), [](const EntityHandle& handle1, const EntityHandle& handle2)
{ {
return handle1->GetComponent<CameraComponent>().GetLayer() < handle2->GetComponent<CameraComponent>().GetLayer(); return handle1->GetComponent<CameraComponent>().GetLayer() < handle2->GetComponent<CameraComponent>().GetLayer();
}); });
} }
else else
m_cameras.Remove(entity); {
for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it)
{
if (it->GetObject() == entity)
{
m_cameras.erase(it);
break;
}
}
}
if (entity->HasComponent<GraphicsComponent>() && entity->HasComponent<NodeComponent>()) if (entity->HasComponent<GraphicsComponent>() && entity->HasComponent<NodeComponent>())
{ {
@ -181,7 +192,7 @@ namespace Ndk
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>(); GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.EnsureBoundingVolumeUpdate(); graphicsComponent.EnsureBoundingVolumeUpdate();
} }
bool forceInvalidation = false; bool forceInvalidation = false;
std::size_t visibilityHash = m_drawableCulling.Cull(camComponent.GetFrustum(), &forceInvalidation); std::size_t visibilityHash = m_drawableCulling.Cull(camComponent.GetFrustum(), &forceInvalidation);

View File

@ -60,29 +60,51 @@ namespace Ndk
const EntityHandle& World::CreateEntity() const EntityHandle& World::CreateEntity()
{ {
EntityId id; EntityId id;
EntityBlock* entBlock;
if (!m_freeIdList.empty()) if (!m_freeIdList.empty())
{ {
// We get an identifier // We get an identifier
id = m_freeIdList.back(); id = m_freeIdList.back();
m_freeIdList.pop_back(); m_freeIdList.pop_back();
entBlock = &m_entities[id];
entBlock->handle.Reset(&entBlock->entity); //< Reset handle (as it was reset when entity got destroyed)
m_entityBlocks[id] = entBlock;
} }
else else
{ {
// We allocate a new entity // We allocate a new entity
id = static_cast<Ndk::EntityId>(m_entities.size()); id = static_cast<Ndk::EntityId>(m_entityBlocks.size());
// We can't use emplace_back due to the scope if (m_entities.capacity() > m_entities.size())
m_entities.push_back(Entity(this, id)); {
NazaraAssert(m_waitingEntities.empty(), "There should be no waiting entities if space is available in main container");
m_entities.push_back(Entity(this, id)); //< We can't use emplace_back due to the scope
entBlock = &m_entities.back();
}
else
{
// Pushing to entities would reallocate vector and thus, invalidate EntityHandles (which we don't want until world update)
// To prevent this, allocate them into a separate vector and move them at update
// For now, we are counting on m_entities grow strategy to keep allocation frequency low
m_waitingEntities.emplace_back(std::make_unique<EntityBlock>(Entity(this, id)));
entBlock = m_waitingEntities.back().get();
}
if (id >= m_entityBlocks.size())
m_entityBlocks.resize(id + 1);
m_entityBlocks[id] = entBlock;
} }
// We initialise the entity and we add it to the list of alive entities // We initialize the entity and we add it to the list of alive entities
Entity& entity = m_entities[id].entity; entBlock->entity.Create();
entity.Create();
m_aliveEntities.emplace_back(&entity); m_aliveEntities.Insert(&entBlock->entity);
m_entities[id].aliveIndex = m_aliveEntities.size() - 1;
return m_aliveEntities.back(); return entBlock->handle;
} }
/*! /*!
@ -96,8 +118,10 @@ namespace Ndk
// First, destruction of entities, then handles // First, destruction of entities, then handles
// This is made to avoid that handle warn uselessly entities before their destruction // This is made to avoid that handle warn uselessly entities before their destruction
m_entities.clear(); m_entities.clear();
m_entityBlocks.clear();
m_waitingEntities.clear();
m_aliveEntities.clear(); m_aliveEntities.Clear();
m_dirtyEntities.Clear(); m_dirtyEntities.Clear();
m_killedEntities.Clear(); m_killedEntities.Clear();
} }
@ -120,7 +144,7 @@ namespace Ndk
return EntityHandle::InvalidHandle; return EntityHandle::InvalidHandle;
} }
EntityHandle clone = CreateEntity(); const EntityHandle& clone = CreateEntity();
const Nz::Bitset<>& componentBits = original->GetComponentBits(); const Nz::Bitset<>& componentBits = original->GetComponentBits();
for (std::size_t i = componentBits.FindFirst(); i != componentBits.npos; i = componentBits.FindNext(i)) for (std::size_t i = componentBits.FindFirst(); i != componentBits.npos; i = componentBits.FindNext(i))
@ -129,41 +153,7 @@ namespace Ndk
clone->AddComponent(std::move(component)); clone->AddComponent(std::move(component));
} }
return GetEntity(clone->GetId()); return clone;
}
/*!
* \brief Kills an entity
*
* \param Pointer to the entity
*
* \remark No change is done if entity is invalid
*/
void World::KillEntity(Entity* entity)
{
if (IsEntityValid(entity))
m_killedEntities.UnboundedSet(entity->GetId(), true);
}
/*!
* \brief Gets an entity
* \return A constant reference to the modified entity
*
* \param id Identifier of the entity
*
* \remark Produces a NazaraError if entity identifier is not valid
*/
const EntityHandle& World::GetEntity(EntityId id)
{
if (IsEntityIdValid(id))
return m_aliveEntities[m_entities[id].aliveIndex];
else
{
NazaraError("Invalid ID");
return EntityHandle::InvalidHandle;
}
} }
/*! /*!
@ -177,45 +167,43 @@ namespace Ndk
if (!m_orderedSystemsUpdated) if (!m_orderedSystemsUpdated)
ReorderSystems(); ReorderSystems();
// Move waiting entities to entity list
if (!m_waitingEntities.empty())
{
constexpr std::size_t MinEntityCapacity = 10; //< We want to be able to grow maximum entity count by at least ten without going to the waiting list
m_entities.reserve(m_entities.size() + m_waitingEntities.size() + MinEntityCapacity);
for (auto& blockPtr : m_waitingEntities)
m_entities.push_back(std::move(*blockPtr));
m_waitingEntities.clear();
// Update entity blocks pointers
for (std::size_t i = 0; i < m_entities.size(); ++i)
m_entityBlocks[i] = &m_entities[i];
}
// Handle killed entities before last call // Handle killed entities before last call
for (std::size_t i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i)) for (std::size_t i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i))
{ {
EntityBlock& block = m_entities[i]; NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range");
Entity& entity = block.entity;
NazaraAssert(entity.IsValid(), "Entity must be valid"); Entity* entity = &m_entityBlocks[i]->entity;
// Destruction of the entity (invalidation of handle by the same way) // Destruction of the entity (invalidation of handle by the same way)
entity.Destroy(); entity->Destroy();
// Send back the identifier of the entity to the free queue // Send back the identifier of the entity to the free queue
m_freeIdList.push_back(entity.GetId()); m_freeIdList.push_back(entity->GetId());
// We take out the handle from the list of alive entities
// With the idiom swap and pop
NazaraAssert(block.aliveIndex < m_aliveEntities.size(), "Alive index out of range");
if (block.aliveIndex < m_aliveEntities.size() - 1) // If it's not the last handle
{
EntityHandle& lastHandle = m_aliveEntities.back();
EntityHandle& myHandle = m_aliveEntities[block.aliveIndex];
myHandle = std::move(lastHandle);
// We don't forget to update the index associated to the entity
m_entities[myHandle->GetId()].aliveIndex = block.aliveIndex;
}
m_aliveEntities.pop_back();
} }
m_killedEntities.Reset(); m_killedEntities.Reset();
// Handle of entities which need an update from the systems // Handle of entities which need an update from the systems
for (std::size_t i = m_dirtyEntities.FindFirst(); i != m_dirtyEntities.npos; i = m_dirtyEntities.FindNext(i)) for (std::size_t i = m_dirtyEntities.FindFirst(); i != m_dirtyEntities.npos; i = m_dirtyEntities.FindNext(i))
{ {
NazaraAssert(i < m_entities.size(), "Entity index out of range"); NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range");
Entity* entity = &m_entities[i].entity; Entity* entity = &m_entityBlocks[i]->entity;
// Check entity validity (as it could have been reported as dirty and killed during the same iteration) // Check entity validity (as it could have been reported as dirty and killed during the same iteration)
if (!entity->IsValid()) if (!entity->IsValid())
@ -242,7 +230,7 @@ namespace Ndk
} }
else else
{ {
// No, it shouldn't, remove it if it's part of the system // No it shouldn't, remove it if it's part of the system
if (partOfSystem) if (partOfSystem)
system->RemoveEntity(entity); system->RemoveEntity(entity);
} }

View File

@ -51,7 +51,7 @@ namespace Nz
virtual void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) = 0; virtual void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) = 0;
virtual void AddPointLight(const PointLight& light); virtual void AddPointLight(const PointLight& light);
virtual void AddSpotLight(const SpotLight& light); virtual void AddSpotLight(const SpotLight& light);
virtual void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) = 0; virtual void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) = 0;
virtual void Clear(bool fully = false); virtual void Clear(bool fully = false);

View File

@ -33,6 +33,7 @@ namespace Nz
virtual Vector3f GetForward() const = 0; virtual Vector3f GetForward() const = 0;
virtual const Frustumf& GetFrustum() const = 0; virtual const Frustumf& GetFrustum() const = 0;
virtual const Matrix4f& GetProjectionMatrix() const = 0; virtual const Matrix4f& GetProjectionMatrix() const = 0;
virtual Nz::ProjectionType GetProjectionType() const = 0;
virtual const RenderTarget* GetTarget() const = 0; virtual const RenderTarget* GetTarget() const = 0;
virtual const Matrix4f& GetViewMatrix() const = 0; virtual const Matrix4f& GetViewMatrix() const = 0;
virtual const Recti& GetViewport() const = 0; virtual const Recti& GetViewport() const = 0;

View File

@ -37,7 +37,7 @@ namespace Nz
void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const float> alphaPtr) override; void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const float> alphaPtr) override;
void AddDrawable(int renderOrder, const Drawable* drawable) override; void AddDrawable(int renderOrder, const Drawable* drawable) override;
void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; void 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 AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override;
void Clear(bool fully = false) override; void Clear(bool fully = false) override;

View File

@ -38,7 +38,7 @@ namespace Nz
void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override;
void AddPointLight(const PointLight& light) override; void AddPointLight(const PointLight& light) override;
void AddSpotLight(const SpotLight& light) override; void AddSpotLight(const SpotLight& light) override;
void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) override; void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override;
private: private:
inline bool IsMaterialSuitable(const Material* material) const; inline bool IsMaterialSuitable(const Material* material) const;

View File

@ -13,6 +13,7 @@
#include <Nazara/Graphics/Material.hpp> #include <Nazara/Graphics/Material.hpp>
#include <Nazara/Math/Box.hpp> #include <Nazara/Math/Box.hpp>
#include <Nazara/Math/Matrix4.hpp> #include <Nazara/Math/Matrix4.hpp>
#include <Nazara/Math/Plane.hpp>
#include <Nazara/Utility/IndexBuffer.hpp> #include <Nazara/Utility/IndexBuffer.hpp>
#include <Nazara/Utility/MeshData.hpp> #include <Nazara/Utility/MeshData.hpp>
#include <Nazara/Utility/VertexBuffer.hpp> #include <Nazara/Utility/VertexBuffer.hpp>
@ -41,7 +42,7 @@ namespace Nz
void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const float> alphaPtr) override; void AddBillboards(int renderOrder, const Material* material, unsigned int count, SparsePtr<const Vector3f> positionPtr, SparsePtr<const float> sizePtr, SparsePtr<const float> anglePtr, SparsePtr<const float> alphaPtr) override;
void AddDrawable(int renderOrder, const Drawable* drawable) override; void AddDrawable(int renderOrder, const Drawable* drawable) override;
void AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) override; void 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 AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override;
void Clear(bool fully = false) override; void Clear(bool fully = false) override;
@ -73,7 +74,7 @@ namespace Nz
std::vector<BillboardData> billboards; std::vector<BillboardData> billboards;
}; };
typedef std::map<const Material*, BatchedBillboardEntry, MaterialComparator> BatchedBillboardContainer; using BatchedBillboardContainer = std::map<const Material*, BatchedBillboardEntry, MaterialComparator>;
struct BatchedBillboardPipelineEntry struct BatchedBillboardPipelineEntry
{ {
@ -81,7 +82,7 @@ namespace Nz
bool enabled = false; bool enabled = false;
}; };
typedef std::map<const MaterialPipeline*, BatchedBillboardPipelineEntry, MaterialPipelineComparator> BillboardPipelineBatches; using BillboardPipelineBatches = std::map<const MaterialPipeline*, BatchedBillboardPipelineEntry, MaterialPipelineComparator>;
/// Sprites /// Sprites
struct SpriteChain_XYZ_Color_UV struct SpriteChain_XYZ_Color_UV
@ -97,7 +98,7 @@ namespace Nz
std::vector<SpriteChain_XYZ_Color_UV> spriteChains; std::vector<SpriteChain_XYZ_Color_UV> spriteChains;
}; };
typedef std::map<const Texture*, BatchedSpriteEntry> SpriteOverlayBatches; using SpriteOverlayBatches = std::map<const Texture*, BatchedSpriteEntry>;
struct BatchedBasicSpriteEntry struct BatchedBasicSpriteEntry
{ {
@ -107,7 +108,7 @@ namespace Nz
bool enabled = false; bool enabled = false;
}; };
typedef std::map<const Material*, BatchedBasicSpriteEntry, MaterialComparator> SpriteMaterialBatches; using SpriteMaterialBatches = std::map<const Material*, BatchedBasicSpriteEntry, MaterialComparator>;
struct BatchedSpritePipelineEntry struct BatchedSpritePipelineEntry
{ {
@ -115,7 +116,7 @@ namespace Nz
bool enabled = false; bool enabled = false;
}; };
typedef std::map<const MaterialPipeline*, BatchedSpritePipelineEntry, MaterialPipelineComparator> SpritePipelineBatches; using SpritePipelineBatches = std::map<const MaterialPipeline*, BatchedSpritePipelineEntry, MaterialPipelineComparator>;
/// Meshes /// Meshes
struct MeshDataComparator struct MeshDataComparator
@ -132,7 +133,7 @@ namespace Nz
Spheref squaredBoundingSphere; Spheref squaredBoundingSphere;
}; };
typedef std::map<MeshData, MeshInstanceEntry, MeshDataComparator> MeshInstanceContainer; using MeshInstanceContainer = std::map<MeshData, MeshInstanceEntry, MeshDataComparator>;
struct BatchedModelEntry struct BatchedModelEntry
{ {
@ -142,7 +143,7 @@ namespace Nz
bool enabled = false; bool enabled = false;
}; };
typedef std::map<const Material*, BatchedModelEntry, MaterialComparator> MeshMaterialBatches; using MeshMaterialBatches = std::map<const Material*, BatchedModelEntry, MaterialComparator>;
struct BatchedMaterialEntry struct BatchedMaterialEntry
{ {
@ -150,25 +151,33 @@ namespace Nz
MeshMaterialBatches materialMap; MeshMaterialBatches materialMap;
}; };
typedef std::map<const MaterialPipeline*, BatchedMaterialEntry, MaterialPipelineComparator> MeshPipelineBatches; using MeshPipelineBatches = std::map<const MaterialPipeline*, BatchedMaterialEntry, MaterialPipelineComparator>;
struct TransparentModelData struct UnbatchedModelData
{ {
Matrix4f transformMatrix; Matrix4f transformMatrix;
MeshData meshData; MeshData meshData;
Spheref squaredBoundingSphere; Spheref obbSphere;
const Material* material; const Material* material;
}; };
typedef std::vector<std::size_t> TransparentModelContainer; struct UnbatchedSpriteData
{
std::size_t spriteCount;
const Material* material;
const Texture* overlay;
const VertexStruct_XYZ_Color_UV* vertices;
};
struct Layer struct Layer
{ {
BillboardPipelineBatches billboards; BillboardPipelineBatches billboards;
SpritePipelineBatches basicSprites; SpritePipelineBatches opaqueSprites;
MeshPipelineBatches opaqueModels; MeshPipelineBatches opaqueModels;
TransparentModelContainer transparentModels; std::vector<std::size_t> depthSortedMeshes;
std::vector<TransparentModelData> transparentModelData; std::vector<std::size_t> depthSortedSprites;
std::vector<UnbatchedModelData> depthSortedMeshData;
std::vector<UnbatchedSpriteData> depthSortedSpriteData;
std::vector<const Drawable*> otherDrawables; std::vector<const Drawable*> otherDrawables;
unsigned int clearCount = 0; unsigned int clearCount = 0;
}; };
@ -179,6 +188,10 @@ namespace Nz
BillboardData* GetBillboardData(int renderOrder, const Material* material, unsigned int count); BillboardData* GetBillboardData(int renderOrder, const Material* material, unsigned int count);
Layer& GetLayer(int i); ///TODO: Inline Layer& GetLayer(int i); ///TODO: Inline
void SortBillboards(Layer& layer, const Planef& nearPlane);
void SortForOrthographic(const AbstractViewer* viewer);
void SortForPerspective(const AbstractViewer* viewer);
void OnIndexBufferInvalidation(const IndexBuffer* indexBuffer); void OnIndexBufferInvalidation(const IndexBuffer* indexBuffer);
void OnMaterialInvalidation(const Material* material); void OnMaterialInvalidation(const Material* material);
void OnTextureInvalidation(const Texture* texture); void OnTextureInvalidation(const Texture* texture);

View File

@ -43,6 +43,7 @@ namespace Nz
void DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const;
void DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const;
void DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const;
void DrawOrderedSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const;
void DrawTransparentModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawTransparentModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const;
const ShaderUniforms* GetShaderUniforms(const Shader* shader) const; const ShaderUniforms* GetShaderUniforms(const Shader* shader) const;
void OnShaderInvalidated(const Shader* shader) const; void OnShaderInvalidated(const Shader* shader) const;

View File

@ -98,7 +98,13 @@ namespace Nz
return 0; return 0;
} }
template<typename T> /*constexpr*/ std::enable_if_t<!std::is_signed<T>::value || !std::is_integral<T>::value, bool> NumberEquals(T a, T b, T maxDifference) template<typename T> /*constexpr*/ std::enable_if_t<std::is_floating_point<T>::value, bool> NumberEquals(T a, T b, T maxDifference)
{
T diff = std::abs(a - b);
return diff <= maxDifference;
}
template<typename T> /*constexpr*/ std::enable_if_t<!std::is_signed<T>::value || (!std::is_integral<T>::value && !std::is_floating_point<T>::value), bool> NumberEquals(T a, T b, T maxDifference)
{ {
if (b > a) if (b > a)
std::swap(a, b); std::swap(a, b);
@ -112,13 +118,8 @@ namespace Nz
if (b > a) if (b > a)
std::swap(a, b); std::swap(a, b);
if ((b < 0) && (a > std::numeric_limits<T>::max() + b)) using UnsignedT = std::make_unsigned_t<T>;
return false; return static_cast<UnsignedT>(a) - static_cast<UnsignedT>(b) <= static_cast<UnsignedT>(maxDifference);
if ((b > 0) && (a < std::numeric_limits<T>::min() + b))
return false;
return std::abs(a - b) <= maxDifference;
} }
} }
@ -487,7 +488,7 @@ namespace Nz
template<typename T, typename T2> template<typename T, typename T2>
constexpr T Lerp(const T& from, const T& to, const T2& interpolation) constexpr T Lerp(const T& from, const T& to, const T2& interpolation)
{ {
return from + interpolation * (to - from); return static_cast<T>(from + interpolation * (to - from));
} }
/*! /*!

View File

@ -74,8 +74,8 @@ namespace Nz
Box& Transform(const Matrix4<T>& matrix, bool applyTranslation = true); Box& Transform(const Matrix4<T>& matrix, bool applyTranslation = true);
Box& Translate(const Vector3<T>& translation); Box& Translate(const Vector3<T>& translation);
T& operator[](unsigned int i); T& operator[](std::size_t i);
T operator[](unsigned int i) const; T operator[](std::size_t i) const;
Box operator*(T scalar) const; Box operator*(T scalar) const;
Box operator*(const Vector3<T>& vec) const; Box operator*(const Vector3<T>& vec) const;

View File

@ -735,24 +735,15 @@ namespace Nz
* \brief Returns the ith element of the box * \brief Returns the ith element of the box
* \return A reference to 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 Access to index greater than 6 is undefined behavior
* \remark Produce a NazaraError if you try to acces to index greather than 6 with NAZARA_MATH_SAFE defined * \remark Produce a NazaraError if you try to access to index greater 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 * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 6
*/ */
template<typename T> template<typename T>
T& Box<T>::operator[](unsigned int i) T& Box<T>::operator[](std::size_t i)
{ {
#if NAZARA_MATH_SAFE NazaraAssert(i < 6, "Index out of range");
if (i >= 6)
{
StringStream ss;
ss << "Index out of range: (" << i << " >= 6)";
NazaraError(ss);
throw std::domain_error(ss.ToString());
}
#endif
return *(&x+i); return *(&x+i);
} }
@ -761,24 +752,15 @@ namespace Nz
* \brief Returns the ith element of the box * \brief Returns the ith element of the box
* \return A value to 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 Access to index greater than 6 is undefined behavior
* \remark Produce a NazaraError if you try to acces to index greather than 6 with NAZARA_MATH_SAFE defined * \remark Produce a NazaraError if you try to access to index greater 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 * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 6
*/ */
template<typename T> template<typename T>
T Box<T>::operator[](unsigned int i) const T Box<T>::operator[](std::size_t i) const
{ {
#if NAZARA_MATH_SAFE NazaraAssert(i < 6, "Index out of range");
if (i >= 6)
{
StringStream ss;
ss << "Index out of range: (" << i << " >= 6)";
NazaraError(ss);
throw std::domain_error(ss.ToString());
}
#endif
return *(&x+i); return *(&x+i);
} }

View File

@ -64,8 +64,8 @@ namespace Nz
Rect& Translate(const Vector2<T>& translation); Rect& Translate(const Vector2<T>& translation);
T& operator[](unsigned int i); T& operator[](std::size_t i);
T operator[](unsigned int i) const; T operator[](std::size_t i) const;
Rect operator*(T scalar) const; Rect operator*(T scalar) const;
Rect operator*(const Vector2<T>& vec) const; Rect operator*(const Vector2<T>& vec) const;

View File

@ -578,23 +578,14 @@ namespace Nz
* \return A reference to 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 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 * \remark Produce a NazaraError if you try to access to index greater 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 * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4
*/ */
template<typename T> template<typename T>
T& Rect<T>::operator[](unsigned int i) T& Rect<T>::operator[](std::size_t i)
{ {
#if NAZARA_MATH_SAFE NazaraAssert(i < 4, "Index out of range");
if (i >= 4)
{
StringStream ss;
ss << "Index out of range: (" << i << " >= 4)";
NazaraError(ss);
throw std::domain_error(ss.ToString());
}
#endif
return *(&x+i); return *(&x+i);
} }
@ -603,24 +594,15 @@ namespace Nz
* \brief Returns the ith element of the rectangle * \brief Returns the ith element of the rectangle
* \return A value to 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 Access to index greater than 4 is undefined behavior
* \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined * \remark Produce a NazaraError if you try to access to index greater 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 * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4
*/ */
template<typename T> template<typename T>
T Rect<T>::operator[](unsigned int i) const T Rect<T>::operator[](std::size_t i) const
{ {
#if NAZARA_MATH_SAFE NazaraAssert(i < 4, "Index out of range");
if (i >= 4)
{
StringStream ss;
ss << "Index out of range: (" << i << " >= 4)";
NazaraError(ss);
throw std::domain_error(ss.ToString());
}
#endif
return *(&x+i); return *(&x+i);
} }

View File

@ -60,8 +60,8 @@ namespace Nz
String ToString() const; String ToString() const;
T& operator[](unsigned int i); T& operator[](std::size_t i);
T operator[](unsigned int i) const; T operator[](std::size_t i) const;
Sphere operator*(T scalar) const; Sphere operator*(T scalar) const;
Sphere& operator=(const Sphere& other) = default; Sphere& operator=(const Sphere& other) = default;

View File

@ -466,24 +466,15 @@ namespace Nz
* \brief Returns the ith element of the sphere * \brief Returns the ith element of the sphere
* \return A reference to 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 Access to index greater than 4 is undefined behavior
* \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined * \remark Produce a NazaraError if you try to access to index greater 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 * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4
*/ */
template<typename T> template<typename T>
T& Sphere<T>::operator[](unsigned int i) T& Sphere<T>::operator[](std::size_t i)
{ {
#if NAZARA_MATH_SAFE NazaraAssert(i < 4, "Index out of range");
if (i >= 4)
{
StringStream ss;
ss << "Index out of range: (" << i << " >= 4)";
NazaraError(ss);
throw std::domain_error(ss.ToString());
}
#endif
return *(&x+i); return *(&x+i);
} }
@ -492,24 +483,15 @@ namespace Nz
* \brief Returns the ith element of the sphere * \brief Returns the ith element of the sphere
* \return A value to 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 Access to index greater than 4 is undefined behavior
* \remark Produce a NazaraError if you try to acces to index greather than 4 with NAZARA_MATH_SAFE defined * \remark Produce a NazaraError if you try to access to index greater 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 * \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of you try to acces to index greather than 4
*/ */
template<typename T> template<typename T>
T Sphere<T>::operator[](unsigned int i) const T Sphere<T>::operator[](std::size_t i) const
{ {
#if NAZARA_MATH_SAFE NazaraAssert(i < 4, "Index out of range");
if (i >= 4)
{
StringStream ss;
ss << "Index out of range: (" << i << " >= 4)";
NazaraError(ss);
throw std::domain_error(ss.ToString());
}
#endif
return *(&x+i); return *(&x+i);
} }

View File

@ -7,7 +7,7 @@
#ifndef NAZARA_ENUMS_NETWORK_HPP #ifndef NAZARA_ENUMS_NETWORK_HPP
#define NAZARA_ENUMS_NETWORK_HPP #define NAZARA_ENUMS_NETWORK_HPP
#include <Nazara/Prerequesites.hpp> #include <Nazara/Core/Flags.hpp>
namespace Nz namespace Nz
{ {
@ -91,6 +91,23 @@ namespace Nz
SocketError_Max = SocketError_UnreachableHost SocketError_Max = SocketError_UnreachableHost
}; };
enum SocketPollEvent
{
SocketPollEvent_Read, //< One or more sockets is ready for a read operation
SocketPollEvent_Write, //< One or more sockets is ready for a write operation
SocketPollEvent_Max = SocketPollEvent_Write
};
template<>
struct EnumAsFlags<SocketPollEvent>
{
static constexpr bool value = true;
static constexpr int max = SocketPollEvent_Max;
};
using SocketPollEventFlags = Flags<SocketPollEvent>;
enum SocketState enum SocketState
{ {
SocketState_Bound, //< The socket is currently bound SocketState_Bound, //< The socket is currently bound

View File

@ -24,10 +24,11 @@ namespace Nz
void Clear(); void Clear();
bool IsReady(const AbstractSocket& socket) const; bool IsReadyToRead(const AbstractSocket& socket) const;
bool IsReadyToWrite(const AbstractSocket& socket) const;
bool IsRegistered(const AbstractSocket& socket) const; bool IsRegistered(const AbstractSocket& socket) const;
bool RegisterSocket(AbstractSocket& socket); bool RegisterSocket(AbstractSocket& socket, SocketPollEventFlags eventFlags);
void UnregisterSocket(AbstractSocket& socket); void UnregisterSocket(AbstractSocket& socket);
bool Wait(UInt64 msTimeout); bool Wait(UInt64 msTimeout);
@ -41,4 +42,4 @@ namespace Nz
#include <Nazara/Network/SocketPoller.inl> #include <Nazara/Network/SocketPoller.inl>
#endif // NAZARA_SOCKETPOLLER_HPP #endif // NAZARA_SOCKETPOLLER_HPP

View File

@ -64,10 +64,10 @@ namespace Nz
struct Callback struct Callback
{ {
ContactEndCallback endCallback; ContactEndCallback endCallback = nullptr;
ContactPreSolveCallback preSolveCallback; ContactPreSolveCallback preSolveCallback = nullptr;
ContactPostSolveCallback postSolveCallback; ContactPostSolveCallback postSolveCallback = nullptr;
ContactStartCallback startCallback; ContactStartCallback startCallback = nullptr;
void* userdata; void* userdata;
}; };

View File

@ -26,15 +26,15 @@ namespace Nz
Reference operator*() const; Reference operator*() const;
Reference operator[](unsigned int index) const; Reference operator[](std::size_t index) const;
IndexIterator& operator=(const IndexIterator& iterator); IndexIterator& operator=(const IndexIterator& iterator);
IndexIterator operator+(unsigned int indexCount) const; IndexIterator operator+(std::size_t indexCount) const;
IndexIterator operator-(unsigned int indexCount) const; IndexIterator operator-(std::size_t indexCount) const;
IndexIterator& operator+=(unsigned int indexCount); IndexIterator& operator+=(std::size_t indexCount);
IndexIterator& operator-=(unsigned int indexCount); IndexIterator& operator-=(std::size_t indexCount);
IndexIterator& operator++(); IndexIterator& operator++();
IndexIterator operator++(int); IndexIterator operator++(int);
@ -50,10 +50,10 @@ namespace Nz
friend bool operator>=(const IndexIterator& lhs, const IndexIterator& rhs); friend bool operator>=(const IndexIterator& lhs, const IndexIterator& rhs);
private: private:
IndexIterator(IndexMapper* mapper, unsigned int index); IndexIterator(IndexMapper* mapper, std::size_t index);
IndexMapper* m_mapper; IndexMapper* m_mapper;
unsigned int m_index; std::size_t m_index;
}; };
class IndexIterator::Reference class IndexIterator::Reference
@ -70,10 +70,10 @@ namespace Nz
operator UInt32() const; operator UInt32() const;
private: private:
Reference(IndexMapper* mapper, unsigned int index); Reference(IndexMapper* mapper, std::size_t index);
IndexMapper* m_mapper; IndexMapper* m_mapper;
unsigned int m_index; std::size_t m_index;
}; };
} }

View File

@ -20,7 +20,7 @@ namespace Nz
{ {
} }
inline IndexIterator::IndexIterator(IndexMapper* mapper, unsigned int index) : inline IndexIterator::IndexIterator(IndexMapper* mapper, std::size_t index) :
m_mapper(mapper), m_mapper(mapper),
m_index(index) m_index(index)
{ {
@ -31,7 +31,7 @@ namespace Nz
return Reference(m_mapper, m_index); return Reference(m_mapper, m_index);
} }
inline IndexIterator::Reference IndexIterator::operator[](unsigned int index) const inline IndexIterator::Reference IndexIterator::operator[](std::size_t index) const
{ {
return Reference(m_mapper, m_index+index); return Reference(m_mapper, m_index+index);
} }
@ -44,24 +44,24 @@ namespace Nz
return *this; return *this;
} }
inline IndexIterator IndexIterator::operator+(unsigned int indexCount) const inline IndexIterator IndexIterator::operator+(std::size_t indexCount) const
{ {
return IndexIterator(m_mapper, m_index + indexCount); return IndexIterator(m_mapper, m_index + indexCount);
} }
inline IndexIterator IndexIterator::operator-(unsigned int indexCount) const inline IndexIterator IndexIterator::operator-(std::size_t indexCount) const
{ {
return IndexIterator(m_mapper, m_index - indexCount); return IndexIterator(m_mapper, m_index - indexCount);
} }
inline IndexIterator& IndexIterator::operator+=(unsigned int indexCount) inline IndexIterator& IndexIterator::operator+=(std::size_t indexCount)
{ {
m_index += indexCount; m_index += indexCount;
return *this; return *this;
} }
inline IndexIterator& IndexIterator::operator-=(unsigned int indexCount) inline IndexIterator& IndexIterator::operator-=(std::size_t indexCount)
{ {
m_index += indexCount; m_index += indexCount;
@ -133,7 +133,7 @@ namespace Nz
/**************************IndexIterator::Reference*************************/ /**************************IndexIterator::Reference*************************/
inline IndexIterator::Reference::Reference(IndexMapper* mapper, unsigned int index) : inline IndexIterator::Reference::Reference(IndexMapper* mapper, std::size_t index) :
m_mapper(mapper), m_mapper(mapper),
m_index(index) m_index(index)
{ {
@ -148,7 +148,7 @@ namespace Nz
inline IndexIterator::Reference& IndexIterator::Reference::operator=(const IndexIterator::Reference& reference) inline IndexIterator::Reference& IndexIterator::Reference::operator=(const IndexIterator::Reference& reference)
{ {
m_mapper->Set(m_index, reference); // Conversion implicite en UInt32 m_mapper->Set(m_index, reference); // Implicit conversion to UInt32
return *this; return *this;
} }

View File

@ -17,9 +17,6 @@ namespace Nz
class IndexIterator; class IndexIterator;
class SubMesh; class SubMesh;
using IndexMapperGetter = UInt32 (*)(const void* buffer, unsigned int i);
using IndexMapperSetter = void (*)(void* buffer, unsigned int i, UInt32 value);
class NAZARA_UTILITY_API IndexMapper class NAZARA_UTILITY_API IndexMapper
{ {
public: public:
@ -29,11 +26,11 @@ namespace Nz
IndexMapper(const SubMesh* subMesh, BufferAccess access = BufferAccess_ReadOnly); IndexMapper(const SubMesh* subMesh, BufferAccess access = BufferAccess_ReadOnly);
~IndexMapper() = default; ~IndexMapper() = default;
UInt32 Get(unsigned int i) const; UInt32 Get(std::size_t i) const;
const IndexBuffer* GetBuffer() const; const IndexBuffer* GetBuffer() const;
unsigned int GetIndexCount() const; std::size_t GetIndexCount() const;
void Set(unsigned int i, UInt32 value); void Set(std::size_t i, UInt32 value);
void Unmap(); void Unmap();
@ -45,10 +42,13 @@ namespace Nz
// Méthodes STD // Méthodes STD
private: private:
using Getter = UInt32(*)(const void* buffer, std::size_t i);
using Setter = void(*)(void* buffer, std::size_t i, UInt32 value);
BufferMapper<IndexBuffer> m_mapper; BufferMapper<IndexBuffer> m_mapper;
IndexMapperGetter m_getter; Getter m_getter;
IndexMapperSetter m_setter; Setter m_setter;
unsigned int m_indexCount; std::size_t m_indexCount;
}; };
} }

View File

@ -24,7 +24,7 @@ namespace Nz
bool Advance(); bool Advance();
UInt32 operator[](unsigned int i) const; UInt32 operator[](std::size_t i) const;
void Unmap(); void Unmap();
@ -32,8 +32,8 @@ namespace Nz
PrimitiveMode m_primitiveMode; PrimitiveMode m_primitiveMode;
UInt32 m_triangleIndices[3]; UInt32 m_triangleIndices[3];
IndexMapper m_indexMapper; IndexMapper m_indexMapper;
unsigned int m_currentIndex; std::size_t m_currentIndex;
unsigned int m_indexCount; std::size_t m_indexCount;
}; };
} }

View File

@ -153,7 +153,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
if (animatedMesh) if (animatedMesh)
{ {
mesh->CreateSkeletal(joints.size()); mesh->CreateSkeletal(UInt32(joints.size()));
Skeleton* skeleton = mesh->GetSkeleton(); Skeleton* skeleton = mesh->GetSkeleton();
@ -171,7 +171,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
mesh->CreateStatic(); mesh->CreateStatic();
// aiMaterial index in scene => Material index and data in Mesh // aiMaterial index in scene => Material index and data in Mesh
std::unordered_map<unsigned int, std::pair<std::size_t, ParameterList>> materials; std::unordered_map<unsigned int, std::pair<UInt32, ParameterList>> materials;
for (unsigned int i = 0; i < scene->mNumMeshes; ++i) for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
{ {
@ -300,7 +300,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue) == aiReturn_SUCCESS) if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue) == aiReturn_SUCCESS)
matData.SetParameter(MaterialData::FaceCulling, !iValue); matData.SetParameter(MaterialData::FaceCulling, !iValue);
matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(materials.size(), std::move(matData)))).first; matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(UInt32(materials.size()), std::move(matData)))).first;
} }
subMesh->SetMaterialIndex(matIt->first); subMesh->SetMaterialIndex(matIt->first);
@ -308,7 +308,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
mesh->AddSubMesh(subMesh); mesh->AddSubMesh(subMesh);
} }
mesh->SetMaterialCount(std::max<UInt32>(materials.size(), 1)); mesh->SetMaterialCount(std::max<UInt32>(UInt32(materials.size()), 1));
for (const auto& pair : materials) for (const auto& pair : materials)
mesh->SetMaterialData(pair.second.first, pair.second.second); mesh->SetMaterialData(pair.second.first, pair.second.second);
} }

View File

@ -43,7 +43,7 @@ Vous pouvez lire des tutoriaux sur l'installation, la compilation et l'utilisati
[Wiki](https://github.com/DigitalPulseSoftware/NazaraEngine/wiki) [Wiki](https://github.com/DigitalPulseSoftware/NazaraEngine/wiki)
[Forum](https://forum.digitalpulsesoftware.net) [Forum](https://forum.digitalpulsesoftware.net)
###Remerciements: ### Remerciements:
- **RafBill** et **Raakz:** Recherche de bugs et/ou tests - **RafBill** et **Raakz:** Recherche de bugs et/ou tests
- **Fissal "DrFisher" Hannoun**: Aide et conseils lors de la conception de l'architecture du moteur - **Fissal "DrFisher" Hannoun**: Aide et conseils lors de la conception de l'architecture du moteur

View File

@ -101,7 +101,7 @@ namespace Nz
{ {
struct HashMD5_state struct HashMD5_state
{ {
UInt32 count[2]; /* message length in bits, lsw first */ std::size_t count[2]; /* message length in bits, lsw first */
UInt32 abcd[4]; /* digest buffer */ UInt32 abcd[4]; /* digest buffer */
UInt8 buf[64]; /* accumulate block */ UInt8 buf[64]; /* accumulate block */
}; };
@ -280,9 +280,9 @@ namespace Nz
void HashMD5::Append(const UInt8* data, std::size_t len) void HashMD5::Append(const UInt8* data, std::size_t len)
{ {
const UInt8 *p = data; const UInt8 *p = data;
int left = len; std::size_t left = len;
int offset = (m_state->count[0] >> 3) & 63; int offset = (m_state->count[0] >> 3) & 63;
UInt32 nbits = len << 3; std::size_t nbits = len << 3;
if (len <= 0) if (len <= 0)
return; return;
@ -296,7 +296,7 @@ namespace Nz
/* Process an initial partial block. */ /* Process an initial partial block. */
if (offset) if (offset)
{ {
int copy = (offset + len > 64 ? 64 - offset : len); std::size_t copy = (offset + len > 64 ? 64 - offset : len);
std::memcpy(m_state->buf + offset, p, copy); std::memcpy(m_state->buf + offset, p, copy);
if (offset + copy < 64) if (offset + copy < 64)

View File

@ -75,8 +75,8 @@ namespace Nz
{ {
struct HashWhirlpool_state struct HashWhirlpool_state
{ {
int bufferBits; // current number of bits on the buffer */ std::size_t bufferBits; // current number of bits on the buffer */
int bufferPos; // current (possibly incomplete) byte slot on the buffer */ std::size_t bufferPos; // current (possibly incomplete) byte slot on the buffer */
UInt8 bitLength[32]; // global number of hashed bits (256-bit counter) */ UInt8 bitLength[32]; // global number of hashed bits (256-bit counter) */
UInt8 buffer[64]; // buffer of data to hash */ UInt8 buffer[64]; // buffer of data to hash */
UInt64 hash[8]; // the hashing state */ UInt64 hash[8]; // the hashing state */
@ -877,8 +877,8 @@ namespace Nz
UInt32 b; UInt32 b;
UInt8* buffer = m_state->buffer; UInt8* buffer = m_state->buffer;
UInt8* bitLength = m_state->bitLength; UInt8* bitLength = m_state->bitLength;
int bufferBits = m_state->bufferBits; std::size_t bufferBits = m_state->bufferBits;
int bufferPos = m_state->bufferPos; std::size_t bufferPos = m_state->bufferPos;
// tally the length of the added data // tally the length of the added data
UInt64 value = len; UInt64 value = len;
@ -968,8 +968,8 @@ namespace Nz
UInt8 *buffer = m_state->buffer; UInt8 *buffer = m_state->buffer;
UInt8 *bitLength = m_state->bitLength; UInt8 *bitLength = m_state->bitLength;
int bufferBits = m_state->bufferBits; std::size_t bufferBits = m_state->bufferBits;
int bufferPos = m_state->bufferPos; std::size_t bufferPos = m_state->bufferPos;
UInt8 *digest = result; UInt8 *digest = result;
// append a '1'-bit // append a '1'-bit

View File

@ -2115,7 +2115,7 @@ namespace Nz
return ptr - m_sharedString->string.get(); return ptr - m_sharedString->string.get();
} }
catch (utf8::not_enough_room& e) catch (utf8::not_enough_room& /*e*/)
{ {
// Returns npos // Returns npos
} }

View File

@ -191,8 +191,8 @@ namespace Nz
void DeferredRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix) void DeferredRenderQueue::AddMesh(int renderOrder, const Material* material, const MeshData& meshData, const Boxf& meshAABB, const Matrix4f& transformMatrix)
{ {
if (material->IsBlendingEnabled()) if (material->IsBlendingEnabled() || material->IsDepthSortingEnabled()) //< Fixme: Deferred Shading should be able to handle depth sorting
// One transparent material ? I don't like it, go see if I'm in the forward queue // Deferred Shading cannot handle blended objects, put them in the forward list
m_forwardQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix); m_forwardQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix);
else else
{ {
@ -254,7 +254,7 @@ namespace Nz
* \param overlay Texture of the sprites * \param overlay Texture of the sprites
*/ */
void DeferredRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay) void DeferredRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay)
{ {
m_forwardQueue->AddSprites(renderOrder, material, vertices, spriteCount, overlay); m_forwardQueue->AddSprites(renderOrder, material, vertices, spriteCount, overlay);
} }

View File

@ -351,7 +351,7 @@ namespace Nz
* \remark Produces a NazaraAssert if material is invalid * \remark Produces a NazaraAssert if material is invalid
*/ */
void DepthRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay) void DepthRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay)
{ {
NazaraAssert(material, "Invalid material"); NazaraAssert(material, "Invalid material");
NazaraUnused(renderOrder); NazaraUnused(renderOrder);

View File

@ -95,7 +95,7 @@ namespace Nz
if (!layer.opaqueModels.empty()) if (!layer.opaqueModels.empty())
DrawOpaqueModels(sceneData, layer); DrawOpaqueModels(sceneData, layer);
if (!layer.basicSprites.empty()) if (!layer.opaqueSprites.empty())
DrawBasicSprites(sceneData, layer); DrawBasicSprites(sceneData, layer);
if (!layer.billboards.empty()) if (!layer.billboards.empty())
@ -219,7 +219,7 @@ namespace Nz
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer); Renderer::SetVertexBuffer(&m_spriteBuffer);
for (auto& pipelinePair : layer.basicSprites) for (auto& pipelinePair : layer.opaqueSprites)
{ {
const MaterialPipeline* pipeline = pipelinePair.first; const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second; auto& pipelineEntry = pipelinePair.second;

View File

@ -376,23 +376,23 @@ namespace Nz
{ {
NazaraAssert(material, "Invalid material"); NazaraAssert(material, "Invalid material");
if (material->IsBlendingEnabled()) if (material->IsDepthSortingEnabled())
{ {
Layer& currentLayer = GetLayer(renderOrder); Layer& currentLayer = GetLayer(renderOrder);
auto& transparentModels = currentLayer.transparentModels; auto& transparentMeshes = currentLayer.depthSortedMeshes;
auto& transparentModelData = currentLayer.transparentModelData; auto& transparentData = currentLayer.depthSortedMeshData;
// The material is transparent, we must draw this mesh using another way (after the rendering of opages objects while sorting them) // The material is marked for depth sorting, we must draw this mesh using another way (after the rendering of opaques objects while sorting them)
std::size_t index = transparentModelData.size(); std::size_t index = transparentData.size();
transparentModelData.resize(index+1); transparentData.resize(index+1);
TransparentModelData& data = transparentModelData.back(); UnbatchedModelData& data = transparentData.back();
data.material = material; data.material = material;
data.meshData = meshData; data.meshData = meshData;
data.squaredBoundingSphere = Spheref(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius()); data.obbSphere = Spheref(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius());
data.transformMatrix = transformMatrix; data.transformMatrix = transformMatrix;
transparentModels.push_back(index); transparentMeshes.push_back(index);
} }
else else
{ {
@ -457,53 +457,74 @@ namespace Nz
* *
* \remark Produces a NazaraAssert if material is invalid * \remark Produces a NazaraAssert if material is invalid
*/ */
void ForwardRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay) void ForwardRenderQueue::AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay)
{ {
NazaraAssert(material, "Invalid material"); NazaraAssert(material, "Invalid material");
Layer& currentLayer = GetLayer(renderOrder); Layer& currentLayer = GetLayer(renderOrder);
SpritePipelineBatches& basicSprites = currentLayer.basicSprites;
const MaterialPipeline* materialPipeline = material->GetPipeline(); if (material->IsDepthSortingEnabled())
auto pipelineIt = basicSprites.find(materialPipeline);
if (pipelineIt == basicSprites.end())
{ {
BatchedSpritePipelineEntry materialEntry; auto& transparentSprites = currentLayer.depthSortedSprites;
pipelineIt = basicSprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; auto& transparentData = currentLayer.depthSortedSpriteData;
// The material is marked for depth sorting, we must draw this mesh using another way (after the rendering of opaques objects while sorting them)
std::size_t index = transparentData.size();
transparentData.resize(index + 1);
UnbatchedSpriteData& data = transparentData.back();
data.material = material;
data.overlay = overlay;
data.spriteCount = spriteCount;
data.vertices = vertices;
transparentSprites.push_back(index);
} }
else
BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second;
pipelineEntry.enabled = true;
SpriteMaterialBatches& materialMap = pipelineEntry.materialMap;
auto matIt = materialMap.find(material);
if (matIt == materialMap.end())
{ {
BatchedBasicSpriteEntry entry; SpritePipelineBatches& sprites = currentLayer.opaqueSprites;
entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation);
matIt = materialMap.insert(SpriteMaterialBatches::value_type(material, std::move(entry))).first; const MaterialPipeline* materialPipeline = material->GetPipeline();
auto pipelineIt = sprites.find(materialPipeline);
if (pipelineIt == sprites.end())
{
BatchedSpritePipelineEntry materialEntry;
pipelineIt = sprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first;
}
BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second;
pipelineEntry.enabled = true;
SpriteMaterialBatches& materialMap = pipelineEntry.materialMap;
auto matIt = materialMap.find(material);
if (matIt == materialMap.end())
{
BatchedBasicSpriteEntry entry;
entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation);
matIt = materialMap.insert(SpriteMaterialBatches::value_type(material, std::move(entry))).first;
}
BatchedBasicSpriteEntry& entry = matIt->second;
entry.enabled = true;
auto& overlayMap = entry.overlayMap;
auto overlayIt = overlayMap.find(overlay);
if (overlayIt == overlayMap.end())
{
BatchedSpriteEntry overlayEntry;
if (overlay)
overlayEntry.textureReleaseSlot.Connect(overlay->OnTextureRelease, this, &ForwardRenderQueue::OnTextureInvalidation);
overlayIt = overlayMap.insert(std::make_pair(overlay, std::move(overlayEntry))).first;
}
auto& spriteVector = overlayIt->second.spriteChains;
spriteVector.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount}));
} }
BatchedBasicSpriteEntry& entry = matIt->second;
entry.enabled = true;
auto& overlayMap = entry.overlayMap;
auto overlayIt = overlayMap.find(overlay);
if (overlayIt == overlayMap.end())
{
BatchedSpriteEntry overlayEntry;
if (overlay)
overlayEntry.textureReleaseSlot.Connect(overlay->OnTextureRelease, this, &ForwardRenderQueue::OnTextureInvalidation);
overlayIt = overlayMap.insert(std::make_pair(overlay, std::move(overlayEntry))).first;
}
auto& spriteVector = overlayIt->second.spriteChains;
spriteVector.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount}));
} }
/*! /*!
@ -545,7 +566,7 @@ namespace Nz
pipelineEntry.enabled = false; pipelineEntry.enabled = false;
} }
for (auto& pipelinePair : layer.basicSprites) for (auto& pipelinePair : layer.opaqueSprites)
{ {
auto& pipelineEntry = pipelinePair.second; auto& pipelineEntry = pipelinePair.second;
@ -596,9 +617,11 @@ namespace Nz
} }
} }
layer.depthSortedMeshes.clear();
layer.depthSortedMeshData.clear();
layer.depthSortedSpriteData.clear();
layer.depthSortedSprites.clear();
layer.otherDrawables.clear(); layer.otherDrawables.clear();
layer.transparentModels.clear();
layer.transparentModelData.clear();
++it; ++it;
} }
} }
@ -613,44 +636,10 @@ namespace Nz
void ForwardRenderQueue::Sort(const AbstractViewer* viewer) void ForwardRenderQueue::Sort(const AbstractViewer* viewer)
{ {
Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near); if (viewer->GetProjectionType() == ProjectionType_Orthogonal)
Vector3f viewerPos = viewer->GetEyePosition(); SortForOrthographic(viewer);
Vector3f viewerNormal = viewer->GetForward(); else
SortForPerspective(viewer);
for (auto& pair : layers)
{
Layer& layer = pair.second;
std::sort(layer.transparentModels.begin(), layer.transparentModels.end(), [&layer, &nearPlane, &viewerNormal] (std::size_t index1, std::size_t index2)
{
const Spheref& sphere1 = layer.transparentModelData[index1].squaredBoundingSphere;
const Spheref& sphere2 = layer.transparentModelData[index2].squaredBoundingSphere;
Vector3f position1 = sphere1.GetNegativeVertex(viewerNormal);
Vector3f position2 = sphere2.GetNegativeVertex(viewerNormal);
return nearPlane.Distance(position1) > nearPlane.Distance(position2);
});
for (auto& pipelinePair : layer.billboards)
{
for (auto& matPair : pipelinePair.second.materialMap)
{
const Material* mat = matPair.first;
if (mat->IsDepthSortingEnabled())
{
BatchedBillboardEntry& entry = matPair.second;
auto& billboardVector = entry.billboards;
std::sort(billboardVector.begin(), billboardVector.end(), [&viewerPos] (const BillboardData& data1, const BillboardData& data2)
{
return viewerPos.SquaredDistance(data1.center) > viewerPos.SquaredDistance(data2.center);
});
}
}
}
}
} }
/*! /*!
@ -715,12 +704,91 @@ namespace Nz
return layer; return layer;
} }
void ForwardRenderQueue::SortBillboards(Layer& layer, const Planef& nearPlane)
{
for (auto& pipelinePair : layer.billboards)
{
for (auto& matPair : pipelinePair.second.materialMap)
{
const Material* mat = matPair.first;
if (mat->IsDepthSortingEnabled())
{
BatchedBillboardEntry& entry = matPair.second;
auto& billboardVector = entry.billboards;
std::sort(billboardVector.begin(), billboardVector.end(), [&nearPlane] (const BillboardData& data1, const BillboardData& data2)
{
return nearPlane.Distance(data1.center) > nearPlane.Distance(data2.center);
});
}
}
}
}
void ForwardRenderQueue::SortForOrthographic(const AbstractViewer * viewer)
{
Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near);
Vector3f viewerPos = viewer->GetEyePosition();
for (auto& pair : layers)
{
Layer& layer = pair.second;
std::sort(layer.depthSortedMeshes.begin(), layer.depthSortedMeshes.end(), [&layer, &nearPlane] (std::size_t index1, std::size_t index2)
{
const Spheref& sphere1 = layer.depthSortedMeshData[index1].obbSphere;
const Spheref& sphere2 = layer.depthSortedMeshData[index2].obbSphere;
return nearPlane.Distance(sphere1.GetPosition()) < nearPlane.Distance(sphere2.GetPosition());
});
std::sort(layer.depthSortedSprites.begin(), layer.depthSortedSprites.end(), [&layer, &nearPlane] (std::size_t index1, std::size_t index2)
{
const Vector3f& pos1 = layer.depthSortedSpriteData[index1].vertices[0].position;
const Vector3f& pos2 = layer.depthSortedSpriteData[index2].vertices[0].position;
return nearPlane.Distance(pos1) < nearPlane.Distance(pos2);
});
SortBillboards(layer, nearPlane);
}
}
void ForwardRenderQueue::SortForPerspective(const AbstractViewer* viewer)
{
Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near);
Vector3f viewerPos = viewer->GetEyePosition();
for (auto& pair : layers)
{
Layer& layer = pair.second;
std::sort(layer.depthSortedMeshes.begin(), layer.depthSortedMeshes.end(), [&layer, &viewerPos] (std::size_t index1, std::size_t index2)
{
const Spheref& sphere1 = layer.depthSortedMeshData[index1].obbSphere;
const Spheref& sphere2 = layer.depthSortedMeshData[index2].obbSphere;
return viewerPos.SquaredDistance(sphere1.GetPosition()) > viewerPos.SquaredDistance(sphere2.GetPosition());
});
std::sort(layer.depthSortedSprites.begin(), layer.depthSortedSprites.end(), [&layer, &viewerPos] (std::size_t index1, std::size_t index2)
{
const Vector3f& pos1 = layer.depthSortedSpriteData[index1].vertices[0].position;
const Vector3f& pos2 = layer.depthSortedSpriteData[index2].vertices[0].position;
return viewerPos.SquaredDistance(pos1) > viewerPos.SquaredDistance(pos2);
});
SortBillboards(layer, nearPlane);
}
}
/*! /*!
* \brief Handle the invalidation of an index buffer * \brief Handle the invalidation of an index buffer
* *
* \param indexBuffer Index buffer being invalidated * \param indexBuffer Index buffer being invalidated
*/ */
void ForwardRenderQueue::OnIndexBufferInvalidation(const IndexBuffer* indexBuffer) void ForwardRenderQueue::OnIndexBufferInvalidation(const IndexBuffer* indexBuffer)
{ {
for (auto& pair : layers) for (auto& pair : layers)
@ -757,7 +825,7 @@ namespace Nz
{ {
Layer& layer = pair.second; Layer& layer = pair.second;
for (auto& pipelineEntry : layer.basicSprites) for (auto& pipelineEntry : layer.opaqueSprites)
pipelineEntry.second.materialMap.erase(material); pipelineEntry.second.materialMap.erase(material);
for (auto& pipelineEntry : layer.billboards) for (auto& pipelineEntry : layer.billboards)
@ -779,7 +847,7 @@ namespace Nz
for (auto& pair : layers) for (auto& pair : layers)
{ {
Layer& layer = pair.second; Layer& layer = pair.second;
for (auto& pipelineEntry : layer.basicSprites) for (auto& pipelineEntry : layer.opaqueSprites)
{ {
for (auto& materialEntry : pipelineEntry.second.materialMap) for (auto& materialEntry : pipelineEntry.second.materialMap)
materialEntry.second.overlayMap.erase(texture); materialEntry.second.overlayMap.erase(texture);

View File

@ -33,8 +33,8 @@ namespace Nz
Vector2f uv; Vector2f uv;
}; };
std::size_t s_maxQuads = std::numeric_limits<UInt16>::max() / 6; UInt32 s_maxQuads = std::numeric_limits<UInt16>::max() / 6;
std::size_t s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB
} }
/*! /*!
@ -101,12 +101,15 @@ namespace Nz
if (!layer.opaqueModels.empty()) if (!layer.opaqueModels.empty())
DrawOpaqueModels(sceneData, layer); DrawOpaqueModels(sceneData, layer);
if (!layer.transparentModels.empty()) if (!layer.depthSortedMeshes.empty())
DrawTransparentModels(sceneData, layer); DrawTransparentModels(sceneData, layer);
if (!layer.basicSprites.empty()) if (!layer.opaqueSprites.empty())
DrawBasicSprites(sceneData, layer); DrawBasicSprites(sceneData, layer);
if (!layer.depthSortedSprites.empty())
DrawOrderedSprites(sceneData, layer);
if (!layer.billboards.empty()) if (!layer.billboards.empty())
DrawBillboards(sceneData, layer); DrawBillboards(sceneData, layer);
@ -301,7 +304,10 @@ namespace Nz
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer); Renderer::SetVertexBuffer(&m_spriteBuffer);
for (auto& pipelinePair : layer.basicSprites) const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
const std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4);
for (auto& pipelinePair : layer.opaqueSprites)
{ {
const MaterialPipeline* pipeline = pipelinePair.first; const MaterialPipeline* pipeline = pipelinePair.first;
auto& pipelineEntry = pipelinePair.second; auto& pipelineEntry = pipelinePair.second;
@ -323,6 +329,9 @@ namespace Nz
// Position of the camera // Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
// Overlay texture unit
shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
lastShader = shader; lastShader = shader;
} }
@ -335,10 +344,6 @@ namespace Nz
{ {
material->Apply(pipelineInstance); material->Apply(pipelineInstance);
unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler()); Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler());
auto& overlayMap = matEntry.overlayMap; auto& overlayMap = matEntry.overlayMap;
@ -362,7 +367,6 @@ namespace Nz
VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer()); VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
std::size_t spriteCount = 0; std::size_t spriteCount = 0;
std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4);
do do
{ {
@ -777,6 +781,142 @@ namespace Nz
} }
} }
void ForwardRenderTechnique::DrawOrderedSprites(const SceneData & sceneData, ForwardRenderQueue::Layer & layer) const
{
NazaraAssert(sceneData.viewer, "Invalid viewer");
Renderer::SetIndexBuffer(&s_quadIndexBuffer);
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer);
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
const Shader* lastShader = nullptr;
const Texture* lastOverlay = nullptr;
const MaterialPipeline::Instance* pipelineInstance = nullptr;
const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
bool updateVertexBuffer = true;
const std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4);
std::size_t alreadyDrawnCount = 0;
std::size_t spriteIndex = 0;
std::size_t spriteChainOffset = 0;
auto splitChainIt = layer.depthSortedSprites.end();
for (auto it = layer.depthSortedSprites.begin(); it != layer.depthSortedSprites.end();)
{
if (updateVertexBuffer)
{
// We open the buffer in writing mode
BufferMapper<VertexBuffer> vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite);
VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
std::size_t availableSpriteSpace = maxSpriteCount;
bool split = false;
for (auto it2 = it; it2 != layer.depthSortedSprites.end(); ++it2)
{
const ForwardRenderQueue::UnbatchedSpriteData& spriteData = layer.depthSortedSpriteData[*it2];
std::size_t count = std::min(availableSpriteSpace, spriteData.spriteCount - spriteChainOffset);
std::memcpy(vertices, spriteData.vertices + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV));
vertices += count * 4;
availableSpriteSpace -= count;
// Have we treated the entire chain ?
if (count != spriteData.spriteCount)
{
// Oops, not enough space to store current chain
spriteChainOffset += count;
splitChainIt = it2;
split = true;
break;
}
// Switch to next sprite chain, if any
spriteChainOffset = 0;
}
spriteIndex = 0;
updateVertexBuffer = false;
if (!split)
splitChainIt = layer.depthSortedSprites.end();
}
std::size_t index = *it;
const ForwardRenderQueue::UnbatchedSpriteData& spriteData = layer.depthSortedSpriteData[index];
const Material* material = spriteData.material;
if (material != lastMaterial)
{
const MaterialPipeline* pipeline = material->GetPipeline();
if (pipeline != lastPipeline)
{
pipelineInstance = &pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
// Uniforms are conserved in our program, there's no point to send them back until they change
if (shader != lastShader)
{
// Index of uniforms in the shader
const ShaderUniforms* shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
// Overlay texture unit
shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
lastShader = shader;
}
lastPipeline = pipeline;
}
material->Apply(*pipelineInstance);
Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler());
lastMaterial = material;
}
const Texture* overlay = (spriteData.overlay) ? spriteData.overlay : &m_whiteTexture;
if (overlay != lastOverlay)
{
Renderer::SetTexture(overlayTextureUnit, overlay);
lastOverlay = overlay;
}
std::size_t spriteCount;
if (it != splitChainIt)
{
spriteCount = spriteData.spriteCount - alreadyDrawnCount;
alreadyDrawnCount = 0;
++it;
}
else
{
spriteCount = spriteChainOffset;
alreadyDrawnCount = spriteCount;
updateVertexBuffer = true;
// Restart at current iterator next time
}
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, spriteIndex * 6, spriteCount * 6);
spriteIndex += spriteCount;
}
}
/*! /*!
* \brief Draws transparent models * \brief Draws transparent models
* *
@ -796,9 +936,9 @@ namespace Nz
const ShaderUniforms* shaderUniforms = nullptr; const ShaderUniforms* shaderUniforms = nullptr;
unsigned int lightCount = 0; unsigned int lightCount = 0;
for (unsigned int index : layer.transparentModels) for (std::size_t index : layer.depthSortedMeshes)
{ {
const ForwardRenderQueue::TransparentModelData& modelData = layer.transparentModelData[index]; const ForwardRenderQueue::UnbatchedModelData& modelData = layer.depthSortedMeshData[index];
// Material // Material
const Material* material = modelData.material; const Material* material = modelData.material;
@ -865,8 +1005,8 @@ namespace Nz
if (shaderUniforms->hasLightUniforms && lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS) if (shaderUniforms->hasLightUniforms && lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS)
{ {
// Compute the closest lights // Compute the closest lights
Vector3f position = matrix.GetTranslation() + modelData.squaredBoundingSphere.GetPosition(); Vector3f position = matrix.GetTranslation() + modelData.obbSphere.GetPosition();
float radius = modelData.squaredBoundingSphere.radius; float radius = modelData.obbSphere.radius;
ChooseLights(Spheref(position, radius), false); ChooseLights(Spheref(position, radius), false);
for (std::size_t i = lightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) for (std::size_t i = lightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)

View File

@ -197,6 +197,7 @@ namespace Nz
pipelineInfo.blending = true; pipelineInfo.blending = true;
pipelineInfo.depthWrite = false; pipelineInfo.depthWrite = false;
pipelineInfo.faceCulling = false; pipelineInfo.faceCulling = false;
pipelineInfo.depthSorting = true;
pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha; pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
pipelineInfo.srcBlend = BlendFunc_SrcAlpha; pipelineInfo.srcBlend = BlendFunc_SrcAlpha;
@ -207,6 +208,7 @@ namespace Nz
pipelineInfo.depthBuffer = true; pipelineInfo.depthBuffer = true;
pipelineInfo.depthWrite = false; pipelineInfo.depthWrite = false;
pipelineInfo.faceCulling = false; pipelineInfo.faceCulling = false;
pipelineInfo.depthSorting = true;
pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha; pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
pipelineInfo.srcBlend = BlendFunc_SrcAlpha; pipelineInfo.srcBlend = BlendFunc_SrcAlpha;

View File

@ -280,7 +280,7 @@ namespace Nz
m_serviceTime = GetElapsedMilliseconds(); m_serviceTime = GetElapsedMilliseconds();
} }
while (m_poller.IsReady(m_socket)); while (m_poller.IsReadyToRead(m_socket));
return 0; return 0;
} }
@ -327,7 +327,7 @@ namespace Nz
} }
} }
m_poller.RegisterSocket(m_socket); m_poller.RegisterSocket(m_socket, SocketPollEvent_Read);
return true; return true;
} }

View File

@ -23,13 +23,19 @@ namespace Nz
void SocketPollerImpl::Clear() void SocketPollerImpl::Clear()
{ {
m_activeSockets.clear(); m_readyToReadSockets.clear();
m_readyToWriteSockets.clear();
m_sockets.clear(); m_sockets.clear();
} }
bool SocketPollerImpl::IsReady(SocketHandle socket) const bool SocketPollerImpl::IsReadyToRead(SocketHandle socket) const
{ {
return m_activeSockets.count(socket) != 0; return m_readyToReadSockets.count(socket) != 0;
}
bool SocketPollerImpl::IsReadyToWrite(SocketHandle socket) const
{
return m_readyToWriteSockets.count(socket) != 0;
} }
bool SocketPollerImpl::IsRegistered(SocketHandle socket) const bool SocketPollerImpl::IsRegistered(SocketHandle socket) const
@ -37,15 +43,21 @@ namespace Nz
return m_sockets.count(socket) != 0; return m_sockets.count(socket) != 0;
} }
bool SocketPollerImpl::RegisterSocket(SocketHandle socket) bool SocketPollerImpl::RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags)
{ {
NazaraAssert(!IsRegistered(socket), "Socket is already registered"); NazaraAssert(!IsRegistered(socket), "Socket is already registered");
epoll_event event; epoll_event entry;
event.events = EPOLLIN; entry.events = 0;
event.data.fd = socket; entry.data.fd = socket;
if (epoll_ctl(m_handle, EPOLL_CTL_ADD, socket, &event) != 0) if (eventFlags & SocketPollEvent_Read)
entry.events |= EPOLLIN;
if (eventFlags & SocketPollEvent_Write)
entry.events |= EPOLLOUT;
if (epoll_ctl(m_handle, EPOLL_CTL_ADD, socket, &entry) != 0)
{ {
NazaraError("Failed to add socket to epoll structure (errno " + String::Number(errno) + ": " + Error::GetLastSystemError() + ')'); NazaraError("Failed to add socket to epoll structure (errno " + String::Number(errno) + ": " + Error::GetLastSystemError() + ')');
return false; return false;
@ -60,7 +72,8 @@ namespace Nz
{ {
NazaraAssert(IsRegistered(socket), "Socket is not registered"); NazaraAssert(IsRegistered(socket), "Socket is not registered");
m_activeSockets.erase(socket); m_readyToReadSockets.erase(socket);
m_readyToWriteSockets.erase(socket);
m_sockets.erase(socket); m_sockets.erase(socket);
if (epoll_ctl(m_handle, EPOLL_CTL_DEL, socket, nullptr) != 0) if (epoll_ctl(m_handle, EPOLL_CTL_DEL, socket, nullptr) != 0)
@ -84,21 +97,27 @@ namespace Nz
return 0; return 0;
} }
m_activeSockets.clear(); m_readyToReadSockets.clear();
m_readyToWriteSockets.clear();
if (activeSockets > 0) if (activeSockets > 0)
{ {
int socketCount = activeSockets; int socketCount = activeSockets;
for (int i = 0; i < socketCount; ++i) for (int i = 0; i < socketCount; ++i)
{ {
if (m_events[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR)) if (m_events[i].events & (EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLERR))
{ {
m_activeSockets.insert(m_events[i].data.fd); if (m_events[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR))
m_readyToReadSockets.insert(m_events[i].data.fd);
if (m_events[i].events & (EPOLLOUT | EPOLLERR))
m_readyToWriteSockets.insert(m_events[i].data.fd);
if (m_events[i].events & EPOLLERR) if (m_events[i].events & EPOLLERR)
NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll with EPOLLERR status"); NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll with EPOLLERR status");
} }
else else
{ {
NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll without EPOLLIN (events: 0x" + String::Number(m_events[i].events, 16) + ')'); NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll without EPOLLIN nor EPOLLOUT flags (events: 0x" + String::Number(m_events[i].events, 16) + ')');
activeSockets--; activeSockets--;
} }
} }

View File

@ -23,16 +23,18 @@ namespace Nz
void Clear(); void Clear();
bool IsReady(SocketHandle socket) const; bool IsReadyToRead(SocketHandle socket) const;
bool IsReadyToWrite(SocketHandle socket) const;
bool IsRegistered(SocketHandle socket) const; bool IsRegistered(SocketHandle socket) const;
bool RegisterSocket(SocketHandle socket); bool RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags);
void UnregisterSocket(SocketHandle socket); void UnregisterSocket(SocketHandle socket);
int Wait(UInt64 msTimeout, SocketError* error); int Wait(UInt64 msTimeout, SocketError* error);
private: private:
std::unordered_set<SocketHandle> m_activeSockets; std::unordered_set<SocketHandle> m_readyToReadSockets;
std::unordered_set<SocketHandle> m_readyToWriteSockets;
std::unordered_set<SocketHandle> m_sockets; std::unordered_set<SocketHandle> m_sockets;
std::vector<epoll_event> m_events; std::vector<epoll_event> m_events;
int m_handle; int m_handle;

View File

@ -10,14 +10,20 @@ namespace Nz
{ {
void SocketPollerImpl::Clear() void SocketPollerImpl::Clear()
{ {
m_activeSockets.clear(); m_readyToReadSockets.clear();
m_readyToWriteSockets.clear();
m_allSockets.clear(); m_allSockets.clear();
m_sockets.clear(); m_sockets.clear();
} }
bool SocketPollerImpl::IsReady(SocketHandle socket) const bool SocketPollerImpl::IsReadyToRead(SocketHandle socket) const
{ {
return m_activeSockets.count(socket) != 0; return m_readyToReadSockets.count(socket) != 0;
}
bool SocketPollerImpl::IsReadyToWrite(SocketHandle socket) const
{
return m_readyToWriteSockets.count(socket) != 0;
} }
bool SocketPollerImpl::IsRegistered(SocketHandle socket) const bool SocketPollerImpl::IsRegistered(SocketHandle socket) const
@ -25,16 +31,22 @@ namespace Nz
return m_allSockets.count(socket) != 0; return m_allSockets.count(socket) != 0;
} }
bool SocketPollerImpl::RegisterSocket(SocketHandle socket) bool SocketPollerImpl::RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags)
{ {
NazaraAssert(!IsRegistered(socket), "Socket is already registered"); NazaraAssert(!IsRegistered(socket), "Socket is already registered");
PollSocket entry = { PollSocket entry = {
socket, socket,
POLLRDNORM, 0,
0 0
}; };
if (eventFlags & SocketPollEvent_Read)
entry.events |= POLLRDNORM;
if (eventFlags & SocketPollEvent_Write)
entry.events |= POLLWRNORM;
m_allSockets[socket] = m_sockets.size(); m_allSockets[socket] = m_sockets.size();
m_sockets.emplace_back(entry); m_sockets.emplace_back(entry);
@ -57,10 +69,11 @@ namespace Nz
// Now move it properly (lastElement is invalid after the following line) and pop it // Now move it properly (lastElement is invalid after the following line) and pop it
m_sockets[entry] = std::move(m_sockets.back()); m_sockets[entry] = std::move(m_sockets.back());
} }
m_sockets.pop_back(); m_sockets.pop_back();
m_activeSockets.erase(socket);
m_allSockets.erase(socket); m_allSockets.erase(socket);
m_readyToReadSockets.erase(socket);
m_readyToWriteSockets.erase(socket);
} }
int SocketPollerImpl::Wait(UInt64 msTimeout, SocketError* error) int SocketPollerImpl::Wait(UInt64 msTimeout, SocketError* error)
@ -68,20 +81,25 @@ namespace Nz
int activeSockets; int activeSockets;
// Reset status of sockets // Reset status of sockets
for (PollSocket& entry : m_sockets)
entry.revents = 0;
activeSockets = SocketImpl::Poll(m_sockets.data(), m_sockets.size(), static_cast<int>(msTimeout), error); activeSockets = SocketImpl::Poll(m_sockets.data(), m_sockets.size(), static_cast<int>(msTimeout), error);
m_activeSockets.clear(); m_readyToReadSockets.clear();
if (activeSockets > 0) m_readyToWriteSockets.clear();
if (activeSockets > 0U)
{ {
int socketRemaining = activeSockets; int socketRemaining = activeSockets;
for (PollSocket& entry : m_sockets) for (PollSocket& entry : m_sockets)
{ {
if (entry.revents & POLLRDNORM) if (entry.revents != 0)
{ {
m_activeSockets.insert(entry.fd); if (entry.revents & POLLRDNORM)
m_readyToReadSockets.insert(entry.fd);
if (entry.revents & POLLWRNORM)
m_readyToWriteSockets.insert(entry.fd);
entry.revents = 0;
if (--socketRemaining == 0) if (--socketRemaining == 0)
break; break;
} }

View File

@ -27,13 +27,14 @@ namespace Nz
bool IsReady(SocketHandle socket) const; bool IsReady(SocketHandle socket) const;
bool IsRegistered(SocketHandle socket) const; bool IsRegistered(SocketHandle socket) const;
bool RegisterSocket(SocketHandle socket); bool RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags);
void UnregisterSocket(SocketHandle socket); void UnregisterSocket(SocketHandle socket);
int Wait(UInt64 msTimeout, SocketError* error); int Wait(UInt64 msTimeout, SocketError* error);
private: private:
std::unordered_set<SocketHandle> m_activeSockets; std::unordered_set<SocketHandle> m_readyToReadSockets;
std::unordered_set<SocketHandle> m_readyToWriteSockets;
std::unordered_map<SocketHandle, std::size_t> m_allSockets; std::unordered_map<SocketHandle, std::size_t> m_allSockets;
std::vector<PollSocket> m_sockets; std::vector<PollSocket> m_sockets;
}; };

View File

@ -57,12 +57,13 @@ namespace Nz
/*! /*!
* \brief Checks if a specific socket is ready to read data * \brief Checks if a specific socket is ready to read data
* *
* This function allows you to read the results of the last Wait operation and if a specific socket is ready. * This function allows you to read the results of the last Wait operation and if a specific socket is ready to read (has incoming data).
* *
* A socket in the ready state (with the exception of TcpServer) has incoming data and can be read without blocking. * A socket in the ready to read state (with the exception of TcpServer) has incoming data and can be read without blocking.
* *
* \remark When used on a TcpServer socket, this function returns true if the server is ready to accept a new client. * \remark You must call Wait before using this function in order to refresh the read state.
* \remark You must call Wait before using this function in order to refresh the state. * \remark A socket must be registered with SocketPollerEvent_Read event flag for its read state to be watched
* \remark A TcpServer socket becomes ready to read when it is ready to accept a new client.
* *
* \param socket Reference to the socket to check * \param socket Reference to the socket to check
* *
@ -70,11 +71,32 @@ namespace Nz
* *
* \see Wait * \see Wait
*/ */
bool SocketPoller::IsReady(const AbstractSocket& socket) const bool SocketPoller::IsReadyToRead(const AbstractSocket& socket) const
{ {
NazaraAssert(IsRegistered(socket), "Socket is not registered in the poller"); NazaraAssert(IsRegistered(socket), "Socket is not registered in the poller");
return m_impl->IsReady(socket.GetNativeHandle()); return m_impl->IsReadyToRead(socket.GetNativeHandle());
}
/*!
* \brief Checks if a specific socket is ready to write data
*
* This function allows you to read the results of the last Wait operation and if a specific socket is ready to write (can be written to without blocking).
*
* \remark You must call Wait before using this function in order to refresh the read state.
* \remark A socket must be registered with SocketPollerEvent_Write event flag for its read state to be watched
*
* \param socket Reference to the socket to check
*
* \return True if the socket is available for writing without blocking, false otherwise
*
* \see Wait
*/
bool SocketPoller::IsReadyToWrite(const AbstractSocket& socket) const
{
NazaraAssert(IsRegistered(socket), "Socket is not registered in the poller");
return m_impl->IsReadyToWrite(socket.GetNativeHandle());
} }
/*! /*!
@ -97,7 +119,7 @@ namespace Nz
/*! /*!
* \brief Register a socket in the SocketPoller * \brief Register a socket in the SocketPoller
* *
* A registered socket is part of the SocketPoller and will be checked by the next Wait operations. * A registered socket is part of the SocketPoller and will be checked by the next Wait operations according to the event flags passed when registered.
* *
* The SocketPoller keeps a reference to the internal handle of registered socket, which should not be freed while it is registered in the SocketPooler. * The SocketPoller keeps a reference to the internal handle of registered socket, which should not be freed while it is registered in the SocketPooler.
* *
@ -107,17 +129,18 @@ namespace Nz
* \remark The socket should not be freed while it is registered in the SocketPooler. * \remark The socket should not be freed while it is registered in the SocketPooler.
* *
* \param socket Reference to the socket to register * \param socket Reference to the socket to register
* \param eventFlags Socket events to watch
* *
* \return True if the socket is registered, false otherwise * \return True if the socket is registered, false otherwise
* *
* \see IsRegistered * \see IsRegistered
* \see UnregisterSocket * \see UnregisterSocket
*/ */
bool SocketPoller::RegisterSocket(AbstractSocket& socket) bool SocketPoller::RegisterSocket(AbstractSocket& socket, SocketPollEventFlags eventFlags)
{ {
NazaraAssert(!IsRegistered(socket), "This socket is already registered in this SocketPoller"); NazaraAssert(!IsRegistered(socket), "This socket is already registered in this SocketPoller");
return m_impl->RegisterSocket(socket.GetNativeHandle()); return m_impl->RegisterSocket(socket.GetNativeHandle(), eventFlags);
} }
/*! /*!
@ -145,7 +168,7 @@ namespace Nz
* \brief Wait until any registered socket switches to a ready state. * \brief Wait until any registered socket switches to a ready state.
* *
* Waits a specific/undetermined amount of time until at least one socket part of the SocketPoller becomes ready. * Waits a specific/undetermined amount of time until at least one socket part of the SocketPoller becomes ready.
* To query the ready state of the registered socket, use the IsReady function. * To query the ready state of the registered socket, use the IsReadyToRead or IsReadyToWrite functions.
* *
* \param msTimeout Maximum time to wait in milliseconds, 0 for infinity * \param msTimeout Maximum time to wait in milliseconds, 0 for infinity
* *

View File

@ -10,29 +10,43 @@ namespace Nz
SocketPollerImpl::SocketPollerImpl() SocketPollerImpl::SocketPollerImpl()
{ {
#if !NAZARA_NETWORK_POLL_SUPPORT #if !NAZARA_NETWORK_POLL_SUPPORT
FD_ZERO(&m_activeSockets); FD_ZERO(&m_readSockets);
FD_ZERO(&m_sockets); FD_ZERO(&m_readyToReadSockets);
FD_ZERO(&m_readyToWriteSockets);
FD_ZERO(&m_writeSockets);
#endif #endif
} }
void SocketPollerImpl::Clear() void SocketPollerImpl::Clear()
{ {
#if NAZARA_NETWORK_POLL_SUPPORT #if NAZARA_NETWORK_POLL_SUPPORT
m_activeSockets.clear();
m_allSockets.clear(); m_allSockets.clear();
m_readyToReadSockets.clear();
m_readyToWriteSockets.clear();
m_sockets.clear(); m_sockets.clear();
#else #else
FD_ZERO(&m_activeSockets); FD_ZERO(&m_readSockets);
FD_ZERO(&m_sockets); FD_ZERO(&m_readyToReadSockets);
FD_ZERO(&m_readyToWriteSockets);
FD_ZERO(&m_writeSockets);
#endif #endif
} }
bool SocketPollerImpl::IsReady(SocketHandle socket) const bool SocketPollerImpl::IsReadyToRead(SocketHandle socket) const
{ {
#if NAZARA_NETWORK_POLL_SUPPORT #if NAZARA_NETWORK_POLL_SUPPORT
return m_activeSockets.count(socket) != 0; return m_readyToReadSockets.count(socket) != 0;
#else #else
return FD_ISSET(socket, &m_activeSockets) != 0; return FD_ISSET(socket, &m_readyToReadSockets) != 0;
#endif
}
bool SocketPollerImpl::IsReadyToWrite(SocketHandle socket) const
{
#if NAZARA_NETWORK_POLL_SUPPORT
return m_readyToWriteSockets.count(socket) != 0;
#else
return FD_ISSET(socket, &m_readyToWriteSockets) != 0;
#endif #endif
} }
@ -41,31 +55,45 @@ namespace Nz
#if NAZARA_NETWORK_POLL_SUPPORT #if NAZARA_NETWORK_POLL_SUPPORT
return m_allSockets.count(socket) != 0; return m_allSockets.count(socket) != 0;
#else #else
return FD_ISSET(socket, &m_sockets) != 0; return FD_ISSET(socket, &m_readSockets) != 0 ||
FD_ISSET(socket, &m_writeSockets) != 0;
#endif #endif
} }
bool SocketPollerImpl::RegisterSocket(SocketHandle socket) bool SocketPollerImpl::RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags)
{ {
NazaraAssert(!IsRegistered(socket), "Socket is already registered"); NazaraAssert(!IsRegistered(socket), "Socket is already registered");
#if NAZARA_NETWORK_POLL_SUPPORT #if NAZARA_NETWORK_POLL_SUPPORT
PollSocket entry = { PollSocket entry = {
socket, socket,
POLLRDNORM, 0,
0 0
}; };
if (eventFlags & SocketPollEvent_Read)
entry.events |= POLLRDNORM;
if (eventFlags & SocketPollEvent_Write)
entry.events |= POLLWRNORM;
m_allSockets[socket] = m_sockets.size(); m_allSockets[socket] = m_sockets.size();
m_sockets.emplace_back(entry); m_sockets.emplace_back(entry);
#else #else
if (m_sockets.fd_count > FD_SETSIZE) for (std::size_t i = 0; i < 2; ++i)
{ {
NazaraError("Socket count exceeding FD_SETSIZE (" + String::Number(FD_SETSIZE) + ")"); if ((eventFlags & ((i == 0) ? SocketPollEvent_Read : SocketPollEvent_Write)) == 0)
return false; continue;
}
FD_SET(socket, &m_sockets); fd_set& targetSet = (i == 0) ? m_readSockets : m_writeSockets;
if (targetSet.fd_count > FD_SETSIZE)
{
NazaraError("Socket count exceeding hard-coded FD_SETSIZE (" + String::Number(FD_SETSIZE) + ")");
return false;
}
FD_SET(socket, &targetSet);
}
#endif #endif
return true; return true;
@ -88,13 +116,16 @@ namespace Nz
// Now move it properly (lastElement is invalid after the following line) and pop it // Now move it properly (lastElement is invalid after the following line) and pop it
m_sockets[entry] = std::move(m_sockets.back()); m_sockets[entry] = std::move(m_sockets.back());
} }
m_sockets.pop_back(); m_sockets.pop_back();
m_activeSockets.erase(socket);
m_allSockets.erase(socket); m_allSockets.erase(socket);
m_readyToReadSockets.erase(socket);
m_readyToWriteSockets.erase(socket);
#else #else
FD_CLR(socket, &m_activeSockets); FD_CLR(socket, &m_readSockets);
FD_CLR(socket, &m_sockets); FD_CLR(socket, &m_readyToReadSockets);
FD_CLR(socket, &m_readyToWriteSockets);
FD_CLR(socket, &m_writeSockets);
#endif #endif
} }
@ -103,35 +134,28 @@ namespace Nz
int activeSockets; int activeSockets;
#if NAZARA_NETWORK_POLL_SUPPORT #if NAZARA_NETWORK_POLL_SUPPORT
// Reset status of sockets
for (PollSocket& entry : m_sockets)
entry.revents = 0;
activeSockets = SocketImpl::Poll(m_sockets.data(), m_sockets.size(), static_cast<int>(msTimeout), error); activeSockets = SocketImpl::Poll(m_sockets.data(), m_sockets.size(), static_cast<int>(msTimeout), error);
m_activeSockets.clear();
if (activeSockets > 0U)
{
int socketRemaining = activeSockets;
for (PollSocket& entry : m_sockets)
{
if (entry.revents & POLLRDNORM)
{
m_activeSockets.insert(entry.fd);
if (--socketRemaining == 0)
break;
}
}
}
#else #else
fd_set* readSet = nullptr;
fd_set* writeSet = nullptr;
m_activeSockets = m_sockets; if (m_readSockets.fd_count > 0)
{
m_readyToReadSockets = m_readSockets;
readSet = &m_readyToReadSockets;
}
if (m_writeSockets.fd_count > 0)
{
m_readyToWriteSockets = m_writeSockets;
readSet = &m_readyToWriteSockets;
}
timeval tv; timeval tv;
tv.tv_sec = static_cast<long>(msTimeout / 1000ULL); tv.tv_sec = static_cast<long>(msTimeout / 1000ULL);
tv.tv_usec = static_cast<long>((msTimeout % 1000ULL) * 1000ULL); tv.tv_usec = static_cast<long>((msTimeout % 1000ULL) * 1000ULL);
activeSockets = ::select(0xDEADBEEF, &m_activeSockets, nullptr, nullptr, (msTimeout > 0) ? &tv : nullptr); //< The first argument is ignored on Windows activeSockets = ::select(0xDEADBEEF, readSet, writeSet, nullptr, (msTimeout > 0) ? &tv : nullptr); //< The first argument is ignored on Windows
if (activeSockets == SOCKET_ERROR) if (activeSockets == SOCKET_ERROR)
{ {
if (error) if (error)

View File

@ -25,24 +25,28 @@ namespace Nz
void Clear(); void Clear();
bool IsReady(SocketHandle socket) const; bool IsReadyToRead(SocketHandle socket) const;
bool IsReadyToWrite(SocketHandle socket) const;
bool IsRegistered(SocketHandle socket) const; bool IsRegistered(SocketHandle socket) const;
bool RegisterSocket(SocketHandle socket); bool RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags);
void UnregisterSocket(SocketHandle socket); void UnregisterSocket(SocketHandle socket);
int Wait(UInt64 msTimeout, SocketError* error); int Wait(UInt64 msTimeout, SocketError* error);
private: private:
#if NAZARA_NETWORK_POLL_SUPPORT #if NAZARA_NETWORK_POLL_SUPPORT
std::unordered_set<SocketHandle> m_activeSockets; std::unordered_set<SocketHandle> m_readyToReadSockets;
std::unordered_set<SocketHandle> m_readyToWriteSockets;
std::unordered_map<SocketHandle, std::size_t> m_allSockets; std::unordered_map<SocketHandle, std::size_t> m_allSockets;
std::vector<PollSocket> m_sockets; std::vector<PollSocket> m_sockets;
#else #else
fd_set m_sockets; fd_set m_readSockets;
fd_set m_activeSockets; fd_set m_readyToReadSockets;
fd_set m_readyToWriteSockets;
fd_set m_writeSockets;
#endif #endif
}; };
} }
#endif // NAZARA_SOCKETPOLLERIMPL_HPP #endif // NAZARA_SOCKETPOLLERIMPL_HPP

View File

@ -62,9 +62,9 @@ namespace Nz
if (cpShape* shape = cpSpacePointQueryNearest(m_handle, { from.x, from.y }, maxDistance, filter, &queryInfo)) if (cpShape* shape = cpSpacePointQueryNearest(m_handle, { from.x, from.y }, maxDistance, filter, &queryInfo))
{ {
result->closestPoint.Set(queryInfo.point.x, queryInfo.point.y); result->closestPoint.Set(Nz::Vector2<cpFloat>(queryInfo.point.x, queryInfo.point.y));
result->distance = queryInfo.distance; result->distance = float(queryInfo.distance);
result->fraction.Set(queryInfo.gradient.x, queryInfo.gradient.y); result->fraction.Set(Nz::Vector2<cpFloat>(queryInfo.gradient.x, queryInfo.gradient.y));
result->nearestBody = static_cast<Nz::RigidBody2D*>(cpShapeGetUserData(shape)); result->nearestBody = static_cast<Nz::RigidBody2D*>(cpShapeGetUserData(shape));
return true; return true;
@ -90,9 +90,9 @@ namespace Nz
ResultType results = static_cast<ResultType>(data); ResultType results = static_cast<ResultType>(data);
RaycastHit hitInfo; RaycastHit hitInfo;
hitInfo.fraction = alpha; hitInfo.fraction = float(alpha);
hitInfo.hitNormal.Set(normal.x, normal.y); hitInfo.hitNormal.Set(Nz::Vector2<cpFloat>(normal.x, normal.y));
hitInfo.hitPos.Set(point.x, point.y); hitInfo.hitPos.Set(Nz::Vector2<cpFloat>(point.x, point.y));
hitInfo.nearestBody = static_cast<Nz::RigidBody2D*>(cpShapeGetUserData(shape)); hitInfo.nearestBody = static_cast<Nz::RigidBody2D*>(cpShapeGetUserData(shape));
results->emplace_back(std::move(hitInfo)); results->emplace_back(std::move(hitInfo));
@ -116,9 +116,9 @@ namespace Nz
if (cpShape* shape = cpSpaceSegmentQueryFirst(m_handle, { from.x, from.y }, { to.x, to.y }, radius, filter, &queryInfo)) if (cpShape* shape = cpSpaceSegmentQueryFirst(m_handle, { from.x, from.y }, { to.x, to.y }, radius, filter, &queryInfo))
{ {
hitInfo->fraction = queryInfo.alpha; hitInfo->fraction = float(queryInfo.alpha);
hitInfo->hitNormal.Set(queryInfo.normal.x, queryInfo.normal.y); hitInfo->hitNormal.Set(Nz::Vector2<cpFloat>(queryInfo.normal.x, queryInfo.normal.y));
hitInfo->hitPos.Set(queryInfo.point.x, queryInfo.point.y); hitInfo->hitPos.Set(Nz::Vector2<cpFloat>(queryInfo.point.x, queryInfo.point.y));
hitInfo->nearestBody = static_cast<Nz::RigidBody2D*>(cpShapeGetUserData(queryInfo.shape)); hitInfo->nearestBody = static_cast<Nz::RigidBody2D*>(cpShapeGetUserData(queryInfo.shape));
return true; return true;

View File

@ -215,7 +215,7 @@ namespace Nz
cpVect vel = cpBodyGetVelocity(m_handle); cpVect vel = cpBodyGetVelocity(m_handle);
Destroy(); Destroy();
Create(mass, moment); Create(float(mass), float(moment));
cpBodySetAngle(m_handle, rot); cpBodySetAngle(m_handle, rot);
cpBodySetPosition(m_handle, pos); cpBodySetPosition(m_handle, pos);

View File

@ -121,9 +121,7 @@ namespace Nz
break; break;
default: default:
// Peut être rajouté par une extension return; //< Block NVidia buffer usage hint for now
ss << "Unknown";
break;
} }
ss << '\n'; ss << '\n';

View File

@ -91,8 +91,8 @@ namespace Nz
bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max()); bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max());
IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage, 0); IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, UInt32(indexCount), parameters.storage, 0);
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent_Skinning), vertexCount, parameters.storage, 0); VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent_Skinning), UInt32(vertexCount), parameters.storage, 0);
// Index buffer // Index buffer
IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite); IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
@ -236,7 +236,7 @@ namespace Nz
// Index buffer // Index buffer
bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max()); bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max());
IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage, 0); IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, UInt32(indexCount), parameters.storage, 0);
IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite); IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
IndexIterator index = indexMapper.begin(); IndexIterator index = indexMapper.begin();
@ -251,7 +251,7 @@ namespace Nz
indexMapper.Unmap(); indexMapper.Unmap();
// Vertex buffer // Vertex buffer
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage, 0); VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), UInt32(vertexCount), parameters.storage, 0);
BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly); BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);
MeshVertex* vertices = static_cast<MeshVertex*>(vertexMapper.GetPointer()); MeshVertex* vertices = static_cast<MeshVertex*>(vertexMapper.GetPointer());

View File

@ -233,8 +233,8 @@ namespace Nz
} }
// Création des buffers // Création des buffers
IndexBufferRef indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), indices.size(), parameters.storage, 0); IndexBufferRef indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), UInt32(indices.size()), parameters.storage, 0);
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage, 0); VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), UInt32(vertexCount), parameters.storage, 0);
// Remplissage des indices // Remplissage des indices
IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly); IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly);

View File

@ -228,7 +228,7 @@ namespace Nz
if (p < 0) if (p < 0)
{ {
p += m_positions.size() - 1; p += static_cast<int>(m_positions.size() - 1);
if (p < 0) if (p < 0)
{ {
Error("Vertex index out of range (" + String::Number(p) + " < 0"); Error("Vertex index out of range (" + String::Number(p) + " < 0");
@ -239,7 +239,7 @@ namespace Nz
if (n < 0) if (n < 0)
{ {
n += m_normals.size() - 1; n += static_cast<int>(m_normals.size() - 1);
if (n < 0) if (n < 0)
{ {
Error("Normal index out of range (" + String::Number(n) + " < 0"); Error("Normal index out of range (" + String::Number(n) + " < 0");
@ -250,7 +250,7 @@ namespace Nz
if (t < 0) if (t < 0)
{ {
t += m_texCoords.size() - 1; t += static_cast<int>(m_texCoords.size() - 1);
if (t < 0) if (t < 0)
{ {
Error("Texture coordinates index out of range (" + String::Number(t) + " < 0"); Error("Texture coordinates index out of range (" + String::Number(t) + " < 0");

View File

@ -12,38 +12,38 @@ namespace Nz
{ {
namespace namespace
{ {
UInt32 GetterSequential(const void* buffer, unsigned int i) UInt32 GetterSequential(const void* buffer, std::size_t i)
{ {
NazaraUnused(buffer); NazaraUnused(buffer);
return i; return static_cast<UInt32>(i);
} }
UInt32 Getter16(const void* buffer, unsigned int i) UInt32 Getter16(const void* buffer, std::size_t i)
{ {
const UInt16* ptr = static_cast<const UInt16*>(buffer); const UInt16* ptr = static_cast<const UInt16*>(buffer);
return ptr[i]; return ptr[i];
} }
UInt32 Getter32(const void* buffer, unsigned int i) UInt32 Getter32(const void* buffer, std::size_t i)
{ {
const UInt32* ptr = static_cast<const UInt32*>(buffer); const UInt32* ptr = static_cast<const UInt32*>(buffer);
return ptr[i]; return ptr[i];
} }
void Setter16(void* buffer, unsigned int i, UInt32 value) void Setter16(void* buffer, std::size_t i, UInt32 value)
{ {
UInt16* ptr = static_cast<UInt16*>(buffer); UInt16* ptr = static_cast<UInt16*>(buffer);
ptr[i] = static_cast<UInt16>(value); ptr[i] = static_cast<UInt16>(value);
} }
void Setter32(void* buffer, unsigned int i, UInt32 value) void Setter32(void* buffer, std::size_t i, UInt32 value)
{ {
UInt32* ptr = static_cast<UInt32*>(buffer); UInt32* ptr = static_cast<UInt32*>(buffer);
ptr[i] = value; ptr[i] = value;
} }
void SetterError(void*, unsigned int, UInt32) void SetterError(void*, std::size_t, UInt32)
{ {
NazaraError("Index buffer opened with read-only access"); NazaraError("Index buffer opened with read-only access");
} }
@ -113,15 +113,9 @@ namespace Nz
{ {
} }
UInt32 IndexMapper::Get(unsigned int i) const UInt32 IndexMapper::Get(std::size_t i) const
{ {
#if NAZARA_UTILITY_SAFE NazaraAssert(i < m_indexCount, "Index out of range");
if (i >= m_indexCount)
{
NazaraError("Index out of range (" + String::Number(i) + " >= " + String::Number(m_indexCount) + ')');
return 0;
}
#endif
return m_getter(m_mapper.GetPointer(), i); return m_getter(m_mapper.GetPointer(), i);
} }
@ -131,20 +125,14 @@ namespace Nz
return m_mapper.GetBuffer(); return m_mapper.GetBuffer();
} }
unsigned int IndexMapper::GetIndexCount() const std::size_t IndexMapper::GetIndexCount() const
{ {
return m_indexCount; return m_indexCount;
} }
void IndexMapper::Set(unsigned int i, UInt32 value) void IndexMapper::Set(std::size_t i, UInt32 value)
{ {
#if NAZARA_UTILITY_SAFE NazaraAssert(i < m_indexCount, "Index out of range");
if (i >= m_indexCount)
{
NazaraError("Index out of range (" + String::Number(i) + " >= " + String::Number(m_indexCount) + ')');
return;
}
#endif
m_setter(m_mapper.GetPointer(), i, value); m_setter(m_mapper.GetPointer(), i, value);
} }

View File

@ -79,7 +79,7 @@ namespace Nz
NazaraAssert(subMesh, "Invalid submesh"); NazaraAssert(subMesh, "Invalid submesh");
NazaraAssert(subMesh->GetAnimationType() == m_impl->animationType, "Submesh animation type doesn't match mesh animation type"); NazaraAssert(subMesh->GetAnimationType() == m_impl->animationType, "Submesh animation type doesn't match mesh animation type");
m_impl->subMeshes.push_back(subMesh); m_impl->subMeshes.emplace_back(subMesh);
InvalidateAABB(); InvalidateAABB();
} }
@ -92,10 +92,10 @@ namespace Nz
NazaraAssert(subMesh, "Invalid submesh"); NazaraAssert(subMesh, "Invalid submesh");
NazaraAssert(subMesh->GetAnimationType() == m_impl->animationType, "Submesh animation type doesn't match mesh animation type"); NazaraAssert(subMesh->GetAnimationType() == m_impl->animationType, "Submesh animation type doesn't match mesh animation type");
UInt32 index = m_impl->subMeshes.size(); std::size_t index = m_impl->subMeshes.size();
m_impl->subMeshes.push_back(subMesh); m_impl->subMeshes.emplace_back(subMesh);
m_impl->subMeshMap[identifier] = index; m_impl->subMeshMap[identifier] = static_cast<UInt32>(index);
InvalidateAABB(); InvalidateAABB();
} }
@ -349,11 +349,11 @@ namespace Nz
if (!m_impl->aabbUpdated) if (!m_impl->aabbUpdated)
{ {
UInt32 subMeshCount = m_impl->subMeshes.size(); std::size_t subMeshCount = m_impl->subMeshes.size();
if (subMeshCount > 0) if (subMeshCount > 0)
{ {
m_impl->aabb.Set(m_impl->subMeshes[0]->GetAABB()); m_impl->aabb.Set(m_impl->subMeshes[0]->GetAABB());
for (UInt32 i = 1; i < subMeshCount; ++i) for (std::size_t i = 1; i < subMeshCount; ++i)
m_impl->aabb.ExtendTo(m_impl->subMeshes[i]->GetAABB()); m_impl->aabb.ExtendTo(m_impl->subMeshes[i]->GetAABB());
} }
else else
@ -407,7 +407,7 @@ namespace Nz
{ {
NazaraAssert(m_impl, "Mesh should be created first"); NazaraAssert(m_impl, "Mesh should be created first");
return m_impl->materialData.size(); return static_cast<UInt32>(m_impl->materialData.size());
} }
Skeleton* Mesh::GetSkeleton() Skeleton* Mesh::GetSkeleton()
@ -466,7 +466,7 @@ namespace Nz
{ {
NazaraAssert(m_impl, "Mesh should be created first"); NazaraAssert(m_impl, "Mesh should be created first");
return m_impl->subMeshes.size(); return static_cast<UInt32>(m_impl->subMeshes.size());
} }
UInt32 Mesh::GetSubMeshIndex(const String& identifier) const UInt32 Mesh::GetSubMeshIndex(const String& identifier) const

View File

@ -243,7 +243,7 @@ namespace Nz
m_workingBounds.MakeZero(); //< Compute bounds as float to speedup bounds computation (as casting between floats and integers is costly) m_workingBounds.MakeZero(); //< Compute bounds as float to speedup bounds computation (as casting between floats and integers is costly)
if (m_font) if (m_font)
m_lines.emplace_back(Line{Rectf(0.f, 0.f, 0.f, m_font->GetSizeInfo(m_characterSize).lineHeight), 0}); m_lines.emplace_back(Line{Rectf(0.f, 0.f, 0.f, float(m_font->GetSizeInfo(m_characterSize).lineHeight)), 0});
else else
m_lines.emplace_back(Line{Rectf::Zero(), 0}); m_lines.emplace_back(Line{Rectf::Zero(), 0});
} }
@ -354,7 +354,7 @@ namespace Nz
{ {
glyph.atlas = nullptr; glyph.atlas = nullptr;
glyph.bounds.Set(m_drawPos.x, m_drawPos.y, float(advance), sizeInfo.lineHeight); glyph.bounds.Set(float(m_drawPos.x), float(m_drawPos.y), float(advance), float(sizeInfo.lineHeight));
glyph.corners[0].Set(glyph.bounds.GetCorner(RectCorner_LeftTop)); glyph.corners[0].Set(glyph.bounds.GetCorner(RectCorner_LeftTop));
glyph.corners[1].Set(glyph.bounds.GetCorner(RectCorner_RightTop)); glyph.corners[1].Set(glyph.bounds.GetCorner(RectCorner_RightTop));
@ -377,7 +377,7 @@ namespace Nz
m_drawPos.x = 0; m_drawPos.x = 0;
m_drawPos.y += sizeInfo.lineHeight; m_drawPos.y += sizeInfo.lineHeight;
m_lines.emplace_back(Line{Rectf(0.f, sizeInfo.lineHeight * m_lines.size(), 0.f, sizeInfo.lineHeight), m_glyphs.size() + 1}); m_lines.emplace_back(Line{Rectf(0.f, float(sizeInfo.lineHeight * m_lines.size()), 0.f, float(sizeInfo.lineHeight)), m_glyphs.size() + 1});
break; break;
} }
} }

View File

@ -72,12 +72,12 @@ namespace Nz
if (!m_impl->aabbUpdated) if (!m_impl->aabbUpdated)
{ {
UInt32 jointCount = m_impl->joints.size(); std::size_t jointCount = m_impl->joints.size();
if (jointCount > 0) if (jointCount > 0)
{ {
Vector3f pos = m_impl->joints[0].GetPosition(); Vector3f pos = m_impl->joints[0].GetPosition();
m_impl->aabb.Set(pos.x, pos.y, pos.z, 0.f, 0.f, 0.f); m_impl->aabb.Set(pos.x, pos.y, pos.z, 0.f, 0.f, 0.f);
for (UInt32 i = 1; i < jointCount; ++i) for (std::size_t i = 1; i < jointCount; ++i)
m_impl->aabb.ExtendTo(m_impl->joints[i].GetPosition()); m_impl->aabb.ExtendTo(m_impl->joints[i].GetPosition());
} }
else else
@ -219,7 +219,7 @@ namespace Nz
} }
#endif #endif
return m_impl->joints.size(); return static_cast<UInt32>(m_impl->joints.size());
} }
int Skeleton::GetJointIndex(const String& jointName) const int Skeleton::GetJointIndex(const String& jointName) const
@ -411,16 +411,9 @@ namespace Nz
String name = m_impl->joints[i].GetName(); String name = m_impl->joints[i].GetName();
if (!name.IsEmpty()) if (!name.IsEmpty())
{ {
#if NAZARA_UTILITY_SAFE NazaraAssert(m_impl->jointMap.find(name) == m_impl->jointMap.end(), "Joint name \"" + name + "\" is already present in joint map");
auto it = m_impl->jointMap.find(name);
if (it != m_impl->jointMap.end())
{
NazaraWarning("Joint name \"" + name + "\" is already present in joint map for joint #" + String::Number(it->second));
continue;
}
#endif
m_impl->jointMap[name] = i; m_impl->jointMap[name] = static_cast<UInt32>(i);
} }
} }

View File

@ -66,18 +66,9 @@ namespace Nz
return true; return true;
} }
UInt32 TriangleIterator::operator[](unsigned int i) const UInt32 TriangleIterator::operator[](std::size_t i) const
{ {
#if NAZARA_UTILITY_SAFE NazaraAssert(i < 3, "Index out of range");
if (i >= 3)
{
StringStream ss;
ss << "Index out of range: (" << i << " >= 3)";
NazaraError(ss);
throw std::domain_error(ss.ToString());
}
#endif
return m_triangleIndices[i]; return m_triangleIndices[i];
} }

View File

@ -1,5 +1,6 @@
#include <Nazara/Math/Algorithm.hpp> #include <Nazara/Math/Algorithm.hpp>
#include <Catch/catch.hpp> #include <Catch/catch.hpp>
#include <limits>
TEST_CASE("Approach", "[MATH][ALGORITHM]") TEST_CASE("Approach", "[MATH][ALGORITHM]")
{ {
@ -48,6 +49,11 @@ TEST_CASE("CountBits", "[MATH][ALGORITHM]")
{ {
REQUIRE(Nz::CountBits(0) == 0); REQUIRE(Nz::CountBits(0) == 0);
} }
SECTION("Number 0xFFFFFFFF has 32 bit set to 1")
{
REQUIRE(Nz::CountBits(0xFFFFFFFF) == 32);
}
} }
TEST_CASE("DegreeToRadian", "[MATH][ALGORITHM]") TEST_CASE("DegreeToRadian", "[MATH][ALGORITHM]")
@ -205,9 +211,19 @@ TEST_CASE("NumberEquals", "[MATH][ALGORITHM]")
CHECK(Nz::NumberEquals(2.35, 2.35, 0.01)); CHECK(Nz::NumberEquals(2.35, 2.35, 0.01));
} }
SECTION("3 and 4 unsigned should be the same at 1") SECTION("0 and 4 unsigned should be the same at 1")
{ {
CHECK(Nz::NumberEquals(3U, 4U, 1U)); CHECK(Nz::NumberEquals(0U, 4U, 4U));
}
SECTION("Maximum integer and -1 should not be equal")
{
CHECK_FALSE(Nz::NumberEquals(std::numeric_limits<int>::max(), -1));
}
SECTION("Maximum integer and minimum integer should not be equal")
{
CHECK_FALSE(Nz::NumberEquals(std::numeric_limits<int>::max(), std::numeric_limits<int>::min()));
} }
} }

View File

@ -27,7 +27,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]")
WHEN("We register the server socket to the poller") WHEN("We register the server socket to the poller")
{ {
REQUIRE(serverPoller.RegisterSocket(server)); REQUIRE(serverPoller.RegisterSocket(server, Nz::SocketPollEvent_Read));
THEN("The poller should have registered our socket") THEN("The poller should have registered our socket")
{ {
@ -48,7 +48,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]")
WHEN("We register the client socket to the poller") WHEN("We register the client socket to the poller")
{ {
REQUIRE(serverPoller.RegisterSocket(serverToClient)); REQUIRE(serverPoller.RegisterSocket(serverToClient, Nz::SocketPollEvent_Read));
THEN("The poller should have registered our socket") THEN("The poller should have registered our socket")
{ {
@ -65,7 +65,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]")
REQUIRE(serverPoller.Wait(1000)); REQUIRE(serverPoller.Wait(1000));
CHECK(serverPoller.IsReady(serverToClient)); CHECK(serverPoller.IsReadyToRead(serverToClient));
CHECK(serverToClient.Read(buffer.data(), buffer.size()) == sent); CHECK(serverToClient.Read(buffer.data(), buffer.size()) == sent);
@ -73,7 +73,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]")
{ {
REQUIRE_FALSE(serverPoller.Wait(100)); REQUIRE_FALSE(serverPoller.Wait(100));
REQUIRE_FALSE(serverPoller.IsReady(serverToClient)); REQUIRE_FALSE(serverPoller.IsReadyToRead(serverToClient));
} }
} }
} }

View File

@ -38,7 +38,7 @@ SCENARIO("BaseSystem", "[NDK][BASESYSTEM]")
WHEN("We add an entity") WHEN("We add an entity")
{ {
const Ndk::EntityHandle& entity = world.CreateEntity(); Ndk::EntityHandle entity = world.CreateEntity();
entity->AddComponent<Ndk::VelocityComponent>(); entity->AddComponent<Ndk::VelocityComponent>();
THEN("System should have it") THEN("System should have it")
@ -50,7 +50,7 @@ SCENARIO("BaseSystem", "[NDK][BASESYSTEM]")
WHEN("We add an entity with excluded component") WHEN("We add an entity with excluded component")
{ {
const Ndk::EntityHandle& entity = world.CreateEntity(); Ndk::EntityHandle entity = world.CreateEntity();
entity->AddComponent<Ndk::VelocityComponent>(); entity->AddComponent<Ndk::VelocityComponent>();
entity->AddComponent<Ndk::NodeComponent>(); entity->AddComponent<Ndk::NodeComponent>();

View File

@ -57,18 +57,18 @@ SCENARIO("Entity", "[NDK][ENTITY]")
{ {
Ndk::World world; Ndk::World world;
Ndk::BaseSystem& system = world.AddSystem<UpdateSystem>(); Ndk::BaseSystem& system = world.AddSystem<UpdateSystem>();
const Ndk::EntityHandle& entity = world.CreateEntity(); Ndk::EntityHandle entity = world.CreateEntity();
WHEN("We add our UpdateComponent") WHEN("We add our UpdateComponent")
{ {
UpdatableComponent& updatableComponent = entity->AddComponent<UpdatableComponent>(); UpdatableComponent& updatableComponent = entity->AddComponent<UpdatableComponent>();
REQUIRE(!updatableComponent.IsUpdated()); CHECK(!updatableComponent.IsUpdated());
THEN("Update the world should update the entity's component") THEN("Update the world should update the entity's component")
{ {
world.Update(1.f); world.Update(1.f);
UpdatableComponent& updatableComponentGet = entity->GetComponent<UpdatableComponent>(); UpdatableComponent& updatableComponentGet = entity->GetComponent<UpdatableComponent>();
REQUIRE(updatableComponentGet.IsUpdated()); CHECK(updatableComponentGet.IsUpdated());
} }
THEN("Update the world should not update the entity's component if it's disabled") THEN("Update the world should not update the entity's component if it's disabled")
@ -76,14 +76,14 @@ SCENARIO("Entity", "[NDK][ENTITY]")
entity->Enable(false); entity->Enable(false);
world.Update(1.f); world.Update(1.f);
UpdatableComponent& updatableComponentGet = entity->GetComponent<UpdatableComponent>(); UpdatableComponent& updatableComponentGet = entity->GetComponent<UpdatableComponent>();
REQUIRE(!updatableComponentGet.IsUpdated()); CHECK(!updatableComponentGet.IsUpdated());
} }
THEN("We can remove its component") THEN("We can remove its component")
{ {
entity->RemoveComponent(Ndk::GetComponentIndex<UpdatableComponent>()); entity->RemoveComponent(Ndk::GetComponentIndex<UpdatableComponent>());
world.Update(1.f); world.Update(1.f);
REQUIRE(!entity->HasComponent<UpdatableComponent>()); CHECK(!entity->HasComponent<UpdatableComponent>());
} }
} }
@ -94,7 +94,7 @@ SCENARIO("Entity", "[NDK][ENTITY]")
THEN("It's no more valid") THEN("It's no more valid")
{ {
REQUIRE(!world.IsEntityValid(entity)); CHECK(!world.IsEntityValid(entity));
} }
} }
} }

View File

@ -7,7 +7,7 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
GIVEN("A world & an entity") GIVEN("A world & an entity")
{ {
Ndk::World world; Ndk::World world;
const Ndk::EntityHandle& entity = world.CreateEntity(); Ndk::EntityHandle entity = world.CreateEntity();
WHEN("We set the ownership of the entity to our owner") WHEN("We set the ownership of the entity to our owner")
{ {
@ -15,13 +15,21 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
THEN("Entity is still valid") THEN("Entity is still valid")
{ {
REQUIRE(entity.IsValid()); CHECK(entity.IsValid());
} }
THEN("Resetting or getting out of scope is no more valid") THEN("Resetting or getting out of scope is no more valid")
{ {
entityOwner.Reset(); entityOwner.Reset();
world.Update(1.f); world.Update(1.f);
CHECK(!entity.IsValid());
}
THEN("Moving an entity owner works")
{
Ndk::EntityOwner entityOwner2(std::move(entityOwner));
entityOwner2.Reset();
world.Update(1.f);
REQUIRE(!entity.IsValid()); REQUIRE(!entity.IsValid());
} }
} }

View File

@ -28,21 +28,79 @@ class TestState : public Ndk::State
bool m_isUpdated; bool m_isUpdated;
}; };
class SecondTestState : public Ndk::State
{
public:
void Enter(Ndk::StateMachine& /*fsm*/) override
{
m_isUpdated = false;
}
bool IsUpdated() const
{
return m_isUpdated;
}
void Leave(Ndk::StateMachine& /*fsm*/) override
{
}
bool Update(Ndk::StateMachine& fsm, float /*elapsedTime*/) override
{
if (fsm.IsTopState(this))
m_isUpdated = true;
return true;
}
private:
bool m_isUpdated;
};
SCENARIO("State & StateMachine", "[NDK][STATE]") SCENARIO("State & StateMachine", "[NDK][STATE]")
{ {
GIVEN("A statemachine with our TestState") GIVEN("A statemachine with our test states")
{ {
std::shared_ptr<TestState> testState = std::make_shared<TestState>(); std::shared_ptr<TestState> testState = std::make_shared<TestState>();
Ndk::StateMachine stateMachine(testState); std::shared_ptr<SecondTestState> secondTestState = std::make_shared<SecondTestState>();
Ndk::StateMachine stateMachine(secondTestState);
stateMachine.PushState(testState);
REQUIRE(!testState->IsUpdated()); REQUIRE(!testState->IsUpdated());
REQUIRE(!secondTestState->IsUpdated());
WHEN("We update our machine") WHEN("We update our machine")
{ {
stateMachine.Update(1.f); stateMachine.Update(1.f);
THEN("Our state has been updated") THEN("Our state on the top has been updated but not the bottom one")
{ {
REQUIRE(stateMachine.IsTopState(testState.get()));
REQUIRE(!stateMachine.IsTopState(secondTestState.get()));
REQUIRE(testState->IsUpdated()); REQUIRE(testState->IsUpdated());
REQUIRE(!secondTestState->IsUpdated());
}
}
WHEN("We exchange the states' positions while emptying the stack")
{
REQUIRE(stateMachine.PopStatesUntil(secondTestState));
REQUIRE(stateMachine.IsTopState(secondTestState.get()));
std::shared_ptr<Ndk::State> oldState = stateMachine.PopState();
REQUIRE(stateMachine.PopState() == nullptr);
stateMachine.SetState(testState);
stateMachine.PushState(oldState);
stateMachine.Update(1.f);
THEN("Both states should be updated")
{
REQUIRE(!stateMachine.IsTopState(testState.get()));
REQUIRE(stateMachine.IsTopState(secondTestState.get()));
REQUIRE(testState->IsUpdated());
REQUIRE(secondTestState->IsUpdated());
} }
} }
} }

View File

@ -11,7 +11,7 @@ SCENARIO("ListenerSystem", "[NDK][LISTENERSYSTEM]")
GIVEN("A world and an entity with listener & node components") GIVEN("A world and an entity with listener & node components")
{ {
Ndk::World world; Ndk::World world;
const Ndk::EntityHandle& entity = world.CreateEntity(); Ndk::EntityHandle entity = world.CreateEntity();
Ndk::ListenerComponent& listenerComponent = entity->AddComponent<Ndk::ListenerComponent>(); Ndk::ListenerComponent& listenerComponent = entity->AddComponent<Ndk::ListenerComponent>();
Ndk::NodeComponent& nodeComponent = entity->AddComponent<Ndk::NodeComponent>(); Ndk::NodeComponent& nodeComponent = entity->AddComponent<Ndk::NodeComponent>();

View File

@ -79,7 +79,7 @@ SCENARIO("World", "[NDK][WORLD]")
AND_WHEN("We update our world with our entity") AND_WHEN("We update our world with our entity")
{ {
REQUIRE(&world.GetSystem(UpdateSystem::systemIndex) == &world.GetSystem<UpdateSystem>()); REQUIRE(&world.GetSystem(UpdateSystem::systemIndex) == &world.GetSystem<UpdateSystem>());
const Ndk::EntityHandle& entity = world.CreateEntity(); Ndk::EntityHandle entity = world.CreateEntity();
UpdatableComponent& component = entity->AddComponent<UpdatableComponent>(); UpdatableComponent& component = entity->AddComponent<UpdatableComponent>();
THEN("Our entity component must be updated") THEN("Our entity component must be updated")