ecs first draft

This commit is contained in:
turanszkij
2018-08-24 17:35:49 +01:00
parent ebe62c5278
commit 212d9f6f4b
7 changed files with 836 additions and 2 deletions
@@ -254,6 +254,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)Utility\stb_image.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)Utility\stb_image_write.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiArchive.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiECS.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiFFTGenerator.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiGPUSortLib.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiGraphicsDevice_DX12.h" />
@@ -354,6 +355,8 @@
<ClInclude Include="$(MSBuildThisFileDirectory)wiRenderTarget.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiResourceManager.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiResourceManager_BindLua.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiSceneSystem.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiSceneSystem_Decl.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiServer.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiSound.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)wiSound_BindLua.h" />
@@ -692,6 +695,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)wiRenderTarget.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)wiResourceManager.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)wiResourceManager_BindLua.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)wiSceneSystem.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)wiServer.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)wiSound.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)wiSound_BindLua.cpp" />
@@ -61,6 +61,9 @@
<Filter Include="Documentation">
<UniqueIdentifier>{052d6b54-4254-4260-ba34-79b6fb943b4a}</UniqueIdentifier>
</Filter>
<Filter Include="ENGINE\System">
<UniqueIdentifier>{f9156b5b-9cc8-436c-9faa-536f7dce4f27}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)LUA\lapi.h">
@@ -1131,6 +1134,15 @@
<ClInclude Include="$(MSBuildThisFileDirectory)wiSceneComponents_BindLua.h">
<Filter>ENGINE\Scripting\LuaBindings</Filter>
</ClInclude>
<ClInclude Include="$(MSBuildThisFileDirectory)wiECS.h">
<Filter>ENGINE\System</Filter>
</ClInclude>
<ClInclude Include="$(MSBuildThisFileDirectory)wiSceneSystem.h">
<Filter>ENGINE\System</Filter>
</ClInclude>
<ClInclude Include="$(MSBuildThisFileDirectory)wiSceneSystem_Decl.h">
<Filter>ENGINE\System</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)LUA\lapi.c">
@@ -1925,6 +1937,9 @@
<ClCompile Include="$(MSBuildThisFileDirectory)wiSceneComponents_BindLua.cpp">
<Filter>ENGINE\Scripting\LuaBindings</Filter>
</ClCompile>
<ClCompile Include="$(MSBuildThisFileDirectory)wiSceneSystem.cpp">
<Filter>ENGINE\System</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Image Include="$(MSBuildThisFileDirectory)fonts\default_font.dds">
+234
View File
@@ -0,0 +1,234 @@
#ifndef _ENTITY_COMPONENT_SYSTEM_H_
#define _ENTITY_COMPONENT_SYSTEM_H_
#include <cstdint>
#include <cassert>
#include <vector>
#include <unordered_map>
namespace wiECS
{
typedef uint64_t Entity;
template<typename T>
class ComponentManager
{
public:
// reservedCount : how much components can be held initially before growing the container
ComponentManager(size_t reservedCount = 0)
{
components.reserve(reservedCount);
entities.reserve(reservedCount);
lookup.reserve(reservedCount);
indices.reserve(reservedCount);
}
// The iterator is an indirection to the components. Components will be moved around to remain compacted, but iterator will always be able to reference a component.
struct iterator
{
size_t value = ~0;
inline iterator operator=(iterator other) { value = other.value; return *this; }
inline bool operator==(iterator other) const { return value == other.value; }
inline bool operator!=(iterator other) const { return value != other.value; }
inline iterator& operator++() { value++; return *this; }
inline iterator operator++(int v) { iterator temp = *this; ++*this; return temp; }
inline iterator& operator--() { value--; return *this; }
inline iterator operator--(int v) { iterator temp = *this; --*this; return temp; }
};
// Clear the whole container, invalidate all iterators
inline void Clear()
{
components.clear();
entities.clear();
lookup.clear();
indices.clear();
dead.clear();
}
// Get the beginning of the iteration sequence (iterator is safe but uses indirection):
inline iterator Begin() const
{
iterator it;
it.value = 0;
return it;
}
// Get the end of the iteration sequence (iterator is safe but uses indirection):
inline iterator End() const
{
iterator it;
it.value = indices.size();
return it;
}
// Check if an iterator is referencing a component or not (iterator is safe but uses indirection):
inline bool IsValid(iterator it) const
{
return it.value < indices.size() && indices[it.value] < components.size();
}
// Check if an iterator is referencing a specific entity or not (iterator is safe but uses indirection):
inline bool IsValid(iterator it, Entity entity) const
{
assert(entities.size() == components.size());
return IsValid(it) && entities[indices[it.value]] == entity;
}
// Create a new component and retrieve iterator (iterator is safe but uses indirection):
inline iterator Create(Entity entity)
{
// Only one of this component type per entity is allowed!
assert(!IsValid(Find(entity)));
iterator it;
if (dead.empty())
{
// There are no dead elements, so we must have the same amount of components as indices:
assert(components.size() == indices.size());
// Any new component and index will just be pushed onto the end:
it.value = components.size();
indices.push_back(it.value);
}
else
{
// We have dead elements, which means components was popped, but indices only swapped, so there must be less components than indices:
assert(components.size() < indices.size());
// Essentially we pop the last dead iterator and update its value to the new component (that will always be pushed to the end of components):
it = dead.back();
dead.pop_back();
indices[it.value] = components.size();
}
// Entity count must always be the same as the number of coponents!
assert(entities.size() == components.size());
assert(lookup.size() == components.size());
// New components are always pushed to the end:
components.push_back(T());
// Also push corresponding entity:
entities.push_back(entity);
// Update the entity lookup table:
lookup[entity] = it;
return it;
}
// Remove a component of a certain entity if it exists (referencing by Entity involves multiple indirection):
inline void Remove(Entity entity)
{
iterator it = Find(entity);
if (IsValid(it))
{
Remove(it);
}
}
// Remove a component referenced by a certain iterator (iterator is safe but uses indirection):
inline void Remove(iterator it)
{
// Iterator should be valid:
assert(IsValid(it));
// Directly index into components and entities array:
const size_t index = indices[it.value];
// Remove the corresponding entry from the lookup table:
const Entity entity = entities[index];
lookup.erase(entity);
// Swap out the dead element with the last one, and shrink the container:
components[index] = std::move(components.back()); // try to use move instead of copy
components.pop_back();
entities[index] = entities.back();
entities.pop_back();
// Because the last element of the container was moved to the position of the removed element, we update its iterator accordingly:
indices[components.size()] = index;
//The current iterator is marked as dead:
indices[it.value] = ~0;
dead.push_back(it);
}
// Swap two components' data that are referenced by iterators (iterator is safe but uses indirection):
inline void Swap(iterator a, iterator b)
{
const size_t index_a = indices[a.value];
const size_t index_b = indices[b.value];
const size_t index_tmp = index_a;
const Entity entity_tmp = entities[index_a];
const T component_tmp = std::move(components[index_a]);
indices[a.value] = index_b;
entities[index_a] = entities[index_b];
components[index_a] = std::move(components[index_b]);
indices[b.value] = index_tmp;
entities[index_b] = entity_tmp;
components[index_b] = std::move(component_tmp);
}
// Find a specific component of a certain entity if exists (referencing by Entity involves multiple indirection):
inline iterator Find(Entity entity) const
{
auto it = lookup.find(entity);
if (it != lookup.end())
{
return it->second;
}
return End();
}
// Retrieve a component specified by an iterator (iterator is safe but uses indirection):
inline T& GetComponent(iterator it)
{
assert(IsValid(it));
return components[indices[it.value]];
}
// Retrieve an entity specified by an iterator (iterator is safe but uses indirection):
inline Entity GetEntity(iterator it) const
{
assert(IsValid(it));
return entities[indices[it.value]];
}
// Retrieve the number of existing entries:
inline size_t GetCount() const { return components.size(); }
// Directly index a specific component without indirection:
// 0 <= index < GetCount()
inline T& GetComponent(size_t index) const { return components[index]; }
// Directly index a specific component without indirection:
// 0 <= index < GetCount()
inline Entity GetEntity(size_t index) const { return entities[index]; }
// Directly index a specific component without indirection:
// 0 <= index < GetCount()
inline T& operator[](size_t index) { return components[index]; }
private:
// This is a linear array of alive components:
std::vector<T> components;
// This is a linear array of entities corresponding to each alive component:
std::vector<Entity> entities;
// This is a lookup table for entities:
std::unordered_map<Entity, iterator> lookup;
// This is the indirection of iterator->component index:
std::vector<size_t> indices;
// The indices will also contain dead elements, and those are saved here:
std::vector<iterator> dead;
};
}
#endif // _ENTITY_COMPONENT_SYSTEM_H_
-2
View File
@@ -34,14 +34,12 @@ namespace wiSceneComponents
struct ForceField;
}
class Lines;
class Cube;
class Translator;
class wiParticle;
class wiEmittedParticle;
class wiHairParticle;
class wiSprite;
class wiSPTree;
class TaskThread;
class PHYSICS;
class wiRenderTarget;
+53
View File
@@ -0,0 +1,53 @@
#include "wiSceneSystem.h"
namespace wiSceneSystem
{
void Scene::Update(float dt)
{
// Update Transform components:
for (size_t i = 0; i < transforms.GetCount(); ++i)
{
auto& transform = transforms[i];
const bool parented = transforms.IsValid(transform.parent_ref);
if (transform.dirty || parented)
{
transform.dirty = false;
XMVECTOR scale_local = XMLoadFloat3(&transform.scale_local);
XMVECTOR rotation_local = XMLoadFloat4(&transform.rotation_local);
XMVECTOR translation_local = XMLoadFloat3(&transform.translation_local);
XMMATRIX world =
XMMatrixScalingFromVector(scale_local) *
XMMatrixRotationQuaternion(rotation_local) *
XMMatrixTranslationFromVector(translation_local);
if (parented)
{
auto& parent = transforms.GetComponent(transform.parent_ref);
XMMATRIX world_parent = XMLoadFloat4x4(&parent.world);
XMMATRIX bindMatrix = XMLoadFloat4x4(&transform.world_parent_bind);
world = world * bindMatrix * world_parent;
}
transform.world_prev = transform.world;
XMStoreFloat4x4(&transform.world, world);
}
}
// Update Bone components:
for (size_t i = 0; i < bones.GetCount(); ++i)
{
auto& bone = bones[i];
auto& transform = transforms.GetComponent(bone.transform_ref);
XMMATRIX inverseBindPoseMatrix = XMLoadFloat4x4(&bone.inverseBindPoseMatrix);
XMMATRIX world = XMLoadFloat4x4(&transform.world);
XMMATRIX skinningMatrix = inverseBindPoseMatrix * world;
XMStoreFloat4x4(&bone.skinningMatrix, skinningMatrix);
}
}
}
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
#pragma once
namespace wiSceneSystem
{
struct Node;
struct Transform;
struct Material;
struct Mesh;
struct Object;
struct Bone;
struct Armature;
struct Light;
struct Camera;
struct EnvironmentProbe;
struct ForceField;
struct Decal;
struct Model;
struct Scene;
}