diff --git a/examples/dev_testing/src/main.cpp b/examples/dev_testing/src/main.cpp index aea3b40..f704383 100644 --- a/examples/dev_testing/src/main.cpp +++ b/examples/dev_testing/src/main.cpp @@ -11,6 +11,7 @@ #include "simpleengine/gfx/model.h" #include "simpleengine/gfx/renderer.h" #include "simpleengine/gfx/texture.h" +#include "simpleengine/scripting/scripting_system.h" #include "simpleengine/vector.h" #include #include @@ -29,9 +30,6 @@ #include #include -#include -#include - #include "entt/entity/fwd.hpp" #include @@ -47,215 +45,25 @@ #include #include #include -#include #include namespace se = simpleengine; -#define AUTO_ARG(x) decltype(x), x - -class LuaTestScriptEvent : public se::Renderable { -public: - sol::state lua; - sol::protected_function_result script_res; - entt::registry registry; - - static void lua_panic_handler(sol::optional maybe_msg) { - std::cerr << "Lua panic: "; - if(maybe_msg) { - std::cerr << maybe_msg->c_str(); - } - std::cerr << std::endl; - } - - static int lua_exception_handler(lua_State* L, sol::optional maybe_exception, sol::string_view description) { - std::cerr << "Lua Exception: "; - if(maybe_exception) { - const std::exception& ex = *maybe_exception; - std::cerr << ex.what(); - } else { - std::cerr.write(description.data(), description.size()); - } - - std::cerr << std::endl; - - return sol::stack::push(L, description); - } - - static std::string lua_error_handler(lua_State* lstate, std::string msg) { - std::cerr << "Lua error: " << msg << std::endl; - return msg; - } - - static sol::protected_function_result lua_protected_function_handler(lua_State* lstate, sol::protected_function_result result) { - std::cerr << "Lua protected function error" << std::endl; - - sol::type t = sol::type_of(lstate, result.stack_index()); - std::string err = "sol: "; - err += to_string(result.status()); - err += " error"; - - std::exception_ptr eptr = std::current_exception(); - if (eptr) { - err += " with a "; - try { - std::rethrow_exception(eptr); - } - catch (const std::exception& ex) { - err += "std::exception -- "; - err.append(ex.what()); - } - catch (const std::string& message) { - err += "thrown message -- "; - err.append(message); - } - catch (const char* message) { - err += "thrown message -- "; - err.append(message); - } - catch (...) { - err.append("thrown but unknown type, cannot serialize into error message"); - } - } - - if (t == sol::type::string) { - err += ": "; - std::string_view serr = sol::stack::unqualified_get(lstate, result.stack_index()); - err.append(serr.data(), serr.size()); - } - - std::cerr << err << std::endl; - /* sol::state_view lua(lstate); - std::string traceback = lua.script("debug.traceback()"); - std::cout << "Traceback: " << traceback << std::endl; */ - - return result; - } - - LuaTestScriptEvent() : se::Renderable(), lua(sol::c_call), registry({})/* , script_res(std::nullopt_t) */ { - register_meta_component(); - - lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::string, sol::lib::debug); - lua.require("registry", sol::c_call, false); // create registry type - // Overwrite the Lua print function to use a logger - lua.globals().set_function("print", [](std::string msg) { - SE_DEBUG("Lua ScriptingEngine", "{}", msg); - }); - - // Make registry available to lua - lua["registry"] = std::ref(this->registry); - - // Registry TransformComponent - lua.new_usertype("TransformComponent", - "type_id", &entt::type_hash::value, - - sol::call_constructor, sol::factories( - [](float px, float py, float pz) { - return se::ecs::TransformComponent(glm::vec3(px, py, pz)); - }, - [](float px, float py, float pz, float rx, float ry, float rz) { - return se::ecs::TransformComponent(glm::vec3(px, py, pz), glm::vec3(rx, ry, rz)); - }, - [](float px, float py, float pz, float rx, float ry, float rz, float sx, float sy, float sz) { - return se::ecs::TransformComponent(glm::vec3(px, py, pz), glm::vec3(rx, ry, rz), glm::vec3(sx, sy, sz)); - } - ), - - sol::meta_function::to_string, [](const se::ecs::TransformComponent& self) { - return glm::to_string(self.transform_matrix); - } //&Transform::to_string - ); - - lua.set_exception_handler(&LuaTestScriptEvent::lua_exception_handler); - - try { - script_res = lua.safe_script(R"LUA( - print('start') - local dog = registry:create() - local cat = registry:create() - print('created cat and cat') - - print('Dog is ' .. dog .. ', and registry size is ' .. registry:size()) - print('Cat is ' .. cat .. ', and cat size is ' .. registry:size()) - - assert(dog == 0 and cat == 1 and registry:size() == 2) - - registry:emplace(dog, TransformComponent(5, 6, 3)) - - assert(registry:has(dog, TransformComponent)) - assert(registry:has(dog, TransformComponent.type_id())) - - assert(not registry:any_of(dog, -1, -2, -3)) - - function update(delta_time) - transform = registry:get(dog, TransformComponent) - print('Dog position = ' .. tostring(transform)) - end - - print('Lua script loaded!') - )LUA", &LuaTestScriptEvent::lua_protected_function_handler); - } catch (sol::error e) { - std::cerr << "Ran into sol2 error: " << e.what() << std::endl; - } catch (std::exception e) { - std::cerr << "Ran into std::exception: " << e.what() << std::endl; - } catch (...) { - std::cerr << "Ran into something :shrug:" << std::endl; - } - } - - virtual void update(const float &delta_time) { - if (script_res.valid()) { - sol::function lua_update = lua["update"]; - - if (lua_update.valid()) { - lua_update(delta_time); - } - } - } - - virtual void input_update(const float &delta_time) { - if (script_res.valid()) { - sol::function lua_input_update = lua["input_update"]; - - if (lua_input_update.valid()) { - lua_input_update(delta_time); - } - } - } - - virtual void render(const float& interpolate_alpha, const float& frame_time) { - if (script_res.valid()) { - sol::function lua_render = lua["render"]; - - if (lua_render.valid()) { - lua_render(interpolate_alpha, frame_time); - } - } - } -}; - class FPSCounterEvent : public se::Renderable { public: double last_frame_time_input; - int frame_count_input; + int frame_count_input = 0; double last_frame_time_tps; - int frame_count_tps; + int frame_count_tps = 0; double last_frame_time_render; - int frame_count_render; + int frame_count_render = 0; - FPSCounterEvent() : se::Renderable() { - last_frame_time_input = glfwGetTime(); - frame_count_input = 0; - - last_frame_time_tps = glfwGetTime(); - frame_count_tps = 0; - - last_frame_time_render = glfwGetTime(); - frame_count_render = 0; + FPSCounterEvent() : se::Renderable(), last_frame_time_input(glfwGetTime()), + last_frame_time_tps(glfwGetTime()), last_frame_time_render(glfwGetTime()) { + } virtual void update(const float &delta_time) { @@ -364,8 +172,9 @@ int main(int argc, char *argv[]) { auto fps_counter = std::make_shared(); game.add_renderable(fps_counter); - auto lua_script = std::make_shared(); - game.add_renderable(lua_script); + auto script_sys = std::make_shared(registry); + script_sys->add_lua_engine(); + game.add_renderable(script_sys); /* game.set_enable_vsync(false); game.set_fps_limit(100); */ diff --git a/include/simpleengine/ecs/component/transform_component.h b/include/simpleengine/ecs/component/transform_component.h index 5f5f00f..5dcb283 100644 --- a/include/simpleengine/ecs/component/transform_component.h +++ b/include/simpleengine/ecs/component/transform_component.h @@ -125,10 +125,16 @@ namespace simpleengine::ecs { return rotation_matrix(degrees, glm::vec3(0, 0, 1)); } - virtual void rotate(float degrees, glm::vec3 rotation_axis) { + virtual void rotate_axis(float degrees, glm::vec3 rotation_axis) { transform_matrix = rotation_matrix(degrees, rotation_axis); } + virtual void rotate(float x_degrees, float y_degrees, float z_degrees) { + transform_matrix = rotation_x_matrix(x_degrees); + transform_matrix = rotation_y_matrix(y_degrees); + transform_matrix = rotation_z_matrix(z_degrees); + } + virtual void rotate_x(float degrees) { transform_matrix = rotation_x_matrix(degrees); } diff --git a/include/simpleengine/scripting/ecs_bindings.h b/include/simpleengine/scripting/ecs_bindings.h deleted file mode 100644 index a85886d..0000000 --- a/include/simpleengine/scripting/ecs_bindings.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once - -#include "entt_meta_helper.h" - -#include -#include -#include - -#include - -#include - -template auto is_valid(const entt::registry *registry, entt::entity entity) { - assert(registry); - return registry->valid(entity); -} -template -auto emplace_component(entt::registry *registry, entt::entity entity, const sol::table& instance, sol::this_state s) { - assert(registry); - auto& comp = - registry->emplace_or_replace(entity, instance.valid() ? instance.as() : Component{}); - return sol::make_reference(s, std::ref(comp)); -} -template auto get_component(entt::registry *registry, entt::entity entity, sol::this_state s) { - assert(registry); - auto& comp = registry->get_or_emplace(entity); - return sol::make_reference(s, std::ref(comp)); -} -template bool has_component(entt::registry *registry, entt::entity entity) { - assert(registry); - return registry->any_of(entity); -} -template auto remove_component(entt::registry *registry, entt::entity entity) { - assert(registry); - return registry->remove(entity); -} -template void clear_component(entt::registry *registry) { - assert(registry); - registry->clear(); -} - -template void register_meta_component() { - using namespace entt::literals; - - entt::meta() - .type() - .template func<&is_valid>("valid"_hs) - .template func<&emplace_component>("emplace"_hs) - .template func<&get_component>("get"_hs) - .template func<&has_component>("has"_hs) - .template func<&clear_component>("clear"_hs) - .template func<&remove_component>("remove"_hs); -} - -auto collect_types(const sol::variadic_args& va) { - std::set types; - std::transform(va.cbegin(), va.cend(), std::inserter(types, types.begin()), - [](const auto& obj) { return deduce_type(obj); }); - return types; -} - -sol::table open_registry(sol::this_state s) { - // To create a registry inside a script: entt.registry.new() - - sol::state_view lua{s}; - auto entt_module = lua["entt"].get_or_create(); - - entt_module.new_usertype("runtime_view", sol::no_constructor, - "size_hint", &entt::runtime_view::size_hint, "contains", - &entt::runtime_view::contains, "each", - [](const entt::runtime_view& self, const sol::function& callback) { - if (!callback.valid()) { - return; - } - for (auto entity : self) { - callback(entity); - } - }); - - using namespace entt::literals; - - entt_module.new_usertype( - "registry", sol::meta_function::construct, sol::factories([] { return entt::registry{}; }), - - "size", &entt::registry::size, - - "alive", &entt::registry::alive, - - "valid", &entt::registry::valid, "current", &entt::registry::current, - - "create", [](entt::registry& self) { return self.create(); }, - "destroy", [](entt::registry& self, entt::entity entity) { return self.destroy(entity); }, - - "emplace", - [](entt::registry& self, entt::entity entity, const sol::table& comp, sol::this_state s) -> sol::object { - if (!comp.valid()) - return sol::lua_nil_t{}; - - const auto maybe_any = invoke_meta_func(get_type_id(comp), "emplace"_hs, &self, entity, comp, s); - return maybe_any ? maybe_any.cast() : sol::lua_nil_t{}; - }, - - "remove", - [](entt::registry& self, entt::entity entity, const sol::object& type_or_id) { - const auto maybe_any = invoke_meta_func(deduce_type(type_or_id), "remove"_hs, &self, entity); - return maybe_any ? maybe_any.cast() : 0; - }, - - "has", - [](entt::registry& self, entt::entity entity, const sol::object& type_or_id) { - const auto maybe_any = invoke_meta_func(deduce_type(type_or_id), "has"_hs, &self, entity); - return maybe_any ? maybe_any.cast() : false; - }, - - "any_of", - [](const sol::table& self, entt::entity entity, const sol::variadic_args& va) { - const auto types = collect_types(va); - const auto has = self["has"].get(); - return std::any_of(types.cbegin(), types.cend(), - [&](auto type_id) { return has(self, entity, type_id).template get(); }); - }, - - "get", - [](entt::registry& self, entt::entity entity, const sol::object& type_or_id, sol::this_state s) { - const auto maybe_any = invoke_meta_func(deduce_type(type_or_id), "get"_hs, &self, entity, s); - return maybe_any ? maybe_any.cast() : sol::lua_nil_t{}; - }, - - "clear", - sol::overload(&entt::registry::clear<>, [](entt::registry& self, sol::object type_or_id) { - invoke_meta_func(deduce_type(type_or_id), "clear"_hs, &self); - }), - - "orphan", &entt::registry::orphan, - - "runtime_view", - [](entt::registry& self, const sol::variadic_args& va) { - const std::set types = collect_types(va); - - entt::runtime_view view{}; - for (const auto& type : types) { - entt::sparse_set& storage = self.storage(type)->second; - view.iterate(storage); - } - - return view; - } - ); - - return entt_module; -} \ No newline at end of file diff --git a/include/simpleengine/scripting/entt_meta_helper.h b/include/simpleengine/scripting/entt_meta_helper.h index 92b2e67..a1085b6 100644 --- a/include/simpleengine/scripting/entt_meta_helper.h +++ b/include/simpleengine/scripting/entt_meta_helper.h @@ -5,42 +5,48 @@ #include -[[nodiscard]] entt::id_type get_type_id(const sol::table &obj) { - const auto f = obj["type_id"].get(); - assert(f.valid() && "type_id not exposed to lua!"); - return f.valid() ? f().get() : -1; -} +namespace simpleengine::scripting { + class EnttMetaHelper { + public: + + [[nodiscard]] static entt::id_type get_type_id(const sol::table &obj) { + const auto f = obj["type_id"].get(); + assert(f.valid() && "type_id not exposed to lua!"); + return f.valid() ? f().get() : -1; + } -template [[nodiscard]] entt::id_type deduce_type(T &&obj) { - switch (obj.get_type()) { - // in lua: registry:has(e, Transform.type_id()) - case sol::type::number: - return obj.template as(); - // in lua: registry:has(e, Transform) - case sol::type::table: - return get_type_id(obj); - default: - // TODO: Dont assert here. Maybe an exception could be thrown and caught by the ScriptingSystem? - assert(false); - return -1; - } -} + template [[nodiscard]] static entt::id_type deduce_type(T &&obj) { + switch (obj.get_type()) { + // in lua: registry:has(e, Transform.type_id()) + case sol::type::number: + return obj.template as(); + // in lua: registry:has(e, Transform) + case sol::type::table: + return get_type_id(obj); + default: + // TODO: Dont assert here. Maybe an exception could be thrown and caught by the ScriptingSystem? + assert(false); + return -1; + } + } -// @see -// https://github.com/skypjack/entt/wiki/Crash-Course:-runtime-reflection-system + // @see + // https://github.com/skypjack/entt/wiki/Crash-Course:-runtime-reflection-system -template -inline auto invoke_meta_func(entt::meta_type meta_type, entt::id_type function_id, Args &&...args) { - if (!meta_type) { - // TODO: Warning message - } else { - if (auto meta_function = meta_type.func(function_id); meta_function) - return meta_function.invoke({}, std::forward(args)...); - } - return entt::meta_any{}; -} + template + inline static auto invoke_meta_func(entt::meta_type meta_type, entt::id_type function_id, Args &&...args) { + if (!meta_type) { + // TODO: Warning message + } else { + if (auto meta_function = meta_type.func(function_id); meta_function) + return meta_function.invoke({}, std::forward(args)...); + } + return entt::meta_any{}; + } -template -inline auto invoke_meta_func(entt::id_type type_id, entt::id_type function_id, Args &&...args) { - return invoke_meta_func(entt::resolve(type_id), function_id, std::forward(args)...); + template + inline static auto invoke_meta_func(entt::id_type type_id, entt::id_type function_id, Args &&...args) { + return invoke_meta_func(entt::resolve(type_id), function_id, std::forward(args)...); + } + }; } \ No newline at end of file diff --git a/include/simpleengine/scripting/lua/ecs_bindings.h b/include/simpleengine/scripting/lua/ecs_bindings.h new file mode 100644 index 0000000..237f907 --- /dev/null +++ b/include/simpleengine/scripting/lua/ecs_bindings.h @@ -0,0 +1,82 @@ +#pragma once + +#include "../entt_meta_helper.h" + +#include +#include +#include + +#include + +#include +#include +#include + +namespace simpleengine::scripting::lua { + class ECSBindings { + private: + template static auto is_valid(const entt::registry *registry, entt::entity entity) { + assert(registry); + return registry->valid(entity); + } + + template + static auto emplace_component(entt::registry *registry, entt::entity entity, const sol::table& instance, sol::this_state s) { + assert(registry); + auto& comp = + registry->emplace_or_replace(entity, instance.valid() ? instance.as() : Component{}); + return sol::make_reference(s, std::ref(comp)); + } + + template static auto get_component(entt::registry *registry, entt::entity entity, sol::this_state s) { + assert(registry); + auto& comp = registry->get_or_emplace(entity); + return sol::make_reference(s, std::ref(comp)); + } + + template static bool has_component(entt::registry *registry, entt::entity entity) { + assert(registry); + return registry->any_of(entity); + } + + template static auto remove_component(entt::registry *registry, entt::entity entity) { + assert(registry); + return registry->remove(entity); + } + + template static void clear_component(entt::registry *registry) { + assert(registry); + registry->clear(); + } + + template static void register_meta_component() { + using namespace entt::literals; + + entt::meta() + .type() + .template func<&is_valid>("valid"_hs) + .template func<&emplace_component>("emplace"_hs) + .template func<&get_component>("get"_hs) + .template func<&has_component>("has"_hs) + .template func<&clear_component>("clear"_hs) + .template func<&remove_component>("remove"_hs); + } + + static auto collect_types(const sol::variadic_args& va) { + std::set types; + std::transform(va.cbegin(), va.cend(), std::inserter(types, types.begin()), + [](const auto& obj) { return EnttMetaHelper::deduce_type(obj); }); + return types; + } + + public: + + /// This binds ALL of the ECS to Lua under the `ecs` table + static sol::table bind_full_ecs(sol::this_state s); + + /// This binds an entt registry to lua. It binds the registry type as `ecs.registry` + static sol::table bind_registry(sol::this_state s); + /// Bind all components to Lua. This will put all components under `ecs.component.*` + static sol::table bind_components(sol::this_state s); + }; +} \ No newline at end of file diff --git a/include/simpleengine/scripting/lua/lua_scripting_engine.h b/include/simpleengine/scripting/lua/lua_scripting_engine.h new file mode 100644 index 0000000..28053c7 --- /dev/null +++ b/include/simpleengine/scripting/lua/lua_scripting_engine.h @@ -0,0 +1,36 @@ +#pragma once + +#include "../scripting_engine.h" +#include + +#include + +namespace simpleengine::ecs { + class Registry; +} + +namespace simpleengine::scripting::lua { + class LuaScriptingEngine : public ScriptingEngine { + private: + sol::state lua; + std::vector script_results; + public: + LuaScriptingEngine(std::shared_ptr entity_registry); + + static void lua_panic_handler(sol::optional maybe_msg); + static int lua_exception_handler(lua_State* L, sol::optional maybe_exception, sol::string_view description); + static sol::protected_function_result lua_protected_function_handler(lua_State* lstate, sol::protected_function_result result); + + virtual void setup_language() override; + virtual void expose_simpleengine() override; + + // TODO: Add some way for the user to get a reference to a script so they can stop it from running + virtual void run_script(std::string lua_code) override; + virtual void run_script_file(std::string path) override; + + virtual void update(const float& delta_time) override; + virtual void input_update(const float& delta_time) override; + virtual void render(const float& interpolate_alpha, const float& frame_time) override; + virtual void destroy() override; + }; +} \ No newline at end of file diff --git a/include/simpleengine/scripting/scripting_engine.h b/include/simpleengine/scripting/scripting_engine.h new file mode 100644 index 0000000..6b09d55 --- /dev/null +++ b/include/simpleengine/scripting/scripting_engine.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace simpleengine::ecs { + class Registry; +} + +namespace simpleengine::scripting { + class ScriptingEngine { + protected: + std::shared_ptr entity_registry; + public: + ScriptingEngine(std::shared_ptr entity_registry) : entity_registry(entity_registry) { + + } + + /// Setup the scripting language VM + virtual void setup_language() = 0; + + /// Expose simpleengine to the scripting language + virtual void expose_simpleengine() = 0; + + virtual void run_script(std::string lua_code) = 0; + virtual void run_script_file(std::string path) = 0; + + virtual void update(const float& delta_time) = 0; + virtual void input_update(const float& delta_time) = 0; + virtual void render(const float& interpolate_alpha, const float& frame_time) = 0; + virtual void destroy() = 0; + }; +} \ No newline at end of file diff --git a/include/simpleengine/scripting/scripting_system.h b/include/simpleengine/scripting/scripting_system.h new file mode 100644 index 0000000..b1f84bc --- /dev/null +++ b/include/simpleengine/scripting/scripting_system.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../ecs/system/system.h" +#include "scripting_engine.h" +#include + +namespace simpleengine::scripting { + class ScriptingSystem : public simpleengine::ecs::system::System { + private: + std::unordered_map> scripting_engines; + public: + ScriptingSystem(std::shared_ptr entity_registry); + + void add_lua_engine(); + + virtual void update(const float& delta_time) override; + virtual void input_update(const float& delta_time) override; + virtual void render(const float& interpolate_alpha, const float& frame_time) override; + virtual void destroy() override; + }; +} \ No newline at end of file diff --git a/src/scripting/lua/ecs_bindings.cpp b/src/scripting/lua/ecs_bindings.cpp new file mode 100644 index 0000000..8e50e0d --- /dev/null +++ b/src/scripting/lua/ecs_bindings.cpp @@ -0,0 +1,167 @@ +#include "scripting/lua/ecs_bindings.h" + +#include "ecs/component/transform_component.h" +#include "scripting/entt_meta_helper.h" + +#include +#include +#include +#include +#include + +namespace simpleengine::scripting::lua { + sol::table ECSBindings::bind_full_ecs(sol::this_state s) { + sol::table ecs_table = bind_registry(s); + bind_components(s); + + return ecs_table; + } + + sol::table ECSBindings::bind_registry(sol::this_state s) { + sol::state_view lua{s}; + auto ecs_module = lua["ecs"].get_or_create(); + + ecs_module.new_usertype("runtime_view", sol::no_constructor, + "size_hint", &entt::runtime_view::size_hint, "contains", + &entt::runtime_view::contains, "each", + [](const entt::runtime_view& self, const sol::function& callback) { + if (!callback.valid()) { + return; + } + for (auto entity : self) { + callback(entity); + } + }); + + using namespace entt::literals; + + ecs_module.new_usertype( + "registry", sol::meta_function::construct, sol::factories([] { return entt::registry{}; }), + + "size", &entt::registry::size, + "alive", &entt::registry::alive, + "valid", &entt::registry::valid, "current", &entt::registry::current, + + "create", [](entt::registry& self) { return self.create(); }, + "destroy", [](entt::registry& self, entt::entity entity) { return self.destroy(entity); }, + + "emplace", + [](entt::registry& self, entt::entity entity, const sol::table& comp, sol::this_state s) -> sol::object { + if (!comp.valid()) + return sol::lua_nil_t{}; + + const auto maybe_any = EnttMetaHelper::invoke_meta_func(EnttMetaHelper::get_type_id(comp), "emplace"_hs, &self, entity, comp, s); + return maybe_any ? maybe_any.cast() : sol::lua_nil_t{}; + }, + + "remove", + [](entt::registry& self, entt::entity entity, const sol::object& type_or_id) { + const auto maybe_any = EnttMetaHelper::invoke_meta_func(EnttMetaHelper::deduce_type(type_or_id), "remove"_hs, &self, entity); + return maybe_any ? maybe_any.cast() : 0; + }, + + "has", + [](entt::registry& self, entt::entity entity, const sol::object& type_or_id) { + const auto maybe_any = EnttMetaHelper::invoke_meta_func(EnttMetaHelper::deduce_type(type_or_id), "has"_hs, &self, entity); + return maybe_any ? maybe_any.cast() : false; + }, + + "any_of", + [](const sol::table& self, entt::entity entity, const sol::variadic_args& va) { + const auto types = collect_types(va); + const auto has = self["has"].get(); + return std::any_of(types.cbegin(), types.cend(), + [&](auto type_id) { return has(self, entity, type_id).template get(); }); + }, + + "get", + [](entt::registry& self, entt::entity entity, const sol::object& type_or_id, sol::this_state s) { + const auto maybe_any = EnttMetaHelper::invoke_meta_func(EnttMetaHelper::deduce_type(type_or_id), "get"_hs, &self, entity, s); + return maybe_any ? maybe_any.cast() : sol::lua_nil_t{}; + }, + + "clear", + sol::overload(&entt::registry::clear<>, [](entt::registry& self, sol::object type_or_id) { + EnttMetaHelper::invoke_meta_func(EnttMetaHelper::deduce_type(type_or_id), "clear"_hs, &self); + }), + + "orphan", &entt::registry::orphan, + + "runtime_view", + [](entt::registry& self, const sol::variadic_args& va) { + const std::set types = collect_types(va); + + entt::runtime_view view{}; + for (const auto& type : types) { + entt::sparse_set& storage = self.storage(type)->second; + view.iterate(storage); + } + + return view; + } + ); + + return ecs_module; + } + + sol::table ECSBindings::bind_components(sol::this_state s) { + sol::state_view lua{s}; + auto ecs_module = lua["ecs"].get_or_create(); + auto comp_module = ecs_module["component"].get_or_create(); + + // TransformComponent + register_meta_component(); + comp_module.new_usertype("TransformComponent", + "type_id", &entt::type_hash::value, + + sol::call_constructor, sol::factories( + [](float px, float py, float pz) { + return simpleengine::ecs::TransformComponent(glm::vec3(px, py, pz)); + }, + [](float px, float py, float pz, float rx, float ry, float rz) { + return simpleengine::ecs::TransformComponent(glm::vec3(px, py, pz), glm::vec3(rx, ry, rz)); + }, + [](float px, float py, float pz, float rx, float ry, float rz, float sx, float sy, float sz) { + return simpleengine::ecs::TransformComponent(glm::vec3(px, py, pz), glm::vec3(rx, ry, rz), glm::vec3(sx, sy, sz)); + } + ), + + "decompose_matrix", [](const ecs::TransformComponent& self) { + glm::vec3 pos, scale; + glm::quat rot; + + self.decompose_matrix(pos, rot, scale); + + return std::tuple(pos, rot, scale); + }, + + // TODO: Implement glm::vec3 + /* "get_pos", &ecs::TransformComponent::get_pos, + "get_scale", &ecs::TransformComponent::get_scale, + "get_rotation_quat", &ecs::TransformComponent::get_rotation_quat, */ + // combine_transform(const glm::mat4& transform_matrix) + // combine_transform(const TransformComponent& transformable) + + "translate", sol::overload([](ecs::TransformComponent& self, float x, float y, float z) { + self.translate(x, y, z); + }), // TODO: Implement glm::vec3 translate + + "rotate", &ecs::TransformComponent::rotate, + "rotate_x", &ecs::TransformComponent::rotate_x, + "rotate_y", &ecs::TransformComponent::rotate_y, + "rotate_z", &ecs::TransformComponent::rotate_z, + + "scale", sol::overload([](ecs::TransformComponent& self, float scalar) { + self.scale(scalar); + }, [](ecs::TransformComponent& self, float x_scalar, float y_scalar, float z_scalar) { + self.scale(glm::vec3(x_scalar, y_scalar, z_scalar)); + }), // TODO: Implement glm::vec3 scale + + sol::meta_function::to_string, [](const ecs::TransformComponent& self) { + return glm::to_string(self.transform_matrix); + } + ); + + return comp_module; + } +} \ No newline at end of file diff --git a/src/scripting/lua/lua_scripting_engine.cpp b/src/scripting/lua/lua_scripting_engine.cpp new file mode 100644 index 0000000..4bbca58 --- /dev/null +++ b/src/scripting/lua/lua_scripting_engine.cpp @@ -0,0 +1,192 @@ +#include "scripting/lua/lua_scripting_engine.h" +#include "scripting/lua/ecs_bindings.h" +#include "scripting/entt_meta_helper.h" +#include "ecs/registry.h" +#include "log/logger.h" + +#include "ecs/component/transform_component.h" +#include "scripting/lua/ecs_bindings.h" + +#include + +#include + +#define AUTO_ARG(x) decltype(x), x + +namespace simpleengine::scripting::lua { + LuaScriptingEngine::LuaScriptingEngine(std::shared_ptr entity_registry) : ScriptingEngine(entity_registry) { + + } + + void LuaScriptingEngine::lua_panic_handler(sol::optional maybe_msg) { + std::cerr << "Lua panic: "; + if(maybe_msg) { + std::cerr << maybe_msg->c_str(); + } + std::cerr << std::endl; + } + + int LuaScriptingEngine::lua_exception_handler(lua_State* L, sol::optional maybe_exception, sol::string_view description) { + std::cerr << "Lua Exception: "; + if(maybe_exception) { + const std::exception& ex = *maybe_exception; + std::cerr << ex.what(); + } else { + std::cerr.write(description.data(), description.size()); + } + + std::cerr << std::endl; + + return sol::stack::push(L, description); + } + + sol::protected_function_result LuaScriptingEngine::lua_protected_function_handler(lua_State* lstate, sol::protected_function_result result) { + std::cerr << "Lua protected function error" << std::endl; + + sol::type t = sol::type_of(lstate, result.stack_index()); + std::string err = "sol: "; + err += to_string(result.status()); + err += " error"; + + std::exception_ptr eptr = std::current_exception(); + if (eptr) { + err += " with a "; + try { + std::rethrow_exception(eptr); + } + catch (const std::exception& ex) { + err += "std::exception -- "; + err.append(ex.what()); + } + catch (const std::string& message) { + err += "thrown message -- "; + err.append(message); + } + catch (const char* message) { + err += "thrown message -- "; + err.append(message); + } + catch (...) { + err.append("thrown but unknown type, cannot serialize into error message"); + } + } + + if (t == sol::type::string) { + err += ": "; + std::string_view serr = sol::stack::unqualified_get(lstate, result.stack_index()); + err.append(serr.data(), serr.size()); + } + + SE_ERROR("Lua ScriptingEngine", "{}", err); + + return result; + } + + void LuaScriptingEngine::setup_language() { + std::cout << "Setup" << std::endl; + + lua = sol::state{sol::c_call}; + lua.set_exception_handler(&LuaScriptingEngine::lua_exception_handler); + + + + // Open all libraries built into sol2. There is ffi and jit, but these are only available + // when using LuaJIT. + lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::coroutine, + sol::lib::string, sol::lib::os, sol::lib::math, sol::lib::table, sol::lib::debug, + sol::lib::bit32, sol::lib::io, sol::lib::utf8); + //lua.require("registry", sol::c_call, false); // create registry type + // Overwrite the Lua print function to use a logger + lua.globals().set_function("print", [](std::string msg) { + SE_DEBUG("Lua ScriptingEngine", "[Lua Output] {}", msg); + }); + } + + void LuaScriptingEngine::expose_simpleengine() { + // Make registry available to lua + lua["registry"] = std::ref(entity_registry->get_inner()); + + // Registry TransformComponent + //bindings::bind_registry(); + lua.require("ecs", sol::c_call, false); // create registry type + //lua.require("registry", sol::c_call, false); // create registry type + + + entt::entity en = entity_registry->get_inner().create(); + lua["dog"] = en; + + run_script(R"LUA( + print('start') + --local dog = registry:create() + local cat = registry:create() + print('created dog and cat') + + print('Dog is ' .. dog .. ', and registry size is ' .. registry:size()) + print('Cat is ' .. cat .. ', and cat size is ' .. registry:size()) + + registry:emplace(dog, ecs.component.TransformComponent(5, 6, 3)) + + assert(registry:has(dog, ecs.component.TransformComponent)) + assert(registry:has(dog, ecs.component.TransformComponent.type_id())) + + assert(not registry:any_of(dog, -1, -2, -3)) + + function update(delta_time) + transform = registry:get(dog, ecs.component.TransformComponent) + print('Dog position = ' .. tostring(transform)) + transform:translate(0, 0, 1) + end + + print('Lua script loaded!') + )LUA"); + } + + void LuaScriptingEngine::run_script(std::string lua_code) { + script_results.emplace_back(lua.script(lua_code, &LuaScriptingEngine::lua_protected_function_handler)); + } + + void LuaScriptingEngine::run_script_file(std::string path) { + script_results.emplace_back(lua.script_file(path, &LuaScriptingEngine::lua_protected_function_handler)); + } + + void LuaScriptingEngine::update(const float& delta_time) { + for (const auto& res : script_results) { + if (res.valid()) { + sol::function lua_update = lua["update"]; + + if (lua_update.valid()) { + lua_update(delta_time); + } + } + } + } + + void LuaScriptingEngine::input_update(const float& delta_time) { + for (const auto& res : script_results) { + if (res.valid()) { + sol::function lua_input_update = lua["input_update"]; + + if (lua_input_update.valid()) { + lua_input_update(delta_time); + } + } + } + } + + void LuaScriptingEngine::render(const float& interpolate_alpha, const float& frame_time) { + for (const auto& res : script_results) { + if (res.valid()) { + sol::function lua_render = lua["render"]; + + if (lua_render.valid()) { + lua_render(interpolate_alpha, frame_time); + } + } + } + } + + void LuaScriptingEngine::destroy() { + std::cout << "Destroy" << std::endl; + } +} \ No newline at end of file diff --git a/src/scripting/scripting_system.cpp b/src/scripting/scripting_system.cpp new file mode 100644 index 0000000..b30a17f --- /dev/null +++ b/src/scripting/scripting_system.cpp @@ -0,0 +1,42 @@ +#include "scripting/scripting_system.h" +#include "ecs/system/system.h" + +#include "scripting/lua/lua_scripting_engine.h" + +namespace simpleengine::scripting { + + ScriptingSystem::ScriptingSystem(std::shared_ptr entity_registry) : simpleengine::ecs::system::System(entity_registry) { + + } + + void ScriptingSystem::add_lua_engine() { + auto lua = std::make_unique(entity_registry); + + lua->setup_language(); + lua->expose_simpleengine(); + + scripting_engines.emplace("lua", std::move(lua)); + } + + void ScriptingSystem::update(const float& delta_time) { + for (const auto& [key, val] : scripting_engines) { + val->update(delta_time); + } + } + + void ScriptingSystem::input_update(const float& delta_time) { + for (const auto& [key, val] : scripting_engines) { + val->input_update(delta_time); + } + } + + void ScriptingSystem::render(const float& interpolate_alpha, const float& frame_time) { + for (const auto& [key, val] : scripting_engines) { + val->render(interpolate_alpha, frame_time); + } + } + + void ScriptingSystem::destroy() { + std::cout << "Scripting system destroyed!" << std::endl; + } +} \ No newline at end of file