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*()>;
BaseComponent(ComponentIndex componentIndex);
BaseComponent(const BaseComponent&) = default;
BaseComponent(BaseComponent&&) = default;
virtual ~BaseComponent();
@ -34,10 +33,12 @@ namespace Ndk
inline static ComponentIndex GetMaxComponentIndex();
BaseComponent& operator=(const BaseComponent&) = default;
BaseComponent& operator=(const BaseComponent&) = delete;
BaseComponent& operator=(BaseComponent&&) = default;
protected:
BaseComponent(const BaseComponent&) = default;
ComponentIndex m_componentIndex;
EntityHandle m_entity;

View File

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

View File

@ -56,7 +56,7 @@ namespace Ndk
* \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;
}
@ -121,10 +121,7 @@ namespace Ndk
inline bool BaseSystem::HasEntity(const Entity* entity) const
{
if (!entity)
return false;
return m_entityBits.UnboundedTest(entity->GetId());
return m_entities.Has(entity);
}
/*!
@ -288,9 +285,7 @@ namespace Ndk
{
NazaraAssert(entity, "Invalid entity");
m_entities.emplace_back(entity);
m_entityBits.UnboundedSet(entity->GetId(), true);
m_entities.Insert(entity);
entity->RegisterSystem(m_systemIndex);
OnEntityAdded(entity);
@ -308,14 +303,7 @@ namespace Ndk
{
NazaraAssert(entity, "Invalid entity");
auto it = std::find(m_entities.begin(), m_entities.end(), *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());
m_entities.Remove(entity);
entity->UnregisterSystem(m_systemIndex);
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()

View File

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

View File

@ -117,16 +117,6 @@ namespace Ndk
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
* \return Size of the camera

View File

@ -51,6 +51,9 @@ namespace Ndk
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;
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
*/

View File

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

View File

@ -6,6 +6,7 @@
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/StringStream.hpp>
#include <algorithm>
#include <cassert>
#include <type_traits>
#include <utility>
@ -257,11 +258,10 @@ namespace Ndk
return m_removedComponentBits;
}
/*!
* \brief Registers a system for the entity
*
* \param index Index of the system
*/
inline void Entity::RegisterEntityList(EntityList* list)
{
m_containedInLists.push_back(list);
}
inline void Entity::RegisterSystem(SystemIndex index)
{
@ -289,6 +289,16 @@ namespace Ndk
* \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)
{
m_systemBits.UnboundedReset(index);

View File

@ -15,46 +15,68 @@ namespace Ndk
{
class NDK_API EntityList
{
public:
using Container = std::vector<EntityHandle>;
friend Entity;
EntityList() = default;
~EntityList() = default;
public:
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 bool Has(const Entity* entity);
inline bool Has(EntityId entity);
inline bool Has(const Entity* entity) const;
inline bool Has(EntityId entity) const;
inline void Insert(Entity* entity);
inline void Remove(Entity* entity);
// STL API
inline Container::iterator begin();
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 iterator begin() const;
inline bool empty() const;
inline iterator end() const;
inline size_type size() const;
inline Container::iterator end();
inline Container::const_iterator end() const;
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;
inline EntityList& operator=(const EntityList& entityList);
inline EntityList& operator=(EntityList&& entityList) noexcept;
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;
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"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp
#include <NDK/EntityList.hpp>
#include <Nazara/Core/Error.hpp>
#include <algorithm>
@ -14,24 +15,71 @@ namespace Ndk
*/
/*!
* \brief Clears the set from every entities
* \brief Construct a new entity list
*/
inline void EntityList::Clear()
inline EntityList::EntityList() :
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
*
* \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)
inline bool EntityList::Has(const Entity* entity) const
{
NazaraAssert(!m_world || !entity || entity->GetWorld() == m_world, "Incompatible world");
return entity && entity->IsValid() && Has(entity->GetId());
}
@ -41,8 +89,7 @@ namespace Ndk
*
* \param id Identifier of the entity
*/
inline bool EntityList::Has(EntityId entity)
inline bool EntityList::Has(EntityId entity) const
{
return m_entityBits.UnboundedTest(entity);
}
@ -50,17 +97,23 @@ namespace Ndk
/*!
* \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 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)
{
NazaraAssert(entity, "Invalid entity");
if (!Has(entity))
{
m_entities.emplace_back(entity);
entity->RegisterEntityList(this);
m_entityBits.UnboundedSet(entity->GetId(), true);
m_world = entity->GetWorld();
}
}
@ -70,89 +123,138 @@ namespace Ndk
* \param entity Pointer to the entity
*
* \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)
{
if (Has(entity))
{
auto it = std::find(m_entities.begin(), m_entities.end(), *entity);
NazaraAssert(it != m_entities.end(), "Entity should be part of the vector");
m_entityBits.Reset(entity->GetId());
std::swap(*it, m_entities.back());
m_entities.pop_back(); // We get it out of the vector
m_entityBits.UnboundedSet(entity->GetId(), false);
entity->UnregisterEntityList(this);
}
}
// Nz::Interface STD
inline EntityList::Container::iterator EntityList::begin()
// STL Interface
inline EntityList::iterator EntityList::begin() const
{
return m_entities.begin();
}
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();
return EntityList::iterator(this, m_entityBits.FindFirst());
}
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"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp
@ -10,6 +10,7 @@
#include <NDK/Prerequesites.hpp>
#include <NDK/State.hpp>
#include <memory>
#include <vector>
namespace Ndk
{
@ -25,14 +26,21 @@ namespace Ndk
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 StateMachine& operator=(StateMachine&& fsm) = default;
StateMachine& operator=(const StateMachine&) = delete;
private:
std::shared_ptr<State> m_currentState;
std::shared_ptr<State> m_nextState;
std::vector<std::shared_ptr<State>> m_states;
};
}

View File

@ -3,7 +3,6 @@
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp
#include <Nazara/Core/Error.hpp>
#include <NDK/StateMachine.hpp>
#include <utility>
namespace Ndk
@ -11,7 +10,7 @@ namespace Ndk
/*!
* \ingroup NDK
* \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
*/
inline StateMachine::StateMachine(std::shared_ptr<State> originalState) :
m_currentState(std::move(originalState))
inline StateMachine::StateMachine(std::shared_ptr<State> originalState)
{
NazaraAssert(m_currentState, "StateMachine must have a state to begin with");
m_currentState->Enter(*this);
NazaraAssert(originalState, "StateMachine must have a state to begin with");
PushState(std::move(originalState));
}
/*!
* \brief Destructs the object
*
* \remark Calls "Leave" on the state
* \remark Calls "Leave" on all the states
*/
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)
{
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
*
* \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
{
return m_currentState;
return m_states.back();
}
/*!
* \brief Updates the state
* \return True if update is successful
* \brief Checks whether the state is on the top of the machine
* \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
*/
inline bool StateMachine::Update(float elapsedTime)
{
if (m_nextState)
{
m_currentState->Leave(*this);
m_currentState = std::move(m_nextState);
m_currentState->Enter(*this);
}
return m_currentState->Update(*this, elapsedTime);
return std::all_of(m_states.begin(), m_states.end(), [=](std::shared_ptr<State>& state) {
return state->Update(*this, elapsedTime);
});
}
}

View File

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

View File

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

View File

@ -82,9 +82,9 @@ namespace Ndk
* \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);
for (unsigned int i = 0; i < count; ++i)
@ -98,7 +98,7 @@ namespace Ndk
* \return A constant reference to the entities
*/
inline const World::EntityList& World::GetEntities()
inline const EntityList& World::GetEntities() const
{
return m_aliveEntities;
}
@ -164,18 +164,53 @@ namespace Ndk
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
*
* This function has the same effect as calling KillEntity for every entity contained in the vector
*
* \param list Set of entities to kill
*/
inline void World::KillEntities(const EntityList& list)
inline void World::KillEntities(const EntityVector& list)
{
for (const EntityHandle& entity : list)
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
* \return true If it is the case
@ -197,7 +232,7 @@ namespace Ndk
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_dirtyEntities = std::move(world.m_dirtyEntities);
m_entityBlocks = std::move(world.m_entityBlocks);
m_freeIdList = std::move(world.m_freeIdList);
m_killedEntities = std::move(world.m_killedEntities);
m_orderedSystems = std::move(world.m_orderedSystems);
m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
m_waitingEntities = std::move(world.m_waitingEntities);
m_entities = std::move(world.m_entities);
for (EntityBlock& block : m_entities)
@ -284,7 +321,7 @@ namespace Ndk
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
}

View File

@ -93,6 +93,17 @@ namespace Ndk
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
* \return A constant reference to the render target of the camera

View File

@ -23,7 +23,9 @@ namespace Ndk
Entity::Entity(Entity&& entity) :
HandledObject(std::move(entity)),
m_components(std::move(entity.m_components)),
m_containedInLists(std::move(entity.m_containedInLists)),
m_componentBits(std::move(entity.m_componentBits)),
m_removedComponentBits(std::move(entity.m_removedComponentBits)),
m_systemBits(std::move(entity.m_systemBits)),
m_id(entity.m_id),
m_world(entity.m_world),
@ -175,9 +177,15 @@ namespace Ndk
m_components.clear();
m_componentBits.Reset();
// And then free every handle
// Free every handle
UnregisterAllHandles();
// Remove from every list
for (EntityList* list : m_containedInLists)
list->NotifyEntityDestruction(this);
m_containedInLists.clear();
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_cameras.Remove(entity);
m_directionalLights.Remove(entity);
m_drawables.Remove(entity);
m_lights.Remove(entity);
m_particleGroups.Remove(entity);
m_pointSpotLights.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>())
{
@ -74,14 +76,23 @@ namespace Ndk
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)
{
return handle1->GetComponent<CameraComponent>().GetLayer() < handle2->GetComponent<CameraComponent>().GetLayer();
});
}
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>())
{
@ -181,7 +192,7 @@ namespace Ndk
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.EnsureBoundingVolumeUpdate();
}
bool forceInvalidation = false;
std::size_t visibilityHash = m_drawableCulling.Cull(camComponent.GetFrustum(), &forceInvalidation);

View File

@ -60,29 +60,51 @@ namespace Ndk
const EntityHandle& World::CreateEntity()
{
EntityId id;
EntityBlock* entBlock;
if (!m_freeIdList.empty())
{
// We get an identifier
id = m_freeIdList.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
{
// 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
m_entities.push_back(Entity(this, id));
if (m_entities.capacity() > m_entities.size())
{
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
Entity& entity = m_entities[id].entity;
entity.Create();
// We initialize the entity and we add it to the list of alive entities
entBlock->entity.Create();
m_aliveEntities.emplace_back(&entity);
m_entities[id].aliveIndex = m_aliveEntities.size() - 1;
m_aliveEntities.Insert(&entBlock->entity);
return m_aliveEntities.back();
return entBlock->handle;
}
/*!
@ -96,8 +118,10 @@ namespace Ndk
// First, destruction of entities, then handles
// This is made to avoid that handle warn uselessly entities before their destruction
m_entities.clear();
m_entityBlocks.clear();
m_waitingEntities.clear();
m_aliveEntities.clear();
m_aliveEntities.Clear();
m_dirtyEntities.Clear();
m_killedEntities.Clear();
}
@ -120,7 +144,7 @@ namespace Ndk
return EntityHandle::InvalidHandle;
}
EntityHandle clone = CreateEntity();
const EntityHandle& clone = CreateEntity();
const Nz::Bitset<>& componentBits = original->GetComponentBits();
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));
}
return GetEntity(clone->GetId());
}
/*!
* \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;
}
return clone;
}
/*!
@ -177,45 +167,43 @@ namespace Ndk
if (!m_orderedSystemsUpdated)
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
for (std::size_t i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i))
{
EntityBlock& block = m_entities[i];
Entity& entity = block.entity;
NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range");
NazaraAssert(entity.IsValid(), "Entity must be valid");
Entity* entity = &m_entityBlocks[i]->entity;
// 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
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_freeIdList.push_back(entity->GetId());
}
m_killedEntities.Reset();
// 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))
{
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)
if (!entity->IsValid())
@ -242,7 +230,7 @@ namespace Ndk
}
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)
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 AddPointLight(const PointLight& 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);

View File

@ -33,6 +33,7 @@ namespace Nz
virtual Vector3f GetForward() const = 0;
virtual const Frustumf& GetFrustum() const = 0;
virtual const Matrix4f& GetProjectionMatrix() const = 0;
virtual Nz::ProjectionType GetProjectionType() const = 0;
virtual const RenderTarget* GetTarget() const = 0;
virtual const Matrix4f& GetViewMatrix() 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 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 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;

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 AddPointLight(const PointLight& light) override;
void AddSpotLight(const SpotLight& light) override;
void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const Texture* overlay = nullptr) override;
void AddSprites(int renderOrder, const Material* material, const VertexStruct_XYZ_Color_UV* vertices, std::size_t spriteCount, const Texture* overlay = nullptr) override;
private:
inline bool IsMaterialSuitable(const Material* material) const;

View File

@ -13,6 +13,7 @@
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Math/Box.hpp>
#include <Nazara/Math/Matrix4.hpp>
#include <Nazara/Math/Plane.hpp>
#include <Nazara/Utility/IndexBuffer.hpp>
#include <Nazara/Utility/MeshData.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 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 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;
@ -73,7 +74,7 @@ namespace Nz
std::vector<BillboardData> billboards;
};
typedef std::map<const Material*, BatchedBillboardEntry, MaterialComparator> BatchedBillboardContainer;
using BatchedBillboardContainer = std::map<const Material*, BatchedBillboardEntry, MaterialComparator>;
struct BatchedBillboardPipelineEntry
{
@ -81,7 +82,7 @@ namespace Nz
bool enabled = false;
};
typedef std::map<const MaterialPipeline*, BatchedBillboardPipelineEntry, MaterialPipelineComparator> BillboardPipelineBatches;
using BillboardPipelineBatches = std::map<const MaterialPipeline*, BatchedBillboardPipelineEntry, MaterialPipelineComparator>;
/// Sprites
struct SpriteChain_XYZ_Color_UV
@ -97,7 +98,7 @@ namespace Nz
std::vector<SpriteChain_XYZ_Color_UV> spriteChains;
};
typedef std::map<const Texture*, BatchedSpriteEntry> SpriteOverlayBatches;
using SpriteOverlayBatches = std::map<const Texture*, BatchedSpriteEntry>;
struct BatchedBasicSpriteEntry
{
@ -107,7 +108,7 @@ namespace Nz
bool enabled = false;
};
typedef std::map<const Material*, BatchedBasicSpriteEntry, MaterialComparator> SpriteMaterialBatches;
using SpriteMaterialBatches = std::map<const Material*, BatchedBasicSpriteEntry, MaterialComparator>;
struct BatchedSpritePipelineEntry
{
@ -115,7 +116,7 @@ namespace Nz
bool enabled = false;
};
typedef std::map<const MaterialPipeline*, BatchedSpritePipelineEntry, MaterialPipelineComparator> SpritePipelineBatches;
using SpritePipelineBatches = std::map<const MaterialPipeline*, BatchedSpritePipelineEntry, MaterialPipelineComparator>;
/// Meshes
struct MeshDataComparator
@ -132,7 +133,7 @@ namespace Nz
Spheref squaredBoundingSphere;
};
typedef std::map<MeshData, MeshInstanceEntry, MeshDataComparator> MeshInstanceContainer;
using MeshInstanceContainer = std::map<MeshData, MeshInstanceEntry, MeshDataComparator>;
struct BatchedModelEntry
{
@ -142,7 +143,7 @@ namespace Nz
bool enabled = false;
};
typedef std::map<const Material*, BatchedModelEntry, MaterialComparator> MeshMaterialBatches;
using MeshMaterialBatches = std::map<const Material*, BatchedModelEntry, MaterialComparator>;
struct BatchedMaterialEntry
{
@ -150,25 +151,33 @@ namespace Nz
MeshMaterialBatches materialMap;
};
typedef std::map<const MaterialPipeline*, BatchedMaterialEntry, MaterialPipelineComparator> MeshPipelineBatches;
using MeshPipelineBatches = std::map<const MaterialPipeline*, BatchedMaterialEntry, MaterialPipelineComparator>;
struct TransparentModelData
struct UnbatchedModelData
{
Matrix4f transformMatrix;
MeshData meshData;
Spheref squaredBoundingSphere;
Spheref obbSphere;
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
{
BillboardPipelineBatches billboards;
SpritePipelineBatches basicSprites;
SpritePipelineBatches opaqueSprites;
MeshPipelineBatches opaqueModels;
TransparentModelContainer transparentModels;
std::vector<TransparentModelData> transparentModelData;
std::vector<std::size_t> depthSortedMeshes;
std::vector<std::size_t> depthSortedSprites;
std::vector<UnbatchedModelData> depthSortedMeshData;
std::vector<UnbatchedSpriteData> depthSortedSpriteData;
std::vector<const Drawable*> otherDrawables;
unsigned int clearCount = 0;
};
@ -179,6 +188,10 @@ namespace Nz
BillboardData* GetBillboardData(int renderOrder, const Material* material, unsigned int count);
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 OnMaterialInvalidation(const Material* material);
void OnTextureInvalidation(const Texture* texture);

View File

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

View File

@ -98,7 +98,13 @@ namespace Nz
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)
std::swap(a, b);
@ -112,13 +118,8 @@ namespace Nz
if (b > a)
std::swap(a, b);
if ((b < 0) && (a > std::numeric_limits<T>::max() + b))
return false;
if ((b > 0) && (a < std::numeric_limits<T>::min() + b))
return false;
return std::abs(a - b) <= maxDifference;
using UnsignedT = std::make_unsigned_t<T>;
return static_cast<UnsignedT>(a) - static_cast<UnsignedT>(b) <= static_cast<UnsignedT>(maxDifference);
}
}
@ -487,7 +488,7 @@ namespace Nz
template<typename T, typename T2>
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& Translate(const Vector3<T>& translation);
T& operator[](unsigned int i);
T operator[](unsigned int i) const;
T& operator[](std::size_t i);
T operator[](std::size_t i) const;
Box operator*(T scalar) const;
Box operator*(const Vector3<T>& vec) const;

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
#ifndef NAZARA_ENUMS_NETWORK_HPP
#define NAZARA_ENUMS_NETWORK_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Flags.hpp>
namespace Nz
{
@ -91,6 +91,23 @@ namespace Nz
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
{
SocketState_Bound, //< The socket is currently bound

View File

@ -24,10 +24,11 @@ namespace Nz
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 RegisterSocket(AbstractSocket& socket);
bool RegisterSocket(AbstractSocket& socket, SocketPollEventFlags eventFlags);
void UnregisterSocket(AbstractSocket& socket);
bool Wait(UInt64 msTimeout);
@ -41,4 +42,4 @@ namespace Nz
#include <Nazara/Network/SocketPoller.inl>
#endif // NAZARA_SOCKETPOLLER_HPP
#endif // NAZARA_SOCKETPOLLER_HPP

View File

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

View File

@ -26,15 +26,15 @@ namespace Nz
Reference operator*() const;
Reference operator[](unsigned int index) const;
Reference operator[](std::size_t index) const;
IndexIterator& operator=(const IndexIterator& iterator);
IndexIterator operator+(unsigned int indexCount) const;
IndexIterator operator-(unsigned int indexCount) const;
IndexIterator operator+(std::size_t indexCount) const;
IndexIterator operator-(std::size_t indexCount) const;
IndexIterator& operator+=(unsigned int indexCount);
IndexIterator& operator-=(unsigned int indexCount);
IndexIterator& operator+=(std::size_t indexCount);
IndexIterator& operator-=(std::size_t indexCount);
IndexIterator& operator++();
IndexIterator operator++(int);
@ -50,10 +50,10 @@ namespace Nz
friend bool operator>=(const IndexIterator& lhs, const IndexIterator& rhs);
private:
IndexIterator(IndexMapper* mapper, unsigned int index);
IndexIterator(IndexMapper* mapper, std::size_t index);
IndexMapper* m_mapper;
unsigned int m_index;
std::size_t m_index;
};
class IndexIterator::Reference
@ -70,10 +70,10 @@ namespace Nz
operator UInt32() const;
private:
Reference(IndexMapper* mapper, unsigned int index);
Reference(IndexMapper* mapper, std::size_t index);
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_index(index)
{
@ -31,7 +31,7 @@ namespace Nz
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);
}
@ -44,24 +44,24 @@ namespace Nz
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);
}
inline IndexIterator IndexIterator::operator-(unsigned int indexCount) const
inline IndexIterator IndexIterator::operator-(std::size_t indexCount) const
{
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;
return *this;
}
inline IndexIterator& IndexIterator::operator-=(unsigned int indexCount)
inline IndexIterator& IndexIterator::operator-=(std::size_t indexCount)
{
m_index += indexCount;
@ -133,7 +133,7 @@ namespace Nz
/**************************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_index(index)
{
@ -148,7 +148,7 @@ namespace Nz
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;
}

View File

@ -17,9 +17,6 @@ namespace Nz
class IndexIterator;
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
{
public:
@ -29,11 +26,11 @@ namespace Nz
IndexMapper(const SubMesh* subMesh, BufferAccess access = BufferAccess_ReadOnly);
~IndexMapper() = default;
UInt32 Get(unsigned int i) const;
UInt32 Get(std::size_t i) 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();
@ -45,10 +42,13 @@ namespace Nz
// Méthodes STD
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;
IndexMapperGetter m_getter;
IndexMapperSetter m_setter;
unsigned int m_indexCount;
Getter m_getter;
Setter m_setter;
std::size_t m_indexCount;
};
}

View File

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

View File

@ -153,7 +153,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
if (animatedMesh)
{
mesh->CreateSkeletal(joints.size());
mesh->CreateSkeletal(UInt32(joints.size()));
Skeleton* skeleton = mesh->GetSkeleton();
@ -171,7 +171,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
mesh->CreateStatic();
// 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)
{
@ -300,7 +300,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue) == aiReturn_SUCCESS)
matData.SetParameter(MaterialData::FaceCulling, !iValue);
matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(materials.size(), std::move(matData)))).first;
matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(UInt32(materials.size()), std::move(matData)))).first;
}
subMesh->SetMaterialIndex(matIt->first);
@ -308,7 +308,7 @@ bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
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)
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)
[Forum](https://forum.digitalpulsesoftware.net)
###Remerciements:
### Remerciements:
- **RafBill** et **Raakz:** Recherche de bugs et/ou tests
- **Fissal "DrFisher" Hannoun**: Aide et conseils lors de la conception de l'architecture du moteur

View File

@ -101,7 +101,7 @@ namespace Nz
{
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 */
UInt8 buf[64]; /* accumulate block */
};
@ -280,9 +280,9 @@ namespace Nz
void HashMD5::Append(const UInt8* data, std::size_t len)
{
const UInt8 *p = data;
int left = len;
std::size_t left = len;
int offset = (m_state->count[0] >> 3) & 63;
UInt32 nbits = len << 3;
std::size_t nbits = len << 3;
if (len <= 0)
return;
@ -296,7 +296,7 @@ namespace Nz
/* Process an initial partial block. */
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);
if (offset + copy < 64)

View File

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

View File

@ -2115,7 +2115,7 @@ namespace Nz
return ptr - m_sharedString->string.get();
}
catch (utf8::not_enough_room& e)
catch (utf8::not_enough_room& /*e*/)
{
// 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)
{
if (material->IsBlendingEnabled())
// One transparent material ? I don't like it, go see if I'm in the forward queue
if (material->IsBlendingEnabled() || material->IsDepthSortingEnabled()) //< Fixme: Deferred Shading should be able to handle depth sorting
// Deferred Shading cannot handle blended objects, put them in the forward list
m_forwardQueue->AddMesh(renderOrder, material, meshData, meshAABB, transformMatrix);
else
{
@ -254,7 +254,7 @@ namespace Nz
* \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);
}

View File

@ -351,7 +351,7 @@ namespace Nz
* \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");
NazaraUnused(renderOrder);

View File

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

View File

@ -376,23 +376,23 @@ namespace Nz
{
NazaraAssert(material, "Invalid material");
if (material->IsBlendingEnabled())
if (material->IsDepthSortingEnabled())
{
Layer& currentLayer = GetLayer(renderOrder);
auto& transparentModels = currentLayer.transparentModels;
auto& transparentModelData = currentLayer.transparentModelData;
auto& transparentMeshes = currentLayer.depthSortedMeshes;
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)
std::size_t index = transparentModelData.size();
transparentModelData.resize(index+1);
// 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);
TransparentModelData& data = transparentModelData.back();
UnbatchedModelData& data = transparentData.back();
data.material = material;
data.meshData = meshData;
data.squaredBoundingSphere = Spheref(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius());
data.obbSphere = Spheref(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius());
data.transformMatrix = transformMatrix;
transparentModels.push_back(index);
transparentMeshes.push_back(index);
}
else
{
@ -457,53 +457,74 @@ namespace Nz
*
* \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");
Layer& currentLayer = GetLayer(renderOrder);
SpritePipelineBatches& basicSprites = currentLayer.basicSprites;
const MaterialPipeline* materialPipeline = material->GetPipeline();
auto pipelineIt = basicSprites.find(materialPipeline);
if (pipelineIt == basicSprites.end())
if (material->IsDepthSortingEnabled())
{
BatchedSpritePipelineEntry materialEntry;
pipelineIt = basicSprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first;
auto& transparentSprites = currentLayer.depthSortedSprites;
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);
}
BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second;
pipelineEntry.enabled = true;
SpriteMaterialBatches& materialMap = pipelineEntry.materialMap;
auto matIt = materialMap.find(material);
if (matIt == materialMap.end())
else
{
BatchedBasicSpriteEntry entry;
entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation);
SpritePipelineBatches& sprites = currentLayer.opaqueSprites;
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;
}
for (auto& pipelinePair : layer.basicSprites)
for (auto& pipelinePair : layer.opaqueSprites)
{
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.transparentModels.clear();
layer.transparentModelData.clear();
++it;
}
}
@ -613,44 +636,10 @@ namespace Nz
void ForwardRenderQueue::Sort(const AbstractViewer* viewer)
{
Planef nearPlane = viewer->GetFrustum().GetPlane(FrustumPlane_Near);
Vector3f viewerPos = viewer->GetEyePosition();
Vector3f viewerNormal = viewer->GetForward();
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);
});
}
}
}
}
if (viewer->GetProjectionType() == ProjectionType_Orthogonal)
SortForOrthographic(viewer);
else
SortForPerspective(viewer);
}
/*!
@ -715,12 +704,91 @@ namespace Nz
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
*
* \param indexBuffer Index buffer being invalidated
*/
void ForwardRenderQueue::OnIndexBufferInvalidation(const IndexBuffer* indexBuffer)
{
for (auto& pair : layers)
@ -757,7 +825,7 @@ namespace Nz
{
Layer& layer = pair.second;
for (auto& pipelineEntry : layer.basicSprites)
for (auto& pipelineEntry : layer.opaqueSprites)
pipelineEntry.second.materialMap.erase(material);
for (auto& pipelineEntry : layer.billboards)
@ -779,7 +847,7 @@ namespace Nz
for (auto& pair : layers)
{
Layer& layer = pair.second;
for (auto& pipelineEntry : layer.basicSprites)
for (auto& pipelineEntry : layer.opaqueSprites)
{
for (auto& materialEntry : pipelineEntry.second.materialMap)
materialEntry.second.overlayMap.erase(texture);

View File

@ -33,8 +33,8 @@ namespace Nz
Vector2f uv;
};
std::size_t s_maxQuads = std::numeric_limits<UInt16>::max() / 6;
std::size_t s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB
UInt32 s_maxQuads = std::numeric_limits<UInt16>::max() / 6;
UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB
}
/*!
@ -101,12 +101,15 @@ namespace Nz
if (!layer.opaqueModels.empty())
DrawOpaqueModels(sceneData, layer);
if (!layer.transparentModels.empty())
if (!layer.depthSortedMeshes.empty())
DrawTransparentModels(sceneData, layer);
if (!layer.basicSprites.empty())
if (!layer.opaqueSprites.empty())
DrawBasicSprites(sceneData, layer);
if (!layer.depthSortedSprites.empty())
DrawOrderedSprites(sceneData, layer);
if (!layer.billboards.empty())
DrawBillboards(sceneData, layer);
@ -301,7 +304,10 @@ namespace Nz
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
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;
auto& pipelineEntry = pipelinePair.second;
@ -323,6 +329,9 @@ namespace Nz
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
// Overlay texture unit
shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
lastShader = shader;
}
@ -335,10 +344,6 @@ namespace Nz
{
material->Apply(pipelineInstance);
unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler());
auto& overlayMap = matEntry.overlayMap;
@ -362,7 +367,6 @@ namespace Nz
VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
std::size_t spriteCount = 0;
std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4);
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
*
@ -796,9 +936,9 @@ namespace Nz
const ShaderUniforms* shaderUniforms = nullptr;
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
const Material* material = modelData.material;
@ -865,8 +1005,8 @@ namespace Nz
if (shaderUniforms->hasLightUniforms && lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS)
{
// Compute the closest lights
Vector3f position = matrix.GetTranslation() + modelData.squaredBoundingSphere.GetPosition();
float radius = modelData.squaredBoundingSphere.radius;
Vector3f position = matrix.GetTranslation() + modelData.obbSphere.GetPosition();
float radius = modelData.obbSphere.radius;
ChooseLights(Spheref(position, radius), false);
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.depthWrite = false;
pipelineInfo.faceCulling = false;
pipelineInfo.depthSorting = true;
pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
pipelineInfo.srcBlend = BlendFunc_SrcAlpha;
@ -207,6 +208,7 @@ namespace Nz
pipelineInfo.depthBuffer = true;
pipelineInfo.depthWrite = false;
pipelineInfo.faceCulling = false;
pipelineInfo.depthSorting = true;
pipelineInfo.dstBlend = BlendFunc_InvSrcAlpha;
pipelineInfo.srcBlend = BlendFunc_SrcAlpha;

View File

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

View File

@ -23,13 +23,19 @@ namespace Nz
void SocketPollerImpl::Clear()
{
m_activeSockets.clear();
m_readyToReadSockets.clear();
m_readyToWriteSockets.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
@ -37,15 +43,21 @@ namespace Nz
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");
epoll_event event;
event.events = EPOLLIN;
event.data.fd = socket;
epoll_event entry;
entry.events = 0;
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() + ')');
return false;
@ -60,7 +72,8 @@ namespace Nz
{
NazaraAssert(IsRegistered(socket), "Socket is not registered");
m_activeSockets.erase(socket);
m_readyToReadSockets.erase(socket);
m_readyToWriteSockets.erase(socket);
m_sockets.erase(socket);
if (epoll_ctl(m_handle, EPOLL_CTL_DEL, socket, nullptr) != 0)
@ -84,21 +97,27 @@ namespace Nz
return 0;
}
m_activeSockets.clear();
m_readyToReadSockets.clear();
m_readyToWriteSockets.clear();
if (activeSockets > 0)
{
int socketCount = activeSockets;
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)
NazaraWarning("Descriptor " + String::Number(m_events[i].data.fd) + " was returned by epoll with EPOLLERR status");
}
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--;
}
}

View File

@ -23,16 +23,18 @@ namespace Nz
void Clear();
bool IsReady(SocketHandle socket) const;
bool IsReadyToRead(SocketHandle socket) const;
bool IsReadyToWrite(SocketHandle socket) const;
bool IsRegistered(SocketHandle socket) const;
bool RegisterSocket(SocketHandle socket);
bool RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags);
void UnregisterSocket(SocketHandle socket);
int Wait(UInt64 msTimeout, SocketError* error);
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::vector<epoll_event> m_events;
int m_handle;

View File

@ -10,14 +10,20 @@ namespace Nz
{
void SocketPollerImpl::Clear()
{
m_activeSockets.clear();
m_readyToReadSockets.clear();
m_readyToWriteSockets.clear();
m_allSockets.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
@ -25,16 +31,22 @@ namespace Nz
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");
PollSocket entry = {
socket,
POLLRDNORM,
0,
0
};
if (eventFlags & SocketPollEvent_Read)
entry.events |= POLLRDNORM;
if (eventFlags & SocketPollEvent_Write)
entry.events |= POLLWRNORM;
m_allSockets[socket] = m_sockets.size();
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
m_sockets[entry] = std::move(m_sockets.back());
}
m_sockets.pop_back();
m_activeSockets.erase(socket);
m_allSockets.erase(socket);
m_readyToReadSockets.erase(socket);
m_readyToWriteSockets.erase(socket);
}
int SocketPollerImpl::Wait(UInt64 msTimeout, SocketError* error)
@ -68,20 +81,25 @@ namespace Nz
int activeSockets;
// 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);
m_activeSockets.clear();
if (activeSockets > 0)
m_readyToReadSockets.clear();
m_readyToWriteSockets.clear();
if (activeSockets > 0U)
{
int socketRemaining = activeSockets;
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)
break;
}

View File

@ -27,13 +27,14 @@ namespace Nz
bool IsReady(SocketHandle socket) const;
bool IsRegistered(SocketHandle socket) const;
bool RegisterSocket(SocketHandle socket);
bool RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags);
void UnregisterSocket(SocketHandle socket);
int Wait(UInt64 msTimeout, SocketError* error);
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::vector<PollSocket> m_sockets;
};

View File

@ -57,12 +57,13 @@ namespace Nz
/*!
* \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 state.
* \remark You must call Wait before using this function in order to refresh the read 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
*
@ -70,11 +71,32 @@ namespace Nz
*
* \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");
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
*
* 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.
*
@ -107,17 +129,18 @@ namespace Nz
* \remark The socket should not be freed while it is registered in the SocketPooler.
*
* \param socket Reference to the socket to register
* \param eventFlags Socket events to watch
*
* \return True if the socket is registered, false otherwise
*
* \see IsRegistered
* \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");
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.
*
* 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
*

View File

@ -10,29 +10,43 @@ namespace Nz
SocketPollerImpl::SocketPollerImpl()
{
#if !NAZARA_NETWORK_POLL_SUPPORT
FD_ZERO(&m_activeSockets);
FD_ZERO(&m_sockets);
FD_ZERO(&m_readSockets);
FD_ZERO(&m_readyToReadSockets);
FD_ZERO(&m_readyToWriteSockets);
FD_ZERO(&m_writeSockets);
#endif
}
void SocketPollerImpl::Clear()
{
#if NAZARA_NETWORK_POLL_SUPPORT
m_activeSockets.clear();
m_allSockets.clear();
m_readyToReadSockets.clear();
m_readyToWriteSockets.clear();
m_sockets.clear();
#else
FD_ZERO(&m_activeSockets);
FD_ZERO(&m_sockets);
FD_ZERO(&m_readSockets);
FD_ZERO(&m_readyToReadSockets);
FD_ZERO(&m_readyToWriteSockets);
FD_ZERO(&m_writeSockets);
#endif
}
bool SocketPollerImpl::IsReady(SocketHandle socket) const
bool SocketPollerImpl::IsReadyToRead(SocketHandle socket) const
{
#if NAZARA_NETWORK_POLL_SUPPORT
return m_activeSockets.count(socket) != 0;
return m_readyToReadSockets.count(socket) != 0;
#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
}
@ -41,31 +55,45 @@ namespace Nz
#if NAZARA_NETWORK_POLL_SUPPORT
return m_allSockets.count(socket) != 0;
#else
return FD_ISSET(socket, &m_sockets) != 0;
return FD_ISSET(socket, &m_readSockets) != 0 ||
FD_ISSET(socket, &m_writeSockets) != 0;
#endif
}
bool SocketPollerImpl::RegisterSocket(SocketHandle socket)
bool SocketPollerImpl::RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags)
{
NazaraAssert(!IsRegistered(socket), "Socket is already registered");
#if NAZARA_NETWORK_POLL_SUPPORT
PollSocket entry = {
socket,
POLLRDNORM,
0,
0
};
if (eventFlags & SocketPollEvent_Read)
entry.events |= POLLRDNORM;
if (eventFlags & SocketPollEvent_Write)
entry.events |= POLLWRNORM;
m_allSockets[socket] = m_sockets.size();
m_sockets.emplace_back(entry);
#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) + ")");
return false;
}
if ((eventFlags & ((i == 0) ? SocketPollEvent_Read : SocketPollEvent_Write)) == 0)
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
return true;
@ -88,13 +116,16 @@ namespace Nz
// Now move it properly (lastElement is invalid after the following line) and pop it
m_sockets[entry] = std::move(m_sockets.back());
}
m_sockets.pop_back();
m_activeSockets.erase(socket);
m_allSockets.erase(socket);
m_readyToReadSockets.erase(socket);
m_readyToWriteSockets.erase(socket);
#else
FD_CLR(socket, &m_activeSockets);
FD_CLR(socket, &m_sockets);
FD_CLR(socket, &m_readSockets);
FD_CLR(socket, &m_readyToReadSockets);
FD_CLR(socket, &m_readyToWriteSockets);
FD_CLR(socket, &m_writeSockets);
#endif
}
@ -103,35 +134,28 @@ namespace Nz
int activeSockets;
#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);
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
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;
tv.tv_sec = static_cast<long>(msTimeout / 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 (error)

View File

@ -25,24 +25,28 @@ namespace Nz
void Clear();
bool IsReady(SocketHandle socket) const;
bool IsReadyToRead(SocketHandle socket) const;
bool IsReadyToWrite(SocketHandle socket) const;
bool IsRegistered(SocketHandle socket) const;
bool RegisterSocket(SocketHandle socket);
bool RegisterSocket(SocketHandle socket, SocketPollEventFlags eventFlags);
void UnregisterSocket(SocketHandle socket);
int Wait(UInt64 msTimeout, SocketError* error);
private:
#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::vector<PollSocket> m_sockets;
#else
fd_set m_sockets;
fd_set m_activeSockets;
fd_set m_readSockets;
fd_set m_readyToReadSockets;
fd_set m_readyToWriteSockets;
fd_set m_writeSockets;
#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))
{
result->closestPoint.Set(queryInfo.point.x, queryInfo.point.y);
result->distance = queryInfo.distance;
result->fraction.Set(queryInfo.gradient.x, queryInfo.gradient.y);
result->closestPoint.Set(Nz::Vector2<cpFloat>(queryInfo.point.x, queryInfo.point.y));
result->distance = float(queryInfo.distance);
result->fraction.Set(Nz::Vector2<cpFloat>(queryInfo.gradient.x, queryInfo.gradient.y));
result->nearestBody = static_cast<Nz::RigidBody2D*>(cpShapeGetUserData(shape));
return true;
@ -90,9 +90,9 @@ namespace Nz
ResultType results = static_cast<ResultType>(data);
RaycastHit hitInfo;
hitInfo.fraction = alpha;
hitInfo.hitNormal.Set(normal.x, normal.y);
hitInfo.hitPos.Set(point.x, point.y);
hitInfo.fraction = float(alpha);
hitInfo.hitNormal.Set(Nz::Vector2<cpFloat>(normal.x, normal.y));
hitInfo.hitPos.Set(Nz::Vector2<cpFloat>(point.x, point.y));
hitInfo.nearestBody = static_cast<Nz::RigidBody2D*>(cpShapeGetUserData(shape));
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))
{
hitInfo->fraction = queryInfo.alpha;
hitInfo->hitNormal.Set(queryInfo.normal.x, queryInfo.normal.y);
hitInfo->hitPos.Set(queryInfo.point.x, queryInfo.point.y);
hitInfo->fraction = float(queryInfo.alpha);
hitInfo->hitNormal.Set(Nz::Vector2<cpFloat>(queryInfo.normal.x, queryInfo.normal.y));
hitInfo->hitPos.Set(Nz::Vector2<cpFloat>(queryInfo.point.x, queryInfo.point.y));
hitInfo->nearestBody = static_cast<Nz::RigidBody2D*>(cpShapeGetUserData(queryInfo.shape));
return true;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,38 +12,38 @@ namespace Nz
{
namespace
{
UInt32 GetterSequential(const void* buffer, unsigned int i)
UInt32 GetterSequential(const void* buffer, std::size_t i)
{
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);
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);
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);
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);
ptr[i] = value;
}
void SetterError(void*, unsigned int, UInt32)
void SetterError(void*, std::size_t, UInt32)
{
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
if (i >= m_indexCount)
{
NazaraError("Index out of range (" + String::Number(i) + " >= " + String::Number(m_indexCount) + ')');
return 0;
}
#endif
NazaraAssert(i < m_indexCount, "Index out of range");
return m_getter(m_mapper.GetPointer(), i);
}
@ -131,20 +125,14 @@ namespace Nz
return m_mapper.GetBuffer();
}
unsigned int IndexMapper::GetIndexCount() const
std::size_t IndexMapper::GetIndexCount() const
{
return m_indexCount;
}
void IndexMapper::Set(unsigned int i, UInt32 value)
void IndexMapper::Set(std::size_t i, UInt32 value)
{
#if NAZARA_UTILITY_SAFE
if (i >= m_indexCount)
{
NazaraError("Index out of range (" + String::Number(i) + " >= " + String::Number(m_indexCount) + ')');
return;
}
#endif
NazaraAssert(i < m_indexCount, "Index out of range");
m_setter(m_mapper.GetPointer(), i, value);
}

View File

@ -79,7 +79,7 @@ namespace Nz
NazaraAssert(subMesh, "Invalid submesh");
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();
}
@ -92,10 +92,10 @@ namespace Nz
NazaraAssert(subMesh, "Invalid submesh");
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->subMeshMap[identifier] = index;
m_impl->subMeshes.emplace_back(subMesh);
m_impl->subMeshMap[identifier] = static_cast<UInt32>(index);
InvalidateAABB();
}
@ -349,11 +349,11 @@ namespace Nz
if (!m_impl->aabbUpdated)
{
UInt32 subMeshCount = m_impl->subMeshes.size();
std::size_t subMeshCount = m_impl->subMeshes.size();
if (subMeshCount > 0)
{
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());
}
else
@ -407,7 +407,7 @@ namespace Nz
{
NazaraAssert(m_impl, "Mesh should be created first");
return m_impl->materialData.size();
return static_cast<UInt32>(m_impl->materialData.size());
}
Skeleton* Mesh::GetSkeleton()
@ -466,7 +466,7 @@ namespace Nz
{
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

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)
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
m_lines.emplace_back(Line{Rectf::Zero(), 0});
}
@ -354,7 +354,7 @@ namespace Nz
{
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[1].Set(glyph.bounds.GetCorner(RectCorner_RightTop));
@ -377,7 +377,7 @@ namespace Nz
m_drawPos.x = 0;
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;
}
}

View File

@ -72,12 +72,12 @@ namespace Nz
if (!m_impl->aabbUpdated)
{
UInt32 jointCount = m_impl->joints.size();
std::size_t jointCount = m_impl->joints.size();
if (jointCount > 0)
{
Vector3f pos = m_impl->joints[0].GetPosition();
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());
}
else
@ -219,7 +219,7 @@ namespace Nz
}
#endif
return m_impl->joints.size();
return static_cast<UInt32>(m_impl->joints.size());
}
int Skeleton::GetJointIndex(const String& jointName) const
@ -411,16 +411,9 @@ namespace Nz
String name = m_impl->joints[i].GetName();
if (!name.IsEmpty())
{
#if NAZARA_UTILITY_SAFE
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
NazaraAssert(m_impl->jointMap.find(name) == m_impl->jointMap.end(), "Joint name \"" + name + "\" is already present in joint map");
m_impl->jointMap[name] = i;
m_impl->jointMap[name] = static_cast<UInt32>(i);
}
}

View File

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

View File

@ -1,5 +1,6 @@
#include <Nazara/Math/Algorithm.hpp>
#include <Catch/catch.hpp>
#include <limits>
TEST_CASE("Approach", "[MATH][ALGORITHM]")
{
@ -48,6 +49,11 @@ TEST_CASE("CountBits", "[MATH][ALGORITHM]")
{
REQUIRE(Nz::CountBits(0) == 0);
}
SECTION("Number 0xFFFFFFFF has 32 bit set to 1")
{
REQUIRE(Nz::CountBits(0xFFFFFFFF) == 32);
}
}
TEST_CASE("DegreeToRadian", "[MATH][ALGORITHM]")
@ -205,9 +211,19 @@ TEST_CASE("NumberEquals", "[MATH][ALGORITHM]")
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")
{
REQUIRE(serverPoller.RegisterSocket(server));
REQUIRE(serverPoller.RegisterSocket(server, Nz::SocketPollEvent_Read));
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")
{
REQUIRE(serverPoller.RegisterSocket(serverToClient));
REQUIRE(serverPoller.RegisterSocket(serverToClient, Nz::SocketPollEvent_Read));
THEN("The poller should have registered our socket")
{
@ -65,7 +65,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]")
REQUIRE(serverPoller.Wait(1000));
CHECK(serverPoller.IsReady(serverToClient));
CHECK(serverPoller.IsReadyToRead(serverToClient));
CHECK(serverToClient.Read(buffer.data(), buffer.size()) == sent);
@ -73,7 +73,7 @@ SCENARIO("SocketPoller", "[NETWORK][SOCKETPOLLER]")
{
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")
{
const Ndk::EntityHandle& entity = world.CreateEntity();
Ndk::EntityHandle entity = world.CreateEntity();
entity->AddComponent<Ndk::VelocityComponent>();
THEN("System should have it")
@ -50,7 +50,7 @@ SCENARIO("BaseSystem", "[NDK][BASESYSTEM]")
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::NodeComponent>();

View File

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

View File

@ -7,7 +7,7 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
GIVEN("A world & an entity")
{
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")
{
@ -15,13 +15,21 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
THEN("Entity is still valid")
{
REQUIRE(entity.IsValid());
CHECK(entity.IsValid());
}
THEN("Resetting or getting out of scope is no more valid")
{
entityOwner.Reset();
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());
}
}

View File

@ -28,21 +28,79 @@ class TestState : public Ndk::State
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]")
{
GIVEN("A statemachine with our TestState")
GIVEN("A statemachine with our test states")
{
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(!secondTestState->IsUpdated());
WHEN("We update our machine")
{
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(!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")
{
Ndk::World world;
const Ndk::EntityHandle& entity = world.CreateEntity();
Ndk::EntityHandle entity = world.CreateEntity();
Ndk::ListenerComponent& listenerComponent = entity->AddComponent<Ndk::ListenerComponent>();
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")
{
REQUIRE(&world.GetSystem(UpdateSystem::systemIndex) == &world.GetSystem<UpdateSystem>());
const Ndk::EntityHandle& entity = world.CreateEntity();
Ndk::EntityHandle entity = world.CreateEntity();
UpdatableComponent& component = entity->AddComponent<UpdatableComponent>();
THEN("Our entity component must be updated")