Merge branch 'master' into physics3d-material

This commit is contained in:
Lynix
2018-02-11 14:39:14 +01:00
19 changed files with 352 additions and 96 deletions

View File

@@ -164,8 +164,10 @@ namespace Ndk
}
else
{
OnUpdate(m_maxUpdateRate);
m_updateCounter -= m_maxUpdateRate;
float updateRate = std::max(elapsedTime, m_maxUpdateRate);
OnUpdate(updateRate);
m_updateCounter -= updateRate;
}
}
}

View File

@@ -29,6 +29,7 @@ namespace Ndk
public:
using EntityVector = std::vector<EntityHandle>;
struct ProfilerData;
inline World(bool addDefaultSystems = true);
World(const World&) = delete;
@@ -46,8 +47,12 @@ namespace Ndk
void Clear() noexcept;
const EntityHandle& CloneEntity(EntityId id);
inline void DisableProfiler();
inline void EnableProfiler(bool enable = true);
inline const EntityHandle& GetEntity(EntityId id);
inline const EntityList& GetEntities() const;
inline const ProfilerData& GetProfilerData() const;
inline BaseSystem& GetSystem(SystemIndex index);
template<typename SystemType> SystemType& GetSystem();
@@ -59,17 +64,27 @@ namespace Ndk
inline bool IsEntityValid(const Entity* entity) const;
inline bool IsEntityIdValid(EntityId id) const;
inline bool IsProfilerEnabled() const;
void Refresh();
inline void RemoveAllSystems();
inline void RemoveSystem(SystemIndex index);
template<typename SystemType> void RemoveSystem();
inline void ResetProfiler();
void Update();
inline void Update(float elapsedTime);
void Update(float elapsedTime);
World& operator=(const World&) = delete;
inline World& operator=(World&& world) noexcept;
struct ProfilerData
{
Nz::UInt64 refreshTime = 0;
std::vector<Nz::UInt64> updateTime;
std::size_t updateCount = 0;
};
private:
inline void Invalidate();
inline void Invalidate(EntityId id);
@@ -95,11 +110,13 @@ namespace Ndk
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;
ProfilerData m_profilerData;
Nz::Bitset<Nz::UInt64> m_dirtyEntities;
Nz::Bitset<Nz::UInt64> m_freeEntityIds;
Nz::Bitset<Nz::UInt64> m_killedEntities;
bool m_orderedSystemsUpdated;
bool m_isProfilerEnabled;
};
}

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 Prerequisites.hpp
#include <NDK/World.hpp>
#include <Nazara/Core/Error.hpp>
#include <type_traits>
@@ -14,7 +15,8 @@ namespace Ndk
*/
inline World::World(bool addDefaultSystems) :
m_orderedSystemsUpdated(false)
m_orderedSystemsUpdated(false),
m_isProfilerEnabled(false)
{
if (addDefaultSystems)
AddDefaultSystems();
@@ -47,7 +49,10 @@ namespace Ndk
// We must ensure that the vector is big enough to hold the new system
if (index >= m_systems.size())
{
m_systems.resize(index + 1);
m_profilerData.updateTime.resize(index + 1, 0);
}
// Affectation and return of system
m_systems[index] = std::move(system);
@@ -82,7 +87,6 @@ namespace Ndk
*
* \param count Number of entities to create
*/
inline World::EntityVector World::CreateEntities(unsigned int count)
{
EntityVector list;
@@ -94,16 +98,79 @@ namespace Ndk
return list;
}
/*!
* \brief Disables the profiler, clearing up results
*
* This is just a shortcut to EnableProfiler(false)
*
* \param enable Should the entity be enabled
*
* \see EnableProfiler
*/
inline void World::DisableProfiler()
{
EnableProfiler(false);
}
/*!
* \brief Enables/Disables the internal profiler
*
* Worlds come with a built-in profiler, allowing to measure update count along with time passed in refresh and system updates.
* This is disabled by default as it adds an small overhead to the update process.
*
* \param enable Should the profiler be enabled
*
* \remark Disabling the profiler clears up results, as if ResetProfiler has been called
*/
inline void World::EnableProfiler(bool enable)
{
if (m_isProfilerEnabled != enable)
{
m_isProfilerEnabled = enable;
if (enable)
ResetProfiler();
}
}
/*!
* \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 handle reference 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 Gets every entities in the world
* \return A constant reference to the entities
*/
inline const EntityList& World::GetEntities() const
{
return m_aliveEntities;
}
/*!
* \brief Gets the latest profiler data
* \return A constant reference to the profiler data
*/
inline const World::ProfilerData& World::GetProfilerData() const
{
return m_profilerData;
}
/*!
* \brief Gets a system in the world by index
* \return A reference to the system
@@ -192,26 +259,6 @@ namespace Ndk
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 handle reference 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
@@ -230,12 +277,22 @@ namespace Ndk
*
* \param id Identifier of the entity
*/
inline bool World::IsEntityIdValid(EntityId id) const
{
return id < m_entityBlocks.size() && m_entityBlocks[id]->entity.IsValid();
}
/*!
* \brief Checks whether or not the profiler is enabled
* \return true If it is the case
*
* \see EnableProfiler
*/
inline bool World::IsProfilerEnabled() const
{
return m_isProfilerEnabled;
}
/*!
* \brief Removes each system from the world
*/
@@ -265,10 +322,21 @@ namespace Ndk
}
}
/*!
* \brief Clear profiler results
*
* This reset the profiler results, filling all counters with zero
*/
inline void World::ResetProfiler()
{
m_profilerData.refreshTime = 0;
m_profilerData.updateCount = 0;
std::fill(m_profilerData.updateTime.begin(), m_profilerData.updateTime.end(), 0);
}
/*!
* \brief Removes a system from the world by type
*/
template<typename SystemType>
void World::RemoveSystem()
{
@@ -278,21 +346,6 @@ namespace Ndk
RemoveSystem(index);
}
/*!
* \brief Updates the world
*
* \param elapsedTime Delta time used for the update
*/
inline void World::Update(float elapsedTime)
{
Update(); //< Update entities
// And then update systems
for (auto& systemPtr : m_orderedSystems)
systemPtr->Update(elapsedTime);
}
/*!
* \brief Moves a world into another world object
* \return A reference to the object
@@ -303,10 +356,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_freeEntityIds = std::move(world.m_freeEntityIds);
m_killedEntities = std::move(world.m_killedEntities);
m_orderedSystems = std::move(world.m_orderedSystems);
m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
m_profilerData = std::move(world.m_profilerData);
m_isProfilerEnabled = m_isProfilerEnabled;
m_entities = std::move(world.m_entities);
for (EntityBlock& block : m_entities)

View File

@@ -107,8 +107,7 @@ namespace Ndk
if (m_shouldQuit)
return false;
m_updateTime = m_updateClock.GetSeconds();
m_updateClock.Restart();
m_updateTime = m_updateClock.Restart() / 1'000'000.f;
for (World& world : m_worlds)
world.Update(m_updateTime);

View File

@@ -179,6 +179,12 @@ namespace Ndk
world.BindMethod("CreateEntity", &World::CreateEntity);
world.BindMethod("CreateEntities", &World::CreateEntities);
world.BindMethod("Clear", &World::Clear);
world.BindMethod("DisableProfiler", &World::DisableProfiler);
world.BindMethod("EnableProfiler", &World::EnableProfiler);
world.BindMethod("IsProfilerEnabled", &World::IsProfilerEnabled);
world.BindMethod("Refresh", &World::Refresh);
world.BindMethod("ResetProfiler", &World::ResetProfiler);
world.BindMethod("Update", &World::Update);
world.BindMethod("IsValidHandle", &WorldHandle::IsValid);
}

View File

@@ -3,6 +3,7 @@
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <NDK/World.hpp>
#include <Nazara/Core/Clock.hpp>
#include <Nazara/Core/Error.hpp>
#include <NDK/BaseComponent.hpp>
#include <NDK/Systems/PhysicsSystem2D.hpp>
@@ -61,11 +62,14 @@ namespace Ndk
{
EntityId id;
EntityBlock* entBlock;
if (!m_freeIdList.empty())
std::size_t freeEntityId = m_freeEntityIds.FindFirst();
if (freeEntityId != m_freeEntityIds.npos)
{
// We get an identifier
id = m_freeIdList.back();
m_freeIdList.pop_back();
m_freeEntityIds.Reset(freeEntityId); //< Remove id from free entity id
id = static_cast<EntityId>(freeEntityId);
entBlock = &m_entities[id];
entBlock->handle.Reset(&entBlock->entity); //< Reset handle (as it was reset when entity got destroyed)
@@ -81,7 +85,7 @@ namespace Ndk
{
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
m_entities.emplace_back(Entity(this, id)); //< We can't make our vector create the entity due to the scope
entBlock = &m_entities.back();
}
else
@@ -124,11 +128,11 @@ namespace Ndk
m_entityBlocks.clear();
m_entities.clear();
m_freeIdList.clear();
m_waitingEntities.clear();
m_aliveEntities.Clear();
m_dirtyEntities.Clear();
m_freeEntityIds.Clear();
m_killedEntities.Clear();
}
@@ -166,12 +170,21 @@ namespace Ndk
}
/*!
* \brief Updates the world
* \brief Refreshes the world
*
* \remark Produces a NazaraAssert if an entity is invalid
* This function will perform all pending operations in the following order:
* - Reorder systems according to their update order if needed
* - Moving newly created entities (whose which allocate never-used id) data and handles to normal entity list, this will invalidate references to world EntityHandle
* - Destroying dead entities and allowing their ids to be used by newly created entities
* - Update dirty entities, destroying their removed components and filtering them along systems
*
* \remark This is called automatically by Update and you most likely won't need to call it yourself
* \remark Calling this outside of Update will not increase the profiler values
*
* \see GetProfilerData
* \see Update
*/
void World::Update()
void World::Refresh()
{
if (!m_orderedSystemsUpdated)
ReorderSystems();
@@ -203,7 +216,7 @@ namespace Ndk
entity->Destroy();
// Send back the identifier of the entity to the free queue
m_freeIdList.push_back(entity->GetId());
m_freeEntityIds.UnboundedSet(entity->GetId());
}
m_killedEntities.Reset();
@@ -248,6 +261,43 @@ namespace Ndk
m_dirtyEntities.Reset();
}
/*!
* \brief Updates the world
* \param elapsedTime Delta time used for the update
*
* This function Refreshes the world and calls the Update function of every active system part of it with the elapsedTime value.
* It also increase the profiler data with the elapsed time passed in Refresh and every system update.
*/
void World::Update(float elapsedTime)
{
if (m_isProfilerEnabled)
{
Nz::UInt64 t1 = Nz::GetElapsedMicroseconds();
Refresh();
Nz::UInt64 t2 = Nz::GetElapsedMicroseconds();
m_profilerData.refreshTime += t2 - t1;
for (auto& systemPtr : m_orderedSystems)
{
systemPtr->Update(elapsedTime);
Nz::UInt64 t3 = Nz::GetElapsedMicroseconds();
m_profilerData.updateTime[systemPtr->GetIndex()] += t3 - t2;
t2 = t3;
}
m_profilerData.updateCount++;
}
else
{
Refresh();
for (auto& systemPtr : m_orderedSystems)
systemPtr->Update(elapsedTime);
}
}
void World::ReorderSystems()
{
m_orderedSystems.clear();