Merge branch 'master' into physics3d-material
This commit is contained in:
commit
af12c5ed92
10
ChangeLog.md
10
ChangeLog.md
|
|
@ -12,6 +12,9 @@ Miscellaneous:
|
||||||
- FirstScene now uses the EventHandler (#151)
|
- FirstScene now uses the EventHandler (#151)
|
||||||
- ⚠️ Rename Prerequesites.hpp to Prerequisites.hpp (#153)
|
- ⚠️ Rename Prerequesites.hpp to Prerequisites.hpp (#153)
|
||||||
- Updated premake5-linux64 with a nightly to fix a build error when a previous version of Nazara was installed on the system.
|
- Updated premake5-linux64 with a nightly to fix a build error when a previous version of Nazara was installed on the system.
|
||||||
|
- Fix compilation with some MinGW distributions
|
||||||
|
- Add Lua unit tests
|
||||||
|
- NDEBUG is now defined in Release
|
||||||
|
|
||||||
Nazara Engine:
|
Nazara Engine:
|
||||||
- VertexMapper:GetComponentPtr no longer throw an error if component is disabled or incompatible with template type, instead a null pointer is returned.
|
- VertexMapper:GetComponentPtr no longer throw an error if component is disabled or incompatible with template type, instead a null pointer is returned.
|
||||||
|
|
@ -59,6 +62,10 @@ Nazara Engine:
|
||||||
- Fix LuaClass not working correctly when Lua stack wasn't empty
|
- Fix LuaClass not working correctly when Lua stack wasn't empty
|
||||||
- Add RigidBody2D simulation control (via EnableSimulation and IsSimulationEnabled), which allows to disable physics and collisions at will.
|
- Add RigidBody2D simulation control (via EnableSimulation and IsSimulationEnabled), which allows to disable physics and collisions at will.
|
||||||
- ⚠️ LuaInstance no longer load all lua libraries on construction, this is done in the new LoadLibraries method which allows you to excludes some libraries
|
- ⚠️ LuaInstance no longer load all lua libraries on construction, this is done in the new LoadLibraries method which allows you to excludes some libraries
|
||||||
|
- Clock::Restart now returns the elapsed microseconds since construction or last Restart call
|
||||||
|
- Add PhysWorld2D::[Get|Set]IterationCount to control how many iterations chipmunk will perform per step.
|
||||||
|
- Add PhysWorld2D::UseSpatialHash to use spatial hashing instead of bounding box trees, which may speedup simulation in some cases.
|
||||||
|
- Add PhysWorld[2D|3D] max step count per Step call (default: 50), to avoid spirals of death when the physics engine simulation time is over step size.
|
||||||
|
|
||||||
Nazara Development Kit:
|
Nazara Development Kit:
|
||||||
- Added ImageWidget (#139)
|
- Added ImageWidget (#139)
|
||||||
|
|
@ -91,6 +98,9 @@ Nazara Development Kit:
|
||||||
- Fix TextAreaWidget::Clear crash
|
- Fix TextAreaWidget::Clear crash
|
||||||
- Add ConstraintComponent2D class
|
- Add ConstraintComponent2D class
|
||||||
- Fix CollisionComponent3D initialization (teleportation to their real coordinates) which could sometimes mess up the physics scene.
|
- Fix CollisionComponent3D initialization (teleportation to their real coordinates) which could sometimes mess up the physics scene.
|
||||||
|
- ⚠️ Renamed World::Update() to World::Refresh() for more clarity and to differentiate it from World::Update(elapsedTime)
|
||||||
|
- World entity ids are now reused from lowest to highest (they were previously reused in reverse order of death)
|
||||||
|
- World now has an internal profiler, allowing to measure the refresh and system update time
|
||||||
|
|
||||||
# 0.4:
|
# 0.4:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -164,8 +164,10 @@ namespace Ndk
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OnUpdate(m_maxUpdateRate);
|
float updateRate = std::max(elapsedTime, m_maxUpdateRate);
|
||||||
m_updateCounter -= m_maxUpdateRate;
|
|
||||||
|
OnUpdate(updateRate);
|
||||||
|
m_updateCounter -= updateRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ namespace Ndk
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using EntityVector = std::vector<EntityHandle>;
|
using EntityVector = std::vector<EntityHandle>;
|
||||||
|
struct ProfilerData;
|
||||||
|
|
||||||
inline World(bool addDefaultSystems = true);
|
inline World(bool addDefaultSystems = true);
|
||||||
World(const World&) = delete;
|
World(const World&) = delete;
|
||||||
|
|
@ -46,8 +47,12 @@ namespace Ndk
|
||||||
void Clear() noexcept;
|
void Clear() noexcept;
|
||||||
const EntityHandle& CloneEntity(EntityId id);
|
const EntityHandle& CloneEntity(EntityId id);
|
||||||
|
|
||||||
|
inline void DisableProfiler();
|
||||||
|
inline void EnableProfiler(bool enable = true);
|
||||||
|
|
||||||
inline const EntityHandle& GetEntity(EntityId id);
|
inline const EntityHandle& GetEntity(EntityId id);
|
||||||
inline const EntityList& GetEntities() const;
|
inline const EntityList& GetEntities() const;
|
||||||
|
inline const ProfilerData& GetProfilerData() const;
|
||||||
inline BaseSystem& GetSystem(SystemIndex index);
|
inline BaseSystem& GetSystem(SystemIndex index);
|
||||||
template<typename SystemType> SystemType& GetSystem();
|
template<typename SystemType> SystemType& GetSystem();
|
||||||
|
|
||||||
|
|
@ -59,17 +64,27 @@ namespace Ndk
|
||||||
|
|
||||||
inline bool IsEntityValid(const Entity* entity) const;
|
inline bool IsEntityValid(const Entity* entity) const;
|
||||||
inline bool IsEntityIdValid(EntityId id) const;
|
inline bool IsEntityIdValid(EntityId id) const;
|
||||||
|
inline bool IsProfilerEnabled() const;
|
||||||
|
|
||||||
|
void Refresh();
|
||||||
|
|
||||||
inline void RemoveAllSystems();
|
inline void RemoveAllSystems();
|
||||||
inline void RemoveSystem(SystemIndex index);
|
inline void RemoveSystem(SystemIndex index);
|
||||||
template<typename SystemType> void RemoveSystem();
|
template<typename SystemType> void RemoveSystem();
|
||||||
|
inline void ResetProfiler();
|
||||||
|
|
||||||
void Update();
|
void Update(float elapsedTime);
|
||||||
inline void Update(float elapsedTime);
|
|
||||||
|
|
||||||
World& operator=(const World&) = delete;
|
World& operator=(const World&) = delete;
|
||||||
inline World& operator=(World&& world) noexcept;
|
inline World& operator=(World&& world) noexcept;
|
||||||
|
|
||||||
|
struct ProfilerData
|
||||||
|
{
|
||||||
|
Nz::UInt64 refreshTime = 0;
|
||||||
|
std::vector<Nz::UInt64> updateTime;
|
||||||
|
std::size_t updateCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline void Invalidate();
|
inline void Invalidate();
|
||||||
inline void Invalidate(EntityId id);
|
inline void Invalidate(EntityId id);
|
||||||
|
|
@ -95,11 +110,13 @@ namespace Ndk
|
||||||
std::vector<EntityBlock> m_entities;
|
std::vector<EntityBlock> m_entities;
|
||||||
std::vector<EntityBlock*> m_entityBlocks;
|
std::vector<EntityBlock*> m_entityBlocks;
|
||||||
std::vector<std::unique_ptr<EntityBlock>> m_waitingEntities;
|
std::vector<std::unique_ptr<EntityBlock>> m_waitingEntities;
|
||||||
std::vector<EntityId> m_freeIdList;
|
|
||||||
EntityList m_aliveEntities;
|
EntityList m_aliveEntities;
|
||||||
|
ProfilerData m_profilerData;
|
||||||
Nz::Bitset<Nz::UInt64> m_dirtyEntities;
|
Nz::Bitset<Nz::UInt64> m_dirtyEntities;
|
||||||
|
Nz::Bitset<Nz::UInt64> m_freeEntityIds;
|
||||||
Nz::Bitset<Nz::UInt64> m_killedEntities;
|
Nz::Bitset<Nz::UInt64> m_killedEntities;
|
||||||
bool m_orderedSystemsUpdated;
|
bool m_orderedSystemsUpdated;
|
||||||
|
bool m_isProfilerEnabled;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// This file is part of the "Nazara Development Kit"
|
// This file is part of the "Nazara Development Kit"
|
||||||
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
||||||
|
|
||||||
|
#include <NDK/World.hpp>
|
||||||
#include <Nazara/Core/Error.hpp>
|
#include <Nazara/Core/Error.hpp>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
|
@ -14,7 +15,8 @@ namespace Ndk
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inline World::World(bool addDefaultSystems) :
|
inline World::World(bool addDefaultSystems) :
|
||||||
m_orderedSystemsUpdated(false)
|
m_orderedSystemsUpdated(false),
|
||||||
|
m_isProfilerEnabled(false)
|
||||||
{
|
{
|
||||||
if (addDefaultSystems)
|
if (addDefaultSystems)
|
||||||
AddDefaultSystems();
|
AddDefaultSystems();
|
||||||
|
|
@ -47,7 +49,10 @@ namespace Ndk
|
||||||
|
|
||||||
// We must ensure that the vector is big enough to hold the new system
|
// We must ensure that the vector is big enough to hold the new system
|
||||||
if (index >= m_systems.size())
|
if (index >= m_systems.size())
|
||||||
|
{
|
||||||
m_systems.resize(index + 1);
|
m_systems.resize(index + 1);
|
||||||
|
m_profilerData.updateTime.resize(index + 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Affectation and return of system
|
// Affectation and return of system
|
||||||
m_systems[index] = std::move(system);
|
m_systems[index] = std::move(system);
|
||||||
|
|
@ -82,7 +87,6 @@ namespace Ndk
|
||||||
*
|
*
|
||||||
* \param count Number of entities to create
|
* \param count Number of entities to create
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inline World::EntityVector World::CreateEntities(unsigned int count)
|
inline World::EntityVector World::CreateEntities(unsigned int count)
|
||||||
{
|
{
|
||||||
EntityVector list;
|
EntityVector list;
|
||||||
|
|
@ -94,16 +98,79 @@ namespace Ndk
|
||||||
return list;
|
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
|
* \brief Gets every entities in the world
|
||||||
* \return A constant reference to the entities
|
* \return A constant reference to the entities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inline const EntityList& World::GetEntities() const
|
inline const EntityList& World::GetEntities() const
|
||||||
{
|
{
|
||||||
return m_aliveEntities;
|
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
|
* \brief Gets a system in the world by index
|
||||||
* \return A reference to the system
|
* \return A reference to the system
|
||||||
|
|
@ -192,26 +259,6 @@ namespace Ndk
|
||||||
KillEntity(entity);
|
KillEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Gets an entity
|
|
||||||
* \return A constant reference to a handle of the entity
|
|
||||||
*
|
|
||||||
* \param id Identifier of the entity
|
|
||||||
*
|
|
||||||
* \remark Handle referenced by this function can move in memory when updating the world, do not keep a 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
|
* \brief Checks whether or not an entity is valid
|
||||||
* \return true If it is the case
|
* \return true If it is the case
|
||||||
|
|
@ -230,12 +277,22 @@ namespace Ndk
|
||||||
*
|
*
|
||||||
* \param id Identifier of the entity
|
* \param id Identifier of the entity
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inline bool World::IsEntityIdValid(EntityId id) const
|
inline bool World::IsEntityIdValid(EntityId id) const
|
||||||
{
|
{
|
||||||
return id < m_entityBlocks.size() && m_entityBlocks[id]->entity.IsValid();
|
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
|
* \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
|
* \brief Removes a system from the world by type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template<typename SystemType>
|
template<typename SystemType>
|
||||||
void World::RemoveSystem()
|
void World::RemoveSystem()
|
||||||
{
|
{
|
||||||
|
|
@ -278,21 +346,6 @@ namespace Ndk
|
||||||
RemoveSystem(index);
|
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
|
* \brief Moves a world into another world object
|
||||||
* \return A reference to the object
|
* \return A reference to the object
|
||||||
|
|
@ -303,10 +356,12 @@ namespace Ndk
|
||||||
m_aliveEntities = std::move(world.m_aliveEntities);
|
m_aliveEntities = std::move(world.m_aliveEntities);
|
||||||
m_dirtyEntities = std::move(world.m_dirtyEntities);
|
m_dirtyEntities = std::move(world.m_dirtyEntities);
|
||||||
m_entityBlocks = std::move(world.m_entityBlocks);
|
m_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_killedEntities = std::move(world.m_killedEntities);
|
||||||
m_orderedSystems = std::move(world.m_orderedSystems);
|
m_orderedSystems = std::move(world.m_orderedSystems);
|
||||||
m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
|
m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
|
||||||
|
m_profilerData = std::move(world.m_profilerData);
|
||||||
|
m_isProfilerEnabled = m_isProfilerEnabled;
|
||||||
|
|
||||||
m_entities = std::move(world.m_entities);
|
m_entities = std::move(world.m_entities);
|
||||||
for (EntityBlock& block : m_entities)
|
for (EntityBlock& block : m_entities)
|
||||||
|
|
|
||||||
|
|
@ -107,8 +107,7 @@ namespace Ndk
|
||||||
if (m_shouldQuit)
|
if (m_shouldQuit)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_updateTime = m_updateClock.GetSeconds();
|
m_updateTime = m_updateClock.Restart() / 1'000'000.f;
|
||||||
m_updateClock.Restart();
|
|
||||||
|
|
||||||
for (World& world : m_worlds)
|
for (World& world : m_worlds)
|
||||||
world.Update(m_updateTime);
|
world.Update(m_updateTime);
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,12 @@ namespace Ndk
|
||||||
world.BindMethod("CreateEntity", &World::CreateEntity);
|
world.BindMethod("CreateEntity", &World::CreateEntity);
|
||||||
world.BindMethod("CreateEntities", &World::CreateEntities);
|
world.BindMethod("CreateEntities", &World::CreateEntities);
|
||||||
world.BindMethod("Clear", &World::Clear);
|
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);
|
world.BindMethod("IsValidHandle", &WorldHandle::IsValid);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
||||||
|
|
||||||
#include <NDK/World.hpp>
|
#include <NDK/World.hpp>
|
||||||
|
#include <Nazara/Core/Clock.hpp>
|
||||||
#include <Nazara/Core/Error.hpp>
|
#include <Nazara/Core/Error.hpp>
|
||||||
#include <NDK/BaseComponent.hpp>
|
#include <NDK/BaseComponent.hpp>
|
||||||
#include <NDK/Systems/PhysicsSystem2D.hpp>
|
#include <NDK/Systems/PhysicsSystem2D.hpp>
|
||||||
|
|
@ -61,11 +62,14 @@ namespace Ndk
|
||||||
{
|
{
|
||||||
EntityId id;
|
EntityId id;
|
||||||
EntityBlock* entBlock;
|
EntityBlock* entBlock;
|
||||||
if (!m_freeIdList.empty())
|
|
||||||
|
std::size_t freeEntityId = m_freeEntityIds.FindFirst();
|
||||||
|
if (freeEntityId != m_freeEntityIds.npos)
|
||||||
{
|
{
|
||||||
// We get an identifier
|
// We get an identifier
|
||||||
id = m_freeIdList.back();
|
m_freeEntityIds.Reset(freeEntityId); //< Remove id from free entity id
|
||||||
m_freeIdList.pop_back();
|
|
||||||
|
id = static_cast<EntityId>(freeEntityId);
|
||||||
|
|
||||||
entBlock = &m_entities[id];
|
entBlock = &m_entities[id];
|
||||||
entBlock->handle.Reset(&entBlock->entity); //< Reset handle (as it was reset when entity got destroyed)
|
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");
|
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();
|
entBlock = &m_entities.back();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -124,11 +128,11 @@ namespace Ndk
|
||||||
m_entityBlocks.clear();
|
m_entityBlocks.clear();
|
||||||
|
|
||||||
m_entities.clear();
|
m_entities.clear();
|
||||||
m_freeIdList.clear();
|
|
||||||
m_waitingEntities.clear();
|
m_waitingEntities.clear();
|
||||||
|
|
||||||
m_aliveEntities.Clear();
|
m_aliveEntities.Clear();
|
||||||
m_dirtyEntities.Clear();
|
m_dirtyEntities.Clear();
|
||||||
|
m_freeEntityIds.Clear();
|
||||||
m_killedEntities.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::Refresh()
|
||||||
void World::Update()
|
|
||||||
{
|
{
|
||||||
if (!m_orderedSystemsUpdated)
|
if (!m_orderedSystemsUpdated)
|
||||||
ReorderSystems();
|
ReorderSystems();
|
||||||
|
|
@ -203,7 +216,7 @@ namespace Ndk
|
||||||
entity->Destroy();
|
entity->Destroy();
|
||||||
|
|
||||||
// Send back the identifier of the entity to the free queue
|
// Send back the identifier of the entity to the free queue
|
||||||
m_freeIdList.push_back(entity->GetId());
|
m_freeEntityIds.UnboundedSet(entity->GetId());
|
||||||
}
|
}
|
||||||
m_killedEntities.Reset();
|
m_killedEntities.Reset();
|
||||||
|
|
||||||
|
|
@ -248,6 +261,43 @@ namespace Ndk
|
||||||
m_dirtyEntities.Reset();
|
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()
|
void World::ReorderSystems()
|
||||||
{
|
{
|
||||||
m_orderedSystems.clear();
|
m_orderedSystems.clear();
|
||||||
|
|
|
||||||
|
|
@ -821,6 +821,7 @@ function NazaraBuild:PrepareGeneric()
|
||||||
|
|
||||||
-- Setup some optimizations for release
|
-- Setup some optimizations for release
|
||||||
filter("configurations:Release*")
|
filter("configurations:Release*")
|
||||||
|
defines("NDEBUG")
|
||||||
optimize("Speed")
|
optimize("Speed")
|
||||||
vectorextensions("SSE2")
|
vectorextensions("SSE2")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -325,9 +325,7 @@ int main()
|
||||||
while (application.Run())
|
while (application.Run())
|
||||||
{
|
{
|
||||||
|
|
||||||
Nz::UInt64 elapsedUS = updateClock.GetMicroseconds();
|
Nz::UInt64 elapsedUS = updateClock.Restart() / 1'000'000;
|
||||||
// On relance l'horloge
|
|
||||||
updateClock.Restart();
|
|
||||||
|
|
||||||
// Mise à jour (Caméra)
|
// Mise à jour (Caméra)
|
||||||
const Nz::UInt64 updateRate = 1000000 / 60; // 60 fois par seconde
|
const Nz::UInt64 updateRate = 1000000 / 60; // 60 fois par seconde
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace Nz
|
||||||
bool IsPaused() const;
|
bool IsPaused() const;
|
||||||
|
|
||||||
void Pause();
|
void Pause();
|
||||||
void Restart();
|
UInt64 Restart();
|
||||||
void Unpause();
|
void Unpause();
|
||||||
|
|
||||||
Clock& operator=(const Clock& clock) = default;
|
Clock& operator=(const Clock& clock) = default;
|
||||||
|
|
@ -46,7 +46,7 @@ namespace Nz
|
||||||
bool m_paused;
|
bool m_paused;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef UInt64 (*ClockFunction)();
|
using ClockFunction = UInt64 (*)();
|
||||||
|
|
||||||
extern NAZARA_CORE_API ClockFunction GetElapsedMicroseconds;
|
extern NAZARA_CORE_API ClockFunction GetElapsedMicroseconds;
|
||||||
extern NAZARA_CORE_API ClockFunction GetElapsedMilliseconds;
|
extern NAZARA_CORE_API ClockFunction GetElapsedMilliseconds;
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ namespace Nz
|
||||||
float GetDamping() const;
|
float GetDamping() const;
|
||||||
Vector2f GetGravity() const;
|
Vector2f GetGravity() const;
|
||||||
cpSpace* GetHandle() const;
|
cpSpace* GetHandle() const;
|
||||||
|
std::size_t GetIterationCount() const;
|
||||||
|
std::size_t GetMaxStepCount() const;
|
||||||
float GetStepSize() const;
|
float GetStepSize() const;
|
||||||
|
|
||||||
bool NearestBodyQuery(const Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RigidBody2D** nearestBody = nullptr);
|
bool NearestBodyQuery(const Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RigidBody2D** nearestBody = nullptr);
|
||||||
|
|
@ -69,10 +71,14 @@ namespace Nz
|
||||||
|
|
||||||
void SetDamping(float dampingValue);
|
void SetDamping(float dampingValue);
|
||||||
void SetGravity(const Vector2f& gravity);
|
void SetGravity(const Vector2f& gravity);
|
||||||
|
void SetIterationCount(std::size_t iterationCount);
|
||||||
|
void SetMaxStepCount(std::size_t maxStepCount);
|
||||||
void SetStepSize(float stepSize);
|
void SetStepSize(float stepSize);
|
||||||
|
|
||||||
void Step(float timestep);
|
void Step(float timestep);
|
||||||
|
|
||||||
|
void UseSpatialHash(float cellSize, std::size_t entityCount);
|
||||||
|
|
||||||
PhysWorld2D& operator=(const PhysWorld2D&) = delete;
|
PhysWorld2D& operator=(const PhysWorld2D&) = delete;
|
||||||
PhysWorld2D& operator=(PhysWorld2D&&) = delete; ///TODO
|
PhysWorld2D& operator=(PhysWorld2D&&) = delete; ///TODO
|
||||||
|
|
||||||
|
|
@ -140,6 +146,7 @@ namespace Nz
|
||||||
|
|
||||||
static_assert(std::is_nothrow_move_constructible<PostStepContainer>::value, "PostStepContainer should be noexcept MoveConstructible");
|
static_assert(std::is_nothrow_move_constructible<PostStepContainer>::value, "PostStepContainer should be noexcept MoveConstructible");
|
||||||
|
|
||||||
|
std::size_t m_maxStepCount;
|
||||||
std::unordered_map<cpCollisionHandler*, std::unique_ptr<Callback>> m_callbacks;
|
std::unordered_map<cpCollisionHandler*, std::unique_ptr<Callback>> m_callbacks;
|
||||||
std::unordered_map<RigidBody2D*, PostStepContainer> m_rigidPostSteps;
|
std::unordered_map<RigidBody2D*, PostStepContainer> m_rigidPostSteps;
|
||||||
cpSpace* m_handle;
|
cpSpace* m_handle;
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,11 @@ namespace Nz
|
||||||
Vector3f GetGravity() const;
|
Vector3f GetGravity() const;
|
||||||
NewtonWorld* GetHandle() const;
|
NewtonWorld* GetHandle() const;
|
||||||
int GetMaterial(const Nz::String& name);
|
int GetMaterial(const Nz::String& name);
|
||||||
|
std::size_t GetMaxStepCount() const;
|
||||||
float GetStepSize() const;
|
float GetStepSize() const;
|
||||||
|
|
||||||
void SetGravity(const Vector3f& gravity);
|
void SetGravity(const Vector3f& gravity);
|
||||||
|
void SetMaxStepCount(std::size_t maxStepCount);
|
||||||
void SetSolverModel(unsigned int model);
|
void SetSolverModel(unsigned int model);
|
||||||
void SetStepSize(float stepSize);
|
void SetStepSize(float stepSize);
|
||||||
|
|
||||||
|
|
@ -72,6 +74,7 @@ namespace Nz
|
||||||
|
|
||||||
std::unordered_map<Nz::UInt64, std::unique_ptr<Callback>> m_callbacks;
|
std::unordered_map<Nz::UInt64, std::unique_ptr<Callback>> m_callbacks;
|
||||||
std::unordered_map<Nz::String, int> m_materialIds;
|
std::unordered_map<Nz::String, int> m_materialIds;
|
||||||
|
std::size_t m_maxStepCount;
|
||||||
Vector3f m_gravity;
|
Vector3f m_gravity;
|
||||||
NewtonWorld* m_world;
|
NewtonWorld* m_world;
|
||||||
float m_stepSize;
|
float m_stepSize;
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ namespace Nz
|
||||||
*/
|
*/
|
||||||
float Clock::GetSeconds() const
|
float Clock::GetSeconds() const
|
||||||
{
|
{
|
||||||
return GetMicroseconds()/1000000.f;
|
return GetMicroseconds()/1'000'000.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
@ -132,15 +132,26 @@ namespace Nz
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Restart the clock
|
* \brief Restart the clock
|
||||||
|
* \return Microseconds elapsed
|
||||||
|
*
|
||||||
* Restarts the clock, putting it's time counter back to zero (as if the clock got constructed).
|
* Restarts the clock, putting it's time counter back to zero (as if the clock got constructed).
|
||||||
|
* It also compute the elapsed microseconds since the last Restart() call without any time loss (a problem that the combination of GetElapsedMicroseconds and Restart have).
|
||||||
*/
|
*/
|
||||||
void Clock::Restart()
|
UInt64 Clock::Restart()
|
||||||
{
|
{
|
||||||
NazaraLock(m_mutex);
|
NazaraLock(m_mutex);
|
||||||
|
|
||||||
|
Nz::UInt64 now = GetElapsedMicroseconds();
|
||||||
|
|
||||||
|
Nz::UInt64 elapsedTime = m_elapsedTime;
|
||||||
|
if (!m_paused)
|
||||||
|
elapsedTime += (now - m_refTime);
|
||||||
|
|
||||||
m_elapsedTime = 0;
|
m_elapsedTime = 0;
|
||||||
m_refTime = GetElapsedMicroseconds();
|
m_refTime = now;
|
||||||
m_paused = false;
|
m_paused = false;
|
||||||
|
|
||||||
|
return elapsedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ namespace Nz
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysWorld2D::PhysWorld2D() :
|
PhysWorld2D::PhysWorld2D() :
|
||||||
|
m_maxStepCount(50),
|
||||||
m_stepSize(0.005f),
|
m_stepSize(0.005f),
|
||||||
m_timestepAccumulator(0.f)
|
m_timestepAccumulator(0.f)
|
||||||
{
|
{
|
||||||
|
|
@ -144,6 +145,16 @@ namespace Nz
|
||||||
return m_handle;
|
return m_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t PhysWorld2D::GetIterationCount() const
|
||||||
|
{
|
||||||
|
return cpSpaceGetIterations(m_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t PhysWorld2D::GetMaxStepCount() const
|
||||||
|
{
|
||||||
|
return m_maxStepCount;
|
||||||
|
}
|
||||||
|
|
||||||
float PhysWorld2D::GetStepSize() const
|
float PhysWorld2D::GetStepSize() const
|
||||||
{
|
{
|
||||||
return m_stepSize;
|
return m_stepSize;
|
||||||
|
|
@ -281,6 +292,16 @@ namespace Nz
|
||||||
cpSpaceSetGravity(m_handle, cpv(gravity.x, gravity.y));
|
cpSpaceSetGravity(m_handle, cpv(gravity.x, gravity.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysWorld2D::SetIterationCount(std::size_t iterationCount)
|
||||||
|
{
|
||||||
|
cpSpaceSetIterations(m_handle, int(iterationCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysWorld2D::SetMaxStepCount(std::size_t maxStepCount)
|
||||||
|
{
|
||||||
|
m_maxStepCount = maxStepCount;
|
||||||
|
}
|
||||||
|
|
||||||
void PhysWorld2D::SetStepSize(float stepSize)
|
void PhysWorld2D::SetStepSize(float stepSize)
|
||||||
{
|
{
|
||||||
m_stepSize = stepSize;
|
m_stepSize = stepSize;
|
||||||
|
|
@ -290,7 +311,8 @@ namespace Nz
|
||||||
{
|
{
|
||||||
m_timestepAccumulator += timestep;
|
m_timestepAccumulator += timestep;
|
||||||
|
|
||||||
while (m_timestepAccumulator >= m_stepSize)
|
std::size_t stepCount = 0;
|
||||||
|
while (m_timestepAccumulator >= m_stepSize && stepCount < m_maxStepCount)
|
||||||
{
|
{
|
||||||
OnPhysWorld2DPreStep(this);
|
OnPhysWorld2DPreStep(this);
|
||||||
|
|
||||||
|
|
@ -309,9 +331,15 @@ namespace Nz
|
||||||
}
|
}
|
||||||
|
|
||||||
m_timestepAccumulator -= m_stepSize;
|
m_timestepAccumulator -= m_stepSize;
|
||||||
|
stepCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysWorld2D::UseSpatialHash(float cellSize, std::size_t entityCount)
|
||||||
|
{
|
||||||
|
cpSpaceUseSpatialHash(m_handle, cpFloat(cellSize), int(entityCount));
|
||||||
|
}
|
||||||
|
|
||||||
void PhysWorld2D::InitCallbacks(cpCollisionHandler* handler, const Callback& callbacks)
|
void PhysWorld2D::InitCallbacks(cpCollisionHandler* handler, const Callback& callbacks)
|
||||||
{
|
{
|
||||||
auto it = m_callbacks.emplace(handler, std::make_unique<Callback>(callbacks)).first;
|
auto it = m_callbacks.emplace(handler, std::make_unique<Callback>(callbacks)).first;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ namespace Nz
|
||||||
{
|
{
|
||||||
PhysWorld3D::PhysWorld3D() :
|
PhysWorld3D::PhysWorld3D() :
|
||||||
m_gravity(Vector3f::Zero()),
|
m_gravity(Vector3f::Zero()),
|
||||||
|
m_maxStepCount(50),
|
||||||
m_stepSize(0.005f),
|
m_stepSize(0.005f),
|
||||||
m_timestepAccumulator(0.f)
|
m_timestepAccumulator(0.f)
|
||||||
{
|
{
|
||||||
|
|
@ -66,6 +67,11 @@ namespace Nz
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t PhysWorld3D::GetMaxStepCount() const
|
||||||
|
{
|
||||||
|
return m_maxStepCount;
|
||||||
|
}
|
||||||
|
|
||||||
float PhysWorld3D::GetStepSize() const
|
float PhysWorld3D::GetStepSize() const
|
||||||
{
|
{
|
||||||
return m_stepSize;
|
return m_stepSize;
|
||||||
|
|
@ -76,6 +82,11 @@ namespace Nz
|
||||||
m_gravity = gravity;
|
m_gravity = gravity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysWorld3D::SetMaxStepCount(std::size_t maxStepCount)
|
||||||
|
{
|
||||||
|
m_maxStepCount = maxStepCount;
|
||||||
|
}
|
||||||
|
|
||||||
void PhysWorld3D::SetSolverModel(unsigned int model)
|
void PhysWorld3D::SetSolverModel(unsigned int model)
|
||||||
{
|
{
|
||||||
NewtonSetSolverModel(m_world, model);
|
NewtonSetSolverModel(m_world, model);
|
||||||
|
|
@ -132,10 +143,12 @@ namespace Nz
|
||||||
{
|
{
|
||||||
m_timestepAccumulator += timestep;
|
m_timestepAccumulator += timestep;
|
||||||
|
|
||||||
while (m_timestepAccumulator >= m_stepSize)
|
std::size_t stepCount = 0;
|
||||||
|
while (m_timestepAccumulator >= m_stepSize && stepCount < m_maxStepCount)
|
||||||
{
|
{
|
||||||
NewtonUpdate(m_world, m_stepSize);
|
NewtonUpdate(m_world, m_stepSize);
|
||||||
m_timestepAccumulator -= m_stepSize;
|
m_timestepAccumulator -= m_stepSize;
|
||||||
|
stepCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,9 +51,8 @@ SCENARIO("EventHandler", "[PLATFORM][EVENTHANDLER][INTERACTIVE][.]")
|
||||||
while (app.Run())
|
while (app.Run())
|
||||||
{
|
{
|
||||||
window.Display();
|
window.Display();
|
||||||
float elapsedTime = elapsedTimeClock.GetSeconds();
|
|
||||||
elapsedTimeClock.Restart();
|
|
||||||
|
|
||||||
|
float elapsedTime = elapsedTimeClock.Restart() / 1'000'000;
|
||||||
if (!fsm.Update(elapsedTime))
|
if (!fsm.Update(elapsedTime))
|
||||||
{
|
{
|
||||||
NazaraError("Failed to update state machine.");
|
NazaraError("Failed to update state machine.");
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
|
||||||
|
|
||||||
Ndk::EntityOwner entityOwner(entity);
|
Ndk::EntityOwner entityOwner(entity);
|
||||||
|
|
||||||
world.Update();
|
world.Refresh();
|
||||||
CHECK(entity.IsValid());
|
CHECK(entity.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,12 +29,12 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
|
||||||
Ndk::EntityOwner entityOwner2(std::move(entityOwner));
|
Ndk::EntityOwner entityOwner2(std::move(entityOwner));
|
||||||
entityOwner.Reset();
|
entityOwner.Reset();
|
||||||
|
|
||||||
world.Update();
|
world.Refresh();
|
||||||
CHECK(entity.IsValid());
|
CHECK(entity.IsValid());
|
||||||
|
|
||||||
entityOwner2.Reset();
|
entityOwner2.Reset();
|
||||||
|
|
||||||
world.Update();
|
world.Refresh();
|
||||||
CHECK(!entity.IsValid());
|
CHECK(!entity.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,12 +47,12 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
|
||||||
entityOwner2 = std::move(entityOwner);
|
entityOwner2 = std::move(entityOwner);
|
||||||
entityOwner.Reset();
|
entityOwner.Reset();
|
||||||
|
|
||||||
world.Update();
|
world.Refresh();
|
||||||
CHECK(entity.IsValid());
|
CHECK(entity.IsValid());
|
||||||
|
|
||||||
entityOwner2.Reset();
|
entityOwner2.Reset();
|
||||||
|
|
||||||
world.Update();
|
world.Refresh();
|
||||||
CHECK(!entity.IsValid());
|
CHECK(!entity.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
|
||||||
Ndk::EntityOwner entityOwner(entity);
|
Ndk::EntityOwner entityOwner(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
world.Update();
|
world.Refresh();
|
||||||
CHECK(!entity.IsValid());
|
CHECK(!entity.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
|
||||||
Ndk::EntityOwner entityOwner(entity);
|
Ndk::EntityOwner entityOwner(entity);
|
||||||
entityOwner.Reset();
|
entityOwner.Reset();
|
||||||
|
|
||||||
world.Update();
|
world.Refresh();
|
||||||
CHECK(!entity.IsValid());
|
CHECK(!entity.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
|
||||||
Ndk::EntityOwner entityOwner(entity);
|
Ndk::EntityOwner entityOwner(entity);
|
||||||
entityOwner = world.CreateEntity();
|
entityOwner = world.CreateEntity();
|
||||||
|
|
||||||
world.Update();
|
world.Refresh();
|
||||||
CHECK(!entity.IsValid());
|
CHECK(!entity.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,7 +100,7 @@ SCENARIO("EntityOwner", "[NDK][ENTITYOWNER]")
|
||||||
|
|
||||||
entityOwner = std::move(entity2);
|
entityOwner = std::move(entity2);
|
||||||
|
|
||||||
world.Update();
|
world.Refresh();
|
||||||
CHECK(!entity.IsValid());
|
CHECK(!entity.IsValid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,40 @@
|
||||||
#include <NDK/System.hpp>
|
#include <NDK/System.hpp>
|
||||||
#include <Catch/catch.hpp>
|
#include <Catch/catch.hpp>
|
||||||
/*
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
class TestSystem : public Ndk::System<TestSystem>
|
class TestSystem : public Ndk::System<TestSystem>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TestSystem(int value) :
|
TestSystem() :
|
||||||
m_value(value)
|
m_updateCounter(0),
|
||||||
|
m_elapsedTime(0.f)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetValue() const
|
|
||||||
{
|
|
||||||
return m_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
~TestSystem() = default;
|
~TestSystem() = default;
|
||||||
|
|
||||||
|
float GetElapsedTime() const
|
||||||
|
{
|
||||||
|
return m_elapsedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetLoopCount() const
|
||||||
|
{
|
||||||
|
return m_updateCounter;
|
||||||
|
}
|
||||||
|
|
||||||
static Ndk::SystemIndex systemIndex;
|
static Ndk::SystemIndex systemIndex;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_value;
|
std::size_t m_updateCounter;
|
||||||
|
float m_elapsedTime;
|
||||||
|
|
||||||
void OnUpdate(float elapsedTime) override
|
void OnUpdate(float elapsedTime) override
|
||||||
{
|
{
|
||||||
|
++m_updateCounter;
|
||||||
|
|
||||||
|
m_elapsedTime += elapsedTime;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -35,16 +45,63 @@ SCENARIO("System", "[NDK][SYSTEM]")
|
||||||
{
|
{
|
||||||
GIVEN("Our TestSystem")
|
GIVEN("Our TestSystem")
|
||||||
{
|
{
|
||||||
TestSystem testSystem(666);
|
TestSystem testSystem;
|
||||||
|
testSystem.SetMaximumUpdateRate(30.f);
|
||||||
|
|
||||||
WHEN("We clone it")
|
float maxTimePerFrame = 1 / 30.f;
|
||||||
{
|
|
||||||
std::unique_ptr<Ndk::BaseSystem> clone = testSystem.Clone();
|
|
||||||
|
|
||||||
THEN("We should get a copy")
|
WHEN("We update it with a higher framerate")
|
||||||
{
|
{
|
||||||
REQUIRE(static_cast<TestSystem*>(clone.get())->GetValue() == 666);
|
float timePerFrame = maxTimePerFrame / 2.f;
|
||||||
|
float elapsedTime = 2.f;
|
||||||
|
|
||||||
|
std::size_t loopCount = static_cast<std::size_t>(std::round(elapsedTime / timePerFrame));
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < loopCount; ++i)
|
||||||
|
testSystem.Update(timePerFrame);
|
||||||
|
|
||||||
|
CHECK(testSystem.GetLoopCount() == loopCount / 2);
|
||||||
|
CHECK(testSystem.GetElapsedTime() == Approx(elapsedTime).epsilon(timePerFrame));
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("We update it with a lower framerate")
|
||||||
|
{
|
||||||
|
float timePerFrame = maxTimePerFrame * 2.f;
|
||||||
|
float elapsedTime = 10.f;
|
||||||
|
|
||||||
|
std::size_t loopCount = static_cast<std::size_t>(std::round(elapsedTime / timePerFrame));
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < loopCount; ++i)
|
||||||
|
testSystem.Update(timePerFrame);
|
||||||
|
|
||||||
|
CHECK(testSystem.GetLoopCount() == loopCount);
|
||||||
|
CHECK(testSystem.GetElapsedTime() == Approx(elapsedTime).epsilon(timePerFrame));
|
||||||
|
|
||||||
|
AND_WHEN("We suddenly increase framerate")
|
||||||
|
{
|
||||||
|
float newTimePerFrame = 1 / 300.f;
|
||||||
|
float newElapsedTime = 100.f;
|
||||||
|
|
||||||
|
std::size_t newLoopCount = static_cast<std::size_t>(std::round(newElapsedTime / newTimePerFrame));
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < newLoopCount; ++i)
|
||||||
|
testSystem.Update(newTimePerFrame);
|
||||||
|
|
||||||
|
CHECK(testSystem.GetLoopCount() == loopCount + newLoopCount / 10);
|
||||||
|
CHECK(testSystem.GetElapsedTime() == Approx(elapsedTime + newElapsedTime).epsilon(newTimePerFrame));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WHEN("We update it with a very low framerate")
|
||||||
|
{
|
||||||
|
float timePerFrame = 0.5f;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < 10; ++i)
|
||||||
|
testSystem.Update(timePerFrame);
|
||||||
|
|
||||||
|
CHECK(testSystem.GetLoopCount() == 10);
|
||||||
|
CHECK(testSystem.GetElapsedTime() == Approx(5.f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
@ -19,7 +19,7 @@ SCENARIO("PhysicsSystem2D", "[NDK][PHYSICSSYSTEM2D]")
|
||||||
Ndk::NodeComponent& nodeComponent = movingEntity->GetComponent<Ndk::NodeComponent>();
|
Ndk::NodeComponent& nodeComponent = movingEntity->GetComponent<Ndk::NodeComponent>();
|
||||||
Ndk::PhysicsComponent2D& physicsComponent2D = movingEntity->AddComponent<Ndk::PhysicsComponent2D>();
|
Ndk::PhysicsComponent2D& physicsComponent2D = movingEntity->AddComponent<Ndk::PhysicsComponent2D>();
|
||||||
|
|
||||||
world.GetSystem<Ndk::PhysicsSystem2D>().SetFixedUpdateRate(30.f);
|
world.GetSystem<Ndk::PhysicsSystem2D>().SetMaximumUpdateRate(0.f);
|
||||||
|
|
||||||
WHEN("We update the world")
|
WHEN("We update the world")
|
||||||
{
|
{
|
||||||
|
|
@ -43,7 +43,7 @@ SCENARIO("PhysicsSystem2D", "[NDK][PHYSICSSYSTEM2D]")
|
||||||
|
|
||||||
world.Update(1.f);
|
world.Update(1.f);
|
||||||
|
|
||||||
THEN("It should moved freely")
|
THEN("It should move freely")
|
||||||
{
|
{
|
||||||
REQUIRE(nodeComponent.GetPosition() == position);
|
REQUIRE(nodeComponent.GetPosition() == position);
|
||||||
movingAABB.Translate(position);
|
movingAABB.Translate(position);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue