basic ecs functions.

main
bog 2023-10-30 21:25:40 +01:00
parent 49aa9be3dd
commit a012aff7f8
12 changed files with 425 additions and 1 deletions

View File

@ -29,6 +29,11 @@ executable('d2d',
'src/Game.cpp', 'src/Game.cpp',
'src/Scene.cpp', 'src/Scene.cpp',
# ecs
'src/ECS.cpp',
'src/BaseComponent.cpp',
'src/BaseSystem.cpp',
# graphics # graphics
'src/Shader.cpp', 'src/Shader.cpp',
'src/Shape.cpp', 'src/Shape.cpp',

13
src/BaseComponent.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "BaseComponent.hpp"
namespace d2
{
/*explicit*/ BaseComponent::BaseComponent(size_t id)
: m_id { id }
{
}
/*virtual*/ BaseComponent::~BaseComponent()
{
}
}

21
src/BaseComponent.hpp Normal file
View File

@ -0,0 +1,21 @@
#ifndef d2_BASECOMPONENT_HPP
#define d2_BASECOMPONENT_HPP
#include "commons.hpp"
namespace d2
{
class BaseComponent
{
public:
explicit BaseComponent(size_t id);
virtual ~BaseComponent();
size_t id() const { return m_id; }
private:
size_t m_id;
};
}
#endif

19
src/BaseSystem.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "BaseSystem.hpp"
namespace d2
{
/*explicit*/ BaseSystem::BaseSystem(size_t id)
: m_id { id }
{
}
/*virtual*/ BaseSystem::~BaseSystem()
{
}
size_t BaseSystem::add_signature(BaseComponent const& component)
{
m_signature |= (1 << component.id());
return m_signature;
}
}

25
src/BaseSystem.hpp Normal file
View File

@ -0,0 +1,25 @@
#ifndef d2_BASESYSTEM_HPP
#define d2_BASESYSTEM_HPP
#include "commons.hpp"
#include "src/BaseComponent.hpp"
namespace d2
{
class BaseSystem
{
public:
explicit BaseSystem(size_t id);
virtual ~BaseSystem();
size_t id() const { return m_id; }
size_t signature() const { return m_signature; }
size_t add_signature(BaseComponent const& component);
private:
size_t m_id = 0;
size_t m_signature = 0;
};
}
#endif

59
src/ECS.cpp Normal file
View File

@ -0,0 +1,59 @@
#include "ECS.hpp"
namespace d2
{
/*explicit*/ ECS::ECS()
{
}
/*virtual*/ ECS::~ECS()
{
}
size_t ECS::spawn(std::vector<std::unique_ptr<BaseComponent>>& components)
{
size_t entity = m_entity_counter;
for (auto& component: components)
{
m_entities[entity][component->id()] = std::move(component);
}
m_entity_counter++;
return entity;
}
BaseComponent const& ECS::getc(size_t entity,
size_t component) const
{
return *m_entities.at(entity).at(component).get();
}
BaseComponent& ECS::getc(size_t entity,
size_t component)
{
return *m_entities.at(entity).at(component).get();
}
size_t ECS::esig(size_t entity) const
{
size_t sig = 0;
for (size_t i=0; i<MAX_COMPONENTS; i++)
{
if (m_entities[entity][i] != nullptr)
{
sig |= (1 << m_entities[entity][i]->id());
}
}
return sig;
}
bool ECS::sig_match(size_t system_signature,
size_t entity_signature) const
{
return (system_signature & entity_signature) == system_signature;
}
}

40
src/ECS.hpp Normal file
View File

@ -0,0 +1,40 @@
#ifndef d2_ECS_HPP
#define d2_ECS_HPP
#include "commons.hpp"
#include "BaseComponent.hpp"
namespace d2
{
constexpr size_t MAX_ENTITIES = 1024;
constexpr size_t MAX_COMPONENTS = 256;
class ECS
{
public:
explicit ECS();
virtual ~ECS();
size_t spawn(std::vector<std::unique_ptr<BaseComponent>>& components);
size_t entity_count() const { return m_entity_counter; }
BaseComponent const& getc(size_t entity,
size_t component) const;
BaseComponent& getc(size_t entity,
size_t component);
size_t esig(size_t entity) const;
bool sig_match(size_t system_signature,
size_t entity_signature) const;
private:
size_t m_entity_counter = 0;
std::array<std::array<std::unique_ptr<BaseComponent>,
MAX_COMPONENTS>,
MAX_ENTITIES> m_entities;
};
}
#endif

View File

@ -179,5 +179,4 @@ namespace d2
SDL_GetMouseState(nullptr, &y); SDL_GetMouseState(nullptr, &y);
return y; return y;
} }
} }

View File

@ -5,6 +5,7 @@
#include "commons.hpp" #include "commons.hpp"
#include "Renderer.hpp" #include "Renderer.hpp"
#include "Scene.hpp" #include "Scene.hpp"
#include "ECS.hpp"
namespace d2 namespace d2
{ {
@ -18,6 +19,9 @@ namespace d2
unsigned height); unsigned height);
virtual ~Game(); virtual ~Game();
ECS const& ecs() const { return m_ecs; }
ECS& ecs() { return m_ecs; }
void run(); void run();
void queue_draw(std::unique_ptr<Object> obj); void queue_draw(std::unique_ptr<Object> obj);
@ -39,6 +43,7 @@ namespace d2
std::unique_ptr<Renderer> m_renderer; std::unique_ptr<Renderer> m_renderer;
std::vector<std::unique_ptr<Object>> m_draw_queue; std::vector<std::unique_ptr<Object>> m_draw_queue;
std::vector<std::unique_ptr<Scene>> m_scenes; std::vector<std::unique_ptr<Scene>> m_scenes;
ECS m_ecs;
}; };
template <typename T, typename... Args> template <typename T, typename... Args>

View File

@ -1,10 +1,27 @@
#include "Script.hpp" #include "Script.hpp"
#include "libguile/list.h"
#include <memory>
namespace d2 namespace d2
{ {
// Script
// ------
/*static*/ std::unique_ptr<Game> Script::game = nullptr; /*static*/ std::unique_ptr<Game> Script::game = nullptr;
/*static*/ glm::vec4 Script::color = {0.0f, 0.0f, 0.0f, 255.0f}; /*static*/ glm::vec4 Script::color = {0.0f, 0.0f, 0.0f, 255.0f};
// ECS
// ----
/*static*/ std::unordered_map<size_t, std::unique_ptr<ScriptComponent>>
ScriptComponent::components;
/*static*/ size_t ScriptSystem::system_id = 0;
/*static*/ std::unordered_map<size_t, std::unique_ptr<ScriptSystem>>
ScriptSystem::systems;
/*static*/ size_t ScriptComponent::comp_id = 0;
/*static*/ void Script::init() /*static*/ void Script::init()
{ {
scm_init_guile(); scm_init_guile();
@ -41,6 +58,27 @@ namespace d2
scm_c_define_gsubr("rootdir", 0, 0, 1, scm_c_define_gsubr("rootdir", 0, 0, 1,
reinterpret_cast<void*>(&fn_rootdir)); reinterpret_cast<void*>(&fn_rootdir));
// ECS
// ----------
scm_c_define_gsubr("world:system", 0, 0, 1,
reinterpret_cast<void*>(&fn_world__system));
scm_c_define_gsubr("world:component", 0, 0, 1,
reinterpret_cast<void*>(&fn_world__component));
scm_c_define_gsubr("world:spawn", 0, 0, 1,
reinterpret_cast<void*>(&fn_world__spawn));
scm_c_define_gsubr("world:esig", 1, 0, 0,
reinterpret_cast<void*>(&fn_world__esig));
scm_c_define_gsubr("world:ssig", 1, 0, 0,
reinterpret_cast<void*>(&fn_world__ssig));
scm_c_define_gsubr("world:getc", 3, 0, 0,
reinterpret_cast<void*>(&fn_world__getc));
scm_c_define_gsubr("world:setc!", 4, 0, 0,
reinterpret_cast<void*>(&fn_world__setc));
scm_c_define_gsubr("world:match?", 2, 0, 0,
reinterpret_cast<void*>(&fn_world__match));
scm_c_define_gsubr("world:process", 1, 0, 0,
reinterpret_cast<void*>(&fn_world__process));
} }
/*static*/ void Script::terminate() /*static*/ void Script::terminate()
@ -202,4 +240,149 @@ namespace d2
return scm_from_locale_string(path.string().c_str()); return scm_from_locale_string(path.string().c_str());
} }
/*static*/ SCM Script::fn_world__system(SCM scm_args)
{
size_t len = scm_to_uint64(scm_length(scm_args));
SCM function = scm_list_ref(scm_args, scm_from_uint64(0));
auto system = std::make_unique<ScriptSystem>(function);
for (size_t i=1; i<len; i++)
{
SCM scm_value = scm_list_ref(scm_args, scm_from_uint64(i));
size_t value = scm_to_uint64(scm_value);
system->add_signature(*ScriptComponent::components[value]);
}
size_t id = system->id();
ScriptSystem::systems[id] = std::move(system);
return scm_from_uint64(id);
}
/*static*/ SCM Script::fn_world__component(SCM scm_args)
{
auto component =
std::make_unique<ScriptComponent>(ScriptComponent::comp_id);
ScriptComponent::comp_id++;
size_t len = scm_to_uint64(scm_length(scm_args));
for (size_t i=0; i<len; i+=2)
{
SCM scm_name =
scm_symbol_to_string(scm_list_ref(scm_args, scm_from_uint64(i)));
std::string name = scm_to_locale_string(scm_name);
SCM value = scm_list_ref(scm_args, scm_from_uint64(i + 1));
component->add(name, value);
}
size_t id = component->id();
ScriptComponent::components[id] = std::move(component);
return scm_from_uint64(id);
}
/*static*/ SCM Script::fn_world__spawn(SCM scm_args)
{
size_t len = scm_to_uint64(scm_length(scm_args));
std::vector<std::unique_ptr<BaseComponent>> comps;
for (size_t i=0; i<len; i++)
{
SCM comp_scm = scm_list_ref(scm_args, scm_from_uint64(i));
size_t comp = scm_to_uint64(comp_scm);
auto& model = ScriptComponent::components[comp];
comps.push_back(std::make_unique<ScriptComponent>(*model));
}
size_t entity = Script::game->ecs().spawn(comps);
return scm_from_uint64(entity);
}
/*static*/ SCM Script::fn_world__esig(SCM scm_entity)
{
size_t entity = scm_to_uint64(scm_entity);
size_t sig = Script::game->ecs().esig(entity);
return scm_from_uint64(sig);
}
/*static*/ SCM Script::fn_world__ssig(SCM scm_system)
{
size_t id = scm_to_uint64(scm_system);
auto const& system = ScriptSystem::systems[id];
size_t signature = system->signature();
return scm_from_uint64(signature);
}
/*static*/ SCM
Script::fn_world__getc(SCM scm_entity, SCM scm_comp, SCM scm_field)
{
size_t entity = scm_to_uint64(scm_entity);
size_t comp = scm_to_uint64(scm_comp);
std::string field =
scm_to_locale_string(scm_symbol_to_string(scm_field));
auto const& component = Script::game->ecs().getc(entity, comp);
auto& script_comp = static_cast<ScriptComponent const&>(component);
if (script_comp.values.find(field) == std::end(script_comp.values))
{
throw script_error {"Cannot find field '" + field + "'"};
}
return script_comp.values.at(field);
}
/*static*/ SCM
Script::fn_world__setc(SCM scm_entity, SCM scm_comp, SCM scm_field,
SCM scm_value)
{
size_t entity = scm_to_uint64(scm_entity);
size_t comp = scm_to_uint64(scm_comp);
std::string field =
scm_to_locale_string(scm_symbol_to_string(scm_field));
auto& component = Script::game->ecs().getc(entity, comp);
auto& script_comp = static_cast<ScriptComponent&>(component);
script_comp.values[field] = scm_value;
return scm_value;
}
/*static*/ SCM Script::fn_world__match(SCM scm_system, SCM scm_entity)
{
size_t sys_sig =
ScriptSystem::systems[scm_to_uint64(scm_system)]->signature();
size_t entity_sig =
Script::game->ecs().esig(scm_to_uint64(scm_entity));
return
scm_from_bool(Script::game->ecs().sig_match(sys_sig, entity_sig));
}
/*static*/ SCM Script::fn_world__process(SCM scm_system)
{
auto const& system = ScriptSystem::systems[scm_to_uint64(scm_system)];
SCM function = system->function;
size_t entities = Script::game->ecs().entity_count();
for (size_t i=0; i<entities; i++)
{
if (Script::game->ecs().sig_match(system->signature(),
Script::game->ecs().esig(i)))
{
scm_call_1(function, scm_from_uint64(i));
}
}
return SCM_BOOL_T;
}
} }

View File

@ -4,6 +4,8 @@
#include "commons.hpp" #include "commons.hpp"
#include "Game.hpp" #include "Game.hpp"
#include "BaseComponent.hpp"
#include "BaseSystem.hpp"
#include "libguile/numbers.h" #include "libguile/numbers.h"
namespace d2 namespace d2
@ -31,6 +33,17 @@ namespace d2
static SCM fn_rootdir(SCM scm_args); static SCM fn_rootdir(SCM scm_args);
static SCM fn_world__system(SCM scm_args);
static SCM fn_world__component(SCM scm_args);
static SCM fn_world__spawn(SCM scm_args);
static SCM fn_world__esig(SCM scm_entity);
static SCM fn_world__ssig(SCM scm_system);
static SCM fn_world__getc(SCM scm_entity, SCM scm_comp, SCM scm_field);
static SCM fn_world__setc(SCM scm_entity, SCM scm_comp,
SCM scm_field, SCM scm_value);
static SCM fn_world__match(SCM scm_system, SCM scm_entity);
static SCM fn_world__process(SCM scm_system);
private: private:
static std::unique_ptr<Game> game; static std::unique_ptr<Game> game;
static glm::vec4 color; static glm::vec4 color;
@ -56,6 +69,45 @@ namespace d2
{ {
} }
}; };
struct ScriptComponent: public BaseComponent
{
static size_t comp_id;
static std::unordered_map<size_t, std::unique_ptr<ScriptComponent>>
components;
std::unordered_map<std::string, SCM> values;
explicit ScriptComponent(ScriptComponent const& other)
: BaseComponent(other.id())
, values { other.values }
{
}
explicit ScriptComponent(size_t id)
: BaseComponent(id)
{
}
void add(std::string const& name, SCM scm_value)
{
values.insert({name, scm_value});
}
};
struct ScriptSystem: public BaseSystem
{
static size_t system_id;
static std::unordered_map<size_t, std::unique_ptr<ScriptSystem>>
systems;
SCM function;
explicit ScriptSystem(SCM p_function)
: BaseSystem(ScriptSystem::system_id++)
, function { p_function }
{
}
};
} }
#endif #endif

View File

@ -7,6 +7,9 @@
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
#include <vector> #include <vector>
#include <array>
#include <memory>
#include <unordered_map>
#include "conf.hpp" #include "conf.hpp"