diff --git a/.gitignore b/.gitignore index a326e396ed35e9edee85b322f8fb4b9ea8fda3d5..77c6695cb968bd971748e1b08b89ec41adc9b09d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ build/ cmake-build-debug/ cmake-build-debug-coverage/ .idea/ +.cache/ imgui.ini diff --git a/demo/main.cpp b/demo/main.cpp index 3aaa99b9288314fa327232faa562edc11ce4172a..f02bb32726d3533b206d7abfa78a63cb71baa4f1 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -12,7 +12,6 @@ #include <fggl/math/ecs.hpp> #include <fggl/data/procedural.hpp> -#include <fggl/ecs/ecs.hpp> #include <fggl/debug/debug.h> #include <fggl/data/storage.hpp> @@ -55,7 +54,7 @@ enum camera_type { cam_free, cam_arcball }; camera_type cam_mode = cam_free; //TODO proper input system -void process_camera(fggl::gfx::Window& window, fggl::ecs2::World& ecs, fggl::gfx::Input& input, fggl::ecs::entity_t cam) { +void process_camera(fggl::gfx::Window& window, fggl::ecs2::World& ecs, fggl::gfx::Input& input) { if ( glfwGetKey(window.handle(), GLFW_KEY_F2) == GLFW_PRESS ) { cam_mode = cam_free; @@ -132,12 +131,19 @@ constexpr float ROT_SPEED = 0.05f; constexpr float PAN_SPEED = 0.05f; constexpr glm::mat4 MAT_IDENTITY(1.0f); -void process_freecam(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx::Input& input, fggl::ecs::entity_t cam) { +void process_freecam(fggl::ecs2::impl::iter& it, fggl::components::Transform* t, fggl::components::Camera* c) { + CameraHacks* hacks = (CameraHacks*)( it.ctx() ); + const fggl::gfx::Window& window = hacks->window; + const fggl::gfx::Input& input = hacks->input; + + auto& camTransform = t[0]; + auto& camComp = c[0]; + float rotationValue = 0.0f; glm::vec3 translation(0.0f); // calulate rotation (user input) - if ( glfwGetKey(window.handle(), GLFW_KEY_Q) == GLFW_PRESS ) { +/* if ( glfwGetKey(window.handle(), GLFW_KEY_Q) == GLFW_PRESS ) { rotationValue = ROT_SPEED; } else if ( glfwGetKey(window.handle(), GLFW_KEY_E) == GLFW_PRESS ) { rotationValue = -ROT_SPEED; @@ -158,14 +164,10 @@ void process_freecam(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx:: if ( glfwGetKey(window.handle(), GLFW_KEY_A) == GLFW_PRESS ) { translation -= fggl::math::FORWARD; - } - - // apply rotation/movement - auto camTransform = ecs.getComponent<fggl::math::Transform>(cam); - auto camComp = ecs.getComponent<fggl::gfx::Camera>(cam); + }*/ - glm::vec4 position( camTransform->origin(), 1.0f ); - glm::vec4 pivot( camComp->target, 1.0f ); + glm::vec4 position( camTransform.origin(), 1.0f ); + glm::vec4 pivot( camComp.target, 1.0f ); // apply movement if ( translation != glm::vec3(0.0f) ) { @@ -186,8 +188,8 @@ void process_freecam(fggl::gfx::Window& window, fggl::ecs::ECS& ecs, fggl::gfx:: position = ( rotation * ( position - pivot ) ) + pivot; } - camTransform->origin( position ); - camComp->target = pivot; + camTransform.origin( position ); + camComp.target = pivot; } void loadShader(fggl::gfx::ShaderCache& cache, const char* name, bool hasGeom) { diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt index ffc795c92f2e3ae72fba3db93edb3f6e3fc8493c..4bea6138e7769bdadf705c7b45e5dc2f2b682b03 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -1,7 +1,6 @@ configure_file(FgglConfig.h.in FgglConfig.h) add_library(fggl fggl.cpp - ecs/ecs.cpp data/model.cpp data/procedural.cpp ) diff --git a/fggl/ecs/component.hpp b/fggl/ecs/component.hpp deleted file mode 100644 index b84fbe859be57aaff22476d961b0b65d2f0572e8..0000000000000000000000000000000000000000 --- a/fggl/ecs/component.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef FGGL_ECS_COMPONENT_H -#define FGGL_ECS_COMPONENT_H - -#include "utility.hpp" - -#include <new> -#include <utility> - -#include <vector> -#include <unordered_map> - -namespace fggl::ecs { - - class ComponentBase { - public: - using data_t = unsigned char; - virtual ~ComponentBase() {}; - - virtual void destroy(data_t* data) const = 0; - virtual void move(data_t* src, data_t* dest) const = 0; - virtual void construct(data_t* data) const = 0; - - virtual std::size_t size() const = 0; - }; - - template<class C> - class Component : public ComponentBase { - public: - virtual void destroy(data_t* data) const override { - C* location = std::launder(reinterpret_cast<C*>(data)); - location->~C(); - } - - virtual void construct(unsigned char* data) const override { -// new (&data[0]) C(); - } - - virtual void move(data_t* src, data_t* dest) const override { - new (&dest[0]) C(std::move(*reinterpret_cast<C*>(src))); - } - - virtual std::size_t size() const { - return sizeof(C); - } - - static component_type_t typeID() { - return TypeIdGenerator<ComponentBase>::GetNewID<C>(); - } - }; - -} - -#endif diff --git a/fggl/ecs/ecs.cpp b/fggl/ecs/ecs.cpp deleted file mode 100644 index d986991b68ce89775f36fb95647b068c5edaabd1..0000000000000000000000000000000000000000 --- a/fggl/ecs/ecs.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "ecs.hpp" - -#include <iostream> - -using namespace fggl::ecs; - -Archetype::Archetype(const archToken_t& token ) : type(token) { - for ( archToken_t::size_type i = 0; i < token.size(); ++i ) { - data.push_back( new unsigned char[default_cap] ); - dataSizes.push_back( default_cap ); - } -} - -ECS::ECS() : m_entityIDCounter(1) {} - -ECS::~ECS() { - for( Archetype* arch : m_archetypes ) { - for ( std::size_t i=0; i < arch->type.size(); ++i ) { - const ComponentBase* const comp = m_componentMap[arch->type[i]]; - const std::size_t& size = comp->size(); - - for ( std::size_t e=0; e<arch->entities.size(); ++e ) { - comp->destroy(&arch->data[i][e*size]); - } - delete[] arch->data[i]; - } - delete arch; - } - - for( componentmap_t::value_type& p : m_componentMap ) - delete p.second; -} - -entity_t ECS::getNewID() { - return m_entityIDCounter++; -} - -entity_t ECS::createEntity( ) { - auto eid = getNewID(); - - Record dummy; - dummy.archetype = nullptr; - dummy.index = 0; - m_entityArchtypes[ eid ] = dummy; - - return eid; -} - -Archetype* ECS::getArchetype(const archToken_t& id) { - for ( auto* arch : m_archetypes ) { - if ( arch->type == id ) { - return arch; - } - } - - // didn't exist, need to make one - Archetype* arch = new Archetype(id); - m_archetypes.push_back( arch ); - - return arch; -} diff --git a/fggl/ecs/ecs.hpp b/fggl/ecs/ecs.hpp deleted file mode 100644 index e62b6836bd46a38264915f159714ec08cacae18e..0000000000000000000000000000000000000000 --- a/fggl/ecs/ecs.hpp +++ /dev/null @@ -1,287 +0,0 @@ -#ifndef FGGL_ECS_H -#define FGGL_ECS_H - -#include "utility.hpp" -#include "component.hpp" - -#include <iostream> -#include <algorithm> -#include <cassert> -#include <string> -#include <vector> -#include <unordered_map> - -namespace fggl::ecs { - using archToken_t = std::vector<component_type_t>; - - struct Archetype { - constexpr static unsigned int default_cap = 0; - const archToken_t type; - std::vector<ComponentBase::data_t*> data; - std::vector<std::size_t> dataSizes; - std::vector<entity_t> entities; - - Archetype(const archToken_t& type_a); - - inline archToken_t create( const component_type_t cid ) const { - assert( !contains(cid) ); - - // create the new type - auto newType = type; - newType.push_back( cid ); - std::sort( newType.begin(), newType.end() ); - return newType; - } - - inline bool contains( const component_type_t cid ) const { - return ( std::find( type.begin(), type.end(), cid ) != type.end() ); - } - }; - - class ECS { - struct Record { - Archetype* archetype; - std::size_t index; - }; - using componentmap_t = std::unordered_map<component_type_t, ComponentBase*>; - using entitymap_t = std::unordered_map<entity_t, Record>; - using archetype_t = std::vector<Archetype*>; - - public: - ECS(); - ~ECS(); - - entity_t getNewID(); - entity_t createEntity(); - void removeEntity(const entity_t eid); - - template<class C> - void registerComponent() { - component_type_t type = Component<C>::typeID(); - if ( m_componentMap.find( type ) != m_componentMap.end() ) - return; - m_componentMap.emplace( type, new Component<C> ); - } - - template<class C> - bool isComponentRegistered() { - component_type_t type = Component<C>::typeID(); - return ( m_componentMap.find(type) != m_componentMap.end() ); - } - - template<class C, typename... Args> - C* addComponent(const entity_t& id, Args&&... args) { - component_type_t type = Component<C>::typeID(); - assert( isComponentRegistered<C>() ); - - Record& record = m_entityArchtypes[id]; - Archetype* oldArch = record.archetype; - C* newComp = nullptr; - Archetype* newArch = nullptr; - - if ( !oldArch ) { - archToken_t newID(1, type); - const ComponentBase* const newCompType = m_componentMap[ type ]; - - // fetch type - newArch = getArchetype( newID ); - assert( newArch->type.size() == 1 ); - - // calculate if we have enouph space to allocate - std::size_t emplacementPos = ensureCapacity( newArch, 0, newCompType ); - newComp = new (&newArch->data[0][emplacementPos])C(std::forward<Args>(args)...); - } else { - // check if the arch contains the component - if ( oldArch->contains(type) ) { - return nullptr; - } - - // create a new archetype with the component - auto newID = oldArch->create( type ); - newArch = getArchetype( newID ); - - // relocate the old data to the new archetype - for ( std::size_t j=0; j < newID.size(); ++j ){ - const component_type_t compType = newID[j]; - const ComponentBase* const comp = m_componentMap.at(compType); - - - // TODO this seems a little suspect - surely we could allocate all blocks at once? - // if per component the arrays could become out of sync... - int newOffset = ensureCapacity(newArch, j, comp); - int oldIdx = getComponentIdx(oldArch, compType); - - if ( oldIdx != -1 ) { - assert( oldArch->contains(compType) ); - const std::size_t compSize = comp->size(); - const std::size_t oldOffset = record.index * compSize; - - comp->move( &oldArch->data[oldIdx][oldOffset], - &newArch->data[j][newOffset] ); - comp->destroy( &oldArch->data[oldIdx][oldOffset] ); - } else { - assert( !oldArch->contains(compType) ); - newComp = new (&newArch->data[j][newOffset]) - C(std::forward<Args>(args)...); - } - } - - // ensure the old archetype is still contigious - const int lastEnt = oldArch->entities.size() - 1; - if ( lastEnt != record.index ) { - for ( std::size_t i=0; i < oldArch->type.size(); ++i ) { - const component_type_t typeID = oldArch->type[i]; - const ComponentBase* const comp = m_componentMap[typeID]; - const std::size_t& compSize = comp->size(); - - // shift the empty record to the end of the list - std::size_t slotOffset = record.index * compSize; - std::size_t lastOffset = lastEnt * compSize; - - // if we're not the last entity, swap - if ( slotOffset != lastOffset ) { - comp->move( &oldArch->data[i][lastOffset], - &oldArch->data[i][slotOffset] ); - comp->destroy( &oldArch->data[i][lastOffset] ); - } - } - - // fix the position - oldArch->entities[ record.index ] = oldArch->entities[ lastEnt ]; - } - oldArch->entities.pop_back(); - } - - // register the new data with the new archetype - newArch->entities.push_back( id ); - record.index = newArch->entities.size() - 1; - record.archetype = newArch; - return newComp; - } - - template<class C> - void removeComponent(const entity_t& entityId); - - template<class C> - bool hasComponent(const entity_t& entityId) const { - const component_type_t componentID = Component<C>::typeID(); - const auto* arch = m_entityArchtypes.at(entityId).archetype; - if ( arch == nullptr ) { - return false; - } - return ( std::find( arch->type.begin(), arch->type.end(), componentID) != - arch->type.end() ); - } - - template<class C> - C* getComponent(const entity_t& entityId) { - assert( hasComponent<C>( entityId ) ); - const auto type = Component<C>::typeID(); - - const ComponentBase* const newComp = m_componentMap[type]; - const auto record = m_entityArchtypes.at(entityId); - const auto* arch = record.archetype; - - // JWR: linear search... seems a little suspect, they're ordered after all - for ( std::size_t i=0; i < arch->type.size(); ++i ) { - if ( arch->type[i] == type ) { - return reinterpret_cast<C*>(& (arch->data[i][ record.index * newComp->size() ]) ); - } - } - - return nullptr; - } - - template<class C> - const C* getComponent(const entity_t& entityId) const { - assert( hasComponent<C>( entityId ) ); - const auto type = Component<C>::typeID(); - - const ComponentBase* const newComp = m_componentMap.at(type); - const auto record = m_entityArchtypes.at(entityId); - const auto* arch = record.archetype; - - // JWR: linear search... seems a little suspect, they're ordered after all - for ( std::size_t i=0; i < arch->type.size(); ++i ) { - if ( arch->type[i] == type ) { - return reinterpret_cast<C*>(& (arch->data[i][ record.index * newComp->size() ]) ); - } - } - - return nullptr; - } - - template<class... Cs> - std::vector<entity_t> getEntityWith() const { - // construct the key - archToken_t key; - (key.push_back( Component<Cs>::typeID() ), ...); - - // entities - std::vector<entity_t> entities; - for ( Archetype* arch : m_archetypes ) { - if ( std::includes( arch->type.begin(), arch->type.end(), key.begin(), key.end() ) ) { - if( !arch->entities.empty() ) { - entities.insert( entities.begin(), arch->entities.begin(), arch->entities.end() ); - } - } - } - - return entities; - } - - private: - entitymap_t m_entityArchtypes; - archetype_t m_archetypes; - entity_t m_entityIDCounter; - componentmap_t m_componentMap; - Archetype* getArchetype(const archToken_t& id); - - inline std::string arch2str(Archetype* arch) { - std::string str; - for ( const auto& type : arch->type ) { - str += std::to_string( type ); - } - return str; - } - - inline std::size_t ensureCapacity(Archetype* arch, const int idx, const ComponentBase* const comp) { - const std::size_t& compSize = comp->size(); - std::size_t currSize = arch->entities.size() * compSize; - std::size_t newSize = currSize + compSize; - - std::size_t cap = arch->dataSizes[idx]; - - if ( newSize > arch->dataSizes[idx] ) { - arch->dataSizes[idx] *= 2; - arch->dataSizes[idx] += compSize; - - // copy data over - unsigned char* newData = new unsigned char[arch->dataSizes[idx]]; - for ( std::size_t e=0; e<arch->entities.size(); ++e ) { - const int offset = e * compSize; - comp->move( &arch->data[idx][offset], &newData[offset] ); - comp->destroy( &arch->data[idx][offset] ); - } - - // free the old data and swap the pointers - delete[] arch->data[idx]; - arch->data[idx] = newData; - } - - return currSize; - } - - inline int getComponentIdx(const Archetype* arch, const component_type_t goal) { - // JWR could do binary search for speedup - for ( std::size_t i=0; i < arch->type.size(); ++i ) { - if ( arch->type[i] == goal ) return i; - } - return -1; - } - - }; - -} - -#endif diff --git a/fggl/ecs/utility.hpp b/fggl/ecs/utility.hpp deleted file mode 100644 index 323074eded41677beae91209d01336d5b136b96c..0000000000000000000000000000000000000000 --- a/fggl/ecs/utility.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef FGGL_ECS_UTILITY_H -#define FGGL_ECS_UTILITY_H - -#include <cstdint> - -namespace fggl::ecs { - - using IDType = std::uint32_t; - using entity_t = IDType; - using component_type_t = IDType; - - constexpr IDType NULL_ENTITY = 0; - - template<class T> - class TypeIdGenerator { - private: - static IDType m_count; - public: - template<class U> - static const IDType GetNewID() { - static const IDType idCounter = m_count++; - return idCounter; - } - }; - template<class T> IDType TypeIdGenerator<T>::m_count = 0; -} - -#endif diff --git a/fggl/gfx/renderer.cpp b/fggl/gfx/renderer.cpp index d288dd30460730f1ccd721440fbf5e2b44b690d5..4f1b3c4208b496623b756c2acd1690be8be08bab 100644 --- a/fggl/gfx/renderer.cpp +++ b/fggl/gfx/renderer.cpp @@ -70,59 +70,6 @@ MeshRenderer::token_t MeshRenderer::upload(fggl::data::Mesh& mesh) { return token; } -void MeshRenderer::render(const Window& window, const fggl::ecs::ECS& ecs, const ecs::entity_t camera, float dt) { - if ( camera == ecs::NULL_ENTITY ) return; - - auto entities = ecs.getEntityWith<MeshRenderer::token_t>(); - if ( entities.size() == 0 ) return; - - total += dt; - - // make sure the correct rendering context is active - window.activate(); - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - glEnable(GL_DEPTH_TEST); - - // camera logic - const auto camTransform = ecs.getComponent<math::Transform>(camera); - const auto camComp = ecs.getComponent<gfx::Camera>(camera); - glm::mat4 proj = glm::perspective( camComp->fov, camComp->aspectRatio, camComp->nearPlane, camComp->farPlane); - glm::mat4 view = glm::lookAt( camTransform->origin(), camComp->target, camTransform->up() ); - - // lighting - glm::vec3 lightPos(20.0f, 20.0f, 15.0f); - - // TODO better performance if grouped by vao first - // TODO the nvidia performance presentation said I shouldn't use uniforms for large data - for ( auto& entity : entities ) { - const auto& transform = ecs.getComponent<fggl::math::Transform>(entity); - const auto& mesh = ecs.getComponent<MeshRenderer::token_t>(entity); - - glm::mat4 model = transform->model(); -// model = glm::rotate(model, glm::radians(total/2048.0f * 360.0f), glm::vec3(0.0f,1.0f,0.0f)); - - auto shader = mesh->pipeline; - glUseProgram( shader ); - - glUniformMatrix4fv( glGetUniformLocation(shader, "model"), 1, GL_FALSE, glm::value_ptr( model ) ); - glUniformMatrix4fv( glGetUniformLocation(shader, "view"), 1, GL_FALSE, glm::value_ptr( view ) ); - glUniformMatrix4fv( glGetUniformLocation(shader, "projection"), 1, GL_FALSE, glm::value_ptr( proj ) ); - - // lighting - GLint lightID = glGetUniformLocation(shader, "lightPos"); - if ( lightID != -1 ) - glUniform3fv( lightID, 1, glm::value_ptr( lightPos ) ); - - glBindVertexArray( mesh->vao ); - glDrawElements( GL_TRIANGLES, mesh->idxSize, GL_UNSIGNED_INT, reinterpret_cast<void*>(mesh->idxOffset) ); - } - - glBindVertexArray(0); - -} namespace fggl::systems { void fglPopulateVertex(flecs::iter& it, const fggl::components::Transform* t, fggl::components::GfxMat* mats) { diff --git a/fggl/gfx/renderer.hpp b/fggl/gfx/renderer.hpp index e606cf9769c4f6baa04a22fc0cbd692e3d881ac0..6f638c46a1afe9a3843815f3927ca66881d3a8b7 100644 --- a/fggl/gfx/renderer.hpp +++ b/fggl/gfx/renderer.hpp @@ -4,7 +4,6 @@ #include <vector> #include <fggl/data/model.hpp> -#include <fggl/ecs/ecs.hpp> #include <fggl/gfx/ogl.hpp> #include <fggl/gfx/ecs.hpp> @@ -18,7 +17,7 @@ namespace fggl::gfx { token_t upload(fggl::data::Mesh& mesh); // are VAO safe across opengl contexts? :S - void render(const Window& window, const ecs::ECS& ecs, const ecs::entity_t camera, float dt); + //void render(const Window& window, const ecs::ECS& ecs, const ecs::entity_t camera, float dt); float total; }; diff --git a/tests/testfggl/CMakeLists.txt b/tests/testfggl/CMakeLists.txt index d7015641f74cd37d57892e911c478a6797d05b12..3e2cad18775d3e9a510b9bfc14b27241c7c85b7a 100644 --- a/tests/testfggl/CMakeLists.txt +++ b/tests/testfggl/CMakeLists.txt @@ -3,7 +3,7 @@ enable_testing() add_executable( fggl_test # TestFggl.cpp - ecs/ecs.cpp + # ecs/ecs.cpp math/types.cpp ) target_include_directories(fggl_test PUBLIC ${PROJECT_BINARY_DIR}) diff --git a/tests/testfggl/ecs/ecs.cpp b/tests/testfggl/ecs/ecs.cpp deleted file mode 100644 index 4b2ffdbff3c2e19ef7f71c0931501608054453b4..0000000000000000000000000000000000000000 --- a/tests/testfggl/ecs/ecs.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include <gtest/gtest.h> -#include <gmock/gmock.h> - -#include <fggl/ecs/ecs.hpp> - -using fggl::ecs::Component; - -namespace { - - class DummyComp1 : public Component<DummyComp1> { - }; - - class DummyComp2 : public Component<DummyComp2> { - public: - int x; - DummyComp2(int tmp) : x(tmp) { - } - DummyComp2( DummyComp2&& other ) : x(other.x) {} - }; - - TEST(ecs, createEntity) { - fggl::ecs::ECS ecs; - auto eid = ecs.createEntity(); - - EXPECT_EQ( 1, eid ); - } - - TEST(ecs, addComponent) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - - auto eid = ecs.createEntity(); - ecs.addComponent<DummyComp1>(eid); - - EXPECT_EQ(1, eid); - EXPECT_EQ(true, ecs.hasComponent<DummyComp1>(eid)); - } - - TEST(ecs, addDuplicateComponent) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - - auto eid = ecs.createEntity(); - ecs.addComponent<DummyComp1>(eid); - EXPECT_EQ(true, ecs.hasComponent<DummyComp1>(eid)); - - auto dumpComp = ecs.addComponent<DummyComp1>(eid); - EXPECT_EQ(true, ecs.hasComponent<DummyComp1>(eid)); - EXPECT_EQ(nullptr, dumpComp); - } - - TEST(ecs, addTwoComponents) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - ecs.registerComponent<DummyComp2>(); - - auto eid = ecs.createEntity(); - auto comp = ecs.addComponent<DummyComp2>(eid, 0xB33F); - EXPECT_EQ(true, ecs.hasComponent<DummyComp2>(eid)); - EXPECT_EQ(0xB33F, comp->x); - - ecs.addComponent<DummyComp1>(eid); - EXPECT_EQ(true, ecs.hasComponent<DummyComp1>(eid)); - EXPECT_EQ(true, ecs.hasComponent<DummyComp2>(eid)); - // danger: comp is now invalid (dangling) ptr - - auto comp2 = ecs.getComponent<DummyComp2>(eid); - EXPECT_EQ(0xB33F, comp2->x); - } - - TEST(ecs, addComponentRemoveAdd) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - - auto eid = ecs.createEntity(); - ecs.addComponent<DummyComp1>(eid); - EXPECT_EQ(true, ecs.hasComponent<DummyComp1>(eid)); - - ecs.addComponent<DummyComp1>(eid); - EXPECT_EQ(true, ecs.hasComponent<DummyComp1>(eid)); - } - - TEST(ecs, hasComponentAddition) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - - auto e1 = ecs.createEntity(); - ecs.addComponent<DummyComp1>(e1); - - EXPECT_EQ(true, ecs.hasComponent<DummyComp1>(e1)); - } - - TEST(ecs, getWithComponentBlank) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - - auto ents = ecs.getEntityWith<DummyComp1>(); - - EXPECT_EQ( 0, ents.size() ); - } - - TEST(ecs, listWithOneComponent) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - ecs.registerComponent<DummyComp2>(); - - // has target component - auto ent1 = ecs.createEntity(); - ecs.addComponent<DummyComp1>(ent1); - - // has another component - auto ent2 = ecs.createEntity(); - ecs.addComponent<DummyComp2>(ent2, 0x4040); - - // has no components - auto ent3 = ecs.createEntity(); - - auto ents = ecs.getEntityWith<DummyComp1>(); - - EXPECT_EQ( 1, ents.size() ); - EXPECT_EQ( ent1, ents[0] ); - } - - TEST(ecs, listWithTwoComponents) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - ecs.registerComponent<DummyComp2>(); - - // has no components - auto ent0 = ecs.createEntity(); - - // has target component - auto ent1 = ecs.createEntity(); - ecs.addComponent<DummyComp1>(ent1); - - // has target + another component - auto ent2 = ecs.createEntity(); - ecs.addComponent<DummyComp1>(ent2); - ecs.addComponent<DummyComp2>(ent2, 0x8080); - - auto ents = ecs.getEntityWith<DummyComp1>(); - - EXPECT_EQ( 2, ents.size() ); - EXPECT_THAT( ents, testing::Contains( ent1 ) ); - EXPECT_THAT( ents, testing::Contains( ent2 ) ); - } - - TEST(ecs, addComponentCurruption) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - ecs.registerComponent<DummyComp2>(); - - // create the two ents first - auto ent1 = ecs.createEntity(); - auto ent2 = ecs.createEntity(); - - // add comp2 in a way that forces reallocation - // this should force a shuffle on comp1's records - ecs.addComponent<DummyComp2>(ent2, 0x1313); - ecs.addComponent<DummyComp2>(ent1, 0x4242); - - // check the values survived insersion - auto dc2 = ecs.getComponent<DummyComp2>(ent2); - auto dc1 = ecs.getComponent<DummyComp2>(ent1); - EXPECT_EQ( 0x1313, dc2->x ); - EXPECT_EQ( 0x4242, dc1->x ); - - // add comp1 to force more shuffling (ent2 should be first in list, forcing a swap) - ecs.addComponent<DummyComp1>(ent2); - - dc2 = ecs.getComponent<DummyComp2>(ent2); - dc1 = ecs.getComponent<DummyComp2>(ent1); - EXPECT_EQ( 0x1313, dc2->x ); - EXPECT_EQ( 0x4242, dc1->x ); - } - - - TEST(ecs, hasComponentRemoval) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - - auto e1 = ecs.createEntity(); - ecs.addComponent<DummyComp1>(e1); - EXPECT_EQ(true, ecs.hasComponent<DummyComp1>(e1)); - - //TODO implement removal - //ecs.removeComponent<DummyComp1>(e1); - //EXPECT_EQ(false, ecs.hasComponent<DummyComp1>(e1)); - } - - TEST(ecs, hasComponentEmpty) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - - auto e1 = ecs.createEntity(); - EXPECT_EQ(false, ecs.hasComponent<DummyComp1>(e1)); - } - - - TEST(ecs, hasComponentOther) { - fggl::ecs::ECS ecs; - ecs.registerComponent<DummyComp1>(); - - auto e1 = ecs.createEntity(); - auto e2 = ecs.createEntity(); - - ecs.addComponent<DummyComp1>(e1); - - EXPECT_EQ(true, ecs.hasComponent<DummyComp1>(e1)); - EXPECT_EQ(false, ecs.hasComponent<DummyComp1>(e2)); - } - -}