Merge branch 'master' into physics3d-material
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user