feat(autogen): basis for lua-driven datatypes

This commit is contained in:
maelstrom 2025-05-04 12:50:14 +02:00
parent fc1c4a06f1
commit 879d92f145
18 changed files with 412 additions and 69 deletions

View file

@ -6,6 +6,8 @@ add_executable(autogen
src/util.cpp src/util.cpp
src/object/analysis.cpp src/object/analysis.cpp
src/object/codegen.cpp src/object/codegen.cpp
src/data/analysis.cpp
src/data/codegen.cpp
) )
set_target_properties(autogen PROPERTIES OUTPUT_NAME "autogen") set_target_properties(autogen PROPERTIES OUTPUT_NAME "autogen")

View file

@ -0,0 +1,126 @@
#include "analysis.h"
#include "../util.h"
#include <clang-c/CXFile.h>
#include <clang-c/CXSourceLocation.h>
#include <clang-c/Index.h>
#include <cstdio>
#include <optional>
using namespace data;
static void processMethod(CXCursor cur, ClassAnalysis* state) {
std::optional<std::string> 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<std::string> 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;
}

View file

@ -0,0 +1,49 @@
#pragma once
#include <clang-c/Index.h>
#include <map>
#include <string>
#include <vector>
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<MethodParameter> parameters;
};
struct ClassAnalysis {
std::string name;
std::string headerPath;
std::vector<PropertyAnalysis> properties;
std::vector<MethodAnalysis> methods;
std::vector<PropertyAnalysis> staticProperties;
std::vector<MethodAnalysis> staticMethods;
};
struct AnalysisState {
std::map<std::string, ClassAnalysis> classes;
};
bool analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state);
}

View file

@ -0,0 +1,54 @@
#include "codegen.h"
#include "analysis.h"
#include <map>
#include <string>
#include <variant>
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);
}

View file

@ -0,0 +1,10 @@
#pragma once
#include "analysis.h"
#include <fstream>
namespace data {
void writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state);
}

View file

@ -6,9 +6,10 @@
#include <cstdio> #include <cstdio>
#include <fstream> #include <fstream>
#include <filesystem> #include <filesystem>
#include "object/analysis.h" #include "object/analysis.h"
#include "object/codegen.h" #include "object/codegen.h"
#include "data/analysis.h"
#include "data/codegen.h"
// namespace data { // namespace data {
// #include "data/analysis.h" // #include "data/analysis.h"
@ -17,20 +18,61 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
int processObject(fs::path srcRoot, fs::path srcPath, fs::path outPath) { // https://clang.llvm.org/docs/LibClang.html
object::AnalysisState state; 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); fs::path relpath = fs::relative(srcPath, srcRoot);
printf("[AUTOGEN] Processing file %s...\n", relpath.c_str()); 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 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()); printf("[AUTOGEN] Generating file %s...\n", relpath.c_str());
std::ofstream outStream(outPath); std::ofstream outStream(outPath);
for (auto& [_, clazz] : state.classes) { if (!objectAnlyState.classes.empty() || !dataAnlyState.classes.empty()) {
object::writeCodeForClass(outStream, clazz); 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(); outStream.close();
@ -40,22 +82,13 @@ int processObject(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
int main(int argc, char** argv) { int main(int argc, char** argv) {
if (argc < 4) { if (argc < 4) {
fprintf(stderr, "Usage: autogen <object|data> <src-root> <src-file> <out-dir>\n"); fprintf(stderr, "Usage: autogen <src-root> <src-file> <out-dir>\n");
return 1; 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]; return processHeader(srcRoot, srcPath, outPath);
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;
} }

View file

@ -167,36 +167,7 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa
} }
// https://clang.llvm.org/docs/LibClang.html // https://clang.llvm.org/docs/LibClang.html
bool object::analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state) { bool object::analyzeClasses(CXCursor cursor, 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;
// Search for classes // Search for classes
x_clang_visitChildren(cursor, [&](CXCursor cur, CXCursor parent) { x_clang_visitChildren(cursor, [&](CXCursor cur, CXCursor parent) {
CXCursorKind kind = clang_getCursorKind(cur); CXCursorKind kind = clang_getCursorKind(cur);

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <clang-c/Index.h>
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
@ -58,6 +59,6 @@ struct AnalysisState {
std::map<std::string, ClassAnalysis> classes; std::map<std::string, ClassAnalysis> classes;
}; };
bool analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state); bool analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state);
} }

View file

@ -206,7 +206,7 @@ static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
out << "\n};\n\n"; 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; std::string strFlags;
if (state.flags & ClassFlag_NotCreatable) if (state.flags & ClassFlag_NotCreatable)
strFlags += " | INSTANCE_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 if (!strFlags.empty()) strFlags = strFlags.substr(3); // Remove leading pipe
else strFlags = "0"; // 0 == No option 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; std::string constructorStr;
if (state.abstract) constructorStr = "nullptr"; if (state.abstract) constructorStr = "nullptr";
else constructorStr = "&" + state.name + "::Create"; else constructorStr = "&" + state.name + "::Create";

View file

@ -5,6 +5,6 @@
namespace object { namespace object {
void writeCodeForClass(std::ofstream& out, ClassAnalysis& state); void writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state);
} }

View file

@ -27,7 +27,7 @@ foreach (SRC ${AUTOGEN_SOURCES})
add_custom_command( add_custom_command(
OUTPUT "${OUT_PATH}" OUTPUT "${OUT_PATH}"
DEPENDS "${SRC_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}") list(APPEND AUTOGEN_OUTS "${OUT_PATH}")

View file

@ -10,7 +10,7 @@
extern "C" { typedef struct lua_State lua_State; } extern "C" { typedef struct lua_State lua_State; }
#define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME : public Data::Base { \ #define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME : public Data::Base { \
const WRAPPED_TYPE value; \ WRAPPED_TYPE value; \
public: \ public: \
CLASS_NAME(WRAPPED_TYPE); \ CLASS_NAME(WRAPPED_TYPE); \
~CLASS_NAME(); \ ~CLASS_NAME(); \

View file

@ -11,7 +11,7 @@
namespace rp = reactphysics3d; namespace rp = reactphysics3d;
namespace Data { namespace Data {
class DEF_DATA CFrame : Base { class DEF_DATA CFrame : public Base {
glm::vec3 translation; glm::vec3 translation;
glm::mat3 rotation; glm::mat3 rotation;
@ -38,9 +38,11 @@ namespace Data {
virtual const Data::String ToString() const override; virtual const Data::String ToString() const override;
virtual void Serialize(pugi::xml_node parent) 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); 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 glm::mat4() const;
operator rp::Transform() const; operator rp::Transform() const;

View file

@ -7,7 +7,7 @@
#include <reactphysics3d/reactphysics3d.h> #include <reactphysics3d/reactphysics3d.h>
namespace Data { namespace Data {
class DEF_DATA Color3 : Base { class DEF_DATA Color3 : public Base {
float r; float r;
float g; float g;
float b; float b;
@ -25,9 +25,11 @@ namespace Data {
virtual const Data::String ToString() const override; virtual const Data::String ToString() const override;
DEF_DATA_METHOD std::string ToHex() const; DEF_DATA_METHOD std::string ToHex() const;
virtual void Serialize(pugi::xml_node node) 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 Data::Variant Deserialize(pugi::xml_node node);
virtual void PushLuaValue(lua_State*) const override;
static void PushLuaLibrary(lua_State*);
operator glm::vec3() const; operator glm::vec3() const;
DEF_DATA_PROP inline float R() const { return r; } DEF_DATA_PROP inline float R() const { return r; }

View file

@ -7,7 +7,7 @@
class Instance; class Instance;
namespace Data { namespace Data {
class InstanceRef : Base { class InstanceRef : public Base {
std::weak_ptr<Instance> ref; std::weak_ptr<Instance> ref;
public: public:
InstanceRef(); InstanceRef();

View file

@ -1,8 +1,10 @@
#include "vector.h" #include "vector.h"
#include <cstdio>
#include <cstdlib>
#include <glm/ext/quaternion_geometric.hpp> #include <glm/ext/quaternion_geometric.hpp>
#include <string> #include <string>
#include "datatypes/base.h"
#include "meta.h" // IWYU pragma: keep #include "meta.h" // IWYU pragma: keep
#include "panic.h"
Data::Vector3::Vector3() : vector(glm::vec3(0, 0, 0)) {}; Data::Vector3::Vector3() : vector(glm::vec3(0, 0, 0)) {};
Data::Vector3::Vector3(const glm::vec3& src) : vector(src) {}; Data::Vector3::Vector3(const glm::vec3& src) : vector(src) {};
@ -111,7 +113,98 @@ std::optional<Data::Variant> Data::Vector3::FromString(std::string string) {
return Data::Vector3(components[0], components[1], components[2]); 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 { void Data::Vector3::PushLuaValue(lua_State* L) const {
// TODO: int n = lua_gettop(L);
panic();
// 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);
} }

View file

@ -9,7 +9,7 @@
namespace rp = reactphysics3d; namespace rp = reactphysics3d;
namespace Data { namespace Data {
class DEF_DATA Vector3 : Base { class DEF_DATA Vector3 : public Base {
glm::vec3 vector; glm::vec3 vector;
public: public:
@ -27,11 +27,13 @@ namespace Data {
virtual const Data::String ToString() const override; virtual const Data::String ToString() const override;
virtual void Serialize(pugi::xml_node node) 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 Data::Variant Deserialize(pugi::xml_node node);
static std::optional<Data::Variant> FromString(std::string); static std::optional<Data::Variant> FromString(std::string);
virtual void PushLuaValue(lua_State*) const override;
static void PushLuaLibrary(lua_State*);
operator glm::vec3() const; operator glm::vec3() const;
operator rp::Vector3() const; operator rp::Vector3() const;

View file

@ -1,5 +1,7 @@
#include "scriptcontext.h" #include "scriptcontext.h"
#include "datatypes/meta.h"
#include "logger.h" #include "logger.h"
#include <cstdint>
#include <luajit-2.1/lauxlib.h> #include <luajit-2.1/lauxlib.h>
#include <luajit-2.1/lua.h> #include <luajit-2.1/lua.h>
#include <luajit-2.1/lualib.h> #include <luajit-2.1/lualib.h>