diff --git a/autogen/CMakeLists.txt b/autogen/CMakeLists.txt index 6f4ed9f..2249577 100644 --- a/autogen/CMakeLists.txt +++ b/autogen/CMakeLists.txt @@ -6,6 +6,8 @@ add_executable(autogen src/util.cpp src/object/analysis.cpp src/object/codegen.cpp + src/data/analysis.cpp + src/data/codegen.cpp ) set_target_properties(autogen PROPERTIES OUTPUT_NAME "autogen") diff --git a/autogen/src/data/analysis.cpp b/autogen/src/data/analysis.cpp new file mode 100644 index 0000000..038d201 --- /dev/null +++ b/autogen/src/data/analysis.cpp @@ -0,0 +1,126 @@ +#include "analysis.h" +#include "../util.h" +#include +#include +#include +#include +#include + +using namespace data; + +static void processMethod(CXCursor cur, ClassAnalysis* state) { + std::optional propertyDef = findAnnotation(cur, "OB::def_data_method"); + if (!propertyDef) return; + + MethodAnalysis anly; + + auto result = parseAnnotationString(propertyDef.value()); + std::string symbolName = x_clang_toString(clang_getCursorDisplayName(cur)); + CXType retType = clang_getCursorResultType(cur); + + anly.name = result["name"]; + anly.functionName = symbolName; + anly.returnType = x_clang_toString(clang_getTypeSpelling(retType)); + + // if name field is not provided, use fieldName instead, but capitalize the first character + if (anly.name == "") { + anly.name = symbolName; + anly.name[0] = std::toupper(anly.name[0]); + } + + // Populate parameter list + // https://stackoverflow.com/a/45867090/16255372 + + for (int i = 0; i < clang_Cursor_getNumArguments(cur); i++) { + CXCursor paramCur = clang_Cursor_getArgument(cur, i); + + std::string paramName = x_clang_toString(clang_getCursorDisplayName(paramCur)); + std::string paramType = x_clang_toString(clang_getTypeSpelling(clang_getCursorType(paramCur))); + + MethodParameter param; + param.name = paramName; + param.type = paramType; + + anly.parameters.push_back(param); + } + + // If it's a static method, push it into the library instead + if (clang_CXXMethod_isStatic(cur)) + state->staticMethods.push_back(anly); + else + state->methods.push_back(anly); +} + +// This processes both methods and fields +static void processProperty(CXCursor cur, ClassAnalysis* state) { + std::optional propertyDef = findAnnotation(cur, "OB::def_data_prop"); + if (!propertyDef) return; + + PropertyAnalysis anly; + + auto result = parseAnnotationString(propertyDef.value()); + std::string symbolName = x_clang_toString(clang_getCursorDisplayName(cur)); + CXType retType = clang_getCursorResultType(cur); + + anly.name = result["name"]; + anly.backingSymbol = symbolName; + anly.valueType = x_clang_toString(clang_getTypeSpelling(retType)); + anly.backingType = clang_getCursorKind(cur) == CXCursor_CXXMethod ? PropertyBackingType::Method : PropertyBackingType::Field; + + // if name field is not provided, use fieldName instead, but capitalize the first character + if (anly.name == "") { + anly.name = symbolName; + anly.name[0] = std::toupper(anly.name[0]); + } + + // If it's a static method, push it into the library instead + if (clang_CXXMethod_isStatic(cur)) + state->staticProperties.push_back(anly); + else + state->properties.push_back(anly); +} + +static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) { + ClassAnalysis anly; + + anly.name = className; + + x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { + CXCursorKind kind = clang_getCursorKind(cur); + + if (kind == CXCursor_CXXMethod || kind == CXCursor_FieldDecl) { + processProperty(cur, &anly); + } + + if (kind == CXCursor_CXXMethod) { + processMethod(cur, &anly); + } + + return CXChildVisit_Continue; + }); + + state->classes[className] = anly; +} + +bool data::analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state) { + // Search for classes + x_clang_visitChildren(cursor, [&](CXCursor cur, CXCursor parent) { + CXCursorKind kind = clang_getCursorKind(cur); + if (kind == CXCursor_Namespace) return CXChildVisit_Recurse; + if (kind != CXCursor_ClassDecl) return CXChildVisit_Continue; + + CXSourceLocation loc = clang_getCursorLocation(cur); + if (!clang_Location_isFromMainFile(loc)) return CXChildVisit_Continue; // This class is not from this header. Skip + + std::string className = x_clang_toString(clang_getCursorDisplayName(cur)); + // Forward-decls can slip through the cracks, this prevents that, but also allows us to filter non-instance classes in the src/objects directory + if (!findAnnotation(cur, "OB::def_data")) return CXChildVisit_Continue; // Class is not "primary" declaration/is not instance, skip + if (state->classes.count(className) > 0) return CXChildVisit_Continue; // Class has already been analyzed, skip... + + processClass(cur, state, className, srcRoot); + + return CXChildVisit_Continue; + }); + + return true; +} \ No newline at end of file diff --git a/autogen/src/data/analysis.h b/autogen/src/data/analysis.h new file mode 100644 index 0000000..bb55c43 --- /dev/null +++ b/autogen/src/data/analysis.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include + +namespace data { + +enum PropertyBackingType { + Method, + Field +}; + +struct PropertyAnalysis { + std::string name; + std::string backingSymbol; + PropertyBackingType backingType; + std::string valueType; +}; + +struct MethodParameter { + std::string name; + std::string type; +}; + +struct MethodAnalysis { + std::string name; + std::string functionName; + std::string returnType; + std::vector parameters; +}; + +struct ClassAnalysis { + std::string name; + std::string headerPath; + std::vector properties; + std::vector methods; + std::vector staticProperties; + std::vector staticMethods; +}; + +struct AnalysisState { + std::map classes; +}; + +bool analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state); + +} \ No newline at end of file diff --git a/autogen/src/data/codegen.cpp b/autogen/src/data/codegen.cpp new file mode 100644 index 0000000..5141e6d --- /dev/null +++ b/autogen/src/data/codegen.cpp @@ -0,0 +1,54 @@ +#include "codegen.h" +#include "analysis.h" +#include +#include +#include + +using namespace data; + +static std::string getLuaMethodFqn(std::string className, std::string methodName) { + return "__lua_impl_" + className + "__" + methodName; +} + +static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) { + out << "static int lib_gc(lua_State*);" + << "static int lib_index(lua_State*);" + << "static int lib_newindex(lua_State*);" + << "static const struct luaL_Reg metatable [] = {" + << " {\"__index\", lib_index}," + << " {\"__newindex\", lib_newindex}," + << " {NULL, NULL} /* end of array */" + << "};"; + + // Create push function + out << "void Data::" << state.name << "::PushLuaLibrary(lua_State* L) {\n"; + + out << " int n = lua_gettop(L);\n" + << " lua_newuserdata(L, 0);\n" + << " luaL_newmetatable(L, \"__mt_" << state.name << "\");\n" + << " luaL_register(L, NULL, library_metatable);\n" + << " lua_setmetatable(L, n+1);\n"; + + out << "}\n"; + + +} + +void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) { + // std::string fqn = "Data::" + state.name; + + // out << "#define __AUTOGEN_EXTRA_INCLUDES__\n"; + // out << "#include \"lua.h\""; + // out << "#include \"" << headerPath << "\"\n\n"; + // out << "const Data::TypeInfo " << fqn << "::TYPE = {\n" + // << " .name = \"" << fqn << "\",\n" + // << " .deserializer = &" << fqn << "::Deserialize,\n" + // << " .fromLuaValue = %" << fqn << "::FromLuaValue,\n" + // << "};\n\n"; + + // out << "const Data::TypeInfo& " << fqn << "::GetType() {\n" + // << " return &TYPE;\n" + // << "};\n\n"; + + // writeLuaLibraryGenerator(out, state); +} \ No newline at end of file diff --git a/autogen/src/data/codegen.h b/autogen/src/data/codegen.h new file mode 100644 index 0000000..6026cb0 --- /dev/null +++ b/autogen/src/data/codegen.h @@ -0,0 +1,10 @@ +#pragma once + +#include "analysis.h" +#include + +namespace data { + +void writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state); + +} \ No newline at end of file diff --git a/autogen/src/main.cpp b/autogen/src/main.cpp index fad9c2a..fe30b6b 100644 --- a/autogen/src/main.cpp +++ b/autogen/src/main.cpp @@ -6,9 +6,10 @@ #include #include #include - #include "object/analysis.h" #include "object/codegen.h" +#include "data/analysis.h" +#include "data/codegen.h" // namespace data { // #include "data/analysis.h" @@ -17,20 +18,61 @@ namespace fs = std::filesystem; -int processObject(fs::path srcRoot, fs::path srcPath, fs::path outPath) { - object::AnalysisState state; +// https://clang.llvm.org/docs/LibClang.html +int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) { + const char* cargs[] = { "-x", "c++", "-I", srcRoot.c_str(), "-D__AUTOGEN__", 0 }; + // THANK YOU SO MUCH THIS STACKOVERFLOW ANSWER IS SO HELPFUL + // https://stackoverflow.com/a/59206378/16255372 + CXIndex index = clang_createIndex(0, 0); + CXTranslationUnit unit = clang_parseTranslationUnit( + index, + srcPath.c_str(), cargs, 5, + nullptr, 0, + CXTranslationUnit_None); + + if (!unit) { + fprintf(stderr, "Failed to parse file\n"); + return 1; + } + + // Print errors + int ndiags = clang_getNumDiagnostics(unit); + for (int i = 0; i < ndiags; i++) { + CXDiagnostic diag = clang_getDiagnostic(unit, i); + CXString str = clang_formatDiagnostic(diag, 0); + fprintf(stderr, "diag: %s\n", clang_getCString(str)); + + clang_disposeString(str); + clang_disposeDiagnostic(diag); + } + + CXCursor cursor = clang_getTranslationUnitCursor(unit); + + object::AnalysisState objectAnlyState; + data::AnalysisState dataAnlyState; fs::path relpath = fs::relative(srcPath, srcRoot); printf("[AUTOGEN] Processing file %s...\n", relpath.c_str()); - object::analyzeClasses(srcPath, srcRoot, &state); + object::analyzeClasses(cursor, srcRoot, &objectAnlyState); + data::analyzeClasses(cursor, srcRoot, &dataAnlyState); fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it printf("[AUTOGEN] Generating file %s...\n", relpath.c_str()); std::ofstream outStream(outPath); - for (auto& [_, clazz] : state.classes) { - object::writeCodeForClass(outStream, clazz); + if (!objectAnlyState.classes.empty() || !dataAnlyState.classes.empty()) { + outStream << "/////////////////////////////////////////////////////////////////////////////////////////\n"; + outStream << "// This file was automatically generated by autogen, and should not be edited manually //\n"; + outStream << "/////////////////////////////////////////////////////////////////////////////////////////\n\n"; + } + + for (auto& [_, clazz] : objectAnlyState.classes) { + object::writeCodeForClass(outStream, relpath, clazz); + } + + for (auto& [_, clazz] : dataAnlyState.classes) { + data::writeCodeForClass(outStream, relpath, clazz); } outStream.close(); @@ -40,22 +82,13 @@ int processObject(fs::path srcRoot, fs::path srcPath, fs::path outPath) { int main(int argc, char** argv) { if (argc < 4) { - fprintf(stderr, "Usage: autogen \n"); + fprintf(stderr, "Usage: autogen \n"); return 1; } - std::string codeType = argv[1]; + fs::path srcRoot = argv[1]; + fs::path srcPath = argv[2]; + fs::path outPath = argv[3]; - fs::path srcRoot = argv[2]; - fs::path srcPath = argv[3]; - fs::path outPath = argv[4]; - - if (codeType == "object") { - return processObject(srcRoot, srcPath, outPath); - } else if (codeType == "data") { - return processObject(srcRoot, srcPath, outPath); - } - - fprintf(stderr, "Unknown class type '%s'\n", codeType.c_str()); - return -1; + return processHeader(srcRoot, srcPath, outPath); } \ No newline at end of file diff --git a/autogen/src/object/analysis.cpp b/autogen/src/object/analysis.cpp index 5415e5a..d730312 100644 --- a/autogen/src/object/analysis.cpp +++ b/autogen/src/object/analysis.cpp @@ -167,36 +167,7 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa } // https://clang.llvm.org/docs/LibClang.html -bool object::analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state) { - const char* cargs[] = { "-x", "c++", "-I", srcRoot.c_str(), "-D__AUTOGEN__", 0 }; - // THANK YOU SO MUCH THIS STACKOVERFLOW ANSWER IS SO HELPFUL - // https://stackoverflow.com/a/59206378/16255372 - CXIndex index = clang_createIndex(0, 0); - CXTranslationUnit unit = clang_parseTranslationUnit( - index, - path.c_str(), cargs, 5, - nullptr, 0, - CXTranslationUnit_None); - - if (!unit) { - fprintf(stderr, "Failed to parse file\n"); - return 1; - } - - // Print errors - int ndiags = clang_getNumDiagnostics(unit); - for (int i = 0; i < ndiags; i++) { - CXDiagnostic diag = clang_getDiagnostic(unit, i); - CXString str = clang_formatDiagnostic(diag, 0); - fprintf(stderr, "diag: %s\n", clang_getCString(str)); - - clang_disposeString(str); - clang_disposeDiagnostic(diag); - } - - CXCursor cursor = clang_getTranslationUnitCursor(unit); - - bool flag = false; +bool object::analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state) { // Search for classes x_clang_visitChildren(cursor, [&](CXCursor cur, CXCursor parent) { CXCursorKind kind = clang_getCursorKind(cur); diff --git a/autogen/src/object/analysis.h b/autogen/src/object/analysis.h index aa40aaa..0fbfea0 100644 --- a/autogen/src/object/analysis.h +++ b/autogen/src/object/analysis.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -58,6 +59,6 @@ struct AnalysisState { std::map classes; }; -bool analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state); +bool analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state); } \ No newline at end of file diff --git a/autogen/src/object/codegen.cpp b/autogen/src/object/codegen.cpp index 0d5e36b..d10c8f2 100644 --- a/autogen/src/object/codegen.cpp +++ b/autogen/src/object/codegen.cpp @@ -206,7 +206,7 @@ static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) { out << "\n};\n\n"; } -void object::writeCodeForClass(std::ofstream& out, ClassAnalysis& state) { +void object::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) { std::string strFlags; if (state.flags & ClassFlag_NotCreatable) strFlags += " | INSTANCE_NOTCREATABLE"; @@ -217,10 +217,6 @@ void object::writeCodeForClass(std::ofstream& out, ClassAnalysis& state) { if (!strFlags.empty()) strFlags = strFlags.substr(3); // Remove leading pipe else strFlags = "0"; // 0 == No option - out << "/////////////////////////////////////////////////////////////////////////////////////////\n"; - out << "// This file was automatically generated by autogen, and should not be edited manually //\n"; - out << "/////////////////////////////////////////////////////////////////////////////////////////\n\n"; - std::string constructorStr; if (state.abstract) constructorStr = "nullptr"; else constructorStr = "&" + state.name + "::Create"; diff --git a/autogen/src/object/codegen.h b/autogen/src/object/codegen.h index 49c20d0..746538a 100644 --- a/autogen/src/object/codegen.h +++ b/autogen/src/object/codegen.h @@ -5,6 +5,6 @@ namespace object { -void writeCodeForClass(std::ofstream& out, ClassAnalysis& state); +void writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state); } \ No newline at end of file diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 2fc4aa9..4408f15 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -27,7 +27,7 @@ foreach (SRC ${AUTOGEN_SOURCES}) add_custom_command( OUTPUT "${OUT_PATH}" DEPENDS "${SRC_PATH}" - COMMAND "${CMAKE_BINARY_DIR}/autogen/autogen" "object" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${SRC_PATH}" "${OUT_PATH}" + COMMAND "${CMAKE_BINARY_DIR}/autogen/autogen" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${SRC_PATH}" "${OUT_PATH}" ) list(APPEND AUTOGEN_OUTS "${OUT_PATH}") diff --git a/core/src/datatypes/base.h b/core/src/datatypes/base.h index ab38d3f..5819673 100644 --- a/core/src/datatypes/base.h +++ b/core/src/datatypes/base.h @@ -10,7 +10,7 @@ extern "C" { typedef struct lua_State lua_State; } #define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME : public Data::Base { \ - const WRAPPED_TYPE value; \ + WRAPPED_TYPE value; \ public: \ CLASS_NAME(WRAPPED_TYPE); \ ~CLASS_NAME(); \ diff --git a/core/src/datatypes/cframe.h b/core/src/datatypes/cframe.h index 04d2f9b..392213a 100644 --- a/core/src/datatypes/cframe.h +++ b/core/src/datatypes/cframe.h @@ -11,7 +11,7 @@ namespace rp = reactphysics3d; namespace Data { - class DEF_DATA CFrame : Base { + class DEF_DATA CFrame : public Base { glm::vec3 translation; glm::mat3 rotation; @@ -38,9 +38,11 @@ namespace Data { virtual const Data::String ToString() const override; virtual void Serialize(pugi::xml_node parent) const override; - virtual void PushLuaValue(lua_State*) const override; static Data::Variant Deserialize(pugi::xml_node node); + virtual void PushLuaValue(lua_State*) const override; + static void PushLuaLibrary(lua_State*); + operator glm::mat4() const; operator rp::Transform() const; diff --git a/core/src/datatypes/color3.h b/core/src/datatypes/color3.h index d8ff28f..14e36e0 100644 --- a/core/src/datatypes/color3.h +++ b/core/src/datatypes/color3.h @@ -7,7 +7,7 @@ #include namespace Data { - class DEF_DATA Color3 : Base { + class DEF_DATA Color3 : public Base { float r; float g; float b; @@ -25,9 +25,11 @@ namespace Data { virtual const Data::String ToString() const override; DEF_DATA_METHOD std::string ToHex() const; virtual void Serialize(pugi::xml_node node) const override; - virtual void PushLuaValue(lua_State*) const override; static Data::Variant Deserialize(pugi::xml_node node); + virtual void PushLuaValue(lua_State*) const override; + static void PushLuaLibrary(lua_State*); + operator glm::vec3() const; DEF_DATA_PROP inline float R() const { return r; } diff --git a/core/src/datatypes/ref.h b/core/src/datatypes/ref.h index 3b812aa..fbcbe15 100644 --- a/core/src/datatypes/ref.h +++ b/core/src/datatypes/ref.h @@ -7,7 +7,7 @@ class Instance; namespace Data { - class InstanceRef : Base { + class InstanceRef : public Base { std::weak_ptr ref; public: InstanceRef(); diff --git a/core/src/datatypes/vector.cpp b/core/src/datatypes/vector.cpp index 119d1fe..7bd8fa7 100644 --- a/core/src/datatypes/vector.cpp +++ b/core/src/datatypes/vector.cpp @@ -1,8 +1,10 @@ #include "vector.h" +#include +#include #include #include +#include "datatypes/base.h" #include "meta.h" // IWYU pragma: keep -#include "panic.h" Data::Vector3::Vector3() : vector(glm::vec3(0, 0, 0)) {}; Data::Vector3::Vector3(const glm::vec3& src) : vector(src) {}; @@ -111,7 +113,98 @@ std::optional Data::Vector3::FromString(std::string string) { return Data::Vector3(components[0], components[1], components[2]); } +// Lua (TEMPORARY) +#include "lua.h" + +static int lib_index(lua_State*); +static const struct luaL_Reg lib_metatable [] = { + {"__index", lib_index}, + {NULL, NULL} /* end of array */ +}; + +static int lib_index(lua_State* L) { + std::string key(lua_tostring(L, 2)); + lua_pop(L, 2); + + if (key == "test") { + Data::String("mabaref").PushLuaValue(L); + return 1; + } + + return luaL_error(L, "%s is not a valid member of %s\n", key.c_str(), "Vector3"); +} + +static int data_index(lua_State*); +static const struct luaL_Reg metatable [] = { + {"__index", data_index}, + {NULL, NULL} /* end of array */ +}; + +static int data_index(lua_State* L) { + auto this__ = (Data::Base*)lua_touserdata(L, 1); + this__->GetType(); + if (&this__->GetType() != &Vector3::TYPE) return luaL_typerror(L, 0, "Vector3"); + Vector3* this_ = (Vector3*)this__; + + std::string key(lua_tostring(L, 2)); + lua_pop(L, 2); + + if (key == "X") { + Data::Float(this_->X()).PushLuaValue(L); + return 1; + } else if (key == "Magnitude") { + lua_pushcfunction(L, [](lua_State* L) { + auto this__ = (Data::Base*)lua_touserdata(L, 1); + if (&this__->GetType() != &Vector3::TYPE) return luaL_typerror(L, 0, "Vector3"); + Vector3* this_ = (Vector3*)this__; + + Data::Float(this_->Magnitude()).PushLuaValue(L); + return 1; + }); + return 1; + } else if (key == "Dot") { + lua_pushcfunction(L, [](lua_State* L) { + auto this__ = (Data::Base*)lua_touserdata(L, 1); + if (&this__->GetType() != &Vector3::TYPE) return luaL_typerror(L, 0, "Vector3"); + Vector3* this_ = (Vector3*)this__; + + auto arg0_ = (Data::Base*)lua_touserdata(L, 2); + if (&arg0_->GetType() != &Vector3::TYPE) return luaL_typerror(L, 1, "Vector3"); + Vector3* arg0 = (Vector3*)arg0_; + + Data::Float(this_->Dot(*arg0)).PushLuaValue(L); + return 1; + }); + return 1; + } + + return luaL_error(L, "%s is not a valid member of %s\n", key.c_str(), "Vector3"); +} + +void Data::Vector3::PushLuaLibrary(lua_State* L) { + int n = lua_gettop(L); + + lua_newuserdata(L, 0); + + // Create the library's metatable + luaL_newmetatable(L, "__mt_lib_Vector3"); + luaL_register(L, NULL, lib_metatable); + + lua_setmetatable(L, n+1); +} + void Data::Vector3::PushLuaValue(lua_State* L) const { - // TODO: - panic(); + int n = lua_gettop(L); + + // I'm torn... should this be Data::Variant, or Data::Base? + // If I ever decouple typing from Data::Base, I'll switch it to variant, + // otherwise, it doesn't make much sense to represent it as one + Vector3* userdata = (Vector3*)lua_newuserdata(L, sizeof(Vector3)); + new(userdata) Vector3(*this); + + // Create the library's metatable + luaL_newmetatable(L, "__mt_Vector3"); + luaL_register(L, NULL, metatable); + + lua_setmetatable(L, n+1); } \ No newline at end of file diff --git a/core/src/datatypes/vector.h b/core/src/datatypes/vector.h index b251234..6d8db39 100644 --- a/core/src/datatypes/vector.h +++ b/core/src/datatypes/vector.h @@ -9,7 +9,7 @@ namespace rp = reactphysics3d; namespace Data { - class DEF_DATA Vector3 : Base { + class DEF_DATA Vector3 : public Base { glm::vec3 vector; public: @@ -27,11 +27,13 @@ namespace Data { virtual const Data::String ToString() const override; virtual void Serialize(pugi::xml_node node) const override; - virtual void PushLuaValue(lua_State*) const override; static Data::Variant Deserialize(pugi::xml_node node); static std::optional FromString(std::string); + virtual void PushLuaValue(lua_State*) const override; + static void PushLuaLibrary(lua_State*); + operator glm::vec3() const; operator rp::Vector3() const; diff --git a/core/src/objects/script/scriptcontext.cpp b/core/src/objects/script/scriptcontext.cpp index b8e8e5d..f982b01 100644 --- a/core/src/objects/script/scriptcontext.cpp +++ b/core/src/objects/script/scriptcontext.cpp @@ -1,5 +1,7 @@ #include "scriptcontext.h" +#include "datatypes/meta.h" #include "logger.h" +#include #include #include #include