Compare commits

..

4 Commits

Author SHA1 Message Date
bog a012aff7f8 basic ecs functions. 2023-10-30 21:25:40 +01:00
bog 49aa9be3dd 🐛 fix mouse y position. 2023-10-29 23:55:22 +01:00
bog aacc650741 rootdir function. 2023-10-29 23:44:19 +01:00
bog 1b72ba842c pressed?, just-pressed and mouse-x, mouse-y functions. 2023-10-29 23:35:26 +01:00
12 changed files with 629 additions and 26 deletions

View File

@ -29,6 +29,11 @@ executable('d2d',
'src/Game.cpp',
'src/Scene.cpp',
# ecs
'src/ECS.cpp',
'src/BaseComponent.cpp',
'src/BaseSystem.cpp',
# graphics
'src/Shader.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

@ -1,5 +1,7 @@
#include <GL/glew.h>
#include "Game.hpp"
#include "SDL_events.h"
#include "SDL_keyboard.h"
#include <glm/glm.hpp>
namespace d2
@ -105,4 +107,76 @@ namespace d2
m_scenes.pop_back();
}
}
bool Game::is_key_pressed(std::string const& keyname) const
{
SDL_Keycode key = SDL_GetKeyFromName(keyname.c_str());
SDL_Scancode scan = SDL_GetScancodeFromKey(key);
Uint8 const* keys = SDL_GetKeyboardState(nullptr);
return keys[scan];
}
bool Game::is_key_just_pressed(std::string const& keyname) const
{
static bool prev = false;
bool pressed = is_key_pressed(keyname);
if (pressed != prev)
{
prev = pressed;
return pressed;
}
return false;
}
bool Game::is_mouse_pressed(std::string const& btn_name) const
{
Uint32 btns = SDL_GetMouseState(nullptr, nullptr);
if (btn_name == "left")
{
return btns & SDL_BUTTON(SDL_BUTTON_LEFT);
}
else if (btn_name == "middle")
{
return btns & SDL_BUTTON(SDL_BUTTON_MIDDLE);
}
else if (btn_name == "right")
{
return btns & SDL_BUTTON(SDL_BUTTON_RIGHT);
}
throw game_error {"Unknown mosue button '" + btn_name + "'."};
}
bool Game::is_mouse_just_pressed(std::string const& btn_name) const
{
static bool prev = false;
bool new_val = is_mouse_pressed(btn_name);
if (new_val != prev)
{
prev = new_val;
return new_val;
}
return false;
}
int Game::mouse_x() const
{
int x;
SDL_GetMouseState(&x, nullptr);
return x;
}
int Game::mouse_y() const
{
int y;
SDL_GetMouseState(nullptr, &y);
return y;
}
}

View File

@ -5,6 +5,7 @@
#include "commons.hpp"
#include "Renderer.hpp"
#include "Scene.hpp"
#include "ECS.hpp"
namespace d2
{
@ -18,6 +19,9 @@ namespace d2
unsigned height);
virtual ~Game();
ECS const& ecs() const { return m_ecs; }
ECS& ecs() { return m_ecs; }
void run();
void queue_draw(std::unique_ptr<Object> obj);
@ -25,12 +29,21 @@ namespace d2
void push(Args... args);
void pop();
bool is_key_pressed(std::string const& keyname) const;
bool is_key_just_pressed(std::string const& keyname) const;
bool is_mouse_pressed(std::string const& btn_name) const;
bool is_mouse_just_pressed(std::string const& btn_name) const;
int mouse_x() const;
int mouse_y() const;
private:
SDL_Window* m_window = nullptr;
SDL_GLContext m_context;
std::unique_ptr<Renderer> m_renderer;
std::vector<std::unique_ptr<Object>> m_draw_queue;
std::vector<std::unique_ptr<Scene>> m_scenes;
ECS m_ecs;
};
template <typename T, typename... Args>

View File

@ -1,15 +1,37 @@
#include "Script.hpp"
#include "libguile/list.h"
#include <memory>
namespace d2
{
// Script
// ------
/*static*/ std::unique_ptr<Game> Script::game = nullptr;
/*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()
{
scm_init_guile();
scm_c_define_gsubr("winconf", 3, 0, 0,
reinterpret_cast<void*>(&fn_winconf));
// Drawing
// -------
scm_c_define_gsubr("rect", 4, 0, 0, reinterpret_cast<void*>(&fn_rect));
scm_c_define_gsubr("color!", 4, 0, 0,
reinterpret_cast<void*>(&fn_set_color));
// Scenes
// ------
@ -19,11 +41,44 @@ namespace d2
scm_c_define_gsubr("scene:pop", 0, 0, 0,
reinterpret_cast<void*>(&fn_scene__pop));
// Drawing
// -------
scm_c_define_gsubr("rect", 4, 0, 0, reinterpret_cast<void*>(&fn_rect));
scm_c_define_gsubr("color!", 4, 0, 0,
reinterpret_cast<void*>(&fn_set_color));
// Inputs
// ------
scm_c_define_gsubr("pressed?", 2, 0, 0,
reinterpret_cast<void*>(&fn_is_pressed));
scm_c_define_gsubr("just-pressed?", 2, 0, 0,
reinterpret_cast<void*>(&fn_is_just_pressed));
scm_c_define_gsubr("mouse-x", 0, 0, 0,
reinterpret_cast<void*>(&fn_mouse_x));
scm_c_define_gsubr("mouse-y", 0, 0, 0,
reinterpret_cast<void*>(&fn_mouse_y));
// Filesystem
// ----------
scm_c_define_gsubr("rootdir", 0, 0, 1,
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()
@ -56,27 +111,7 @@ namespace d2
return SCM_BOOL_T;
}
/*static*/ SCM Script::fn_scene__push(SCM scm_function, SCM scm_ctx)
{
if (scm_is_null(scm_ctx))
{
Script::game->push<ScriptScene>(scm_function);
}
else
{
Script::game->push<ScriptScene>(scm_function, scm_ctx);
}
return SCM_BOOL_T;
}
/*static*/ SCM Script::fn_scene__pop()
{
Script::game->pop();
return SCM_BOOL_T;
}
/*static*/ SCM Script::fn_rect(SCM scm_x, SCM scm_y, SCM scm_w, SCM scm_h)
/*static*/ SCM Script::fn_rect(SCM scm_x, SCM scm_y, SCM scm_w, SCM scm_h)
{
float x = scm_to_double(scm_x);
float y = scm_to_double(scm_y);
@ -116,4 +151,238 @@ namespace d2
return SCM_BOOL_T;
}
/*static*/ SCM Script::fn_scene__push(SCM scm_function, SCM scm_ctx)
{
if (scm_is_null(scm_ctx))
{
Script::game->push<ScriptScene>(scm_function);
}
else
{
Script::game->push<ScriptScene>(scm_function, scm_ctx);
}
return SCM_BOOL_T;
}
/*static*/ SCM Script::fn_scene__pop()
{
Script::game->pop();
return SCM_BOOL_T;
}
/*static*/ SCM Script::fn_is_pressed(SCM scm_device, SCM scm_keyname)
{
std::string device =
scm_to_locale_string(scm_symbol_to_string(scm_device));
std::string keyname = scm_to_locale_string(scm_keyname);
if (device == "key")
{
bool result = Script::game->is_key_pressed(keyname);
return scm_from_bool(result);
}
if (device == "mouse")
{
bool result = Script::game->is_mouse_pressed(keyname);
return scm_from_bool(result);
}
throw script_error {"Unknown device '" + device + "'."};
}
/*static*/ SCM Script::fn_is_just_pressed(SCM scm_device, SCM scm_keyname)
{
std::string device =
scm_to_locale_string(scm_symbol_to_string(scm_device));
std::string keyname = scm_to_locale_string(scm_keyname);
if (device == "key")
{
bool result = Script::game->is_key_just_pressed(keyname);
return scm_from_bool(result);
}
if (device == "mouse")
{
bool result = Script::game->is_mouse_just_pressed(keyname);
return scm_from_bool(result);
}
throw script_error {"Unknown device '" + device + "'."};
}
/*static*/ SCM Script::fn_mouse_x()
{
return scm_from_int32(Script::game->mouse_x());
}
/*static*/ SCM Script::fn_mouse_y()
{
return scm_from_int32(Script::game->mouse_y());
}
/*static*/ SCM Script::fn_rootdir(SCM scm_args)
{
std::filesystem::path path =
std::filesystem::current_path();
size_t sz = scm_to_int32(scm_length(scm_args));
for (size_t i=0; i<sz; i++)
{
SCM scm_value = scm_list_ref(scm_args, scm_from_uint64(i));
auto value = scm_to_locale_string(scm_value);
path = path / value;
}
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,10 +4,14 @@
#include "commons.hpp"
#include "Game.hpp"
#include "BaseComponent.hpp"
#include "BaseSystem.hpp"
#include "libguile/numbers.h"
namespace d2
{
D2_ERROR(script_error);
struct Script
{
static void init();
@ -21,6 +25,25 @@ namespace d2
static SCM fn_scene__push(SCM scm_function, SCM scm_ctx);
static SCM fn_scene__pop();
static SCM fn_is_pressed(SCM scm_device, SCM scm_keyname);
static SCM fn_is_just_pressed(SCM scm_device, SCM scm_keyname);
static SCM fn_mouse_x();
static SCM fn_mouse_y();
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:
static std::unique_ptr<Game> game;
static glm::vec4 color;
@ -46,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

View File

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