Compare commits
No commits in common. "10d69ce7ac10b490080eba674181af1b18c518ef" and "3a3b2d12c9d1fd7fa007cf6b54b42850ea7632d0" have entirely different histories.
10d69ce7ac
...
3a3b2d12c9
74 changed files with 1010 additions and 1567 deletions
Binary file not shown.
Before Width: | Height: | Size: 537 B |
Binary file not shown.
Before Width: | Height: | Size: 825 B |
Binary file not shown.
Before Width: | Height: | Size: 802 B |
Binary file not shown.
Before Width: | Height: | Size: 774 B |
|
@ -174,25 +174,6 @@ static bool hasMethod(CXCursor cur, std::string methodName) {
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hasGenericMethod(CXCursor cur, std::string methodName) {
|
|
||||||
bool found = false;
|
|
||||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
|
||||||
CXCursorKind kind = clang_getCursorKind(cur);
|
|
||||||
if (kind != CXCursor_CXXMethod) return CXChildVisit_Continue;
|
|
||||||
|
|
||||||
if (x_clang_toString(clang_getCursorSpelling(cur)) != methodName) return CXChildVisit_Continue;
|
|
||||||
|
|
||||||
int numArgs = clang_Cursor_getNumArguments(cur);
|
|
||||||
CXCursor lastParam = clang_Cursor_getArgument(cur, numArgs - 1);
|
|
||||||
std::string lastParamType = x_clang_toString(clang_getTypeSpelling(clang_getCursorType(lastParam)));
|
|
||||||
if (lastParamType != "const TypeMeta") return CXChildVisit_Continue;
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
return CXChildVisit_Break;
|
|
||||||
});
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
|
static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
|
||||||
ClassAnalysis anly;
|
ClassAnalysis anly;
|
||||||
|
|
||||||
|
@ -203,8 +184,6 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa
|
||||||
anly.serializedName = result["name"];
|
anly.serializedName = result["name"];
|
||||||
anly.hasFromString = hasMethod(cur, "FromString");
|
anly.hasFromString = hasMethod(cur, "FromString");
|
||||||
anly.isSerializable = hasMethod(cur, "Serialize") && hasMethod(cur, "Deserialize");
|
anly.isSerializable = hasMethod(cur, "Serialize") && hasMethod(cur, "Deserialize");
|
||||||
anly.hasGenericDeserializer = hasGenericMethod(cur, "Deserialize");
|
|
||||||
anly.hasGenericFromString = hasGenericMethod(cur, "FromString");
|
|
||||||
|
|
||||||
if (anly.serializedName == "")
|
if (anly.serializedName == "")
|
||||||
anly.serializedName = className;
|
anly.serializedName = className;
|
||||||
|
|
|
@ -37,8 +37,6 @@ struct ClassAnalysis {
|
||||||
std::string headerPath;
|
std::string headerPath;
|
||||||
bool hasFromString;
|
bool hasFromString;
|
||||||
bool isSerializable;
|
bool isSerializable;
|
||||||
bool hasGenericDeserializer;
|
|
||||||
bool hasGenericFromString;
|
|
||||||
std::vector<PropertyAnalysis> properties;
|
std::vector<PropertyAnalysis> properties;
|
||||||
std::vector<MethodAnalysis> methods;
|
std::vector<MethodAnalysis> methods;
|
||||||
std::vector<PropertyAnalysis> staticProperties;
|
std::vector<PropertyAnalysis> staticProperties;
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
using namespace data;
|
using namespace data;
|
||||||
|
|
||||||
static std::map<std::string, std::string> MAPPED_TYPE = {
|
static std::map<std::string, std::string> MAPPED_TYPE = {
|
||||||
|
{ "bool", "Data::Bool" },
|
||||||
|
{ "int", "Data::Int" },
|
||||||
|
{ "float", "Data::Float" },
|
||||||
|
{ "std::string", "Data::String" },
|
||||||
{ "glm::vec3", "Vector3" },
|
{ "glm::vec3", "Vector3" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,36 +30,16 @@ static std::map<std::string, std::string> LUA_TEST_FUNCS = {
|
||||||
{ "std::string", "lua_isstring" },
|
{ "std::string", "lua_isstring" },
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::map<std::string, std::string> LUA_PUSH_FUNCS = {
|
|
||||||
{ "bool", "lua_pushboolean" },
|
|
||||||
{ "int", "lua_pushinteger" },
|
|
||||||
{ "float", "lua_pushnumber" },
|
|
||||||
// Handled specially
|
|
||||||
// { "std::string", "lua_pushstring" },
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::string getLuaMethodFqn(std::string className, std::string methodName) {
|
static std::string getLuaMethodFqn(std::string className, std::string methodName) {
|
||||||
return "__lua_impl__" + className + "__" + methodName;
|
return "__lua_impl__" + className + "__" + methodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string getMtName(std::string type) {
|
static std::string getMtName(std::string type) {
|
||||||
// if (type.starts_with("Data::"))
|
if (type.starts_with("Data::"))
|
||||||
// return "__mt_" + type.substr(6);
|
return "__mt_" + type.substr(6);
|
||||||
return "__mt_" + type;
|
return "__mt_" + type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string pushLuaValue(std::string type, std::string expr) {
|
|
||||||
if (type == "std::string")
|
|
||||||
return "lua_pushstring(L, " + expr + ".c_str())";
|
|
||||||
std::string mappedType = MAPPED_TYPE[type];
|
|
||||||
if (mappedType != "")
|
|
||||||
return mappedType + "(" + expr + ").PushLuaValue(L)";
|
|
||||||
std::string pushFunc = LUA_PUSH_FUNCS[type];
|
|
||||||
if (pushFunc != "")
|
|
||||||
return pushFunc + "(L, " + expr + ")";
|
|
||||||
return expr + ".PushLuaValue(L)";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void writeLuaGetArgument(std::ofstream& out, std::string type, int narg, bool member) {
|
static void writeLuaGetArgument(std::ofstream& out, std::string type, int narg, bool member) {
|
||||||
std::string varname = "arg" + std::to_string(narg);
|
std::string varname = "arg" + std::to_string(narg);
|
||||||
narg += 1; // Arguments start at 1
|
narg += 1; // Arguments start at 1
|
||||||
|
@ -85,7 +69,7 @@ static void writeLuaTestArgument(std::ofstream& out, std::string type, int narg,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
||||||
std::string fqn = "" + state.name;
|
std::string fqn = "Data::" + state.name;
|
||||||
|
|
||||||
// Collect all method names to account for overloaded functions
|
// Collect all method names to account for overloaded functions
|
||||||
std::map<std::string, std::vector<MethodAnalysis>> methods;
|
std::map<std::string, std::vector<MethodAnalysis>> methods;
|
||||||
|
@ -148,7 +132,11 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
||||||
|
|
||||||
// Return result
|
// Return result
|
||||||
if (methodImpl.returnType != "void") {
|
if (methodImpl.returnType != "void") {
|
||||||
out << " " << pushLuaValue(methodImpl.returnType, "result") << ";\n";
|
std::string mappedType = MAPPED_TYPE[methodImpl.returnType];
|
||||||
|
if (mappedType == "")
|
||||||
|
out << " result.PushLuaValue(L);\n";
|
||||||
|
else
|
||||||
|
out << " " << mappedType << "(result).PushLuaValue(L);\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (methodImpl.returnType == "void")
|
if (methodImpl.returnType == "void")
|
||||||
|
@ -217,7 +205,11 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
||||||
|
|
||||||
// Return result
|
// Return result
|
||||||
if (methodImpl.returnType != "void") {
|
if (methodImpl.returnType != "void") {
|
||||||
out << " " << pushLuaValue(methodImpl.returnType, "result") << ";\n";
|
std::string mappedType = MAPPED_TYPE[methodImpl.returnType];
|
||||||
|
if (mappedType == "")
|
||||||
|
out << " result.PushLuaValue(L);\n";
|
||||||
|
else
|
||||||
|
out << " " << mappedType << "(result).PushLuaValue(L);\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (methodImpl.returnType == "void")
|
if (methodImpl.returnType == "void")
|
||||||
|
@ -236,42 +228,45 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
std::string fqn = state.name;
|
std::string fqn = "Data::" + state.name;
|
||||||
|
|
||||||
out << "static int data_" << state.name << "_gc(lua_State*);\n"
|
out << "static int data_gc(lua_State*);\n"
|
||||||
"static int data_" << state.name << "_index(lua_State*);\n"
|
"static int data_index(lua_State*);\n"
|
||||||
"static int data_" << state.name << "_tostring(lua_State*);\n"
|
"static int data_tostring(lua_State*);\n"
|
||||||
"static const struct luaL_Reg " << state.name << "_metatable [] = {\n"
|
"static const struct luaL_Reg metatable [] = {\n"
|
||||||
" {\"__gc\", data_" << state.name << "_gc},\n"
|
" {\"__gc\", data_gc},\n"
|
||||||
" {\"__index\", data_" << state.name << "_index},\n"
|
" {\"__index\", data_index},\n"
|
||||||
" {\"__tostring\", data_" << state.name << "_tostring},\n"
|
" {\"__tostring\", data_tostring},\n"
|
||||||
" {NULL, NULL} /* end of array */\n"
|
" {NULL, NULL} /* end of array */\n"
|
||||||
"};\n\n";
|
"};\n\n";
|
||||||
|
|
||||||
out << "void " << state.name << "::PushLuaValue(lua_State* L) const {\n"
|
out << "void Data::" << state.name << "::PushLuaValue(lua_State* L) const {\n"
|
||||||
" int n = lua_gettop(L);\n"
|
" int n = lua_gettop(L);\n"
|
||||||
|
|
||||||
|
// " // I'm torn... should this be Data::Variant, or Data::Base?\n"
|
||||||
|
// " // If I ever decouple typing from Data::Base, I'll switch it to variant,\n"
|
||||||
|
// " // otherwise, it doesn't make much sense to represent it as one\n"
|
||||||
" " << fqn << "** userdata = (" << fqn << "**)lua_newuserdata(L, sizeof(" << fqn << "));\n"
|
" " << fqn << "** userdata = (" << fqn << "**)lua_newuserdata(L, sizeof(" << fqn << "));\n"
|
||||||
" *userdata = new " << fqn << "(*this);\n"
|
" *userdata = new " << fqn << "(*this);\n"
|
||||||
|
|
||||||
" // Create the library's metatable\n"
|
" // Create the library's metatable\n"
|
||||||
" luaL_newmetatable(L, \"__mt_" << state.name << "\");\n"
|
" luaL_newmetatable(L, \"__mt_" << state.name << "\");\n"
|
||||||
" luaL_register(L, NULL, " << state.name << "_metatable);\n"
|
" luaL_register(L, NULL, metatable);\n"
|
||||||
|
|
||||||
" lua_setmetatable(L, n+1);\n"
|
" lua_setmetatable(L, n+1);\n"
|
||||||
"}\n\n";
|
"}\n\n";
|
||||||
|
|
||||||
|
|
||||||
out << "result<Variant, LuaCastError> " << state.name << "::FromLuaValue(lua_State* L, int idx) {\n"
|
out << "result<Data::Variant, LuaCastError> Data::" << state.name << "::FromLuaValue(lua_State* L, int idx) {\n"
|
||||||
" " << fqn << "** userdata = (" << fqn << "**) luaL_testudata(L, idx, \"" << getMtName(state.name) << "\");\n"
|
" " << fqn << "** userdata = (" << fqn << "**) luaL_testudata(L, idx, \"" << getMtName(state.name) << "\");\n"
|
||||||
" if (userdata == nullptr)\n"
|
" if (userdata == nullptr)\n"
|
||||||
" return LuaCastError(lua_typename(L, idx), \"" << state.name << "\");\n"
|
" return LuaCastError(lua_typename(L, idx), \"" << state.name << "\");\n"
|
||||||
" return Variant(**userdata);\n"
|
" return Data::Variant(**userdata);\n"
|
||||||
"}\n\n";
|
"}\n\n";
|
||||||
|
|
||||||
// Indexing methods and properties
|
// Indexing methods and properties
|
||||||
|
|
||||||
out << "static int data_" << state.name << "_index(lua_State* L) {\n"
|
out << "static int data_index(lua_State* L) {\n"
|
||||||
" " << fqn << "* this_ = *(" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n"
|
" " << fqn << "* this_ = *(" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n"
|
||||||
"\n"
|
"\n"
|
||||||
" std::string key(lua_tostring(L, 2));\n"
|
" std::string key(lua_tostring(L, 2));\n"
|
||||||
|
@ -297,7 +292,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
valueExpr = "this_->" + prop.backingSymbol + "()";
|
valueExpr = "this_->" + prop.backingSymbol + "()";
|
||||||
|
|
||||||
// This largely depends on the type
|
// This largely depends on the type
|
||||||
out << " " << pushLuaValue(type, valueExpr) << ";\n";
|
out << " " << type << "(" << valueExpr << ").PushLuaValue(L);\n";
|
||||||
out << " return 1;\n";
|
out << " return 1;\n";
|
||||||
|
|
||||||
out << " }";
|
out << " }";
|
||||||
|
@ -323,7 +318,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
|
|
||||||
// ToString
|
// ToString
|
||||||
|
|
||||||
out << "\nint data_" << state.name << "_tostring(lua_State* L) {\n"
|
out << "\nint data_tostring(lua_State* L) {\n"
|
||||||
" " << fqn << "* this_ = *(" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n"
|
" " << fqn << "* this_ = *(" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n"
|
||||||
" lua_pushstring(L, std::string(this_->ToString()).c_str());\n"
|
" lua_pushstring(L, std::string(this_->ToString()).c_str());\n"
|
||||||
" return 1;\n"
|
" return 1;\n"
|
||||||
|
@ -331,7 +326,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
|
|
||||||
out << "\nint data_" << state.name << "_gc(lua_State* L) {\n"
|
out << "\nint data_gc(lua_State* L) {\n"
|
||||||
" " << fqn << "** userdata = (" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n"
|
" " << fqn << "** userdata = (" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n"
|
||||||
" delete *userdata;\n"
|
" delete *userdata;\n"
|
||||||
" return 0;\n"
|
" return 0;\n"
|
||||||
|
@ -339,18 +334,15 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
std::string fqn = state.name;
|
std::string fqn = "Data::" + state.name;
|
||||||
|
|
||||||
// If there are no static methods or properties, no need to create a library
|
out << "static int lib_index(lua_State*);\n"
|
||||||
if (state.staticMethods.size() == 0 && state.staticProperties.size() == 0) return;
|
"static const struct luaL_Reg lib_metatable [] = {\n"
|
||||||
|
" {\"__index\", lib_index},\n"
|
||||||
out << "static int lib_" << state.name << "_index(lua_State*);\n"
|
|
||||||
"static const struct luaL_Reg lib_" << state.name << "_metatable [] = {\n"
|
|
||||||
" {\"__index\", lib_" << state.name << "_index},\n"
|
|
||||||
" {NULL, NULL} /* end of array */\n"
|
" {NULL, NULL} /* end of array */\n"
|
||||||
"};\n\n";
|
"};\n\n";
|
||||||
|
|
||||||
out << "void " << state.name << "::PushLuaLibrary(lua_State* L) {\n"
|
out << "void Data::" << state.name << "::PushLuaLibrary(lua_State* L) {\n"
|
||||||
" lua_getglobal(L, \"_G\");\n"
|
" lua_getglobal(L, \"_G\");\n"
|
||||||
" lua_pushstring(L, \"" << state.name << "\");\n"
|
" lua_pushstring(L, \"" << state.name << "\");\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -358,7 +350,7 @@ static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
"\n"
|
"\n"
|
||||||
" // Create the library's metatable\n"
|
" // Create the library's metatable\n"
|
||||||
" luaL_newmetatable(L, \"__mt_lib_" << state.name << "\");\n"
|
" luaL_newmetatable(L, \"__mt_lib_" << state.name << "\");\n"
|
||||||
" luaL_register(L, NULL, lib_" << state.name << "_metatable);\n"
|
" luaL_register(L, NULL, lib_metatable);\n"
|
||||||
" lua_setmetatable(L, -2);\n"
|
" lua_setmetatable(L, -2);\n"
|
||||||
"\n"
|
"\n"
|
||||||
" lua_rawset(L, -3);\n"
|
" lua_rawset(L, -3);\n"
|
||||||
|
@ -367,7 +359,7 @@ static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
|
|
||||||
// Indexing methods and properties
|
// Indexing methods and properties
|
||||||
|
|
||||||
out << "static int lib_" << state.name << "_index(lua_State* L) {\n"
|
out << "static int lib_index(lua_State* L) {\n"
|
||||||
" std::string key(lua_tostring(L, 2));\n"
|
" std::string key(lua_tostring(L, 2));\n"
|
||||||
" lua_pop(L, 2);\n"
|
" lua_pop(L, 2);\n"
|
||||||
"\n";
|
"\n";
|
||||||
|
@ -390,7 +382,7 @@ static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
else if (prop.backingType == PropertyBackingType::Method)
|
else if (prop.backingType == PropertyBackingType::Method)
|
||||||
valueExpr = fqn + "::" + prop.backingSymbol + "()";
|
valueExpr = fqn + "::" + prop.backingSymbol + "()";
|
||||||
|
|
||||||
out << " " << pushLuaValue(type, valueExpr) << ";\n";
|
out << " " << type << "(" << valueExpr << ").PushLuaValue(L);\n";
|
||||||
out << " return 1;\n";
|
out << " return 1;\n";
|
||||||
|
|
||||||
out << " }";
|
out << " }";
|
||||||
|
@ -416,29 +408,22 @@ static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) {
|
void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) {
|
||||||
|
std::string fqn = "Data::" + state.name;
|
||||||
|
|
||||||
out << "#define __AUTOGEN_EXTRA_INCLUDES__\n";
|
out << "#define __AUTOGEN_EXTRA_INCLUDES__\n";
|
||||||
out << "#include \"" << headerPath << "\"\n\n";
|
out << "#include \"" << headerPath << "\"\n\n";
|
||||||
out << "#include \"datatypes/variant.h\"\n";
|
out << "#include \"datatypes/meta.h\"\n";
|
||||||
out << "#include <pugixml.hpp>\n";
|
out << "#include <pugixml.hpp>\n";
|
||||||
out << "#include \"lua.h\"\n\n";
|
out << "#include \"lua.h\"\n\n";
|
||||||
out << "const TypeDesc " << state.name << "::TYPE = {\n"
|
out << "const Data::TypeInfo " << fqn << "::TYPE = {\n"
|
||||||
<< " .name = \"" << state.serializedName << "\",\n";
|
<< " .name = \"" << state.serializedName << "\",\n"
|
||||||
if (state.isSerializable) {
|
<< " .deserializer = &" << fqn << "::Deserialize,\n";
|
||||||
out << " .serialize = toVariantFunction(&" << state.name << "::Serialize),";
|
if (state.hasFromString) out << " .fromString = &" << fqn << "::FromString,\n";
|
||||||
if (state.hasGenericDeserializer)
|
out << " .fromLuaValue = &" << fqn << "::FromLuaValue,\n"
|
||||||
out << " .deserialize = toVariantGenerator(&" << state.name << "::Deserialize),\n";
|
<< "};\n\n";
|
||||||
else
|
|
||||||
out << " .deserialize = toVariantGeneratorNoMeta(&" << state.name << "::Deserialize),\n";
|
out << "const Data::TypeInfo& " << fqn << "::GetType() const {\n"
|
||||||
}
|
<< " return TYPE;\n"
|
||||||
out << " .toString = toVariantFunction(&" << state.name << "::ToString),";
|
|
||||||
if (state.hasFromString) {
|
|
||||||
if (state.hasGenericFromString)
|
|
||||||
out << " .fromString = toVariantGenerator(&" << state.name << "::FromString),\n";
|
|
||||||
else
|
|
||||||
out << " .fromString = toVariantGeneratorNoMeta(&" << state.name << "::FromString),\n";
|
|
||||||
}
|
|
||||||
out << " .pushLuaValue = toVariantFunction(&" << state.name << "::PushLuaValue),"
|
|
||||||
<< " .fromLuaValue = toVariantGenerator(&" << state.name << "::FromLuaValue),\n"
|
|
||||||
<< "};\n\n";
|
<< "};\n\n";
|
||||||
|
|
||||||
writeLuaMethodImpls(out, state);
|
writeLuaMethodImpls(out, state);
|
||||||
|
|
|
@ -54,7 +54,7 @@ int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
|
||||||
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(cursor, srcRoot, &objectAnlyState);
|
object::analyzeClasses(cursor, srcRoot, &objectAnlyState);
|
||||||
analyzeClasses(cursor, srcRoot, &dataAnlyState);
|
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
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& [_, clazz] : dataAnlyState.classes) {
|
for (auto& [_, clazz] : dataAnlyState.classes) {
|
||||||
writeCodeForClass(outStream, relpath, clazz);
|
data::writeCodeForClass(outStream, relpath, clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
outStream.close();
|
outStream.close();
|
||||||
|
|
|
@ -16,17 +16,13 @@ static std::map<std::string, std::string> CATEGORY_STR = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::map<std::string, std::string> MAPPED_TYPE = {
|
static std::map<std::string, std::string> MAPPED_TYPE = {
|
||||||
|
{ "bool", "Data::Bool" },
|
||||||
|
{ "int", "Data::Int" },
|
||||||
|
{ "float", "Data::Float" },
|
||||||
|
{ "std::string", "Data::String" },
|
||||||
{ "glm::vec3", "Vector3" },
|
{ "glm::vec3", "Vector3" },
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::map<std::string, std::string> TYPEINFO_REFS = {
|
|
||||||
{ "glm::vec3", "Vector3::TYPE" },
|
|
||||||
{ "bool", "BOOL_TYPE" },
|
|
||||||
{ "int", "INT_TYPE" },
|
|
||||||
{ "float", "FLOAT_TYPE" },
|
|
||||||
{ "std::string", "STRING_TYPE" },
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::map<std::string, std::monostate> ENUM_TYPES = {
|
static std::map<std::string, std::monostate> ENUM_TYPES = {
|
||||||
{ "SurfaceType", std::monostate() }
|
{ "SurfaceType", std::monostate() }
|
||||||
};
|
};
|
||||||
|
@ -44,7 +40,7 @@ static std::string parseWeakPtr(std::string weakPtrType) {
|
||||||
static std::string castFromVariant(std::string valueStr, std::string fieldType) {
|
static std::string castFromVariant(std::string valueStr, std::string fieldType) {
|
||||||
// Manual exception for now, enums will get their own system eventually
|
// Manual exception for now, enums will get their own system eventually
|
||||||
if (fieldType == "SurfaceType") {
|
if (fieldType == "SurfaceType") {
|
||||||
return "(SurfaceType)(int)" + valueStr + ".get<int>()";
|
return "(SurfaceType)(int)" + valueStr + ".get<Data::Int>()";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string mappedType = MAPPED_TYPE[fieldType];
|
std::string mappedType = MAPPED_TYPE[fieldType];
|
||||||
|
@ -54,13 +50,13 @@ static std::string castFromVariant(std::string valueStr, std::string fieldType)
|
||||||
static std::string castToVariant(std::string valueStr, std::string fieldType) {
|
static std::string castToVariant(std::string valueStr, std::string fieldType) {
|
||||||
// Manual exception for now, enums will get their own system eventually
|
// Manual exception for now, enums will get their own system eventually
|
||||||
if (fieldType == "SurfaceType") {
|
if (fieldType == "SurfaceType") {
|
||||||
return "(int)" + valueStr;
|
return "Data::Int((int)" + valueStr + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::shared_ptr<Instance>
|
// InstanceRef
|
||||||
std::string subtype = parseWeakPtr(fieldType);
|
std::string subtype = parseWeakPtr(fieldType);
|
||||||
if (!subtype.empty()) {
|
if (!subtype.empty()) {
|
||||||
return "Variant(" + valueStr + ".expired() ? InstanceRef() : InstanceRef(std::dynamic_pointer_cast<Instance>(" + valueStr + ".lock())))";
|
return "Data::Variant(" + valueStr + ".expired() ? Data::InstanceRef() : Data::InstanceRef(std::dynamic_pointer_cast<Instance>(" + valueStr + ".lock())))";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string mappedType = MAPPED_TYPE[fieldType];
|
std::string mappedType = MAPPED_TYPE[fieldType];
|
||||||
|
@ -71,13 +67,13 @@ static std::string castToVariant(std::string valueStr, std::string fieldType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) {
|
static void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
out << "fallible<MemberNotFound, AssignToReadOnlyMember> " << state.name << "::InternalSetPropertyValue(std::string name, Variant value) {";
|
out << "fallible<MemberNotFound, AssignToReadOnlyMember> " << state.name << "::InternalSetPropertyValue(std::string name, Data::Variant value) {";
|
||||||
|
|
||||||
out << "\n ";
|
out << "\n ";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto& prop : state.properties) {
|
for (auto& prop : state.properties) {
|
||||||
out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {";
|
out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {";
|
||||||
// std::shared_ptr<Instance>
|
// InstanceRef
|
||||||
std::string subtype = parseWeakPtr(prop.backingFieldType);
|
std::string subtype = parseWeakPtr(prop.backingFieldType);
|
||||||
|
|
||||||
if (prop.flags & PropertyFlag_Readonly) {
|
if (prop.flags & PropertyFlag_Readonly) {
|
||||||
|
@ -87,7 +83,7 @@ static void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
} else if (prop.cframeMember == CFrameMember_Rotation) {
|
} else if (prop.cframeMember == CFrameMember_Rotation) {
|
||||||
out << "\n this->" << prop.fieldName << " = CFrame::FromEulerAnglesXYZ(value.get<Vector3>()) + this->" << prop.fieldName << ".Position();";
|
out << "\n this->" << prop.fieldName << " = CFrame::FromEulerAnglesXYZ(value.get<Vector3>()) + this->" << prop.fieldName << ".Position();";
|
||||||
} else if (!subtype.empty()) {
|
} else if (!subtype.empty()) {
|
||||||
out << "\n std::weak_ptr<Instance> ref = value.get<InstanceRef>();"
|
out << "\n std::weak_ptr<Instance> ref = value.get<Data::InstanceRef>();"
|
||||||
<< "\n this->" << prop.fieldName << " = ref.expired() ? std::weak_ptr<" << subtype << ">() : std::dynamic_pointer_cast<" << subtype << ">(ref.lock());";
|
<< "\n this->" << prop.fieldName << " = ref.expired() ? std::weak_ptr<" << subtype << ">() : std::dynamic_pointer_cast<" << subtype << ">(ref.lock());";
|
||||||
} else {
|
} else {
|
||||||
out << "\n this->" << prop.fieldName << " = " << castFromVariant("value", prop.backingFieldType) << ";";
|
out << "\n this->" << prop.fieldName << " = " << castFromVariant("value", prop.backingFieldType) << ";";
|
||||||
|
@ -132,7 +128,7 @@ static void writePropertyUpdateHandler(std::ofstream& out, ClassAnalysis state)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
out << "result<Variant, MemberNotFound> " << state.name << "::InternalGetPropertyValue(std::string name) {";
|
out << "result<Data::Variant, MemberNotFound> " << state.name << "::InternalGetPropertyValue(std::string name) {";
|
||||||
|
|
||||||
out << "\n ";
|
out << "\n ";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
@ -140,11 +136,11 @@ static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {";
|
out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {";
|
||||||
|
|
||||||
if (prop.cframeMember == CFrameMember_Position) {
|
if (prop.cframeMember == CFrameMember_Position) {
|
||||||
out << "\n return Variant(" << prop.fieldName << ".Position());";
|
out << "\n return Data::Variant(" << prop.fieldName << ".Position());";
|
||||||
} else if (prop.cframeMember == CFrameMember_Rotation) {
|
} else if (prop.cframeMember == CFrameMember_Rotation) {
|
||||||
out << "\n return Variant(" << prop.fieldName << ".ToEulerAnglesXYZ());";
|
out << "\n return Data::Variant(" << prop.fieldName << ".ToEulerAnglesXYZ());";
|
||||||
} else {
|
} else {
|
||||||
out << "\n return Variant(" << castToVariant(prop.fieldName, prop.backingFieldType) << ");";
|
out << "\n return Data::Variant(" << castToVariant(prop.fieldName, prop.backingFieldType) << ");";
|
||||||
}
|
}
|
||||||
|
|
||||||
out << "\n }";
|
out << "\n }";
|
||||||
|
@ -157,7 +153,7 @@ static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
for (auto& signal : state.signals) {
|
for (auto& signal : state.signals) {
|
||||||
out << (first ? "" : " else ") << "if (name == \"" << signal.name << "\") {";
|
out << (first ? "" : " else ") << "if (name == \"" << signal.name << "\") {";
|
||||||
|
|
||||||
out << "\n return Variant(SignalRef(" << signal.sourceFieldName << "));";
|
out << "\n return Data::Variant(Data::SignalRef(" << signal.sourceFieldName << "));";
|
||||||
|
|
||||||
out << "\n }";
|
out << "\n }";
|
||||||
first = false;
|
first = false;
|
||||||
|
@ -190,10 +186,10 @@ static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
for (auto& prop : state.properties) {
|
for (auto& prop : state.properties) {
|
||||||
out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {";
|
out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {";
|
||||||
|
|
||||||
std::string typeInfo = TYPEINFO_REFS[prop.backingFieldType];
|
std::string type = MAPPED_TYPE[prop.backingFieldType];
|
||||||
if (typeInfo.empty()) typeInfo = prop.backingFieldType + "::TYPE";
|
if (type.empty()) type = prop.backingFieldType;
|
||||||
if (!parseWeakPtr(prop.backingFieldType).empty()) typeInfo = "InstanceRef::TYPE";
|
if (type == "SurfaceType") type = "Data::Int";
|
||||||
if (prop.backingFieldType == "SurfaceType") typeInfo = "INT_TYPE";
|
if (!parseWeakPtr(prop.backingFieldType).empty()) type = "Data::InstanceRef";
|
||||||
|
|
||||||
std::string strFlags;
|
std::string strFlags;
|
||||||
if (prop.flags & PropertyFlag_Readonly)
|
if (prop.flags & PropertyFlag_Readonly)
|
||||||
|
@ -210,7 +206,7 @@ static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
std::string category = CATEGORY_STR[prop.category];
|
std::string category = CATEGORY_STR[prop.category];
|
||||||
if (category.empty()) category = "PROP_CATEGORY_DATA";
|
if (category.empty()) category = "PROP_CATEGORY_DATA";
|
||||||
|
|
||||||
out << "\n return PropertyMeta { &" << typeInfo << ", " << strFlags << ", " << category << " };";
|
out << "\n return PropertyMeta { &" << type << "::TYPE, " << strFlags << ", " << category << " };";
|
||||||
|
|
||||||
out << "\n }";
|
out << "\n }";
|
||||||
first = false;
|
first = false;
|
||||||
|
@ -224,8 +220,8 @@ static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
|
||||||
|
|
||||||
std::string strFlags;
|
std::string strFlags;
|
||||||
strFlags += "PROP_READONLY";
|
strFlags += "PROP_READONLY";
|
||||||
strFlags += " | PROP_HIDDEN";
|
strFlags += "| PROP_HIDDEN";
|
||||||
strFlags += " | PROP_NOSAVE";
|
strFlags += "| PROP_NOSAVE";
|
||||||
|
|
||||||
out << "\n return PropertyMeta { &SignalRef::TYPE, " << strFlags << " };";
|
out << "\n return PropertyMeta { &SignalRef::TYPE, " << strFlags << " };";
|
||||||
|
|
||||||
|
@ -256,8 +252,7 @@ void object::writeCodeForClass(std::ofstream& out, std::string headerPath, Class
|
||||||
|
|
||||||
out << "#define __AUTOGEN_EXTRA_INCLUDES__\n";
|
out << "#define __AUTOGEN_EXTRA_INCLUDES__\n";
|
||||||
out << "#include \"" << state.headerPath << "\"\n\n";
|
out << "#include \"" << state.headerPath << "\"\n\n";
|
||||||
out << "#include \"datatypes/variant.h\"\n\n";
|
out << "#include \"datatypes/meta.h\"\n\n";
|
||||||
out << "#include \"datatypes/primitives.h\"\n\n";
|
|
||||||
out << "const InstanceType " << state.name << "::TYPE = {\n"
|
out << "const InstanceType " << state.name << "::TYPE = {\n"
|
||||||
<< " .super = &" << state.baseClass << "::TYPE,\n"
|
<< " .super = &" << state.baseClass << "::TYPE,\n"
|
||||||
<< " .className = \"" << state.name << "\",\n"
|
<< " .className = \"" << state.name << "\",\n"
|
||||||
|
|
|
@ -52,7 +52,7 @@ int main() {
|
||||||
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
for (std::shared_ptr<Instance> inst : gWorkspace()->GetChildren()) {
|
for (InstanceRef inst : gWorkspace()->GetChildren()) {
|
||||||
if (inst->GetClass()->className != "Part") continue;
|
if (inst->GetClass()->className != "Part") continue;
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
||||||
gWorkspace()->SyncPartPhysics(part);
|
gWorkspace()->SyncPartPhysics(part);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// TEMPORARY COMMON DATA FOR DIFFERENT INTERNAL COMPONENTS
|
// TEMPORARY COMMON DATA FOR DIFFERENT INTERNAL COMPONENTS
|
||||||
|
|
||||||
#include "objects/datamodel.h"
|
#include "objects/datamodel.h"
|
||||||
#include "datatypes/variant.h"
|
#include "datatypes/meta.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -14,11 +14,11 @@ std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
||||||
Handles editorToolHandles;
|
Handles editorToolHandles;
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Instance>> currentSelection;
|
std::vector<InstanceRefWeak> currentSelection;
|
||||||
std::vector<SelectionUpdateHandler> selectionUpdateListeners;
|
std::vector<SelectionUpdateHandler> selectionUpdateListeners;
|
||||||
std::vector<PropertyUpdateHandler> propertyUpdatelisteners;
|
std::vector<PropertyUpdateHandler> propertyUpdatelisteners;
|
||||||
|
|
||||||
void setSelection(std::vector<std::shared_ptr<Instance>> newSelection, bool fromExplorer) {
|
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer) {
|
||||||
for (SelectionUpdateHandler handler : selectionUpdateListeners) {
|
for (SelectionUpdateHandler handler : selectionUpdateListeners) {
|
||||||
handler(currentSelection, newSelection, fromExplorer);
|
handler(currentSelection, newSelection, fromExplorer);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ void setSelection(std::vector<std::shared_ptr<Instance>> newSelection, bool from
|
||||||
currentSelection = newSelection;
|
currentSelection = newSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<Instance>> getSelection() {
|
const std::vector<InstanceRefWeak> getSelection() {
|
||||||
return currentSelection;
|
return currentSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ void addSelectionListener(SelectionUpdateHandler handler) {
|
||||||
selectionUpdateListeners.push_back(handler);
|
selectionUpdateListeners.push_back(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendPropertyUpdatedSignal(std::shared_ptr<Instance> instance, std::string property, Variant newValue) {
|
void sendPropertyUpdatedSignal(InstanceRef instance, std::string property, Data::Variant newValue) {
|
||||||
for (PropertyUpdateHandler handler : propertyUpdatelisteners) {
|
for (PropertyUpdateHandler handler : propertyUpdatelisteners) {
|
||||||
handler(instance, property, newValue);
|
handler(instance, property, newValue);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
|
|
||||||
class Instance;
|
class Instance;
|
||||||
// typedef std::function<void(std::shared_ptr<Instance> element, std::optional<std::shared_ptr<Instance>> newParent)> HierarchyUpdateHandler;
|
// typedef std::function<void(std::shared_ptr<Instance> element, std::optional<std::shared_ptr<Instance>> newParent)> HierarchyUpdateHandler;
|
||||||
typedef std::function<void(std::shared_ptr<Instance> object, std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent)> HierarchyPreUpdateHandler;
|
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPreUpdateHandler;
|
||||||
typedef std::function<void(std::shared_ptr<Instance> object, std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent)> HierarchyPostUpdateHandler;
|
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPostUpdateHandler;
|
||||||
typedef std::function<void(std::vector<std::shared_ptr<Instance>> oldSelection, std::vector<std::shared_ptr<Instance>> newSelection, bool fromExplorer)> SelectionUpdateHandler;
|
typedef std::function<void(std::vector<InstanceRefWeak> oldSelection, std::vector<InstanceRefWeak> newSelection, bool fromExplorer)> SelectionUpdateHandler;
|
||||||
typedef std::function<void(std::shared_ptr<Instance> instance, std::string property, Variant newValue)> PropertyUpdateHandler;
|
typedef std::function<void(InstanceRef instance, std::string property, Data::Variant newValue)> PropertyUpdateHandler;
|
||||||
|
|
||||||
// TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS
|
// TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
|
||||||
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
||||||
extern Handles editorToolHandles;
|
extern Handles editorToolHandles;
|
||||||
|
|
||||||
void setSelection(std::vector<std::shared_ptr<Instance>> newSelection, bool fromExplorer = false);
|
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer = false);
|
||||||
const std::vector<std::shared_ptr<Instance>> getSelection();
|
const std::vector<InstanceRefWeak> getSelection();
|
||||||
void addSelectionListener(SelectionUpdateHandler handler);
|
void addSelectionListener(SelectionUpdateHandler handler);
|
||||||
|
|
||||||
void sendPropertyUpdatedSignal(std::shared_ptr<Instance> instance, std::string property, Variant newValue);
|
void sendPropertyUpdatedSignal(InstanceRef instance, std::string property, Data::Variant newValue);
|
||||||
void addPropertyUpdateListener(PropertyUpdateHandler handler);
|
void addPropertyUpdateListener(PropertyUpdateHandler handler);
|
|
@ -26,7 +26,8 @@
|
||||||
|
|
||||||
#define AUTOGEN_PREAMBLE_DATA \
|
#define AUTOGEN_PREAMBLE_DATA \
|
||||||
public: \
|
public: \
|
||||||
static const TypeDesc TYPE; \
|
virtual const TypeInfo& GetType() const override; \
|
||||||
virtual void PushLuaValue(lua_State*) const; \
|
static const TypeInfo TYPE; \
|
||||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int idx); \
|
virtual void PushLuaValue(lua_State*) const override; \
|
||||||
|
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx); \
|
||||||
private:
|
private:
|
||||||
|
|
162
core/src/datatypes/base.cpp
Normal file
162
core/src/datatypes/base.cpp
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
#include "base.h"
|
||||||
|
#include "error/data.h"
|
||||||
|
#include "meta.h"
|
||||||
|
#include <ios>
|
||||||
|
#include <sstream>
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
|
#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE, TYPE_NAME) Data::CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : value(in) {} \
|
||||||
|
Data::CLASS_NAME::~CLASS_NAME() = default; \
|
||||||
|
Data::CLASS_NAME::operator const WRAPPED_TYPE() const { return value; } \
|
||||||
|
const Data::TypeInfo Data::CLASS_NAME::TYPE = { \
|
||||||
|
.name = TYPE_NAME, \
|
||||||
|
.deserializer = &Data::CLASS_NAME::Deserialize, \
|
||||||
|
.fromString = &Data::CLASS_NAME::FromString, \
|
||||||
|
.fromLuaValue = &Data::CLASS_NAME::FromLuaValue, \
|
||||||
|
}; \
|
||||||
|
const Data::TypeInfo& Data::CLASS_NAME::GetType() const { return Data::CLASS_NAME::TYPE; }; \
|
||||||
|
void Data::CLASS_NAME::Serialize(pugi::xml_node node) const { node.text().set(std::string(this->ToString())); }
|
||||||
|
|
||||||
|
Data::Base::~Base() {};
|
||||||
|
|
||||||
|
Data::Null::Null() {};
|
||||||
|
Data::Null::~Null() = default;
|
||||||
|
const Data::TypeInfo Data::Null::TYPE = {
|
||||||
|
.name = "null",
|
||||||
|
.deserializer = &Data::Null::Deserialize,
|
||||||
|
};
|
||||||
|
const Data::TypeInfo& Data::Null::GetType() const { return Data::Null::TYPE; };
|
||||||
|
|
||||||
|
const Data::String Data::Null::ToString() const {
|
||||||
|
return Data::String("null");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::Null::Serialize(pugi::xml_node node) const {
|
||||||
|
node.text().set("null");
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Null::Deserialize(pugi::xml_node node) {
|
||||||
|
return Data::Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::Null::PushLuaValue(lua_State* L) const {
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
result<Data::Variant, LuaCastError> Data::Null::FromLuaValue(lua_State* L, int idx) {
|
||||||
|
return Data::Variant(Data::Null());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
IMPL_WRAPPER_CLASS(Bool, bool, "bool")
|
||||||
|
IMPL_WRAPPER_CLASS(Int, int, "int")
|
||||||
|
IMPL_WRAPPER_CLASS(Float, float, "float")
|
||||||
|
IMPL_WRAPPER_CLASS(String, std::string, "string")
|
||||||
|
|
||||||
|
// ToString
|
||||||
|
|
||||||
|
const Data::String Data::Bool::ToString() const {
|
||||||
|
return Data::String(value ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
const Data::String Data::Int::ToString() const {
|
||||||
|
return Data::String(std::to_string(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Data::String Data::Float::ToString() const {
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << std::noshowpoint << value;
|
||||||
|
return Data::String(stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
const Data::String Data::String::ToString() const {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize
|
||||||
|
|
||||||
|
Data::Variant Data::Bool::Deserialize(pugi::xml_node node) {
|
||||||
|
return Data::Bool(node.text().as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Int::Deserialize(pugi::xml_node node) {
|
||||||
|
return Data::Int(node.text().as_int());
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Float::Deserialize(pugi::xml_node node) {
|
||||||
|
return Data::Float(node.text().as_float());
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::String::Deserialize(pugi::xml_node node) {
|
||||||
|
return Data::String(node.text().as_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromString
|
||||||
|
|
||||||
|
std::optional<Data::Variant> Data::Bool::FromString(std::string string) {
|
||||||
|
return Data::Bool(string[0] == 't' || string[0] == 'T' || string[0] == '1' || string[0] == 'y' || string[0] == 'Y');
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Data::Variant> Data::Int::FromString(std::string string) {
|
||||||
|
char* endPos;
|
||||||
|
int value = (int)std::strtol(string.c_str(), &endPos, 10);
|
||||||
|
if (endPos == string.c_str()) return std::nullopt;
|
||||||
|
return Data::Int(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Data::Variant> Data::Float::FromString(std::string string) {
|
||||||
|
char* endPos;
|
||||||
|
float value = std::strtof(string.c_str(), &endPos);
|
||||||
|
if (endPos == string.c_str()) return std::nullopt;
|
||||||
|
return Data::Float(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Data::Variant> Data::String::FromString(std::string string) {
|
||||||
|
return Data::String(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushLuaValue
|
||||||
|
|
||||||
|
void Data::Bool::PushLuaValue(lua_State* L) const {
|
||||||
|
lua_pushboolean(L, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::Int::PushLuaValue(lua_State* L) const {
|
||||||
|
lua_pushinteger(L, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::Float::PushLuaValue(lua_State* L) const {
|
||||||
|
lua_pushnumber(L, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::String::PushLuaValue(lua_State* L) const {
|
||||||
|
lua_pushstring(L, value.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromLuaValue
|
||||||
|
|
||||||
|
result<Data::Variant, LuaCastError> Data::Bool::FromLuaValue(lua_State* L, int idx) {
|
||||||
|
if (!lua_isboolean(L, idx))
|
||||||
|
return LuaCastError(lua_typename(L, idx), "boolean");
|
||||||
|
return Data::Variant(Data::Bool(lua_toboolean(L, idx)));
|
||||||
|
}
|
||||||
|
|
||||||
|
result<Data::Variant, LuaCastError> Data::Int::FromLuaValue(lua_State* L, int idx) {
|
||||||
|
if (!lua_isnumber(L, idx))
|
||||||
|
return LuaCastError(lua_typename(L, idx), "integer");
|
||||||
|
return Data::Variant(Data::Int((int)lua_tonumber(L, idx)));
|
||||||
|
}
|
||||||
|
|
||||||
|
result<Data::Variant, LuaCastError> Data::Float::FromLuaValue(lua_State* L, int idx) {
|
||||||
|
if (!lua_isnumber(L, idx))
|
||||||
|
return LuaCastError(lua_typename(L, idx), "float");
|
||||||
|
return Data::Variant(Data::Float((float)lua_tonumber(L, idx)));
|
||||||
|
}
|
||||||
|
|
||||||
|
result<Data::Variant, LuaCastError> Data::String::FromLuaValue(lua_State* L, int idx) {
|
||||||
|
if (!lua_tostring(L, idx))
|
||||||
|
return LuaCastError(lua_typename(L, idx), "string");
|
||||||
|
return Data::Variant(Data::String(lua_tostring(L, idx)));
|
||||||
|
}
|
|
@ -10,39 +10,67 @@ extern "C" { typedef struct lua_State lua_State; }
|
||||||
|
|
||||||
namespace pugi { class xml_node; };
|
namespace pugi { class xml_node; };
|
||||||
|
|
||||||
class Variant;
|
#define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME : public Data::Base { \
|
||||||
struct TypeMeta;
|
WRAPPED_TYPE value; \
|
||||||
|
public: \
|
||||||
typedef std::function<void(Variant, pugi::xml_node)> Serialize;
|
CLASS_NAME(WRAPPED_TYPE); \
|
||||||
typedef std::function<result<Variant, DataParseError>(pugi::xml_node, const TypeMeta)> Deserialize;
|
~CLASS_NAME(); \
|
||||||
typedef std::function<std::string(Variant)> ToString;
|
operator const WRAPPED_TYPE() const; \
|
||||||
typedef std::function<result<Variant, DataParseError>(std::string, const TypeMeta)> FromString;
|
virtual const TypeInfo& GetType() const override; \
|
||||||
typedef std::function<result<Variant, LuaCastError>(lua_State*, int idx)> FromLuaValue;
|
static const TypeInfo TYPE; \
|
||||||
typedef std::function<void(Variant self, lua_State*)> PushLuaValue;
|
\
|
||||||
|
virtual const Data::String ToString() const override; \
|
||||||
// Describes a concrete type
|
virtual void Serialize(pugi::xml_node node) const override; \
|
||||||
struct TypeDesc {
|
virtual void PushLuaValue(lua_State*) const override; \
|
||||||
std::string name;
|
\
|
||||||
Serialize serialize;
|
static Data::Variant Deserialize(pugi::xml_node node); \
|
||||||
Deserialize deserialize;
|
static std::optional<Data::Variant> FromString(std::string); \
|
||||||
ToString toString;
|
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx); \
|
||||||
FromString fromString;
|
|
||||||
PushLuaValue pushLuaValue;
|
|
||||||
FromLuaValue fromLuaValue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Enum;
|
namespace Data {
|
||||||
struct InstanceType;
|
class Variant;
|
||||||
|
typedef std::function<Data::Variant(pugi::xml_node)> Deserializer;
|
||||||
|
typedef std::function<std::optional<Data::Variant>(std::string)> FromString;
|
||||||
|
typedef std::function<result<Data::Variant, LuaCastError>(lua_State*, int idx)> FromLuaValue;
|
||||||
|
|
||||||
// Describes a meta-type, which consists of a concrete type, and some generic argument.
|
struct TypeInfo {
|
||||||
struct TypeMeta {
|
std::string name;
|
||||||
const TypeDesc* descriptor;
|
Deserializer deserializer;
|
||||||
union {
|
FromString fromString;
|
||||||
Enum* enum_; // Applicable for EnumItem
|
FromLuaValue fromLuaValue;
|
||||||
InstanceType* instType; // Applicable for InstanceRef
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline TypeMeta(const TypeDesc* descriptor) : descriptor(descriptor) {}
|
class String;
|
||||||
TypeMeta(Enum*);
|
class Base {
|
||||||
TypeMeta(InstanceType*);
|
public:
|
||||||
};
|
virtual ~Base();
|
||||||
|
virtual const TypeInfo& GetType() const = 0;
|
||||||
|
virtual const Data::String ToString() const = 0;
|
||||||
|
virtual void Serialize(pugi::xml_node node) const = 0;
|
||||||
|
virtual void PushLuaValue(lua_State*) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Null : Base {
|
||||||
|
public:
|
||||||
|
Null();
|
||||||
|
~Null();
|
||||||
|
virtual const TypeInfo& GetType() const override;
|
||||||
|
static const TypeInfo TYPE;
|
||||||
|
|
||||||
|
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 result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
DEF_WRAPPER_CLASS(Bool, bool)
|
||||||
|
DEF_WRAPPER_CLASS(Int, int)
|
||||||
|
DEF_WRAPPER_CLASS(Float, float)
|
||||||
|
DEF_WRAPPER_CLASS(String, std::string)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#undef DEF_WRAPPER_CLASS
|
|
@ -1,24 +1,23 @@
|
||||||
#include "cframe.h"
|
#include "cframe.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "error/data.h"
|
|
||||||
#include "physics/util.h"
|
#include "physics/util.h"
|
||||||
#include <glm/ext/matrix_transform.hpp>
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
#include <glm/gtc/matrix_inverse.hpp>
|
#include <glm/gtc/matrix_inverse.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
#include <glm/matrix.hpp>
|
#include <glm/matrix.hpp>
|
||||||
#include <reactphysics3d/mathematics/Transform.h>
|
#include <reactphysics3d/mathematics/Transform.h>
|
||||||
#include "datatypes/variant.h"
|
#include "datatypes/meta.h"
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
#include <glm/gtx/euler_angles.hpp>
|
#include <glm/gtx/euler_angles.hpp>
|
||||||
// #include "variant.h" // IWYU pragma: keep
|
// #include "meta.h" // IWYU pragma: keep
|
||||||
|
|
||||||
const CFrame CFrame::IDENTITY(glm::vec3(0, 0, 0), glm::mat3(1.f));
|
const Data::CFrame Data::CFrame::IDENTITY(glm::vec3(0, 0, 0), glm::mat3(1.f));
|
||||||
const CFrame CFrame::YToZ(glm::vec3(0, 0, 0), glm::mat3(glm::vec3(1, 0, 0), glm::vec3(0, 0, 1), glm::vec3(0, 1, 0)));
|
const Data::CFrame Data::CFrame::YToZ(glm::vec3(0, 0, 0), glm::mat3(glm::vec3(1, 0, 0), glm::vec3(0, 0, 1), glm::vec3(0, 1, 0)));
|
||||||
|
|
||||||
CFrame::CFrame() : CFrame::CFrame(glm::vec3(0, 0, 0), glm::mat3(1.f)) {}
|
Data::CFrame::CFrame() : Data::CFrame::CFrame(glm::vec3(0, 0, 0), glm::mat3(1.f)) {}
|
||||||
|
|
||||||
CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22)
|
Data::CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22)
|
||||||
: translation(x, y, z)
|
: translation(x, y, z)
|
||||||
, rotation({
|
, rotation({
|
||||||
// { R00, R01, R02 },
|
// { R00, R01, R02 },
|
||||||
|
@ -30,17 +29,17 @@ CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float
|
||||||
}) {
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame::CFrame(glm::vec3 translation, glm::mat3 rotation)
|
Data::CFrame::CFrame(glm::vec3 translation, glm::mat3 rotation)
|
||||||
: translation(translation)
|
: translation(translation)
|
||||||
, rotation(rotation) {
|
, rotation(rotation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame::CFrame(Vector3 position, glm::quat quat)
|
Data::CFrame::CFrame(Vector3 position, glm::quat quat)
|
||||||
: translation(position)
|
: translation(position)
|
||||||
, rotation(quat) {
|
, rotation(quat) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame::CFrame(const rp::Transform& transform) : CFrame::CFrame(rpToGlm(transform.getPosition()), rpToGlm(transform.getOrientation())) {
|
Data::CFrame::CFrame(const rp::Transform& transform) : Data::CFrame::CFrame(rpToGlm(transform.getPosition()), rpToGlm(transform.getOrientation())) {
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::mat3 lookAt(Vector3 position, Vector3 lookAt, Vector3 up) {
|
glm::mat3 lookAt(Vector3 position, Vector3 lookAt, Vector3 up) {
|
||||||
|
@ -53,35 +52,35 @@ glm::mat3 lookAt(Vector3 position, Vector3 lookAt, Vector3 up) {
|
||||||
return { s, u, -f };
|
return { s, u, -f };
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame::CFrame(Vector3 position, Vector3 lookAt, Vector3 up)
|
Data::CFrame::CFrame(Vector3 position, Vector3 lookAt, Vector3 up)
|
||||||
: translation(position)
|
: translation(position)
|
||||||
, rotation(::lookAt(position, lookAt, up)) {
|
, rotation(::lookAt(position, lookAt, up)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame::~CFrame() = default;
|
Data::CFrame::~CFrame() = default;
|
||||||
|
|
||||||
const std::string CFrame::ToString() const {
|
const Data::String Data::CFrame::ToString() const {
|
||||||
return std::to_string(X()) + ", " + std::to_string(Y()) + ", " + std::to_string(Z());
|
return std::to_string(X()) + ", " + std::to_string(Y()) + ", " + std::to_string(Z());
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame CFrame::pointToward(Vector3 position, Vector3 toward) {
|
Data::CFrame Data::CFrame::pointToward(Vector3 position, Vector3 toward) {
|
||||||
return CFrame(position, position + toward, (abs(toward.Dot(Vector3(0, 1, 0))) > 0.999) ? Vector3(0, 0, 1) : Vector3(0, 1, 0));
|
return Data::CFrame(position, position + toward, (abs(toward.Dot(Vector3(0, 1, 0))) > 0.999) ? Vector3(0, 0, 1) : Vector3(0, 1, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame CFrame::pointAligned(Vector3 position, Vector3 toward, Vector3 up, Vector3 right) {
|
Data::CFrame Data::CFrame::pointAligned(Vector3 position, Vector3 toward, Vector3 up, Vector3 right) {
|
||||||
return CFrame(position, position + toward, (abs(toward.Dot(up)) > 0.999) ? right : up);
|
return Data::CFrame(position, position + toward, (abs(toward.Dot(up)) > 0.999) ? right : up);
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame::operator glm::mat4() const {
|
Data::CFrame::operator glm::mat4() const {
|
||||||
// Always make sure to translate the position first, then rotate. Matrices work backwards
|
// Always make sure to translate the position first, then rotate. Matrices work backwards
|
||||||
return glm::translate(glm::mat4(1.0f), this->translation) * glm::mat4(this->rotation);
|
return glm::translate(glm::mat4(1.0f), this->translation) * glm::mat4(this->rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame::operator rp::Transform() const {
|
Data::CFrame::operator rp::Transform() const {
|
||||||
return rp::Transform(glmToRp(translation), glmToRp(rotation));
|
return rp::Transform(glmToRp(translation), glmToRp(rotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 CFrame::ToEulerAnglesXYZ() {
|
Vector3 Data::CFrame::ToEulerAnglesXYZ() {
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
float z;
|
float z;
|
||||||
|
@ -89,37 +88,37 @@ Vector3 CFrame::ToEulerAnglesXYZ() {
|
||||||
return Vector3(x, y, z);
|
return Vector3(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame CFrame::FromEulerAnglesXYZ(Vector3 vector) {
|
Data::CFrame Data::CFrame::FromEulerAnglesXYZ(Vector3 vector) {
|
||||||
glm::mat3 mat = glm::eulerAngleXYZ(vector.X(), vector.Y(), vector.Z());
|
glm::mat3 mat = glm::eulerAngleXYZ(vector.X(), vector.Y(), vector.Z());
|
||||||
return CFrame((glm::vec3)Vector3::ZERO, mat);
|
return Data::CFrame((glm::vec3)Vector3::ZERO, mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame CFrame::Inverse() const {
|
Data::CFrame Data::CFrame::Inverse() const {
|
||||||
return CFrame { -translation * glm::transpose(glm::inverse(rotation)), glm::inverse(rotation) };
|
return CFrame { -translation * glm::transpose(glm::inverse(rotation)), glm::inverse(rotation) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
|
|
||||||
CFrame CFrame::operator *(CFrame otherFrame) const {
|
Data::CFrame Data::CFrame::operator *(Data::CFrame otherFrame) const {
|
||||||
return CFrame { this->translation + this->rotation * otherFrame.translation, this->rotation * otherFrame.rotation };
|
return CFrame { this->translation + this->rotation * otherFrame.translation, this->rotation * otherFrame.rotation };
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 CFrame::operator *(Vector3 vector) const {
|
Vector3 Data::CFrame::operator *(Vector3 vector) const {
|
||||||
return this->translation + this->rotation * vector;
|
return this->translation + this->rotation * vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame CFrame::operator +(Vector3 vector) const {
|
Data::CFrame Data::CFrame::operator +(Vector3 vector) const {
|
||||||
return CFrame { this->translation + glm::vec3(vector), this->rotation };
|
return CFrame { this->translation + glm::vec3(vector), this->rotation };
|
||||||
}
|
}
|
||||||
|
|
||||||
CFrame CFrame::operator -(Vector3 vector) const {
|
Data::CFrame Data::CFrame::operator -(Vector3 vector) const {
|
||||||
return *this + -vector;
|
return *this + -vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialization
|
// Serialization
|
||||||
|
|
||||||
void CFrame::Serialize(pugi::xml_node node) const {
|
void Data::CFrame::Serialize(pugi::xml_node node) const {
|
||||||
node.append_child("X").text().set(std::to_string(this->X()));
|
node.append_child("X").text().set(std::to_string(this->X()));
|
||||||
node.append_child("Y").text().set(std::to_string(this->Y()));
|
node.append_child("Y").text().set(std::to_string(this->Y()));
|
||||||
node.append_child("Z").text().set(std::to_string(this->Z()));
|
node.append_child("Z").text().set(std::to_string(this->Z()));
|
||||||
|
@ -135,8 +134,8 @@ void CFrame::Serialize(pugi::xml_node node) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
result<CFrame, DataParseError> CFrame::Deserialize(pugi::xml_node node) {
|
Data::Variant Data::CFrame::Deserialize(pugi::xml_node node) {
|
||||||
return CFrame(
|
return Data::CFrame(
|
||||||
node.child("X").text().as_float(),
|
node.child("X").text().as_float(),
|
||||||
node.child("Y").text().as_float(),
|
node.child("Y").text().as_float(),
|
||||||
node.child("Z").text().as_float(),
|
node.child("Z").text().as_float(),
|
||||||
|
|
|
@ -3,69 +3,72 @@
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "datatypes/annotation.h"
|
#include "datatypes/annotation.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "error/data.h"
|
|
||||||
#include <glm/ext/quaternion_float.hpp>
|
#include <glm/ext/quaternion_float.hpp>
|
||||||
#include <glm/gtc/matrix_access.hpp>
|
#include <glm/gtc/matrix_access.hpp>
|
||||||
#include <glm/matrix.hpp>
|
#include <glm/matrix.hpp>
|
||||||
|
|
||||||
namespace reactphysics3d { class Transform; };
|
namespace reactphysics3d { class Transform; };
|
||||||
|
|
||||||
class DEF_DATA_(name="CoordinateFrame") CFrame {
|
namespace Data {
|
||||||
AUTOGEN_PREAMBLE_DATA
|
class DEF_DATA_(name="CoordinateFrame") CFrame : public Base {
|
||||||
|
AUTOGEN_PREAMBLE_DATA
|
||||||
|
|
||||||
glm::vec3 translation;
|
glm::vec3 translation;
|
||||||
glm::mat3 rotation;
|
glm::mat3 rotation;
|
||||||
|
|
||||||
|
CFrame(glm::vec3, glm::mat3);
|
||||||
|
public:
|
||||||
|
// CFrame(float x, float y, float z);
|
||||||
|
// CFrame(const glm::vec3&);
|
||||||
|
// CFrame(const rp::Vector3&);
|
||||||
|
DEF_DATA_CTOR CFrame();
|
||||||
|
DEF_DATA_CTOR CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22);
|
||||||
|
DEF_DATA_CTOR CFrame(Vector3 , Vector3 lookAt, Vector3 up = Vector3(0, 1, 0));
|
||||||
|
CFrame(const reactphysics3d::Transform&);
|
||||||
|
CFrame(Vector3 position, glm::quat quat);
|
||||||
|
~CFrame();
|
||||||
|
|
||||||
CFrame(glm::vec3, glm::mat3);
|
// Same as CFrame(position, position + toward), but makes sure that up and toward are not linearly dependant
|
||||||
public:
|
static CFrame pointToward(Vector3 position, Vector3 toward);
|
||||||
// CFrame(float x, float y, float z);
|
// Creates a cframe looking at position + toward, whilst aligning its up to up.
|
||||||
// CFrame(const glm::vec3&);
|
// If up and toward are approximately linearly dependent (their absolute dot product > 0.999),
|
||||||
// CFrame(const rp::Vector3&);
|
// then the right is used instead
|
||||||
DEF_DATA_CTOR CFrame();
|
// Up and right must NOT be linearly dependent
|
||||||
DEF_DATA_CTOR CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22);
|
static CFrame pointAligned(Vector3 position, Vector3 toward, Vector3 up, Vector3 right);
|
||||||
DEF_DATA_CTOR CFrame(Vector3 , Vector3 lookAt, Vector3 up = Vector3(0, 1, 0));
|
|
||||||
CFrame(const reactphysics3d::Transform&);
|
|
||||||
CFrame(Vector3 position, glm::quat quat);
|
|
||||||
~CFrame();
|
|
||||||
|
|
||||||
// Same as CFrame(position, position + toward), but makes sure that up and toward are not linearly dependant
|
DEF_DATA_PROP static const CFrame IDENTITY;
|
||||||
static CFrame pointToward(Vector3 position, Vector3 toward);
|
static const CFrame YToZ;
|
||||||
// Creates a cframe looking at position + toward, whilst aligning its up to up.
|
|
||||||
// If up and toward are approximately linearly dependent (their absolute dot product > 0.999),
|
|
||||||
// then the right is used instead
|
|
||||||
// Up and right must NOT be linearly dependent
|
|
||||||
static CFrame pointAligned(Vector3 position, Vector3 toward, Vector3 up, Vector3 right);
|
|
||||||
|
|
||||||
DEF_DATA_PROP static const CFrame IDENTITY;
|
virtual const Data::String ToString() const override;
|
||||||
static const CFrame YToZ;
|
virtual void Serialize(pugi::xml_node parent) const override;
|
||||||
|
static Data::Variant Deserialize(pugi::xml_node node);
|
||||||
|
|
||||||
virtual const std::string ToString() const;
|
static void PushLuaLibrary(lua_State*);
|
||||||
virtual void Serialize(pugi::xml_node parent) const;
|
|
||||||
static result<CFrame, DataParseError> Deserialize(pugi::xml_node node);
|
|
||||||
|
|
||||||
static void PushLuaLibrary(lua_State*);
|
operator glm::mat4() const;
|
||||||
|
operator reactphysics3d::Transform() const;
|
||||||
|
|
||||||
operator glm::mat4() const;
|
//inline static CFrame identity() { }
|
||||||
operator reactphysics3d::Transform() const;
|
DEF_DATA_PROP inline Vector3 Position() const { return translation; }
|
||||||
|
DEF_DATA_PROP inline CFrame Rotation() const { return CFrame { glm::vec3(0, 0, 0), rotation }; }
|
||||||
|
DEF_DATA_METHOD CFrame Inverse() const;
|
||||||
|
DEF_DATA_PROP inline float X() const { return translation.x; }
|
||||||
|
DEF_DATA_PROP inline float Y() const { return translation.y; }
|
||||||
|
DEF_DATA_PROP inline float Z() const { return translation.z; }
|
||||||
|
|
||||||
//inline static CFrame identity() { }
|
DEF_DATA_PROP inline Vector3 RightVector() { return glm::column(rotation, 0); }
|
||||||
DEF_DATA_PROP inline Vector3 Position() const { return translation; }
|
DEF_DATA_PROP inline Vector3 UpVector() { return glm::column(rotation, 1); }
|
||||||
DEF_DATA_PROP inline CFrame Rotation() const { return CFrame { glm::vec3(0, 0, 0), rotation }; }
|
DEF_DATA_PROP inline Vector3 LookVector() { return -glm::column(rotation, 2); }
|
||||||
DEF_DATA_METHOD CFrame Inverse() const;
|
|
||||||
DEF_DATA_PROP inline float X() const { return translation.x; }
|
|
||||||
DEF_DATA_PROP inline float Y() const { return translation.y; }
|
|
||||||
DEF_DATA_PROP inline float Z() const { return translation.z; }
|
|
||||||
|
|
||||||
DEF_DATA_PROP inline Vector3 RightVector() { return glm::column(rotation, 0); }
|
DEF_DATA_METHOD Vector3 ToEulerAnglesXYZ();
|
||||||
DEF_DATA_PROP inline Vector3 UpVector() { return glm::column(rotation, 1); }
|
DEF_DATA_METHOD static CFrame FromEulerAnglesXYZ(Vector3);
|
||||||
DEF_DATA_PROP inline Vector3 LookVector() { return -glm::column(rotation, 2); }
|
|
||||||
|
|
||||||
DEF_DATA_METHOD Vector3 ToEulerAnglesXYZ();
|
// Operators
|
||||||
DEF_DATA_METHOD static CFrame FromEulerAnglesXYZ(Vector3);
|
DEF_DATA_OP Data::CFrame operator *(Data::CFrame) const;
|
||||||
|
DEF_DATA_OP Vector3 operator *(Vector3) const;
|
||||||
|
DEF_DATA_OP Data::CFrame operator +(Vector3) const;
|
||||||
|
DEF_DATA_OP Data::CFrame operator -(Vector3) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Operators
|
using Data::CFrame;
|
||||||
DEF_DATA_OP CFrame operator *(CFrame) const;
|
|
||||||
DEF_DATA_OP Vector3 operator *(Vector3) const;
|
|
||||||
DEF_DATA_OP CFrame operator +(Vector3) const;
|
|
||||||
DEF_DATA_OP CFrame operator -(Vector3) const;
|
|
||||||
};
|
|
|
@ -1,22 +1,21 @@
|
||||||
#include "color3.h"
|
#include "color3.h"
|
||||||
#include "datatypes/variant.h"
|
#include "datatypes/meta.h"
|
||||||
#include "error/data.h"
|
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
Color3::Color3(float r, float g, float b) : r(std::clamp(r, 0.f, 1.f)), g(std::clamp(g, 0.f, 1.f)), b(std::clamp(b, 0.f, 1.f)) {};
|
Data::Color3::Color3(float r, float g, float b) : r(std::clamp(r, 0.f, 1.f)), g(std::clamp(g, 0.f, 1.f)), b(std::clamp(b, 0.f, 1.f)) {};
|
||||||
Color3::Color3(const glm::vec3& vec) : r(std::clamp(vec.x, 0.f, 1.f)), g(std::clamp(vec.y, 0.f, 1.f)), b(std::clamp(vec.z, 0.f, 1.f)) {};
|
Data::Color3::Color3(const glm::vec3& vec) : r(std::clamp(vec.x, 0.f, 1.f)), g(std::clamp(vec.y, 0.f, 1.f)), b(std::clamp(vec.z, 0.f, 1.f)) {};
|
||||||
|
|
||||||
Color3::~Color3() = default;
|
Data::Color3::~Color3() = default;
|
||||||
|
|
||||||
const std::string Color3::ToString() const {
|
const Data::String Data::Color3::ToString() const {
|
||||||
return std::to_string(int(r*256)) + ", " + std::to_string(int(g*256)) + ", " + std::to_string(int(b*256));
|
return std::to_string(int(r*256)) + ", " + std::to_string(int(g*256)) + ", " + std::to_string(int(b*256));
|
||||||
}
|
}
|
||||||
|
|
||||||
Color3::operator glm::vec3() const { return glm::vec3(r, g, b); };
|
Data::Color3::operator glm::vec3() const { return glm::vec3(r, g, b); };
|
||||||
|
|
||||||
std::string Color3::ToHex() const {
|
std::string Data::Color3::ToHex() const {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "FF" << std::hex << std::uppercase << std::setfill('0')
|
ss << "FF" << std::hex << std::uppercase << std::setfill('0')
|
||||||
<< std::setw(2) << int(r*255)
|
<< std::setw(2) << int(r*255)
|
||||||
|
@ -26,7 +25,7 @@ std::string Color3::ToHex() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Color3 Color3::FromHex(std::string hex) {
|
Data::Color3 Data::Color3::FromHex(std::string hex) {
|
||||||
float r = float(std::stoi(hex.substr(2, 2), nullptr, 16)) / 255;
|
float r = float(std::stoi(hex.substr(2, 2), nullptr, 16)) / 255;
|
||||||
float g = float(std::stoi(hex.substr(4, 2), nullptr, 16)) / 255;
|
float g = float(std::stoi(hex.substr(4, 2), nullptr, 16)) / 255;
|
||||||
float b = float(std::stoi(hex.substr(6, 2), nullptr, 16)) / 255;
|
float b = float(std::stoi(hex.substr(6, 2), nullptr, 16)) / 255;
|
||||||
|
@ -36,10 +35,10 @@ Color3 Color3::FromHex(std::string hex) {
|
||||||
|
|
||||||
// Serialization
|
// Serialization
|
||||||
|
|
||||||
void Color3::Serialize(pugi::xml_node node) const {
|
void Data::Color3::Serialize(pugi::xml_node node) const {
|
||||||
node.text().set(this->ToHex());
|
node.text().set(this->ToHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
result<Color3, DataParseError> Color3::Deserialize(pugi::xml_node node) {
|
Data::Variant Data::Color3::Deserialize(pugi::xml_node node) {
|
||||||
return Color3::FromHex(node.text().get());
|
return Color3::FromHex(node.text().get());
|
||||||
}
|
}
|
|
@ -2,33 +2,36 @@
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "datatypes/annotation.h"
|
#include "datatypes/annotation.h"
|
||||||
#include "error/data.h"
|
|
||||||
#include <glm/ext/vector_float3.hpp>
|
#include <glm/ext/vector_float3.hpp>
|
||||||
|
|
||||||
class DEF_DATA Color3 {
|
namespace Data {
|
||||||
AUTOGEN_PREAMBLE_DATA
|
class DEF_DATA Color3 : public Base {
|
||||||
|
AUTOGEN_PREAMBLE_DATA
|
||||||
|
|
||||||
float r;
|
float r;
|
||||||
float g;
|
float g;
|
||||||
float b;
|
float b;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DEF_DATA_CTOR Color3(float r, float g, float b);
|
||||||
|
Color3(const glm::vec3&);
|
||||||
|
~Color3();
|
||||||
|
|
||||||
public:
|
DEF_DATA_METHOD static Color3 FromHex(std::string hex);
|
||||||
DEF_DATA_CTOR Color3(float r, float g, float b);
|
|
||||||
Color3(const glm::vec3&);
|
|
||||||
~Color3();
|
|
||||||
|
|
||||||
DEF_DATA_METHOD static Color3 FromHex(std::string hex);
|
virtual const Data::String ToString() const override;
|
||||||
|
DEF_DATA_METHOD std::string ToHex() const;
|
||||||
|
virtual void Serialize(pugi::xml_node node) const override;
|
||||||
|
static Data::Variant Deserialize(pugi::xml_node node);
|
||||||
|
|
||||||
virtual const std::string ToString() const;
|
static void PushLuaLibrary(lua_State*);
|
||||||
DEF_DATA_METHOD std::string ToHex() const;
|
|
||||||
virtual void Serialize(pugi::xml_node node) const;
|
|
||||||
static result<Color3, DataParseError> Deserialize(pugi::xml_node node);
|
|
||||||
|
|
||||||
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 G() const { return g; }
|
||||||
|
DEF_DATA_PROP inline float B() const { return b; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
DEF_DATA_PROP inline float R() const { return r; }
|
using Data::Color3;
|
||||||
DEF_DATA_PROP inline float G() const { return g; }
|
|
||||||
DEF_DATA_PROP inline float B() const { return b; }
|
|
||||||
};
|
|
|
@ -1,66 +0,0 @@
|
||||||
#include "enum.h"
|
|
||||||
#include "datatypes/base.h"
|
|
||||||
#include "datatypes/variant.h"
|
|
||||||
#include "error/data.h"
|
|
||||||
#include <pugixml.hpp>
|
|
||||||
|
|
||||||
TypeMeta::TypeMeta(Enum* enum_) : enum_(enum_), descriptor(&EnumItem::TYPE) {}
|
|
||||||
|
|
||||||
Enum::Enum(_EnumData* data) : data(data) {}
|
|
||||||
|
|
||||||
std::vector<EnumItem> Enum::GetEnumItems() const {
|
|
||||||
std::vector<EnumItem> enumItems;
|
|
||||||
|
|
||||||
for (int i = 0; i < data->count; i++) {
|
|
||||||
enumItems.push_back(EnumItem(data, data->values[i].second, data->values[i].first));
|
|
||||||
}
|
|
||||||
|
|
||||||
return enumItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<EnumItem> Enum::FromName(std::string name) const {
|
|
||||||
for (int i = 0; i < data->count; i++) {
|
|
||||||
if (data->values[i].second == name)
|
|
||||||
return EnumItem(data, name, data->values[i].first);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<EnumItem> Enum::FromValue(int value) const {
|
|
||||||
for (int i = 0; i < data->count; i++) {
|
|
||||||
if (data->values[i].first == value)
|
|
||||||
return EnumItem(data, data->values[i].second, value);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
EnumItem::EnumItem(_EnumData* parentData, std::string name, int value) : parentData(parentData), name(name), value(value) {}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
std::string Enum::ToString() const {
|
|
||||||
return "Enum." + this->data->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
std::string EnumItem::ToString() const {
|
|
||||||
return "Enum." + parentData->name + "." + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnumItem::Serialize(pugi::xml_node node) const {
|
|
||||||
node.set_name("token");
|
|
||||||
node.text().set(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
result<EnumItem, DataParseError> EnumItem::Deserialize(pugi::xml_node node, const TypeMeta info) {
|
|
||||||
auto result = info.enum_->FromValue(node.text().as_int());
|
|
||||||
if (result.has_value()) return result.value();
|
|
||||||
return DataParseError(node.text().as_string(), "EnumItem");
|
|
||||||
}
|
|
||||||
|
|
||||||
result<EnumItem, DataParseError> EnumItem::FromString(std::string string, const TypeMeta info) {
|
|
||||||
auto result = info.enum_->FromName(string);
|
|
||||||
if (result.has_value()) return result.value();
|
|
||||||
return DataParseError(string, "EnumItem");
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "base.h"
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include "datatypes/annotation.h"
|
|
||||||
#include "error/data.h"
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
struct _EnumData {
|
|
||||||
std::pair<int, std::string>* values;
|
|
||||||
std::string name;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
|
|
||||||
class EnumItem;
|
|
||||||
|
|
||||||
class DEF_DATA Enum {
|
|
||||||
_EnumData* data;
|
|
||||||
public:
|
|
||||||
Enum(_EnumData*);
|
|
||||||
|
|
||||||
static const TypeDesc TYPE;
|
|
||||||
|
|
||||||
inline _EnumData* InternalType() const { return this->data; };
|
|
||||||
std::vector<EnumItem> GetEnumItems() const;
|
|
||||||
std::optional<EnumItem> FromName(std::string) const;
|
|
||||||
std::optional<EnumItem> FromValue(int) const;
|
|
||||||
|
|
||||||
std::string ToString() const;
|
|
||||||
void PushLuaValue(lua_State*) const;
|
|
||||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int);
|
|
||||||
};
|
|
||||||
|
|
||||||
class DEF_DATA EnumItem {
|
|
||||||
_EnumData* parentData;
|
|
||||||
std::string name;
|
|
||||||
int value;
|
|
||||||
public:
|
|
||||||
EnumItem(_EnumData*, std::string, int);
|
|
||||||
|
|
||||||
static const TypeDesc TYPE;
|
|
||||||
|
|
||||||
inline std::string Name() const { return this->name; }
|
|
||||||
inline int Value() const { return this->value; }
|
|
||||||
inline Enum EnumType() const { return Enum(this->parentData); }
|
|
||||||
|
|
||||||
static result<EnumItem, DataParseError> FromString(std::string, const TypeMeta);
|
|
||||||
std::string ToString() const;
|
|
||||||
void Serialize(pugi::xml_node) const;
|
|
||||||
static result<EnumItem, DataParseError> Deserialize(pugi::xml_node, const TypeMeta);
|
|
||||||
void PushLuaValue(lua_State*) const;
|
|
||||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int);
|
|
||||||
};
|
|
48
core/src/datatypes/meta.cpp
Normal file
48
core/src/datatypes/meta.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include "meta.h"
|
||||||
|
#include "datatypes/base.h"
|
||||||
|
#include "datatypes/cframe.h"
|
||||||
|
#include "datatypes/ref.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "panic.h"
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
Data::String Data::Variant::ToString() const {
|
||||||
|
return std::visit([](auto&& it) {
|
||||||
|
return it.ToString();
|
||||||
|
}, this->wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::Variant::Serialize(pugi::xml_node node) const {
|
||||||
|
std::visit([&](auto&& it) {
|
||||||
|
it.Serialize(node);
|
||||||
|
}, this->wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Data::Variant::PushLuaValue(lua_State* state) const {
|
||||||
|
return std::visit([&](auto&& it) {
|
||||||
|
return it.PushLuaValue(state);
|
||||||
|
}, this->wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::Variant Data::Variant::Deserialize(pugi::xml_node node) {
|
||||||
|
if (Data::TYPE_MAP.count(node.name()) == 0) {
|
||||||
|
Logger::fatalErrorf("Unknown type for property: '%s'", node.name());
|
||||||
|
panic();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Data::TypeInfo* type = Data::TYPE_MAP[node.name()];
|
||||||
|
return type->deserializer(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, const Data::TypeInfo*> Data::TYPE_MAP = {
|
||||||
|
{ "null", &Data::Null::TYPE },
|
||||||
|
{ "bool", &Data::Bool::TYPE },
|
||||||
|
{ "int", &Data::Int::TYPE },
|
||||||
|
{ "float", &Data::Float::TYPE },
|
||||||
|
{ "string", &Data::String::TYPE },
|
||||||
|
{ "Vector3", &Data::Vector3::TYPE },
|
||||||
|
{ "CoordinateFrame", &Data::CFrame::TYPE },
|
||||||
|
{ "Color3", &Data::Color3::TYPE },
|
||||||
|
{ "Ref", &Data::InstanceRef::TYPE },
|
||||||
|
};
|
49
core/src/datatypes/meta.h
Normal file
49
core/src/datatypes/meta.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
#include <map>
|
||||||
|
#include "base.h"
|
||||||
|
#include "datatypes/color3.h"
|
||||||
|
#include "datatypes/ref.h"
|
||||||
|
#include "datatypes/signal.h"
|
||||||
|
#include "vector.h"
|
||||||
|
#include "cframe.h"
|
||||||
|
|
||||||
|
// #define __VARIANT_TYPE std::variant< \
|
||||||
|
// Null, \
|
||||||
|
// Bool, \
|
||||||
|
// Int, \
|
||||||
|
// Float, \
|
||||||
|
// String \
|
||||||
|
// >
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
typedef std::variant<
|
||||||
|
Null,
|
||||||
|
Bool,
|
||||||
|
Int,
|
||||||
|
Float,
|
||||||
|
String,
|
||||||
|
Vector3,
|
||||||
|
CFrame,
|
||||||
|
Color3,
|
||||||
|
InstanceRef,
|
||||||
|
SignalRef,
|
||||||
|
SignalConnectionRef
|
||||||
|
> __VARIANT_TYPE;
|
||||||
|
|
||||||
|
class Variant {
|
||||||
|
__VARIANT_TYPE wrapped;
|
||||||
|
public:
|
||||||
|
template <typename T> Variant(T obj) : wrapped(obj) {}
|
||||||
|
template <typename T> T get() { return std::get<T>(wrapped); }
|
||||||
|
Data::String ToString() const;
|
||||||
|
|
||||||
|
void Serialize(pugi::xml_node node) const;
|
||||||
|
void PushLuaValue(lua_State* state) const;
|
||||||
|
static Data::Variant Deserialize(pugi::xml_node node);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map of all data types to their type names
|
||||||
|
extern std::map<std::string, const TypeInfo*> TYPE_MAP;
|
||||||
|
}
|
|
@ -1,212 +0,0 @@
|
||||||
#include "primitives.h"
|
|
||||||
#include "error/data.h"
|
|
||||||
#include "variant.h"
|
|
||||||
#include <pugixml.hpp>
|
|
||||||
#include "lua.h"
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
// null
|
|
||||||
|
|
||||||
void Null_Serialize(Variant self, pugi::xml_node node) {
|
|
||||||
node.text().set("null");
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> Null_Deserialize(pugi::xml_node node, const TypeMeta) {
|
|
||||||
return std::monostate();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string Null_ToString(Variant self) {
|
|
||||||
return "null";
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> Null_FromString(std::string string, const TypeMeta) {
|
|
||||||
return std::monostate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Null_PushLuaValue(Variant self, lua_State* L) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, LuaCastError> Null_FromLuaValue(lua_State* L, int idx) {
|
|
||||||
return Variant(std::monostate());
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeDesc NULL_TYPE {
|
|
||||||
"null",
|
|
||||||
Null_Serialize,
|
|
||||||
Null_Deserialize,
|
|
||||||
Null_ToString,
|
|
||||||
Null_FromString,
|
|
||||||
Null_PushLuaValue,
|
|
||||||
Null_FromLuaValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// /null
|
|
||||||
|
|
||||||
// bool
|
|
||||||
|
|
||||||
void Bool_Serialize(Variant self, pugi::xml_node node) {
|
|
||||||
node.text().set(self.get<bool>() ? "true" : "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> Bool_Deserialize(pugi::xml_node node, const TypeMeta) {
|
|
||||||
return node.text().as_bool();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string Bool_ToString(Variant self) {
|
|
||||||
return self.get<bool>() ? "true" : "false";
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> Bool_FromString(std::string string, const TypeMeta) {
|
|
||||||
return string[0] == 't' || string[0] == 'T' || string[0] == '1' || string[0] == 'y' || string[0] == 'Y';
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bool_PushLuaValue(Variant self, lua_State* L) {
|
|
||||||
lua_pushboolean(L, self.get<bool>());
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, LuaCastError> Bool_FromLuaValue(lua_State* L, int idx) {
|
|
||||||
if (!lua_isboolean(L, idx))
|
|
||||||
return LuaCastError(lua_typename(L, idx), "boolean");
|
|
||||||
return Variant(lua_toboolean(L, idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeDesc BOOL_TYPE {
|
|
||||||
"bool",
|
|
||||||
Bool_Serialize,
|
|
||||||
Bool_Deserialize,
|
|
||||||
Bool_ToString,
|
|
||||||
Bool_FromString,
|
|
||||||
Bool_PushLuaValue,
|
|
||||||
Bool_FromLuaValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// /bool
|
|
||||||
|
|
||||||
// int
|
|
||||||
|
|
||||||
void Int_Serialize(Variant self, pugi::xml_node node) {
|
|
||||||
node.text().set(self.get<int>());
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> Int_Deserialize(pugi::xml_node node, const TypeMeta) {
|
|
||||||
return node.text().as_int();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string Int_ToString(Variant self) {
|
|
||||||
return std::to_string(self.get<int>());
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> Int_FromString(std::string string, const TypeMeta) {
|
|
||||||
char* endPos;
|
|
||||||
int value = (int)std::strtol(string.c_str(), &endPos, 10);
|
|
||||||
if (endPos == string.c_str()) return DataParseError(string, "int");
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Int_PushLuaValue(Variant self, lua_State* L) {
|
|
||||||
lua_pushinteger(L, self.get<int>());
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, LuaCastError> Int_FromLuaValue(lua_State* L, int idx) {
|
|
||||||
if (!lua_isnumber(L, idx))
|
|
||||||
return LuaCastError(lua_typename(L, idx), "integer");
|
|
||||||
return Variant((int)lua_tonumber(L, idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeDesc INT_TYPE {
|
|
||||||
"int",
|
|
||||||
Int_Serialize,
|
|
||||||
Int_Deserialize,
|
|
||||||
Int_ToString,
|
|
||||||
Int_FromString,
|
|
||||||
Int_PushLuaValue,
|
|
||||||
Int_FromLuaValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// /int
|
|
||||||
|
|
||||||
// float
|
|
||||||
|
|
||||||
void Float_Serialize(Variant self, pugi::xml_node node) {
|
|
||||||
node.text().set(self.get<float>());
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> Float_Deserialize(pugi::xml_node node, const TypeMeta) {
|
|
||||||
return node.text().as_float();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string Float_ToString(Variant self) {
|
|
||||||
std::stringstream stream;
|
|
||||||
stream << std::noshowpoint << self.get<float>();
|
|
||||||
return stream.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> Float_FromString(std::string string, const TypeMeta) {
|
|
||||||
char* endPos;
|
|
||||||
float value = std::strtof(string.c_str(), &endPos);
|
|
||||||
if (endPos == string.c_str()) return DataParseError(string, "float");
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Float_PushLuaValue(Variant self, lua_State* L) {
|
|
||||||
lua_pushnumber(L, self.get<float>());
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, LuaCastError> Float_FromLuaValue(lua_State* L, int idx) {
|
|
||||||
if (!lua_isnumber(L, idx))
|
|
||||||
return LuaCastError(lua_typename(L, idx), "float");
|
|
||||||
return Variant((float)lua_tonumber(L, idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeDesc FLOAT_TYPE {
|
|
||||||
"float",
|
|
||||||
Float_Serialize,
|
|
||||||
Float_Deserialize,
|
|
||||||
Float_ToString,
|
|
||||||
Float_FromString,
|
|
||||||
Float_PushLuaValue,
|
|
||||||
Float_FromLuaValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// /float
|
|
||||||
|
|
||||||
// string
|
|
||||||
|
|
||||||
void String_Serialize(Variant self, pugi::xml_node node) {
|
|
||||||
node.text().set(self.get<std::string>());
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> String_Deserialize(pugi::xml_node node, const TypeMeta) {
|
|
||||||
return node.text().as_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string String_ToString(Variant self) {
|
|
||||||
return self.get<std::string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> String_FromString(std::string string, const TypeMeta) {
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String_PushLuaValue(Variant self, lua_State* L) {
|
|
||||||
lua_pushstring(L, self.get<std::string>().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, LuaCastError> String_FromLuaValue(lua_State* L, int idx) {
|
|
||||||
if (!lua_tostring(L, idx))
|
|
||||||
return LuaCastError(lua_typename(L, idx), "string");
|
|
||||||
return Variant(lua_tostring(L, idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeDesc STRING_TYPE {
|
|
||||||
"string",
|
|
||||||
String_Serialize,
|
|
||||||
String_Deserialize,
|
|
||||||
String_ToString,
|
|
||||||
String_FromString,
|
|
||||||
String_PushLuaValue,
|
|
||||||
String_FromLuaValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// /string
|
|
|
@ -1,9 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "base.h"
|
|
||||||
|
|
||||||
extern const TypeDesc NULL_TYPE;
|
|
||||||
extern const TypeDesc BOOL_TYPE;
|
|
||||||
extern const TypeDesc INT_TYPE;
|
|
||||||
extern const TypeDesc FLOAT_TYPE;
|
|
||||||
extern const TypeDesc STRING_TYPE;
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include "datatypes/base.h"
|
#include "datatypes/base.h"
|
||||||
#include "error/data.h"
|
#include "error/data.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "variant.h" // IWYU pragma: keep
|
#include "meta.h" // IWYU pragma: keep
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
|
@ -10,40 +10,34 @@
|
||||||
#include "objects/base/member.h"
|
#include "objects/base/member.h"
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
TypeMeta::TypeMeta(InstanceType* instType) : instType(instType), descriptor(&InstanceRef::TYPE) {}
|
Data::InstanceRef::InstanceRef() {};
|
||||||
|
Data::InstanceRef::InstanceRef(std::weak_ptr<Instance> instance) : ref(instance) {};
|
||||||
|
Data::InstanceRef::~InstanceRef() = default;
|
||||||
|
|
||||||
InstanceRef::InstanceRef() {};
|
const Data::TypeInfo Data::InstanceRef::TYPE = {
|
||||||
InstanceRef::InstanceRef(std::weak_ptr<Instance> instance) : ref(instance) {};
|
|
||||||
InstanceRef::~InstanceRef() = default;
|
|
||||||
|
|
||||||
const TypeDesc InstanceRef::TYPE = {
|
|
||||||
.name = "Ref",
|
.name = "Ref",
|
||||||
.serialize = toVariantFunction(&InstanceRef::Serialize),
|
.deserializer = &Data::InstanceRef::Deserialize,
|
||||||
.deserialize = toVariantGeneratorNoMeta(&InstanceRef::Deserialize),
|
.fromLuaValue = &Data::InstanceRef::FromLuaValue,
|
||||||
.toString = toVariantFunction(&InstanceRef::ToString),
|
|
||||||
.fromString = nullptr,
|
|
||||||
.pushLuaValue = toVariantFunction(&InstanceRef::PushLuaValue),
|
|
||||||
.fromLuaValue = &InstanceRef::FromLuaValue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string InstanceRef::ToString() const {
|
const Data::TypeInfo& Data::InstanceRef::GetType() const { return Data::InstanceRef::TYPE; };
|
||||||
|
|
||||||
|
const Data::String Data::InstanceRef::ToString() const {
|
||||||
return ref.expired() ? "" : ref.lock()->name;
|
return ref.expired() ? "" : ref.lock()->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceRef::operator std::weak_ptr<Instance>() {
|
Data::InstanceRef::operator std::weak_ptr<Instance>() {
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialization
|
// Serialization
|
||||||
|
|
||||||
void InstanceRef::Serialize(pugi::xml_node node) const {
|
void Data::InstanceRef::Serialize(pugi::xml_node node) const {
|
||||||
// Handled by Instance
|
// node.text().set(this->ToHex());
|
||||||
panic();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result<InstanceRef, DataParseError> InstanceRef::Deserialize(pugi::xml_node node) {
|
Data::Variant Data::InstanceRef::Deserialize(pugi::xml_node node) {
|
||||||
// Handled by Instance
|
return Data::InstanceRef();
|
||||||
panic();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int inst_gc(lua_State*);
|
static int inst_gc(lua_State*);
|
||||||
|
@ -56,7 +50,7 @@ static const struct luaL_Reg metatable [] = {
|
||||||
{NULL, NULL} /* end of array */
|
{NULL, NULL} /* end of array */
|
||||||
};
|
};
|
||||||
|
|
||||||
void InstanceRef::PushLuaValue(lua_State* L) const {
|
void Data::InstanceRef::PushLuaValue(lua_State* L) const {
|
||||||
if (ref.expired()) return lua_pushnil(L);
|
if (ref.expired()) return lua_pushnil(L);
|
||||||
|
|
||||||
int n = lua_gettop(L);
|
int n = lua_gettop(L);
|
||||||
|
@ -74,14 +68,14 @@ void InstanceRef::PushLuaValue(lua_State* L) const {
|
||||||
lua_setmetatable(L, n+1);
|
lua_setmetatable(L, n+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
result<Variant, LuaCastError> InstanceRef::FromLuaValue(lua_State* L, int idx) {
|
result<Data::Variant, LuaCastError> Data::InstanceRef::FromLuaValue(lua_State* L, int idx) {
|
||||||
if (lua_isnil(L, idx))
|
if (lua_isnil(L, idx))
|
||||||
return Variant(InstanceRef());
|
return Data::Variant(Data::InstanceRef());
|
||||||
if (!lua_isuserdata(L, idx))
|
if (!lua_isuserdata(L, idx))
|
||||||
return LuaCastError(lua_typename(L, idx), "Instance");
|
return LuaCastError(lua_typename(L, idx), "Instance");
|
||||||
// TODO: overhaul this to support other types
|
// TODO: overhaul this to support other types
|
||||||
auto userdata = (std::shared_ptr<Instance>**)lua_touserdata(L, idx);
|
auto userdata = (std::shared_ptr<Instance>**)lua_touserdata(L, idx);
|
||||||
return Variant(InstanceRef(**userdata));
|
return Data::Variant(Data::InstanceRef(**userdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int inst_gc(lua_State* L) {
|
static int inst_gc(lua_State* L) {
|
||||||
|
@ -103,7 +97,7 @@ static int inst_index(lua_State* L) {
|
||||||
// Read property
|
// Read property
|
||||||
std::optional<PropertyMeta> meta = inst->GetPropertyMeta(key);
|
std::optional<PropertyMeta> meta = inst->GetPropertyMeta(key);
|
||||||
if (meta) {
|
if (meta) {
|
||||||
Variant value = inst->GetPropertyValue(key).expect();
|
Data::Variant value = inst->GetPropertyValue(key).expect();
|
||||||
value.PushLuaValue(L);
|
value.PushLuaValue(L);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -111,7 +105,7 @@ static int inst_index(lua_State* L) {
|
||||||
// Look for child
|
// Look for child
|
||||||
std::optional<std::shared_ptr<Instance>> child = inst->FindFirstChild(key);
|
std::optional<std::shared_ptr<Instance>> child = inst->FindFirstChild(key);
|
||||||
if (child) {
|
if (child) {
|
||||||
InstanceRef(child.value()).PushLuaValue(L);
|
Data::InstanceRef(child.value()).PushLuaValue(L);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,8 +127,7 @@ static int inst_newindex(lua_State* L) {
|
||||||
if (key == "Parent" && inst->IsParentLocked())
|
if (key == "Parent" && inst->IsParentLocked())
|
||||||
return luaL_error(L, "Cannot set property Parent (%s) of %s, parent is locked", inst->GetParent() ? inst->GetParent().value()->name.c_str() : "NULL", inst->GetClass()->className.c_str());
|
return luaL_error(L, "Cannot set property Parent (%s) of %s, parent is locked", inst->GetParent() ? inst->GetParent().value()->name.c_str() : "NULL", inst->GetClass()->className.c_str());
|
||||||
|
|
||||||
// TODO: Make this work for enums, this is not a solution!!
|
result<Data::Variant, LuaCastError> value = meta->type->fromLuaValue(L, -1);
|
||||||
result<Variant, LuaCastError> value = meta->type.descriptor->fromLuaValue(L, -1);
|
|
||||||
lua_pop(L, 3);
|
lua_pop(L, 3);
|
||||||
|
|
||||||
if (value.isError())
|
if (value.isError())
|
||||||
|
|
|
@ -6,20 +6,23 @@
|
||||||
|
|
||||||
class Instance;
|
class Instance;
|
||||||
|
|
||||||
class InstanceRef {
|
namespace Data {
|
||||||
std::weak_ptr<Instance> ref;
|
class InstanceRef : public Base {
|
||||||
public:
|
std::weak_ptr<Instance> ref;
|
||||||
InstanceRef();
|
public:
|
||||||
InstanceRef(std::weak_ptr<Instance>);
|
InstanceRef();
|
||||||
~InstanceRef();
|
InstanceRef(std::weak_ptr<Instance>);
|
||||||
|
~InstanceRef();
|
||||||
|
|
||||||
static const TypeDesc TYPE;
|
virtual const TypeInfo& GetType() const override;
|
||||||
|
static const TypeInfo TYPE;
|
||||||
|
|
||||||
operator std::weak_ptr<Instance>();
|
operator std::weak_ptr<Instance>();
|
||||||
|
|
||||||
virtual const std::string ToString() const;
|
virtual const Data::String ToString() const override;
|
||||||
virtual void Serialize(pugi::xml_node node) const;
|
virtual void Serialize(pugi::xml_node node) const override;
|
||||||
virtual void PushLuaValue(lua_State*) const;
|
virtual void PushLuaValue(lua_State*) const override;
|
||||||
static result<InstanceRef, DataParseError> Deserialize(pugi::xml_node node);
|
static Data::Variant Deserialize(pugi::xml_node node);
|
||||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "signal.h"
|
#include "signal.h"
|
||||||
#include "datatypes/base.h"
|
#include "datatypes/base.h"
|
||||||
#include "variant.h"
|
#include "meta.h"
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <luajit-2.1/lauxlib.h>
|
#include <luajit-2.1/lauxlib.h>
|
||||||
|
@ -53,13 +53,13 @@ static void stackdump(lua_State* L) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaSignalConnection::Call(std::vector<Variant> args) {
|
void LuaSignalConnection::Call(std::vector<Data::Variant> args) {
|
||||||
lua_State* thread = lua_newthread(state);
|
lua_State* thread = lua_newthread(state);
|
||||||
|
|
||||||
// Push function
|
// Push function
|
||||||
lua_rawgeti(thread, LUA_REGISTRYINDEX, function);
|
lua_rawgeti(thread, LUA_REGISTRYINDEX, function);
|
||||||
|
|
||||||
for (Variant arg : args) {
|
for (Data::Variant arg : args) {
|
||||||
arg.PushLuaValue(thread);
|
arg.PushLuaValue(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,11 +74,11 @@ void LuaSignalConnection::Call(std::vector<Variant> args) {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
CSignalConnection::CSignalConnection(std::function<void(std::vector<Variant>)> func, std::weak_ptr<Signal> parent) : SignalConnection(parent) {
|
CSignalConnection::CSignalConnection(std::function<void(std::vector<Data::Variant>)> func, std::weak_ptr<Signal> parent) : SignalConnection(parent) {
|
||||||
this->function = func;
|
this->function = func;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSignalConnection::Call(std::vector<Variant> args) {
|
void CSignalConnection::Call(std::vector<Data::Variant> args) {
|
||||||
function(args);
|
function(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ void CSignalConnection::Call(std::vector<Variant> args) {
|
||||||
|
|
||||||
SignalConnectionHolder::SignalConnectionHolder() : heldConnection() {}
|
SignalConnectionHolder::SignalConnectionHolder() : heldConnection() {}
|
||||||
SignalConnectionHolder::SignalConnectionHolder(std::shared_ptr<SignalConnection> connection) : heldConnection(connection) {}
|
SignalConnectionHolder::SignalConnectionHolder(std::shared_ptr<SignalConnection> connection) : heldConnection(connection) {}
|
||||||
SignalConnectionHolder::SignalConnectionHolder(SignalConnectionRef other) : heldConnection(other) {}
|
SignalConnectionHolder::SignalConnectionHolder(Data::SignalConnectionRef other) : heldConnection(other) {}
|
||||||
|
|
||||||
SignalConnectionHolder::~SignalConnectionHolder() {
|
SignalConnectionHolder::~SignalConnectionHolder() {
|
||||||
// printf("Prediscon!\n");
|
// printf("Prediscon!\n");
|
||||||
|
@ -97,7 +97,7 @@ SignalConnectionHolder::~SignalConnectionHolder() {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
SignalConnectionRef Signal::Connect(std::function<void(std::vector<Variant>)> callback) {
|
SignalConnectionRef Signal::Connect(std::function<void(std::vector<Data::Variant>)> callback) {
|
||||||
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<CSignalConnection>(callback, weak_from_this()));
|
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<CSignalConnection>(callback, weak_from_this()));
|
||||||
connections.push_back(conn);
|
connections.push_back(conn);
|
||||||
return SignalConnectionRef(conn);
|
return SignalConnectionRef(conn);
|
||||||
|
@ -109,7 +109,7 @@ SignalConnectionRef Signal::Connect(lua_State* state) {
|
||||||
return SignalConnectionRef(conn);
|
return SignalConnectionRef(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalConnectionRef Signal::Once(std::function<void(std::vector<Variant>)> callback) {
|
SignalConnectionRef Signal::Once(std::function<void(std::vector<Data::Variant>)> callback) {
|
||||||
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<CSignalConnection>(callback, weak_from_this()));
|
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<CSignalConnection>(callback, weak_from_this()));
|
||||||
onceConnections.push_back(conn);
|
onceConnections.push_back(conn);
|
||||||
return SignalConnectionRef(conn);
|
return SignalConnectionRef(conn);
|
||||||
|
@ -132,7 +132,7 @@ int Signal::Wait(lua_State* thread) {
|
||||||
return lua_yield(thread, 0);
|
return lua_yield(thread, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Signal::Fire(std::vector<Variant> args) {
|
void Signal::Fire(std::vector<Data::Variant> args) {
|
||||||
for (std::shared_ptr<SignalConnection> connection : connections) {
|
for (std::shared_ptr<SignalConnection> connection : connections) {
|
||||||
connection->Call(args);
|
connection->Call(args);
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ void Signal::Fire(std::vector<Variant> args) {
|
||||||
auto prevThreads = std::move(waitingThreads);
|
auto prevThreads = std::move(waitingThreads);
|
||||||
waitingThreads = std::vector<std::pair<int, lua_State*>>();
|
waitingThreads = std::vector<std::pair<int, lua_State*>>();
|
||||||
for (auto& [threadId, thread] : prevThreads) {
|
for (auto& [threadId, thread] : prevThreads) {
|
||||||
for (Variant arg : args) {
|
for (Data::Variant arg : args) {
|
||||||
arg.PushLuaValue(thread);
|
arg.PushLuaValue(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ void Signal::Fire(std::vector<Variant> args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Signal::Fire() {
|
void Signal::Fire() {
|
||||||
return Fire(std::vector<Variant> {});
|
return Fire(std::vector<Data::Variant> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Signal::DisconnectAll() {
|
void Signal::DisconnectAll() {
|
||||||
|
@ -222,29 +222,29 @@ static const struct luaL_Reg signal_metatable [] = {
|
||||||
{NULL, NULL} /* end of array */
|
{NULL, NULL} /* end of array */
|
||||||
};
|
};
|
||||||
|
|
||||||
SignalRef::SignalRef(std::weak_ptr<Signal> ref) : signal(ref) {}
|
Data::SignalRef::SignalRef(std::weak_ptr<Signal> ref) : signal(ref) {}
|
||||||
SignalRef::~SignalRef() = default;
|
Data::SignalRef::~SignalRef() = default;
|
||||||
|
|
||||||
const TypeDesc SignalRef::TYPE = {
|
const Data::TypeInfo Data::SignalRef::TYPE = {
|
||||||
.name = "Signal",
|
.name = "Signal",
|
||||||
.toString = toVariantFunction(&SignalRef::ToString),
|
.fromLuaValue = &Data::SignalRef::FromLuaValue,
|
||||||
.pushLuaValue = toVariantFunction(&SignalRef::PushLuaValue),
|
|
||||||
.fromLuaValue = &SignalRef::FromLuaValue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string SignalRef::ToString() const {
|
const Data::TypeInfo& Data::SignalRef::GetType() const { return Data::SignalRef::TYPE; };
|
||||||
return "Signal";
|
|
||||||
|
const Data::String Data::SignalRef::ToString() const {
|
||||||
|
return Data::String("Signal");
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalRef::operator std::weak_ptr<Signal>() {
|
Data::SignalRef::operator std::weak_ptr<Signal>() {
|
||||||
return signal;
|
return signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalRef::Serialize(pugi::xml_node node) const {
|
void Data::SignalRef::Serialize(pugi::xml_node node) const {
|
||||||
// Not serializable
|
// Not serializable
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalRef::PushLuaValue(lua_State* L) const {
|
void Data::SignalRef::PushLuaValue(lua_State* L) const {
|
||||||
int n = lua_gettop(L);
|
int n = lua_gettop(L);
|
||||||
|
|
||||||
auto userdata = (std::weak_ptr<Signal>**)lua_newuserdata(L, sizeof(std::weak_ptr<Signal>));
|
auto userdata = (std::weak_ptr<Signal>**)lua_newuserdata(L, sizeof(std::weak_ptr<Signal>));
|
||||||
|
@ -257,10 +257,10 @@ void SignalRef::PushLuaValue(lua_State* L) const {
|
||||||
lua_setmetatable(L, n+1);
|
lua_setmetatable(L, n+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
result<Variant, LuaCastError> SignalRef::FromLuaValue(lua_State* L, int idx) {
|
result<Data::Variant, LuaCastError> Data::SignalRef::FromLuaValue(lua_State* L, int idx) {
|
||||||
auto userdata = (std::weak_ptr<Signal>**)luaL_checkudata(L, 1, "__mt_signal");
|
auto userdata = (std::weak_ptr<Signal>**)luaL_checkudata(L, 1, "__mt_signal");
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
return Variant(SignalRef(**userdata));
|
return Data::Variant(Data::SignalRef(**userdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int signal_gc(lua_State* L) {
|
static int signal_gc(lua_State* L) {
|
||||||
|
@ -343,29 +343,29 @@ static const struct luaL_Reg signalconnection_metatable [] = {
|
||||||
{NULL, NULL} /* end of array */
|
{NULL, NULL} /* end of array */
|
||||||
};
|
};
|
||||||
|
|
||||||
SignalConnectionRef::SignalConnectionRef(std::weak_ptr<SignalConnection> ref) : signalConnection(ref) {}
|
Data::SignalConnectionRef::SignalConnectionRef(std::weak_ptr<SignalConnection> ref) : signalConnection(ref) {}
|
||||||
SignalConnectionRef::~SignalConnectionRef() = default;
|
Data::SignalConnectionRef::~SignalConnectionRef() = default;
|
||||||
|
|
||||||
const TypeDesc SignalConnectionRef::TYPE = {
|
const Data::TypeInfo Data::SignalConnectionRef::TYPE = {
|
||||||
.name = "Signal",
|
.name = "Signal",
|
||||||
.toString = toVariantFunction(&SignalConnectionRef::ToString),
|
.fromLuaValue = &Data::SignalConnectionRef::FromLuaValue,
|
||||||
.pushLuaValue = toVariantFunction(&SignalConnectionRef::PushLuaValue),
|
|
||||||
.fromLuaValue = &SignalConnectionRef::FromLuaValue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string SignalConnectionRef::ToString() const {
|
const Data::TypeInfo& Data::SignalConnectionRef::GetType() const { return Data::SignalConnectionRef::TYPE; };
|
||||||
return "Connection";
|
|
||||||
|
const Data::String Data::SignalConnectionRef::ToString() const {
|
||||||
|
return Data::String("Connection");
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalConnectionRef::operator std::weak_ptr<SignalConnection>() {
|
Data::SignalConnectionRef::operator std::weak_ptr<SignalConnection>() {
|
||||||
return signalConnection;
|
return signalConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalConnectionRef::Serialize(pugi::xml_node node) const {
|
void Data::SignalConnectionRef::Serialize(pugi::xml_node node) const {
|
||||||
// Not serializable
|
// Not serializable
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalConnectionRef::PushLuaValue(lua_State* L) const {
|
void Data::SignalConnectionRef::PushLuaValue(lua_State* L) const {
|
||||||
int n = lua_gettop(L);
|
int n = lua_gettop(L);
|
||||||
|
|
||||||
auto userdata = (std::weak_ptr<SignalConnection>**)lua_newuserdata(L, sizeof(std::weak_ptr<SignalConnection>));
|
auto userdata = (std::weak_ptr<SignalConnection>**)lua_newuserdata(L, sizeof(std::weak_ptr<SignalConnection>));
|
||||||
|
@ -378,10 +378,10 @@ void SignalConnectionRef::PushLuaValue(lua_State* L) const {
|
||||||
lua_setmetatable(L, n+1);
|
lua_setmetatable(L, n+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
result<Variant, LuaCastError> SignalConnectionRef::FromLuaValue(lua_State* L, int idx) {
|
result<Data::Variant, LuaCastError> Data::SignalConnectionRef::FromLuaValue(lua_State* L, int idx) {
|
||||||
auto userdata = (std::weak_ptr<SignalConnection>**)luaL_checkudata(L, 1, "__mt_signalconnection");
|
auto userdata = (std::weak_ptr<SignalConnection>**)luaL_checkudata(L, 1, "__mt_signalconnection");
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
return Variant(SignalConnectionRef(**userdata));
|
return Data::Variant(Data::SignalConnectionRef(**userdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int signalconnection_tostring(lua_State* L) {
|
static int signalconnection_tostring(lua_State* L) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
class Instance;
|
class Instance;
|
||||||
class Signal;
|
class Signal;
|
||||||
|
|
||||||
class SignalConnectionRef;
|
namespace Data { class SignalConnectionRef; }
|
||||||
|
|
||||||
class SignalConnection : public std::enable_shared_from_this<SignalConnection> {
|
class SignalConnection : public std::enable_shared_from_this<SignalConnection> {
|
||||||
protected:
|
protected:
|
||||||
|
@ -23,7 +23,7 @@ protected:
|
||||||
|
|
||||||
SignalConnection(std::weak_ptr<Signal> parent);
|
SignalConnection(std::weak_ptr<Signal> parent);
|
||||||
|
|
||||||
virtual void Call(std::vector<Variant>) = 0;
|
virtual void Call(std::vector<Data::Variant>) = 0;
|
||||||
friend Signal;
|
friend Signal;
|
||||||
public:
|
public:
|
||||||
inline bool Connected() { return !parentSignal.expired(); };
|
inline bool Connected() { return !parentSignal.expired(); };
|
||||||
|
@ -33,13 +33,13 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
class CSignalConnection : public SignalConnection {
|
class CSignalConnection : public SignalConnection {
|
||||||
std::function<void(std::vector<Variant>)> function;
|
std::function<void(std::vector<Data::Variant>)> function;
|
||||||
|
|
||||||
friend Signal;
|
friend Signal;
|
||||||
protected:
|
protected:
|
||||||
void Call(std::vector<Variant>) override;
|
void Call(std::vector<Data::Variant>) override;
|
||||||
public:
|
public:
|
||||||
CSignalConnection(std::function<void(std::vector<Variant>)>, std::weak_ptr<Signal> parent);
|
CSignalConnection(std::function<void(std::vector<Data::Variant>)>, std::weak_ptr<Signal> parent);
|
||||||
};
|
};
|
||||||
|
|
||||||
class LuaSignalConnection : public SignalConnection {
|
class LuaSignalConnection : public SignalConnection {
|
||||||
|
@ -48,7 +48,7 @@ class LuaSignalConnection : public SignalConnection {
|
||||||
|
|
||||||
friend Signal;
|
friend Signal;
|
||||||
protected:
|
protected:
|
||||||
void Call(std::vector<Variant>) override;
|
void Call(std::vector<Data::Variant>) override;
|
||||||
public:
|
public:
|
||||||
LuaSignalConnection(lua_State*, std::weak_ptr<Signal> parent);
|
LuaSignalConnection(lua_State*, std::weak_ptr<Signal> parent);
|
||||||
LuaSignalConnection (const LuaSignalConnection&) = delete;
|
LuaSignalConnection (const LuaSignalConnection&) = delete;
|
||||||
|
@ -63,7 +63,7 @@ class SignalConnectionHolder {
|
||||||
public:
|
public:
|
||||||
SignalConnectionHolder();
|
SignalConnectionHolder();
|
||||||
SignalConnectionHolder(std::shared_ptr<SignalConnection>);
|
SignalConnectionHolder(std::shared_ptr<SignalConnection>);
|
||||||
SignalConnectionHolder(SignalConnectionRef other);
|
SignalConnectionHolder(Data::SignalConnectionRef other);
|
||||||
~SignalConnectionHolder();
|
~SignalConnectionHolder();
|
||||||
|
|
||||||
// Prevent SignalConnectionHolder being accidentally copied, making it useless
|
// Prevent SignalConnectionHolder being accidentally copied, making it useless
|
||||||
|
@ -90,12 +90,12 @@ public:
|
||||||
Signal& operator= (const Signal&) = delete;
|
Signal& operator= (const Signal&) = delete;
|
||||||
|
|
||||||
void DisconnectAll();
|
void DisconnectAll();
|
||||||
void Fire(std::vector<Variant> args);
|
void Fire(std::vector<Data::Variant> args);
|
||||||
void Fire();
|
void Fire();
|
||||||
SignalConnectionRef Connect(std::function<void(std::vector<Variant>)> callback);
|
Data::SignalConnectionRef Connect(std::function<void(std::vector<Data::Variant>)> callback);
|
||||||
SignalConnectionRef Connect(lua_State*);
|
Data::SignalConnectionRef Connect(lua_State*);
|
||||||
SignalConnectionRef Once(std::function<void(std::vector<Variant>)> callback);
|
Data::SignalConnectionRef Once(std::function<void(std::vector<Data::Variant>)> callback);
|
||||||
SignalConnectionRef Once(lua_State*);
|
Data::SignalConnectionRef Once(lua_State*);
|
||||||
int Wait(lua_State*);
|
int Wait(lua_State*);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,36 +105,43 @@ public:
|
||||||
virtual ~SignalSource();
|
virtual ~SignalSource();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SignalRef {
|
namespace Data {
|
||||||
std::weak_ptr<Signal> signal;
|
class SignalRef : public Data::Base {
|
||||||
|
std::weak_ptr<Signal> signal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SignalRef(std::weak_ptr<Signal>);
|
SignalRef(std::weak_ptr<Signal>);
|
||||||
~SignalRef();
|
~SignalRef();
|
||||||
|
|
||||||
static const TypeDesc TYPE;
|
virtual const TypeInfo& GetType() const override;
|
||||||
|
static const TypeInfo TYPE;
|
||||||
|
|
||||||
operator std::weak_ptr<Signal>();
|
operator std::weak_ptr<Signal>();
|
||||||
|
|
||||||
virtual const std::string ToString() const;
|
virtual const Data::String ToString() const override;
|
||||||
virtual void Serialize(pugi::xml_node node) const;
|
virtual void Serialize(pugi::xml_node node) const override;
|
||||||
virtual void PushLuaValue(lua_State*) const;
|
virtual void PushLuaValue(lua_State*) const override;
|
||||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SignalConnectionRef {
|
class SignalConnectionRef : public Data::Base {
|
||||||
std::weak_ptr<SignalConnection> signalConnection;
|
std::weak_ptr<SignalConnection> signalConnection;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SignalConnectionRef(std::weak_ptr<SignalConnection>);
|
SignalConnectionRef(std::weak_ptr<SignalConnection>);
|
||||||
~SignalConnectionRef();
|
~SignalConnectionRef();
|
||||||
|
|
||||||
static const TypeDesc TYPE;
|
virtual const TypeInfo& GetType() const override;
|
||||||
|
static const TypeInfo TYPE;
|
||||||
|
|
||||||
operator std::weak_ptr<SignalConnection>();
|
operator std::weak_ptr<SignalConnection>();
|
||||||
|
|
||||||
virtual const std::string ToString() const;
|
virtual const Data::String ToString() const override;
|
||||||
virtual void Serialize(pugi::xml_node node) const;
|
virtual void Serialize(pugi::xml_node node) const override;
|
||||||
virtual void PushLuaValue(lua_State*) const;
|
virtual void PushLuaValue(lua_State*) const override;
|
||||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using Data::SignalRef;
|
||||||
|
using Data::SignalConnectionRef;
|
|
@ -1,88 +0,0 @@
|
||||||
#include "variant.h"
|
|
||||||
#include "datatypes/base.h"
|
|
||||||
#include "datatypes/cframe.h"
|
|
||||||
#include "datatypes/enum.h"
|
|
||||||
#include "datatypes/primitives.h"
|
|
||||||
#include "datatypes/ref.h"
|
|
||||||
#include "datatypes/signal.h"
|
|
||||||
#include "datatypes/vector.h"
|
|
||||||
#include "error/data.h"
|
|
||||||
#include "logger.h"
|
|
||||||
#include "panic.h"
|
|
||||||
#include <pugixml.hpp>
|
|
||||||
#include <string>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
[[noreturn]] inline void unreachable() {
|
|
||||||
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
|
|
||||||
__assume(false);
|
|
||||||
#else // GCC, Clang
|
|
||||||
__builtin_unreachable();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeDesc* VARIANT_TYPES[] {
|
|
||||||
&NULL_TYPE,
|
|
||||||
&BOOL_TYPE,
|
|
||||||
&INT_TYPE,
|
|
||||||
&FLOAT_TYPE,
|
|
||||||
&STRING_TYPE,
|
|
||||||
&Vector3::TYPE,
|
|
||||||
&CFrame::TYPE,
|
|
||||||
&Color3::TYPE,
|
|
||||||
&InstanceRef::TYPE,
|
|
||||||
&SignalRef::TYPE,
|
|
||||||
&SignalConnectionRef::TYPE,
|
|
||||||
&Enum::TYPE,
|
|
||||||
&EnumItem::TYPE,
|
|
||||||
};
|
|
||||||
|
|
||||||
const TypeMeta Variant::GetTypeMeta() const {
|
|
||||||
return VARIANT_TYPES[wrapped.index()];
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeDesc* Variant::GetType() const {
|
|
||||||
return VARIANT_TYPES[wrapped.index()];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Variant::ToString() const {
|
|
||||||
if (!VARIANT_TYPES[wrapped.index()]->pushLuaValue) {
|
|
||||||
Logger::fatalErrorf("Data type %s does not implement toString", VARIANT_TYPES[wrapped.index()]->name.c_str());
|
|
||||||
}
|
|
||||||
return VARIANT_TYPES[wrapped.index()]->toString(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Variant::Serialize(pugi::xml_node node) const {
|
|
||||||
if (!VARIANT_TYPES[wrapped.index()]->pushLuaValue) {
|
|
||||||
Logger::fatalErrorf("Data type %s does not implement serializer", VARIANT_TYPES[wrapped.index()]->name.c_str());
|
|
||||||
}
|
|
||||||
VARIANT_TYPES[wrapped.index()]->serialize(*this, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Variant::PushLuaValue(lua_State* state) const {
|
|
||||||
if (!VARIANT_TYPES[wrapped.index()]->pushLuaValue) {
|
|
||||||
Logger::fatalErrorf("Data type %s does not implement pushLuaValue", VARIANT_TYPES[wrapped.index()]->name.c_str());
|
|
||||||
}
|
|
||||||
VARIANT_TYPES[wrapped.index()]->pushLuaValue(*this, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
result<Variant, DataParseError> Variant::Deserialize(pugi::xml_node node, const TypeMeta type) {
|
|
||||||
if (!type.descriptor->deserialize) {
|
|
||||||
Logger::fatalErrorf("Data type %s does not implement deserialize", type.descriptor->name.c_str());
|
|
||||||
return DataParseError(node.text().as_string(), type.descriptor->name);
|
|
||||||
}
|
|
||||||
return type.descriptor->deserialize(node, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, const TypeDesc*> TYPE_MAP = {
|
|
||||||
{ "null", &NULL_TYPE },
|
|
||||||
{ "bool", &BOOL_TYPE },
|
|
||||||
{ "int", &INT_TYPE },
|
|
||||||
{ "float", &FLOAT_TYPE },
|
|
||||||
{ "string", &STRING_TYPE },
|
|
||||||
{ "Vector3", &Vector3::TYPE },
|
|
||||||
{ "CoordinateFrame", &CFrame::TYPE },
|
|
||||||
{ "Color3", &Color3::TYPE },
|
|
||||||
{ "Ref", &InstanceRef::TYPE },
|
|
||||||
{ "token", &EnumItem::TYPE },
|
|
||||||
};
|
|
|
@ -1,94 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <variant>
|
|
||||||
#include <map>
|
|
||||||
#include "base.h"
|
|
||||||
#include "datatypes/color3.h"
|
|
||||||
#include "datatypes/enum.h"
|
|
||||||
#include "datatypes/ref.h"
|
|
||||||
#include "datatypes/signal.h"
|
|
||||||
#include "error/data.h"
|
|
||||||
#include "vector.h"
|
|
||||||
#include "cframe.h"
|
|
||||||
|
|
||||||
// #define __VARIANT_TYPE std::variant< \
|
|
||||||
// Null, \
|
|
||||||
// Bool, \
|
|
||||||
// Int, \
|
|
||||||
// Float, \
|
|
||||||
// String \
|
|
||||||
// >
|
|
||||||
|
|
||||||
typedef std::variant<
|
|
||||||
std::monostate,
|
|
||||||
bool,
|
|
||||||
int,
|
|
||||||
float,
|
|
||||||
std::string,
|
|
||||||
Vector3,
|
|
||||||
CFrame,
|
|
||||||
Color3,
|
|
||||||
InstanceRef,
|
|
||||||
SignalRef,
|
|
||||||
SignalConnectionRef,
|
|
||||||
Enum,
|
|
||||||
EnumItem
|
|
||||||
> __VARIANT_TYPE;
|
|
||||||
|
|
||||||
class Variant {
|
|
||||||
__VARIANT_TYPE wrapped;
|
|
||||||
public:
|
|
||||||
template <typename T, std::enable_if_t<std::is_constructible_v<__VARIANT_TYPE, T>, int> = 0> Variant(T obj) : wrapped(obj) {}
|
|
||||||
template <typename T, std::enable_if_t<std::is_constructible_v<__VARIANT_TYPE, T>, int> = 0> T get() { return std::get<T>(wrapped); }
|
|
||||||
std::string ToString() const;
|
|
||||||
|
|
||||||
const TypeMeta GetTypeMeta() const;
|
|
||||||
const TypeDesc* GetType() const;
|
|
||||||
|
|
||||||
void Serialize(pugi::xml_node node) const;
|
|
||||||
void PushLuaValue(lua_State* state) const;
|
|
||||||
static result<Variant, DataParseError> Deserialize(pugi::xml_node node, const TypeMeta);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename R, typename ...Args>
|
|
||||||
std::function<R(Variant, Args...)> toVariantFunction(R(T::*f)(Args...)) {
|
|
||||||
return [f](Variant var, Args... args) {
|
|
||||||
return (var.get<T>().*f)(args...);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename R, typename ...Args>
|
|
||||||
std::function<R(Variant, Args...)> toVariantFunction(R(T::*f)(Args...) const) {
|
|
||||||
return [f](Variant var, Args... args) {
|
|
||||||
return (var.get<T>().*f)(args...);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename ...Args>
|
|
||||||
std::function<Variant(Args...)> toVariantGenerator(T(f)(Args...)) {
|
|
||||||
return [f](Args... args) {
|
|
||||||
return (Variant)f(args...);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename ...Args, typename ...E>
|
|
||||||
std::function<result<Variant, E...>(Args...)> toVariantGenerator(result<T, E...>(f)(Args...)) {
|
|
||||||
return [f](Args... args) -> result<Variant, E...> {
|
|
||||||
auto result = f(args...);
|
|
||||||
if (result.isSuccess()) return (Variant)(result.success().value());
|
|
||||||
return result.error().value();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename ...Args, typename ...E>
|
|
||||||
std::function<result<Variant, E...>(Args..., const TypeMeta)> toVariantGeneratorNoMeta(result<T, E...>(f)(Args...)) {
|
|
||||||
return [f](Args... args, const TypeMeta) -> result<Variant, E...> {
|
|
||||||
auto result = f(args...);
|
|
||||||
if (result.isSuccess()) return (Variant)(result.success().value());
|
|
||||||
return result.error().value();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map of all data types to their type names
|
|
||||||
extern std::map<std::string, const TypeDesc*> TYPE_MAP;
|
|
|
@ -7,96 +7,95 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
#include "datatypes/base.h"
|
#include "datatypes/base.h"
|
||||||
#include "datatypes/variant.h"
|
#include "datatypes/meta.h"
|
||||||
#include "error/data.h"
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace rp = reactphysics3d;
|
namespace rp = reactphysics3d;
|
||||||
|
|
||||||
Vector3::Vector3() : vector(glm::vec3(0, 0, 0)) {};
|
Data::Vector3::Vector3() : vector(glm::vec3(0, 0, 0)) {};
|
||||||
Vector3::Vector3(const glm::vec3& src) : vector(src) {};
|
Data::Vector3::Vector3(const glm::vec3& src) : vector(src) {};
|
||||||
Vector3::Vector3(const rp::Vector3& src) : vector(glm::vec3(src.x, src.y, src.z)) {};
|
Data::Vector3::Vector3(const rp::Vector3& src) : vector(glm::vec3(src.x, src.y, src.z)) {};
|
||||||
Vector3::Vector3(float x, const float y, float z) : vector(glm::vec3(x, y, z)) {};
|
Data::Vector3::Vector3(float x, const float y, float z) : vector(glm::vec3(x, y, z)) {};
|
||||||
|
|
||||||
Vector3::~Vector3() = default;
|
Data::Vector3::~Vector3() = default;
|
||||||
|
|
||||||
Vector3 Vector3::ZERO(0, 0, 0);
|
Data::Vector3 Data::Vector3::ZERO(0, 0, 0);
|
||||||
Vector3 Vector3::ONE(1, 1, 1);
|
Data::Vector3 Data::Vector3::ONE(1, 1, 1);
|
||||||
|
|
||||||
const std::string Vector3::ToString() const {
|
const Data::String Data::Vector3::ToString() const {
|
||||||
// https://stackoverflow.com/a/46424921/16255372
|
// https://stackoverflow.com/a/46424921/16255372
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
stream << std::setprecision(8) << std::noshowpoint << X() << ", " << Y() << ", " << Z();
|
stream << std::setprecision(8) << std::noshowpoint << X() << ", " << Y() << ", " << Z();
|
||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3::operator glm::vec3() const { return vector; };
|
Data::Vector3::operator glm::vec3() const { return vector; };
|
||||||
Vector3::operator rp::Vector3() const { return rp::Vector3(X(), Y(), Z()); };
|
Data::Vector3::operator rp::Vector3() const { return rp::Vector3(X(), Y(), Z()); };
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
|
|
||||||
Vector3 Vector3::operator *(float scale) const {
|
Data::Vector3 Data::Vector3::operator *(float scale) const {
|
||||||
return Vector3(this->X() * scale, this->Y() * scale, this->Z() * scale);
|
return Data::Vector3(this->X() * scale, this->Y() * scale, this->Z() * scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 Vector3::operator /(float scale) const {
|
Data::Vector3 Data::Vector3::operator /(float scale) const {
|
||||||
return Vector3(this->X() / scale, this->Y() / scale, this->Z() / scale);
|
return Data::Vector3(this->X() / scale, this->Y() / scale, this->Z() / scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Component-wise
|
// Component-wise
|
||||||
Vector3 Vector3::operator *(Vector3 other) const {
|
Data::Vector3 Data::Vector3::operator *(Data::Vector3 other) const {
|
||||||
return Vector3(this->X() * other.X(), this->Y() * other.Y(), this->Z() * other.Z());
|
return Data::Vector3(this->X() * other.X(), this->Y() * other.Y(), this->Z() * other.Z());
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 Vector3::operator +(Vector3 other) const {
|
Data::Vector3 Data::Vector3::operator +(Data::Vector3 other) const {
|
||||||
return Vector3(this->X() + other.X(), this->Y() + other.Y(), this->Z() + other.Z());
|
return Data::Vector3(this->X() + other.X(), this->Y() + other.Y(), this->Z() + other.Z());
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 Vector3::operator -(Vector3 other) const {
|
Data::Vector3 Data::Vector3::operator -(Data::Vector3 other) const {
|
||||||
return Vector3(this->X() - other.X(), this->Y() - other.Y(), this->Z() - other.Z());
|
return Data::Vector3(this->X() - other.X(), this->Y() - other.Y(), this->Z() - other.Z());
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 Vector3::operator -() const {
|
Data::Vector3 Data::Vector3::operator -() const {
|
||||||
return Vector3(-this->X(), -this->Y(), -this->Z());
|
return Data::Vector3(-this->X(), -this->Y(), -this->Z());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Vector3::operator ==(Vector3 other) const {
|
bool Data::Vector3::operator ==(Data::Vector3 other) const {
|
||||||
return this->X() == other.X() && this->Y() == other.Y() && this->Z() == other.Z();
|
return this->X() == other.X() && this->Y() == other.Y() && this->Z() == other.Z();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Vector3::operator <(Vector3 other) const {
|
bool Data::Vector3::operator <(Data::Vector3 other) const {
|
||||||
return X() < other.X() && Y() < other.Y() && Z() < other.Z();
|
return X() < other.X() && Y() < other.Y() && Z() < other.Z();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Vector3::operator >(Vector3 other) const {
|
bool Data::Vector3::operator >(Data::Vector3 other) const {
|
||||||
return X() > other.X() && Y() > other.Y() && Z() > other.Z();
|
return X() > other.X() && Y() > other.Y() && Z() > other.Z();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 Vector3::Cross(Vector3 other) const {
|
Data::Vector3 Data::Vector3::Cross(Data::Vector3 other) const {
|
||||||
return glm::cross(this->vector, other.vector);
|
return glm::cross(this->vector, other.vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Vector3::Dot(Vector3 other) const {
|
float Data::Vector3::Dot(Data::Vector3 other) const {
|
||||||
return glm::dot(this->vector, other.vector);
|
return glm::dot(this->vector, other.vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialization
|
// Serialization
|
||||||
|
|
||||||
void Vector3::Serialize(pugi::xml_node node) const {
|
void Data::Vector3::Serialize(pugi::xml_node node) const {
|
||||||
node.append_child("X").text().set(std::to_string(this->X()));
|
node.append_child("X").text().set(std::to_string(this->X()));
|
||||||
node.append_child("Y").text().set(std::to_string(this->Y()));
|
node.append_child("Y").text().set(std::to_string(this->Y()));
|
||||||
node.append_child("Z").text().set(std::to_string(this->Z()));
|
node.append_child("Z").text().set(std::to_string(this->Z()));
|
||||||
}
|
}
|
||||||
|
|
||||||
result<Vector3, DataParseError> Vector3::Deserialize(pugi::xml_node node) {
|
Data::Variant Data::Vector3::Deserialize(pugi::xml_node node) {
|
||||||
return Vector3(node.child("X").text().as_float(), node.child("Y").text().as_float(), node.child("Z").text().as_float());
|
return Data::Vector3(node.child("X").text().as_float(), node.child("Y").text().as_float(), node.child("Z").text().as_float());
|
||||||
}
|
}
|
||||||
|
|
||||||
result<Vector3, DataParseError> Vector3::FromString(std::string string) {
|
std::optional<Data::Variant> Data::Vector3::FromString(std::string string) {
|
||||||
float components[3];
|
float components[3];
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
if (string.length() == 0) return DataParseError(string, "Vector3");
|
if (string.length() == 0) return std::nullopt;
|
||||||
while (string[0] == ' ' && string.length() > 0) string.erase(0, 1);
|
while (string[0] == ' ' && string.length() > 0) string.erase(0, 1);
|
||||||
size_t nextPos = string.find(",");
|
size_t nextPos = string.find(",");
|
||||||
if (nextPos == -1) nextPos = string.length();
|
if (nextPos == -1) nextPos = string.length();
|
||||||
|
@ -105,10 +104,10 @@ result<Vector3, DataParseError> Vector3::FromString(std::string string) {
|
||||||
|
|
||||||
char* cpos;
|
char* cpos;
|
||||||
float value = std::strtof(term.c_str(), &cpos);
|
float value = std::strtof(term.c_str(), &cpos);
|
||||||
if (cpos == term.c_str()) return DataParseError(string, "Vector3");
|
if (cpos == term.c_str()) return std::nullopt;
|
||||||
|
|
||||||
components[i] = value;
|
components[i] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Vector3(components[0], components[1], components[2]);
|
return Data::Vector3(components[0], components[1], components[2]);
|
||||||
}
|
}
|
|
@ -2,61 +2,64 @@
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "datatypes/annotation.h"
|
#include "datatypes/annotation.h"
|
||||||
#include "error/data.h"
|
|
||||||
#include <glm/ext/vector_float3.hpp>
|
#include <glm/ext/vector_float3.hpp>
|
||||||
#include <glm/geometric.hpp>
|
#include <glm/geometric.hpp>
|
||||||
|
|
||||||
namespace reactphysics3d { class Vector3; };
|
namespace reactphysics3d { class Vector3; };
|
||||||
|
|
||||||
class DEF_DATA Vector3 {
|
namespace Data {
|
||||||
AUTOGEN_PREAMBLE_DATA
|
class DEF_DATA Vector3 : public Base {
|
||||||
glm::vec3 vector;
|
AUTOGEN_PREAMBLE_DATA
|
||||||
|
glm::vec3 vector;
|
||||||
public:
|
|
||||||
DEF_DATA_CTOR Vector3();
|
|
||||||
DEF_DATA_CTOR Vector3(float x, float y, float z);
|
|
||||||
Vector3(const glm::vec3&);
|
|
||||||
Vector3(const reactphysics3d::Vector3&);
|
|
||||||
~Vector3();
|
|
||||||
|
|
||||||
DEF_DATA_PROP static Vector3 ZERO;
|
|
||||||
DEF_DATA_PROP static Vector3 ONE;
|
|
||||||
|
|
||||||
virtual const std::string ToString() const;
|
|
||||||
virtual void Serialize(pugi::xml_node node) const;
|
|
||||||
|
|
||||||
static result<Vector3, DataParseError> Deserialize(pugi::xml_node node);
|
|
||||||
static result<Vector3, DataParseError> FromString(std::string);
|
|
||||||
|
|
||||||
static void PushLuaLibrary(lua_State*);
|
public:
|
||||||
|
DEF_DATA_CTOR Vector3();
|
||||||
|
DEF_DATA_CTOR Vector3(float x, float y, float z);
|
||||||
|
Vector3(const glm::vec3&);
|
||||||
|
Vector3(const reactphysics3d::Vector3&);
|
||||||
|
~Vector3();
|
||||||
|
|
||||||
operator glm::vec3() const;
|
DEF_DATA_PROP static Data::Vector3 ZERO;
|
||||||
operator reactphysics3d::Vector3() const;
|
DEF_DATA_PROP static Data::Vector3 ONE;
|
||||||
|
|
||||||
DEF_DATA_PROP inline float X() const { return vector.x; }
|
virtual const Data::String ToString() const override;
|
||||||
DEF_DATA_PROP inline float Y() const { return vector.y; }
|
virtual void Serialize(pugi::xml_node node) const override;
|
||||||
DEF_DATA_PROP inline float Z() const { return vector.z; }
|
|
||||||
DEF_DATA_METHOD inline float Magnitude() const { return glm::length(vector); }
|
|
||||||
DEF_DATA_METHOD inline Vector3 Unit() const { return glm::normalize(vector); }
|
|
||||||
DEF_DATA_METHOD inline Vector3 Abs() const { return glm::abs(vector); }
|
|
||||||
|
|
||||||
DEF_DATA_METHOD Vector3 Cross(Vector3) const;
|
static Data::Variant Deserialize(pugi::xml_node node);
|
||||||
DEF_DATA_METHOD float Dot(Vector3) const;
|
static std::optional<Data::Variant> FromString(std::string);
|
||||||
|
|
||||||
|
static void PushLuaLibrary(lua_State*);
|
||||||
|
|
||||||
// Operators
|
operator glm::vec3() const;
|
||||||
DEF_DATA_OP Vector3 operator *(float) const;
|
operator reactphysics3d::Vector3() const;
|
||||||
DEF_DATA_OP Vector3 operator /(float) const;
|
|
||||||
DEF_DATA_OP Vector3 operator *(Vector3) const; // Component-wise
|
|
||||||
DEF_DATA_OP Vector3 operator +(Vector3) const;
|
|
||||||
DEF_DATA_OP Vector3 operator -(Vector3) const;
|
|
||||||
DEF_DATA_OP Vector3 operator -() const;
|
|
||||||
|
|
||||||
DEF_DATA_OP bool operator <(Vector3) const;
|
DEF_DATA_PROP inline float X() const { return vector.x; }
|
||||||
DEF_DATA_OP bool operator >(Vector3) const;
|
DEF_DATA_PROP inline float Y() const { return vector.y; }
|
||||||
|
DEF_DATA_PROP inline float Z() const { return vector.z; }
|
||||||
|
DEF_DATA_METHOD inline float Magnitude() const { return glm::length(vector); }
|
||||||
|
DEF_DATA_METHOD inline Data::Vector3 Unit() const { return glm::normalize(vector); }
|
||||||
|
DEF_DATA_METHOD inline Data::Vector3 Abs() const { return glm::abs(vector); }
|
||||||
|
|
||||||
DEF_DATA_OP bool operator ==(Vector3) const;
|
DEF_DATA_METHOD Data::Vector3 Cross(Data::Vector3) const;
|
||||||
};
|
DEF_DATA_METHOD float Dot(Data::Vector3) const;
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
DEF_DATA_OP Data::Vector3 operator *(float) const;
|
||||||
|
DEF_DATA_OP Data::Vector3 operator /(float) const;
|
||||||
|
DEF_DATA_OP Data::Vector3 operator *(Data::Vector3) const; // Component-wise
|
||||||
|
DEF_DATA_OP Data::Vector3 operator +(Data::Vector3) const;
|
||||||
|
DEF_DATA_OP Data::Vector3 operator -(Data::Vector3) const;
|
||||||
|
DEF_DATA_OP Data::Vector3 operator -() const;
|
||||||
|
|
||||||
inline void printVec(Vector3 vec) {
|
DEF_DATA_OP bool operator <(Data::Vector3) const;
|
||||||
|
DEF_DATA_OP bool operator >(Data::Vector3) const;
|
||||||
|
|
||||||
|
DEF_DATA_OP bool operator ==(Data::Vector3) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using Data::Vector3;
|
||||||
|
|
||||||
|
inline void printVec(Data::Vector3 vec) {
|
||||||
printf("(%f, %f, %f)\n", vec.X(), vec.Y(), vec.Z());
|
printf("(%f, %f, %f)\n", vec.X(), vec.Y(), vec.Z());
|
||||||
}
|
}
|
|
@ -4,10 +4,5 @@
|
||||||
|
|
||||||
class LuaCastError : public Error {
|
class LuaCastError : public Error {
|
||||||
public:
|
public:
|
||||||
inline LuaCastError(std::string sourceType, std::string targetType) : Error("LuaCastError", "Attempt to cast " + sourceType + " to " + targetType) {}
|
inline LuaCastError(std::string sourceType, std::string targetType) : Error("InstanceCastError", "Attempt to cast " + sourceType + " to " + targetType) {}
|
||||||
};
|
|
||||||
|
|
||||||
class DataParseError : public Error {
|
|
||||||
public:
|
|
||||||
inline DataParseError(std::string parsedString, std::string targetType) : Error("DataParseError", "Failed to parse '" + parsedString + "' into value of type " + targetType) {}
|
|
||||||
};
|
};
|
|
@ -24,8 +24,6 @@ class [[nodiscard]] result {
|
||||||
|
|
||||||
std::variant<success_state, error_state> value;
|
std::variant<success_state, error_state> value;
|
||||||
public:
|
public:
|
||||||
// Helper for std::variant, etc.
|
|
||||||
template <typename ...T_Args, std::enable_if_t<std::is_constructible_v<T_Result, T_Args...>, int> = 0> result(T_Args... args) : value(success_state { T_Result(args...) }) {}
|
|
||||||
result(T_Result success) : value(success_state { success }) {}
|
result(T_Result success) : value(success_state { success }) {}
|
||||||
result(std::variant<T_Errors...> error) : value(error_state { error }) {}
|
result(std::variant<T_Errors...> error) : value(error_state { error }) {}
|
||||||
template <typename T_Error, std::enable_if_t<std::disjunction_v<std::is_same<T_Error, T_Errors>...>, int> = 0>
|
template <typename T_Error, std::enable_if_t<std::disjunction_v<std::is_same<T_Error, T_Errors>...>, int> = 0>
|
||||||
|
@ -63,7 +61,7 @@ public:
|
||||||
|
|
||||||
// Equivalent to .success
|
// Equivalent to .success
|
||||||
operator std::optional<T_Result>() { return success(); }
|
operator std::optional<T_Result>() { return success(); }
|
||||||
explicit operator bool() const { return isSuccess(); } // Explicity is necessary to prevent auto-casting from result to Variant, for instance
|
operator bool() { return isSuccess(); }
|
||||||
bool operator !() { return isError(); }
|
bool operator !() { return isError(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,8 @@
|
||||||
#define AUTOGEN_PREAMBLE \
|
#define AUTOGEN_PREAMBLE \
|
||||||
protected: \
|
protected: \
|
||||||
virtual result<PropertyMeta, MemberNotFound> InternalGetPropertyMeta(std::string name) override; \
|
virtual result<PropertyMeta, MemberNotFound> InternalGetPropertyMeta(std::string name) override; \
|
||||||
virtual fallible<MemberNotFound, AssignToReadOnlyMember> InternalSetPropertyValue(std::string name, Variant value) override; \
|
virtual fallible<MemberNotFound, AssignToReadOnlyMember> InternalSetPropertyValue(std::string name, Data::Variant value) override; \
|
||||||
virtual result<Variant, MemberNotFound> InternalGetPropertyValue(std::string name) override; \
|
virtual result<Data::Variant, MemberNotFound> InternalGetPropertyValue(std::string name) override; \
|
||||||
virtual void InternalUpdateProperty(std::string name) override; \
|
virtual void InternalUpdateProperty(std::string name) override; \
|
||||||
virtual std::vector<std::string> InternalGetProperties() override; \
|
virtual std::vector<std::string> InternalGetProperties() override; \
|
||||||
public: \
|
public: \
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include "instance.h"
|
#include "instance.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "datatypes/primitives.h"
|
#include "datatypes/meta.h"
|
||||||
#include "datatypes/variant.h"
|
|
||||||
#include "datatypes/base.h"
|
#include "datatypes/base.h"
|
||||||
#include "datatypes/ref.h"
|
#include "datatypes/ref.h"
|
||||||
#include "error/instance.h"
|
#include "error/instance.h"
|
||||||
|
@ -97,7 +96,7 @@ void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedCh
|
||||||
}
|
}
|
||||||
|
|
||||||
OnAncestryChanged(updatedChild, newParent);
|
OnAncestryChanged(updatedChild, newParent);
|
||||||
AncestryChanged->Fire({updatedChild.has_value() ? InstanceRef(updatedChild.value()) : InstanceRef(), newParent.has_value() ? InstanceRef(newParent.value()) : InstanceRef()});
|
AncestryChanged->Fire({updatedChild.has_value() ? Data::InstanceRef(updatedChild.value()) : Data::InstanceRef(), newParent.has_value() ? Data::InstanceRef(newParent.value()) : Data::InstanceRef()});
|
||||||
|
|
||||||
// Old workspace used to exist, and workspaces differ
|
// Old workspace used to exist, and workspaces differ
|
||||||
if (!oldWorkspace.expired() && oldWorkspace != _workspace) {
|
if (!oldWorkspace.expired() && oldWorkspace != _workspace) {
|
||||||
|
@ -110,7 +109,7 @@ void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedCh
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update ancestry in descendants
|
// Update ancestry in descendants
|
||||||
for (std::shared_ptr<Instance> child : children) {
|
for (InstanceRef child : children) {
|
||||||
child->updateAncestry(updatedChild, newParent);
|
child->updateAncestry(updatedChild, newParent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,11 +179,11 @@ void Instance::OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
result<Variant, MemberNotFound> Instance::GetPropertyValue(std::string name) {
|
result<Data::Variant, MemberNotFound> Instance::GetPropertyValue(std::string name) {
|
||||||
return InternalGetPropertyValue(name);
|
return InternalGetPropertyValue(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fallible<MemberNotFound, AssignToReadOnlyMember> Instance::SetPropertyValue(std::string name, Variant value, bool sendUpdateEvent) {
|
fallible<MemberNotFound, AssignToReadOnlyMember> Instance::SetPropertyValue(std::string name, Data::Variant value, bool sendUpdateEvent) {
|
||||||
auto result = InternalSetPropertyValue(name, value);
|
auto result = InternalSetPropertyValue(name, value);
|
||||||
if (result.isSuccess() && sendUpdateEvent) {
|
if (result.isSuccess() && sendUpdateEvent) {
|
||||||
InternalUpdateProperty(name);
|
InternalUpdateProperty(name);
|
||||||
|
@ -198,33 +197,33 @@ result<PropertyMeta, MemberNotFound> Instance::GetPropertyMeta(std::string name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
result<Variant, MemberNotFound> Instance::InternalGetPropertyValue(std::string name) {
|
result<Data::Variant, MemberNotFound> Instance::InternalGetPropertyValue(std::string name) {
|
||||||
if (name == "Name") {
|
if (name == "Name") {
|
||||||
return Variant(this->name);
|
return Data::Variant(Data::String(this->name));
|
||||||
} else if (name == "Parent") {
|
} else if (name == "Parent") {
|
||||||
return Variant(InstanceRef(this->parent));
|
return Data::Variant(Data::InstanceRef(this->parent));
|
||||||
} else if (name == "ClassName") {
|
} else if (name == "ClassName") {
|
||||||
return Variant(GetClass()->className);
|
return Data::Variant(Data::String(GetClass()->className));
|
||||||
}
|
}
|
||||||
return MemberNotFound(GetClass()->className, name);
|
return MemberNotFound(GetClass()->className, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
result<PropertyMeta, MemberNotFound> Instance::InternalGetPropertyMeta(std::string name) {
|
result<PropertyMeta, MemberNotFound> Instance::InternalGetPropertyMeta(std::string name) {
|
||||||
if (name == "Name") {
|
if (name == "Name") {
|
||||||
return PropertyMeta { &STRING_TYPE };
|
return PropertyMeta { &Data::String::TYPE };
|
||||||
} else if (name == "Parent") {
|
} else if (name == "Parent") {
|
||||||
return PropertyMeta { &InstanceRef::TYPE, PROP_NOSAVE };
|
return PropertyMeta { &Data::InstanceRef::TYPE, };
|
||||||
} else if (name == "ClassName") {
|
} else if (name == "ClassName") {
|
||||||
return PropertyMeta { &STRING_TYPE, PROP_NOSAVE | PROP_READONLY };
|
return PropertyMeta { &Data::String::TYPE, PROP_NOSAVE | PROP_READONLY };
|
||||||
}
|
}
|
||||||
return MemberNotFound(GetClass()->className, name);
|
return MemberNotFound(GetClass()->className, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fallible<MemberNotFound, AssignToReadOnlyMember> Instance::InternalSetPropertyValue(std::string name, Variant value) {
|
fallible<MemberNotFound, AssignToReadOnlyMember> Instance::InternalSetPropertyValue(std::string name, Data::Variant value) {
|
||||||
if (name == "Name") {
|
if (name == "Name") {
|
||||||
this->name = (std::string)value.get<std::string>();
|
this->name = (std::string)value.get<Data::String>();
|
||||||
} else if (name == "Parent") {
|
} else if (name == "Parent") {
|
||||||
std::weak_ptr<Instance> ref = value.get<InstanceRef>();
|
std::weak_ptr<Instance> ref = value.get<Data::InstanceRef>();
|
||||||
SetParent(ref.expired() ? std::nullopt : std::make_optional(ref.lock()));
|
SetParent(ref.expired() ? std::nullopt : std::make_optional(ref.lock()));
|
||||||
} else if (name == "ClassName") {
|
} else if (name == "ClassName") {
|
||||||
return AssignToReadOnlyMember(GetClass()->className, name);
|
return AssignToReadOnlyMember(GetClass()->className, name);
|
||||||
|
@ -260,7 +259,7 @@ std::vector<std::string> Instance::GetProperties() {
|
||||||
|
|
||||||
// Serialization
|
// Serialization
|
||||||
|
|
||||||
void Instance::Serialize(pugi::xml_node parent, RefStateSerialize state) {
|
void Instance::Serialize(pugi::xml_node parent) {
|
||||||
pugi::xml_node node = parent.append_child("Item");
|
pugi::xml_node node = parent.append_child("Item");
|
||||||
node.append_attribute("class").set_value(this->GetClass()->className);
|
node.append_attribute("class").set_value(this->GetClass()->className);
|
||||||
|
|
||||||
|
@ -270,55 +269,24 @@ void Instance::Serialize(pugi::xml_node parent, RefStateSerialize state) {
|
||||||
PropertyMeta meta = GetPropertyMeta(name).expect("Meta of declared property is missing");
|
PropertyMeta meta = GetPropertyMeta(name).expect("Meta of declared property is missing");
|
||||||
if (meta.flags & (PROP_NOSAVE | PROP_READONLY)) continue; // This property should not be serialized. Skip...
|
if (meta.flags & (PROP_NOSAVE | PROP_READONLY)) continue; // This property should not be serialized. Skip...
|
||||||
|
|
||||||
pugi::xml_node propertyNode = propertiesNode.append_child(meta.type.descriptor->name);
|
pugi::xml_node propertyNode = propertiesNode.append_child(meta.type->name);
|
||||||
propertyNode.append_attribute("name").set_value(name);
|
propertyNode.append_attribute("name").set_value(name);
|
||||||
|
GetPropertyValue(name).expect("Declared property is missing").Serialize(propertyNode);
|
||||||
// Update std::shared_ptr<Instance> properties using map above
|
|
||||||
if (meta.type.descriptor == &InstanceRef::TYPE) {
|
|
||||||
std::weak_ptr<Instance> refWeak = GetPropertyValue(name).expect("Declared property is missing").get<InstanceRef>();
|
|
||||||
if (refWeak.expired()) continue;
|
|
||||||
|
|
||||||
auto ref = refWeak.lock();
|
|
||||||
auto remappedRef = state->remappedInstances[ref]; // TODO: I think this is okay? Maybe?? Add null check?
|
|
||||||
|
|
||||||
if (remappedRef != "") {
|
|
||||||
// If the instance has already been remapped, set the new value
|
|
||||||
propertyNode.text().set(remappedRef);
|
|
||||||
} else {
|
|
||||||
// Otheriise, queue this property to be updated later, and keep its current value
|
|
||||||
auto& refs = state->refsAwaitingRemap[ref];
|
|
||||||
refs.push_back(propertyNode);
|
|
||||||
state->refsAwaitingRemap[ref] = refs;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GetPropertyValue(name).expect("Declared property is missing").Serialize(propertyNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remap self
|
|
||||||
std::string remappedId = "OB" + std::to_string(state->count++);
|
|
||||||
state->remappedInstances[shared_from_this()] = remappedId;
|
|
||||||
node.append_attribute("referent").set_value(remappedId);
|
|
||||||
|
|
||||||
// Remap queued properties
|
|
||||||
for (pugi::xml_node ref : state->refsAwaitingRemap[shared_from_this()]) {
|
|
||||||
ref.text().set(remappedId);
|
|
||||||
}
|
|
||||||
state->refsAwaitingRemap[shared_from_this()].clear();
|
|
||||||
|
|
||||||
// Add children
|
// Add children
|
||||||
for (std::shared_ptr<Instance> child : this->children) {
|
for (InstanceRef child : this->children) {
|
||||||
child->Serialize(node, state);
|
child->Serialize(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result<std::shared_ptr<Instance>, NoSuchInstance> Instance::Deserialize(pugi::xml_node node, RefStateDeserialize state) {
|
result<InstanceRef, NoSuchInstance> Instance::Deserialize(pugi::xml_node node) {
|
||||||
std::string className = node.attribute("class").value();
|
std::string className = node.attribute("class").value();
|
||||||
if (INSTANCE_MAP.count(className) == 0) {
|
if (INSTANCE_MAP.count(className) == 0) {
|
||||||
return NoSuchInstance(className);
|
return NoSuchInstance(className);
|
||||||
}
|
}
|
||||||
// This will error if an abstract instance is used in the file. Oh well, not my prob rn.
|
// This will error if an abstract instance is used in the file. Oh well, not my prob rn.
|
||||||
std::shared_ptr<Instance> object = INSTANCE_MAP[className]->constructor();
|
InstanceRef object = INSTANCE_MAP[className]->constructor();
|
||||||
object->GetChildren();
|
object->GetChildren();
|
||||||
|
|
||||||
// const InstanceType* type = INSTANCE_MAP.at(className);
|
// const InstanceType* type = INSTANCE_MAP.at(className);
|
||||||
|
@ -332,51 +300,13 @@ result<std::shared_ptr<Instance>, NoSuchInstance> Instance::Deserialize(pugi::xm
|
||||||
Logger::fatalErrorf("Attempt to set unknown property '%s' of %s", propertyName.c_str(), object->GetClass()->className.c_str());
|
Logger::fatalErrorf("Attempt to set unknown property '%s' of %s", propertyName.c_str(), object->GetClass()->className.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto meta = meta_.expect();
|
Data::Variant value = Data::Variant::Deserialize(propertyNode);
|
||||||
|
object->SetPropertyValue(propertyName, value).expect("Declared property was missing");
|
||||||
// Update std::shared_ptr<Instance> properties using map above
|
|
||||||
if (meta.type.descriptor == &InstanceRef::TYPE) {
|
|
||||||
if (propertyNode.text().empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::string refId = propertyNode.text().as_string();
|
|
||||||
auto remappedRef = state->remappedInstances[refId]; // TODO: I think this is okay? Maybe?? Add null check?
|
|
||||||
|
|
||||||
if (remappedRef) {
|
|
||||||
// If the instance has already been remapped, set the new value
|
|
||||||
object->SetPropertyValue(propertyName, InstanceRef(remappedRef)).expect();
|
|
||||||
} else {
|
|
||||||
// Otheriise, queue this property to be updated later, and keep its current value
|
|
||||||
auto& refs = state->refsAwaitingRemap[refId];
|
|
||||||
refs.push_back(std::make_pair(object, propertyName));
|
|
||||||
state->refsAwaitingRemap[refId] = refs;
|
|
||||||
|
|
||||||
object->SetPropertyValue(propertyName, InstanceRef()).expect();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto valueResult = Variant::Deserialize(propertyNode, meta.type);
|
|
||||||
if (valueResult.isError()) {
|
|
||||||
valueResult.logError();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto value = valueResult.expect();
|
|
||||||
object->SetPropertyValue(propertyName, value).expect("Declared property was missing");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remap self
|
|
||||||
std::string remappedId = node.attribute("referent").value();
|
|
||||||
state->remappedInstances[remappedId] = object;
|
|
||||||
|
|
||||||
// Remap queued properties
|
|
||||||
for (std::pair<std::shared_ptr<Instance>, std::string> ref : state->refsAwaitingRemap[remappedId]) {
|
|
||||||
ref.first->SetPropertyValue(ref.second, InstanceRef(object)).expect();
|
|
||||||
}
|
|
||||||
state->refsAwaitingRemap[remappedId].clear();
|
|
||||||
|
|
||||||
// Read children
|
// Read children
|
||||||
for (pugi::xml_node childNode : node.children("Item")) {
|
for (pugi::xml_node childNode : node.children("Item")) {
|
||||||
result<std::shared_ptr<Instance>, NoSuchInstance> child = Instance::Deserialize(childNode, state);
|
result<InstanceRef, NoSuchInstance> child = Instance::Deserialize(childNode);
|
||||||
if (child.isError()) {
|
if (child.isError()) {
|
||||||
std::get<NoSuchInstance>(child.error().value()).logMessage();
|
std::get<NoSuchInstance>(child.error().value()).logMessage();
|
||||||
continue;
|
continue;
|
||||||
|
@ -432,7 +362,7 @@ DescendantsIterator::self_type DescendantsIterator::operator++(int _) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::shared_ptr<Instance>> Instance::Clone(RefStateClone state) {
|
std::optional<std::shared_ptr<Instance>> Instance::Clone(RefState<_RefStatePropertyCell> state) {
|
||||||
std::shared_ptr<Instance> newInstance = GetClass()->constructor();
|
std::shared_ptr<Instance> newInstance = GetClass()->constructor();
|
||||||
|
|
||||||
// Copy properties
|
// Copy properties
|
||||||
|
@ -441,9 +371,9 @@ std::optional<std::shared_ptr<Instance>> Instance::Clone(RefStateClone state) {
|
||||||
|
|
||||||
if (meta.flags & (PROP_READONLY | PROP_NOSAVE)) continue;
|
if (meta.flags & (PROP_READONLY | PROP_NOSAVE)) continue;
|
||||||
|
|
||||||
// Update std::shared_ptr<Instance> properties using map above
|
// Update InstanceRef properties using map above
|
||||||
if (meta.type.descriptor == &InstanceRef::TYPE) {
|
if (meta.type == &Data::InstanceRef::TYPE) {
|
||||||
std::weak_ptr<Instance> refWeak = GetPropertyValue(property).expect().get<InstanceRef>();
|
std::weak_ptr<Instance> refWeak = GetPropertyValue(property).expect().get<Data::InstanceRef>();
|
||||||
if (refWeak.expired()) continue;
|
if (refWeak.expired()) continue;
|
||||||
|
|
||||||
auto ref = refWeak.lock();
|
auto ref = refWeak.lock();
|
||||||
|
@ -451,17 +381,17 @@ std::optional<std::shared_ptr<Instance>> Instance::Clone(RefStateClone state) {
|
||||||
|
|
||||||
if (remappedRef) {
|
if (remappedRef) {
|
||||||
// If the instance has already been remapped, set the new value
|
// If the instance has already been remapped, set the new value
|
||||||
newInstance->SetPropertyValue(property, InstanceRef(remappedRef)).expect();
|
newInstance->SetPropertyValue(property, Data::InstanceRef(remappedRef)).expect();
|
||||||
} else {
|
} else {
|
||||||
// Otheriise, queue this property to be updated later, and keep its current value
|
// Otheriise, queue this property to be updated later, and keep its current value
|
||||||
auto& refs = state->refsAwaitingRemap[ref];
|
auto& refs = state->refsAwaitingRemap[ref];
|
||||||
refs.push_back(std::make_pair(newInstance, property));
|
refs.push_back(std::make_pair(newInstance, property));
|
||||||
state->refsAwaitingRemap[ref] = refs;
|
state->refsAwaitingRemap[ref] = refs;
|
||||||
|
|
||||||
newInstance->SetPropertyValue(property, InstanceRef(ref)).expect();
|
newInstance->SetPropertyValue(property, Data::InstanceRef(ref)).expect();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Variant value = GetPropertyValue(property).expect();
|
Data::Variant value = GetPropertyValue(property).expect();
|
||||||
newInstance->SetPropertyValue(property, value).expect();
|
newInstance->SetPropertyValue(property, value).expect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -471,9 +401,8 @@ std::optional<std::shared_ptr<Instance>> Instance::Clone(RefStateClone state) {
|
||||||
|
|
||||||
// Remap queued properties
|
// Remap queued properties
|
||||||
for (std::pair<std::shared_ptr<Instance>, std::string> ref : state->refsAwaitingRemap[shared_from_this()]) {
|
for (std::pair<std::shared_ptr<Instance>, std::string> ref : state->refsAwaitingRemap[shared_from_this()]) {
|
||||||
ref.first->SetPropertyValue(ref.second, InstanceRef(newInstance)).expect();
|
ref.first->SetPropertyValue(ref.second, Data::InstanceRef(newInstance)).expect();
|
||||||
}
|
}
|
||||||
state->refsAwaitingRemap[shared_from_this()].clear();
|
|
||||||
|
|
||||||
// Clone children
|
// Clone children
|
||||||
for (std::shared_ptr<Instance> child : GetChildren()) {
|
for (std::shared_ptr<Instance> child : GetChildren()) {
|
||||||
|
@ -492,9 +421,9 @@ std::vector<std::pair<std::string, std::shared_ptr<Instance>>> Instance::GetRefe
|
||||||
|
|
||||||
for (std::string property : propertyNames) {
|
for (std::string property : propertyNames) {
|
||||||
PropertyMeta meta = GetPropertyMeta(property).expect();
|
PropertyMeta meta = GetPropertyMeta(property).expect();
|
||||||
if (meta.type.descriptor != &InstanceRef::TYPE) continue;
|
if (meta.type != &Data::InstanceRef::TYPE) continue;
|
||||||
|
|
||||||
std::weak_ptr<Instance> ref = GetPropertyValue(property).expect().get<InstanceRef>();
|
std::weak_ptr<Instance> ref = GetPropertyValue(property).expect().get<Data::InstanceRef>();
|
||||||
if (ref.expired()) continue;
|
if (ref.expired()) continue;
|
||||||
referenceProperties.push_back(std::make_pair(property, ref.lock()));
|
referenceProperties.push_back(std::make_pair(property, ref.lock()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ struct InstanceType {
|
||||||
InstanceFlags flags;
|
InstanceFlags flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::pair<std::shared_ptr<Instance>, std::string> _RefStatePropertyCell;
|
||||||
|
|
||||||
class DescendantsIterator;
|
class DescendantsIterator;
|
||||||
class JointInstance;
|
class JointInstance;
|
||||||
|
|
||||||
|
@ -69,8 +71,8 @@ protected:
|
||||||
Instance(const InstanceType*);
|
Instance(const InstanceType*);
|
||||||
virtual ~Instance();
|
virtual ~Instance();
|
||||||
|
|
||||||
virtual result<Variant, MemberNotFound> InternalGetPropertyValue(std::string name);
|
virtual result<Data::Variant, MemberNotFound> InternalGetPropertyValue(std::string name);
|
||||||
virtual fallible<MemberNotFound, AssignToReadOnlyMember> InternalSetPropertyValue(std::string name, Variant value);
|
virtual fallible<MemberNotFound, AssignToReadOnlyMember> InternalSetPropertyValue(std::string name, Data::Variant value);
|
||||||
virtual result<PropertyMeta, MemberNotFound> InternalGetPropertyMeta(std::string name);
|
virtual result<PropertyMeta, MemberNotFound> InternalGetPropertyMeta(std::string name);
|
||||||
virtual void InternalUpdateProperty(std::string name);
|
virtual void InternalUpdateProperty(std::string name);
|
||||||
virtual std::vector<std::string> InternalGetProperties();
|
virtual std::vector<std::string> InternalGetProperties();
|
||||||
|
@ -114,8 +116,8 @@ public:
|
||||||
std::string GetFullName();
|
std::string GetFullName();
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
result<Variant, MemberNotFound> GetPropertyValue(std::string name);
|
result<Data::Variant, MemberNotFound> GetPropertyValue(std::string name);
|
||||||
fallible<MemberNotFound, AssignToReadOnlyMember> SetPropertyValue(std::string name, Variant value, bool sendUpdateEvent = true);
|
fallible<MemberNotFound, AssignToReadOnlyMember> SetPropertyValue(std::string name, Data::Variant value, bool sendUpdateEvent = true);
|
||||||
result<PropertyMeta, MemberNotFound> GetPropertyMeta(std::string name);
|
result<PropertyMeta, MemberNotFound> GetPropertyMeta(std::string name);
|
||||||
// Manually trigger the update of a property. Useful internally when setting properties directly
|
// Manually trigger the update of a property. Useful internally when setting properties directly
|
||||||
void UpdateProperty(std::string name);
|
void UpdateProperty(std::string name);
|
||||||
|
@ -133,11 +135,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialization
|
// Serialization
|
||||||
void Serialize(pugi::xml_node parent, RefStateSerialize state = std::make_shared<__RefStateSerialize>());
|
void Serialize(pugi::xml_node parent);
|
||||||
static result<std::shared_ptr<Instance>, NoSuchInstance> Deserialize(pugi::xml_node node, RefStateDeserialize state = std::make_shared<__RefStateDeserialize>());
|
static result<std::shared_ptr<Instance>, NoSuchInstance> Deserialize(pugi::xml_node node);
|
||||||
std::optional<std::shared_ptr<Instance>> Clone(RefStateClone state = std::make_shared<__RefStateClone>());
|
std::optional<std::shared_ptr<Instance>> Clone(RefState<_RefStatePropertyCell> state = std::make_shared<__RefState<_RefStatePropertyCell>>());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::shared_ptr<Instance> InstanceRef;
|
||||||
|
typedef std::weak_ptr<Instance> InstanceRefWeak;
|
||||||
|
|
||||||
// https://gist.github.com/jeetsukumaran/307264
|
// https://gist.github.com/jeetsukumaran/307264
|
||||||
class DescendantsIterator {
|
class DescendantsIterator {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -24,7 +24,7 @@ enum PropertyCategory {
|
||||||
const int PROPERTY_CATEGORY_MAX = PROP_CATEGORY_SURFACE_INPUT;
|
const int PROPERTY_CATEGORY_MAX = PROP_CATEGORY_SURFACE_INPUT;
|
||||||
|
|
||||||
struct PropertyMeta {
|
struct PropertyMeta {
|
||||||
const TypeMeta type;
|
const Data::TypeInfo* type;
|
||||||
PropertyFlags flags;
|
PropertyFlags flags;
|
||||||
PropertyCategory category = PROP_CATEGORY_DATA;
|
PropertyCategory category = PROP_CATEGORY_DATA;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,26 +2,16 @@
|
||||||
|
|
||||||
// Helper struct used for remapping reference when cloning/serializing
|
// Helper struct used for remapping reference when cloning/serializing
|
||||||
|
|
||||||
#include "datatypes/base.h"
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
class Instance;
|
class Instance;
|
||||||
|
|
||||||
template <typename T, typename U, typename K>
|
template <typename T>
|
||||||
struct __RefState {
|
struct __RefState {
|
||||||
std::map<K, U> remappedInstances;
|
std::map<std::shared_ptr<Instance>, std::shared_ptr<Instance>> remappedInstances;
|
||||||
std::map<K, std::vector<T>> refsAwaitingRemap;
|
std::map<std::shared_ptr<Instance>, std::vector<T>> refsAwaitingRemap;
|
||||||
int count = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename U, typename K>
|
template <typename T>
|
||||||
using RefState = std::shared_ptr<__RefState<T, U, K>>;
|
using RefState = std::shared_ptr<__RefState<T>>;
|
||||||
|
|
||||||
typedef __RefState<std::pair<std::shared_ptr<Instance>, std::string>, std::shared_ptr<Instance>, std::shared_ptr<Instance>> __RefStateClone;
|
|
||||||
typedef __RefState<pugi::xml_node, std::string, std::shared_ptr<Instance>> __RefStateSerialize;
|
|
||||||
typedef __RefState<std::pair<std::shared_ptr<Instance>, std::string>, std::shared_ptr<Instance>, std::string> __RefStateDeserialize;
|
|
||||||
|
|
||||||
typedef std::shared_ptr<__RefStateClone> RefStateClone;
|
|
||||||
typedef std::shared_ptr<__RefStateSerialize> RefStateSerialize;
|
|
||||||
typedef std::shared_ptr<__RefStateDeserialize> RefStateDeserialize;
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "objects/base/service.h"
|
#include "objects/base/service.h"
|
||||||
#include "objects/meta.h"
|
#include "objects/meta.h"
|
||||||
#include "objects/script/serverscriptservice.h"
|
#include "objects/script/serverscriptservice.h"
|
||||||
#include "datatypes/variant.h"
|
#include "datatypes/meta.h"
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "panic.h"
|
#include "panic.h"
|
||||||
|
@ -49,7 +49,7 @@ void DataModel::SaveToFile(std::optional<std::string> path) {
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_node root = doc.append_child("openblocks");
|
pugi::xml_node root = doc.append_child("openblocks");
|
||||||
|
|
||||||
for (std::shared_ptr<Instance> child : this->GetChildren()) {
|
for (InstanceRef child : this->GetChildren()) {
|
||||||
child->Serialize(root);
|
child->Serialize(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,50 @@ void DataModel::SaveToFile(std::optional<std::string> path) {
|
||||||
Logger::info("Place saved successfully");
|
Logger::info("Place saved successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DataModel::DeserializeService(pugi::xml_node node) {
|
||||||
|
std::string className = node.attribute("class").value();
|
||||||
|
if (INSTANCE_MAP.count(className) == 0) {
|
||||||
|
Logger::fatalErrorf("Unknown service: '%s'", className.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (services.count(className) != 0) {
|
||||||
|
Logger::fatalErrorf("Service %s defined multiple times in file", className.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will error if an abstract instance is used in the file. Oh well, not my prob rn.
|
||||||
|
InstanceRef object = INSTANCE_MAP[className]->constructor();
|
||||||
|
AddChild(object);
|
||||||
|
|
||||||
|
// Read properties
|
||||||
|
pugi::xml_node propertiesNode = node.child("Properties");
|
||||||
|
for (pugi::xml_node propertyNode : propertiesNode) {
|
||||||
|
std::string propertyName = propertyNode.attribute("name").value();
|
||||||
|
auto meta_ = object->GetPropertyMeta(propertyName);
|
||||||
|
if (!meta_) {
|
||||||
|
Logger::fatalErrorf("Attempt to set unknown property '%s' of %s", propertyName.c_str(), object->GetClass()->className.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Data::Variant value = Data::Variant::Deserialize(propertyNode);
|
||||||
|
object->SetPropertyValue(propertyName, value).expect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add children
|
||||||
|
for (pugi::xml_node childNode : node.children("Item")) {
|
||||||
|
result<InstanceRef, NoSuchInstance> child = Instance::Deserialize(childNode);
|
||||||
|
if (child.isError()) {
|
||||||
|
std::get<NoSuchInstance>(child.error().value()).logMessage();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
object->AddChild(child.expect());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We add the service to the list
|
||||||
|
// All services get init'd at once in InitServices
|
||||||
|
this->services[className] = std::dynamic_pointer_cast<Service>(object);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<DataModel> DataModel::LoadFromFile(std::string path) {
|
std::shared_ptr<DataModel> DataModel::LoadFromFile(std::string path) {
|
||||||
std::ifstream inStream(path);
|
std::ifstream inStream(path);
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
|
@ -66,28 +110,9 @@ std::shared_ptr<DataModel> DataModel::LoadFromFile(std::string path) {
|
||||||
|
|
||||||
pugi::xml_node rootNode = doc.child("openblocks");
|
pugi::xml_node rootNode = doc.child("openblocks");
|
||||||
std::shared_ptr<DataModel> newModel = std::make_shared<DataModel>();
|
std::shared_ptr<DataModel> newModel = std::make_shared<DataModel>();
|
||||||
RefStateDeserialize state = std::make_shared<__RefStateDeserialize>();
|
|
||||||
|
|
||||||
for (pugi::xml_node childNode : rootNode.children("Item")) {
|
for (pugi::xml_node childNode : rootNode.children("Item")) {
|
||||||
// Make sure the class hasn't already been deserialized
|
newModel->DeserializeService(childNode);
|
||||||
std::string className = childNode.attribute("class").value();
|
|
||||||
|
|
||||||
// TODO: Make this push its children into the first service, or however it is actually done in the thing
|
|
||||||
// for parity
|
|
||||||
if (newModel->services.count(className) != 0) {
|
|
||||||
Logger::fatalErrorf("Service %s defined multiple times in file", className.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = Instance::Deserialize(childNode, state);
|
|
||||||
if (result.isError()) {
|
|
||||||
Logger::errorf("Failed to deserialize service: %s", result.errorMessage()->c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto service = result.expect();
|
|
||||||
newModel->AddChild(service);
|
|
||||||
newModel->services[className] = std::dynamic_pointer_cast<Service>(service);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newModel->Init();
|
newModel->Init();
|
||||||
|
@ -121,7 +146,7 @@ result<std::optional<std::shared_ptr<Service>>, NoSuchService> DataModel::FindSe
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<DataModel> DataModel::CloneModel() {
|
std::shared_ptr<DataModel> DataModel::CloneModel() {
|
||||||
RefStateClone state = std::make_shared<__RefStateClone>();
|
RefState<_RefStatePropertyCell> state = std::make_shared<__RefState<_RefStatePropertyCell>>();
|
||||||
std::shared_ptr<DataModel> newModel = DataModel::New();
|
std::shared_ptr<DataModel> newModel = DataModel::New();
|
||||||
|
|
||||||
// Copy properties
|
// Copy properties
|
||||||
|
@ -130,9 +155,9 @@ std::shared_ptr<DataModel> DataModel::CloneModel() {
|
||||||
|
|
||||||
if (meta.flags & (PROP_READONLY | PROP_NOSAVE)) continue;
|
if (meta.flags & (PROP_READONLY | PROP_NOSAVE)) continue;
|
||||||
|
|
||||||
// Update std::shared_ptr<Instance> properties using map above
|
// Update InstanceRef properties using map above
|
||||||
if (meta.type.descriptor == &InstanceRef::TYPE) {
|
if (meta.type == &Data::InstanceRef::TYPE) {
|
||||||
std::weak_ptr<Instance> refWeak = GetPropertyValue(property).expect().get<InstanceRef>();
|
std::weak_ptr<Instance> refWeak = GetPropertyValue(property).expect().get<Data::InstanceRef>();
|
||||||
if (refWeak.expired()) continue;
|
if (refWeak.expired()) continue;
|
||||||
|
|
||||||
auto ref = refWeak.lock();
|
auto ref = refWeak.lock();
|
||||||
|
@ -140,17 +165,17 @@ std::shared_ptr<DataModel> DataModel::CloneModel() {
|
||||||
|
|
||||||
if (remappedRef) {
|
if (remappedRef) {
|
||||||
// If the instance has already been remapped, set the new value
|
// If the instance has already been remapped, set the new value
|
||||||
newModel->SetPropertyValue(property, InstanceRef(remappedRef)).expect();
|
newModel->SetPropertyValue(property, Data::InstanceRef(remappedRef)).expect();
|
||||||
} else {
|
} else {
|
||||||
// Otheriise, queue this property to be updated later, and keep its current value
|
// Otheriise, queue this property to be updated later, and keep its current value
|
||||||
auto& refs = state->refsAwaitingRemap[ref];
|
auto& refs = state->refsAwaitingRemap[ref];
|
||||||
refs.push_back(std::make_pair(newModel, property));
|
refs.push_back(std::make_pair(newModel, property));
|
||||||
state->refsAwaitingRemap[ref] = refs;
|
state->refsAwaitingRemap[ref] = refs;
|
||||||
|
|
||||||
newModel->SetPropertyValue(property, InstanceRef(ref)).expect();
|
newModel->SetPropertyValue(property, Data::InstanceRef(ref)).expect();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Variant value = GetPropertyValue(property).expect();
|
Data::Variant value = GetPropertyValue(property).expect();
|
||||||
newModel->SetPropertyValue(property, value).expect();
|
newModel->SetPropertyValue(property, value).expect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +185,7 @@ std::shared_ptr<DataModel> DataModel::CloneModel() {
|
||||||
|
|
||||||
// Remap queued properties
|
// Remap queued properties
|
||||||
for (std::pair<std::shared_ptr<Instance>, std::string> ref : state->refsAwaitingRemap[shared_from_this()]) {
|
for (std::pair<std::shared_ptr<Instance>, std::string> ref : state->refsAwaitingRemap[shared_from_this()]) {
|
||||||
ref.first->SetPropertyValue(ref.second, InstanceRef(newModel)).expect();
|
ref.first->SetPropertyValue(ref.second, Data::InstanceRef(newModel)).expect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone services
|
// Clone services
|
||||||
|
|
|
@ -16,8 +16,8 @@ class Service;
|
||||||
class DEF_INST_(abstract) DataModel : public Instance {
|
class DEF_INST_(abstract) DataModel : public Instance {
|
||||||
AUTOGEN_PREAMBLE
|
AUTOGEN_PREAMBLE
|
||||||
private:
|
private:
|
||||||
// void DeserializeService(pugi::xml_node node, RefStateDeserialize);
|
void DeserializeService(pugi::xml_node node);
|
||||||
static void cloneService(std::shared_ptr<DataModel> target, std::shared_ptr<Service>, RefStateClone);
|
static void cloneService(std::shared_ptr<DataModel> target, std::shared_ptr<Service>, RefState<_RefStatePropertyCell>);
|
||||||
public:
|
public:
|
||||||
std::map<std::string, std::shared_ptr<Service>> services;
|
std::map<std::string, std::shared_ptr<Service>> services;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
#include "folder.h"
|
|
||||||
|
|
||||||
Folder::Folder(): Instance(&TYPE) {}
|
|
||||||
Folder::~Folder() = default;
|
|
|
@ -1,19 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "objects/annotation.h"
|
|
||||||
#include "objects/base/instance.h"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// The simplest instance
|
|
||||||
// Has no functionality of its own, used purely for organizational/grouping purposes
|
|
||||||
|
|
||||||
class DEF_INST_(explorer_icon="folder") Folder : public Instance {
|
|
||||||
AUTOGEN_PREAMBLE
|
|
||||||
|
|
||||||
public:
|
|
||||||
Folder();
|
|
||||||
~Folder();
|
|
||||||
|
|
||||||
static inline std::shared_ptr<Folder> New() { return std::make_shared<Folder>(); };
|
|
||||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Folder>(); };
|
|
||||||
};
|
|
|
@ -1,11 +1,9 @@
|
||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
#include "objects/folder.h"
|
|
||||||
#include "objects/joint/jointinstance.h"
|
#include "objects/joint/jointinstance.h"
|
||||||
#include "objects/joint/rotate.h"
|
#include "objects/joint/rotate.h"
|
||||||
#include "objects/joint/rotatev.h"
|
#include "objects/joint/rotatev.h"
|
||||||
#include "objects/joint/weld.h"
|
#include "objects/joint/weld.h"
|
||||||
#include "objects/jointsservice.h"
|
#include "objects/jointsservice.h"
|
||||||
#include "objects/model.h"
|
|
||||||
#include "objects/part.h"
|
#include "objects/part.h"
|
||||||
#include "objects/joint/snap.h"
|
#include "objects/joint/snap.h"
|
||||||
#include "objects/script.h"
|
#include "objects/script.h"
|
||||||
|
@ -25,8 +23,6 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
||||||
{ "RotateV", &RotateV::TYPE },
|
{ "RotateV", &RotateV::TYPE },
|
||||||
{ "JointInstance", &JointInstance::TYPE },
|
{ "JointInstance", &JointInstance::TYPE },
|
||||||
{ "Script", &Script::TYPE },
|
{ "Script", &Script::TYPE },
|
||||||
{ "Model", &Model::TYPE },
|
|
||||||
// { "Folder", &Folder::TYPE },
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
#include "model.h"
|
|
||||||
|
|
||||||
Model::Model(): Instance(&TYPE) {}
|
|
||||||
Model::~Model() = default;
|
|
|
@ -1,18 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "objects/annotation.h"
|
|
||||||
#include "objects/base/instance.h"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// Group object for Parts
|
|
||||||
|
|
||||||
class DEF_INST_(explorer_icon="model") Model : public Instance {
|
|
||||||
AUTOGEN_PREAMBLE
|
|
||||||
|
|
||||||
public:
|
|
||||||
Model();
|
|
||||||
~Model();
|
|
||||||
|
|
||||||
static inline std::shared_ptr<Model> New() { return std::make_shared<Model>(); };
|
|
||||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Model>(); };
|
|
||||||
};
|
|
|
@ -224,7 +224,7 @@ void Part::MakeJoints() {
|
||||||
// TEMPORARY
|
// TEMPORARY
|
||||||
// TODO: Use more efficient algorithm to *actually* find nearby parts)
|
// TODO: Use more efficient algorithm to *actually* find nearby parts)
|
||||||
for (auto it = workspace().value()->GetDescendantsStart(); it != workspace().value()->GetDescendantsEnd(); it++) {
|
for (auto it = workspace().value()->GetDescendantsStart(); it != workspace().value()->GetDescendantsEnd(); it++) {
|
||||||
std::shared_ptr<Instance> obj = *it;
|
InstanceRef obj = *it;
|
||||||
if (obj == shared_from_this()) continue; // Skip ourselves
|
if (obj == shared_from_this()) continue; // Skip ourselves
|
||||||
if (obj->GetClass()->className != "Part") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
|
if (obj->GetClass()->className != "Part") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
|
||||||
std::shared_ptr<Part> otherPart = obj->CastTo<Part>().expect();
|
std::shared_ptr<Part> otherPart = obj->CastTo<Part>().expect();
|
||||||
|
|
|
@ -107,7 +107,7 @@ public:
|
||||||
|
|
||||||
static inline std::shared_ptr<Part> New() { return std::make_shared<Part>(); };
|
static inline std::shared_ptr<Part> New() { return std::make_shared<Part>(); };
|
||||||
static inline std::shared_ptr<Part> New(PartConstructParams params) { return std::make_shared<Part>(params); };
|
static inline std::shared_ptr<Part> New(PartConstructParams params) { return std::make_shared<Part>(params); };
|
||||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Part>(); };
|
static inline InstanceRef Create() { return std::make_shared<Part>(); };
|
||||||
|
|
||||||
inline Vector3 position() { return cframe.Position(); }
|
inline Vector3 position() { return cframe.Position(); }
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,10 @@ void Script::Run() {
|
||||||
// Initialize script globals
|
// Initialize script globals
|
||||||
lua_getglobal(Lt, "_G");
|
lua_getglobal(Lt, "_G");
|
||||||
|
|
||||||
InstanceRef(dataModel().value()).PushLuaValue(Lt);
|
Data::InstanceRef(dataModel().value()).PushLuaValue(Lt);
|
||||||
lua_setfield(Lt, -2, "game");
|
lua_setfield(Lt, -2, "game");
|
||||||
|
|
||||||
InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(Lt);
|
Data::InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(Lt);
|
||||||
lua_setfield(Lt, -2, "workspace");
|
lua_setfield(Lt, -2, "workspace");
|
||||||
|
|
||||||
lua_pushlightuserdata(Lt, scriptContext.get());
|
lua_pushlightuserdata(Lt, scriptContext.get());
|
||||||
|
|
|
@ -43,9 +43,9 @@ void ScriptContext::InitService() {
|
||||||
// luaopen_debug(state);
|
// luaopen_debug(state);
|
||||||
luaopen_bit(state);
|
luaopen_bit(state);
|
||||||
|
|
||||||
Vector3::PushLuaLibrary(state);
|
Data::Vector3::PushLuaLibrary(state);
|
||||||
CFrame::PushLuaLibrary(state);
|
Data::CFrame::PushLuaLibrary(state);
|
||||||
Color3::PushLuaLibrary(state);
|
Data::Color3::PushLuaLibrary(state);
|
||||||
|
|
||||||
// TODO: custom os library
|
// TODO: custom os library
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
// Container class for server scripts
|
// Container class for server scripts
|
||||||
// Also handles/manages running server scripts on run
|
// Also handles/manages running server scripts on run
|
||||||
class DEF_INST_SERVICE_(explorer_icon="server-scripts") ServerScriptService : public Service {
|
class DEF_INST_SERVICE ServerScriptService : public Service {
|
||||||
AUTOGEN_PREAMBLE
|
AUTOGEN_PREAMBLE
|
||||||
protected:
|
protected:
|
||||||
void InitService() override;
|
void InitService() override;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
#include "datatypes/variant.h"
|
#include "datatypes/meta.h"
|
||||||
#include "datatypes/ref.h"
|
#include "datatypes/ref.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
|
@ -33,11 +33,11 @@ void PhysicsEventListener::onContact(const rp::CollisionCallback::CallbackData&
|
||||||
auto part1 = reinterpret_cast<Part*>(pair.getBody2()->getUserData())->shared<Part>();
|
auto part1 = reinterpret_cast<Part*>(pair.getBody2()->getUserData())->shared<Part>();
|
||||||
|
|
||||||
if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactStart) {
|
if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactStart) {
|
||||||
part0->Touched->Fire({ (Variant)InstanceRef(part1) });
|
part0->Touched->Fire({ (Data::Variant)Data::InstanceRef(part1) });
|
||||||
part1->Touched->Fire({ (Variant)InstanceRef(part0) });
|
part1->Touched->Fire({ (Data::Variant)Data::InstanceRef(part0) });
|
||||||
} else if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactExit) {
|
} else if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactExit) {
|
||||||
part0->TouchEnded->Fire({ (Variant)InstanceRef(part1) });
|
part0->TouchEnded->Fire({ (Data::Variant)Data::InstanceRef(part1) });
|
||||||
part1->TouchEnded->Fire({ (Variant)InstanceRef(part0) });
|
part1->TouchEnded->Fire({ (Data::Variant)Data::InstanceRef(part0) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ void Workspace::InitService() {
|
||||||
|
|
||||||
// Sync all parts
|
// Sync all parts
|
||||||
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
||||||
std::shared_ptr<Instance> obj = *it;
|
InstanceRef obj = *it;
|
||||||
if (!obj->IsA<Part>()) continue;
|
if (!obj->IsA<Part>()) continue;
|
||||||
std::shared_ptr<Part> part = obj->CastTo<Part>().expect();
|
std::shared_ptr<Part> part = obj->CastTo<Part>().expect();
|
||||||
this->SyncPartPhysics(part);
|
this->SyncPartPhysics(part);
|
||||||
|
@ -68,7 +68,7 @@ void Workspace::InitService() {
|
||||||
|
|
||||||
// Activate all joints
|
// Activate all joints
|
||||||
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
||||||
std::shared_ptr<Instance> obj = *it;
|
InstanceRef obj = *it;
|
||||||
if (!obj->IsA<JointInstance>()) continue;
|
if (!obj->IsA<JointInstance>()) continue;
|
||||||
std::shared_ptr<JointInstance> joint = obj->CastTo<JointInstance>().expect();
|
std::shared_ptr<JointInstance> joint = obj->CastTo<JointInstance>().expect();
|
||||||
joint->UpdateProperty("Part0");
|
joint->UpdateProperty("Part0");
|
||||||
|
@ -127,19 +127,17 @@ void Workspace::PhysicsStep(float deltaTime) {
|
||||||
// Step the simulation a few steps
|
// Step the simulation a few steps
|
||||||
physicsWorld->update(std::min(deltaTime / 2, (1/60.f)));
|
physicsWorld->update(std::min(deltaTime / 2, (1/60.f)));
|
||||||
|
|
||||||
|
// Naive implementation. Parts are only considered so if they are just under Workspace
|
||||||
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
|
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
|
||||||
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
||||||
std::shared_ptr<Instance> obj = *it;
|
InstanceRef obj = *it;
|
||||||
if (!obj->IsA<Part>()) continue;
|
if (obj->GetClass()->className != "Part") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(obj);
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(obj);
|
||||||
|
|
||||||
// Sync properties
|
|
||||||
const rp::Transform& transform = part->rigidBody->getTransform();
|
const rp::Transform& transform = part->rigidBody->getTransform();
|
||||||
part->cframe = CFrame(transform);
|
part->cframe = CFrame(transform);
|
||||||
part->velocity = part->rigidBody->getLinearVelocity();
|
part->velocity = part->rigidBody->getLinearVelocity();
|
||||||
|
|
||||||
// part->rigidBody->enableGravity(true);
|
// part->rigidBody->enableGravity(true);
|
||||||
// RotateV/Motor joint
|
|
||||||
for (auto& joint : part->secondaryJoints) {
|
for (auto& joint : part->secondaryJoints) {
|
||||||
if (joint.expired() || !joint.lock()->IsA("RotateV")) continue;
|
if (joint.expired() || !joint.lock()->IsA("RotateV")) continue;
|
||||||
|
|
||||||
|
@ -148,16 +146,6 @@ void Workspace::PhysicsStep(float deltaTime) {
|
||||||
// part->rigidBody->enableGravity(false);
|
// part->rigidBody->enableGravity(false);
|
||||||
part->rigidBody->setAngularVelocity(-(motor->part0.lock()->cframe * motor->c0).LookVector() * rate);
|
part->rigidBody->setAngularVelocity(-(motor->part0.lock()->cframe * motor->c0).LookVector() * rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy fallen parts
|
|
||||||
if (part->cframe.Position().Y() < this->fallenPartsDestroyHeight) {
|
|
||||||
auto parent = part->GetParent();
|
|
||||||
part->Destroy();
|
|
||||||
|
|
||||||
// If the parent of the part is a Model, destroy it too
|
|
||||||
if (parent.has_value() && parent.value()->IsA("Model"))
|
|
||||||
parent.value()->Destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,6 @@ public:
|
||||||
Workspace();
|
Workspace();
|
||||||
~Workspace();
|
~Workspace();
|
||||||
|
|
||||||
DEF_PROP float fallenPartsDestroyHeight = -500;
|
|
||||||
|
|
||||||
// static inline std::shared_ptr<Workspace> New() { return std::make_shared<Workspace>(); };
|
// static inline std::shared_ptr<Workspace> New() { return std::make_shared<Workspace>(); };
|
||||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Workspace>(); };
|
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Workspace>(); };
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include "partassembly.h"
|
#include "partassembly.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "datatypes/cframe.h"
|
#include "datatypes/cframe.h"
|
||||||
#include "datatypes/variant.h"
|
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "math_helper.h"
|
#include "math_helper.h"
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
|
@ -33,7 +32,7 @@ PartAssembly::PartAssembly(std::vector<std::shared_ptr<Part>> parts, bool worldM
|
||||||
_bounds = size;
|
_bounds = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
PartAssembly PartAssembly::FromSelection(std::vector<std::shared_ptr<Instance>> newSelection) {
|
PartAssembly PartAssembly::FromSelection(std::vector<std::weak_ptr<Instance>> newSelection) {
|
||||||
std::vector<std::shared_ptr<Part>> selection;
|
std::vector<std::shared_ptr<Part>> selection;
|
||||||
|
|
||||||
for (std::weak_ptr<Instance> obj : newSelection) {
|
for (std::weak_ptr<Instance> obj : newSelection) {
|
||||||
|
@ -49,7 +48,6 @@ void PartAssembly::SetOrigin(CFrame newOrigin) {
|
||||||
for (auto part : parts) {
|
for (auto part : parts) {
|
||||||
part->cframe = newOrigin * (_assemblyOrigin.Inverse() * part->cframe);
|
part->cframe = newOrigin * (_assemblyOrigin.Inverse() * part->cframe);
|
||||||
part->UpdateProperty("CFrame");
|
part->UpdateProperty("CFrame");
|
||||||
// sendPropertyUpdatedSignal(part, "CFrame", Variant(part->cframe));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_assemblyOrigin = newOrigin;
|
_assemblyOrigin = newOrigin;
|
||||||
|
@ -59,7 +57,6 @@ void PartAssembly::TransformBy(CFrame transform) {
|
||||||
for (auto part : parts) {
|
for (auto part : parts) {
|
||||||
part->cframe = transform * part->cframe;
|
part->cframe = transform * part->cframe;
|
||||||
part->UpdateProperty("CFrame");
|
part->UpdateProperty("CFrame");
|
||||||
sendPropertyUpdatedSignal(part, "CFrame", Variant(part->cframe));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_assemblyOrigin = transform * _assemblyOrigin;
|
_assemblyOrigin = transform * _assemblyOrigin;
|
||||||
|
@ -69,7 +66,6 @@ void PartAssembly::Scale(Vector3 newSize, bool scaleUp) {
|
||||||
if (parts.size() == 1) {
|
if (parts.size() == 1) {
|
||||||
parts[0]->size = newSize;
|
parts[0]->size = newSize;
|
||||||
parts[0]->UpdateProperty("Size");
|
parts[0]->UpdateProperty("Size");
|
||||||
sendPropertyUpdatedSignal(parts[0], "Size", Variant((Vector3)parts[0]->size));
|
|
||||||
_bounds = newSize;
|
_bounds = newSize;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -82,10 +78,8 @@ void PartAssembly::Scale(Vector3 newSize, bool scaleUp) {
|
||||||
localOff = localOff * factor;
|
localOff = localOff * factor;
|
||||||
part->cframe = part->cframe.Rotation() + _assemblyOrigin * localOff;
|
part->cframe = part->cframe.Rotation() + _assemblyOrigin * localOff;
|
||||||
part->UpdateProperty("CFrame");
|
part->UpdateProperty("CFrame");
|
||||||
sendPropertyUpdatedSignal(part, "CFrame", Variant(part->cframe));
|
|
||||||
part->size *= factor;
|
part->size *= factor;
|
||||||
part->UpdateProperty("Size");
|
part->UpdateProperty("Size");
|
||||||
sendPropertyUpdatedSignal(part, "Size", Variant((Vector3)part->size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_bounds = _bounds * factor;
|
_bounds = _bounds * factor;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
class Part;
|
class Part;
|
||||||
class Instance;
|
class Instance;
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<Instance>> getSelection();
|
const std::vector<std::weak_ptr<Instance>> getSelection();
|
||||||
|
|
||||||
class PartAssembly {
|
class PartAssembly {
|
||||||
CFrame _assemblyOrigin;
|
CFrame _assemblyOrigin;
|
||||||
|
@ -17,7 +17,7 @@ class PartAssembly {
|
||||||
public:
|
public:
|
||||||
PartAssembly(std::vector<std::shared_ptr<Part>>, bool worldMode = false);
|
PartAssembly(std::vector<std::shared_ptr<Part>>, bool worldMode = false);
|
||||||
|
|
||||||
static PartAssembly FromSelection(std::vector<std::shared_ptr<Instance>> selection = getSelection());
|
static PartAssembly FromSelection(std::vector<std::weak_ptr<Instance>> selection = getSelection());
|
||||||
|
|
||||||
inline CFrame assemblyOrigin() { return _assemblyOrigin; };
|
inline CFrame assemblyOrigin() { return _assemblyOrigin; };
|
||||||
inline Vector3 bounds() { return _bounds; };
|
inline Vector3 bounds() { return _bounds; };
|
||||||
|
|
|
@ -133,7 +133,7 @@ void renderParts() {
|
||||||
// Sort by nearest
|
// Sort by nearest
|
||||||
std::map<float, std::shared_ptr<Part>> sorted;
|
std::map<float, std::shared_ptr<Part>> sorted;
|
||||||
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
|
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
|
||||||
std::shared_ptr<Instance> inst = *it;
|
InstanceRef inst = *it;
|
||||||
if (inst->GetClass()->className != "Part") continue;
|
if (inst->GetClass()->className != "Part") continue;
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
||||||
if (part->transparency > 0.00001) {
|
if (part->transparency > 0.00001) {
|
||||||
|
@ -227,7 +227,7 @@ void renderSurfaceExtras() {
|
||||||
ghostShader->set("viewPos", camera.cameraPos);
|
ghostShader->set("viewPos", camera.cameraPos);
|
||||||
|
|
||||||
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
|
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
|
||||||
std::shared_ptr<Instance> inst = *it;
|
InstanceRef inst = *it;
|
||||||
if (!inst->IsA("Part")) continue;
|
if (!inst->IsA("Part")) continue;
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
|
@ -367,7 +367,7 @@ void renderAABB() {
|
||||||
ghostShader->set("color", glm::vec3(1.f, 0.f, 0.f));
|
ghostShader->set("color", glm::vec3(1.f, 0.f, 0.f));
|
||||||
|
|
||||||
// Sort by nearest
|
// Sort by nearest
|
||||||
for (std::shared_ptr<Instance> inst : gWorkspace()->GetChildren()) {
|
for (InstanceRef inst : gWorkspace()->GetChildren()) {
|
||||||
if (inst->GetClass()->className != "Part") continue;
|
if (inst->GetClass()->className != "Part") continue;
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
||||||
glm::mat4 model = CFrame::IDENTITY + part->cframe.Position();
|
glm::mat4 model = CFrame::IDENTITY + part->cframe.Position();
|
||||||
|
@ -407,7 +407,7 @@ void renderWireframe() {
|
||||||
wireframeShader->set("color", glm::vec3(1.f, 0.f, 0.f));
|
wireframeShader->set("color", glm::vec3(1.f, 0.f, 0.f));
|
||||||
|
|
||||||
// Sort by nearest
|
// Sort by nearest
|
||||||
for (std::shared_ptr<Instance> inst : gWorkspace()->GetChildren()) {
|
for (InstanceRef inst : gWorkspace()->GetChildren()) {
|
||||||
if (inst->GetClass()->className != "Part") continue;
|
if (inst->GetClass()->className != "Part") continue;
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
||||||
glm::mat4 model = part->cframe;
|
glm::mat4 model = part->cframe;
|
||||||
|
@ -451,7 +451,7 @@ void renderOutlines() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
|
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
|
||||||
std::shared_ptr<Instance> inst = *it;
|
InstanceRef inst = *it;
|
||||||
if (inst->GetClass() != &Part::TYPE) continue;
|
if (inst->GetClass() != &Part::TYPE) continue;
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
||||||
if (!part->selected) continue;
|
if (!part->selected) continue;
|
||||||
|
@ -556,14 +556,14 @@ void renderRotationArcs() {
|
||||||
// Pass in the camera position
|
// Pass in the camera position
|
||||||
handleShader->set("viewPos", camera.cameraPos);
|
handleShader->set("viewPos", camera.cameraPos);
|
||||||
|
|
||||||
PartAssembly assembly = PartAssembly::FromSelection();
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(getSelection()[0].lock());
|
||||||
|
|
||||||
for (HandleFace face : HandleFace::Faces) {
|
for (HandleFace face : HandleFace::Faces) {
|
||||||
if (glm::any(glm::lessThan(face.normal, glm::vec3(0)))) continue;
|
if (glm::any(glm::lessThan(face.normal, glm::vec3(0)))) continue;
|
||||||
glm::mat4 model = assembly.assemblyOrigin() * CFrame(glm::vec3(0), face.normal, glm::vec3(0, 1.01, 0.1));
|
glm::mat4 model = part->cframe * CFrame(glm::vec3(0), face.normal, glm::vec3(0, 1.01, 0.1));
|
||||||
handleShader->set("model", model);
|
handleShader->set("model", model);
|
||||||
|
|
||||||
float radius = glm::max(assembly.bounds().X(), assembly.bounds().Y(), assembly.bounds().Z()) / 2.f + 2.f;
|
float radius = glm::max(part->size.x, part->size.y, part->size.z) / 2.f + 2.f;
|
||||||
|
|
||||||
handleShader->set("material", Material {
|
handleShader->set("material", Material {
|
||||||
.diffuse = glm::abs(face.normal),
|
.diffuse = glm::abs(face.normal),
|
||||||
|
|
|
@ -8,5 +8,5 @@ namespace Data { class CFrame; class Color3; };
|
||||||
void renderInit(GLFWwindow* window, int width, int height);
|
void renderInit(GLFWwindow* window, int width, int height);
|
||||||
void render(GLFWwindow* window);
|
void render(GLFWwindow* window);
|
||||||
void setViewport(int width, int height);
|
void setViewport(int width, int height);
|
||||||
void addDebugRenderCFrame(CFrame);
|
void addDebugRenderCFrame(Data::CFrame);
|
||||||
void addDebugRenderCFrame(CFrame, Color3);
|
void addDebugRenderCFrame(Data::CFrame, Data::Color3);
|
|
@ -20,6 +20,6 @@ enum SurfaceType {
|
||||||
SurfaceMotor = 7,
|
SurfaceMotor = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Vector3;
|
namespace Data { class Vector3; } using Data::Vector3;
|
||||||
NormalId faceFromNormal(Vector3);
|
NormalId faceFromNormal(Vector3);
|
||||||
Vector3 normalFromFace(NormalId);
|
Vector3 normalFromFace(NormalId);
|
|
@ -1,4 +1,3 @@
|
||||||
#include "error/data.h"
|
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
#include <qsoundeffect.h>
|
#include <qsoundeffect.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "./ui_mainwindow.h"
|
|
||||||
#include "mainglwidget.h"
|
#include "mainglwidget.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
|
@ -17,14 +16,13 @@
|
||||||
#include "physics/util.h"
|
#include "physics/util.h"
|
||||||
#include "rendering/renderer.h"
|
#include "rendering/renderer.h"
|
||||||
#include "rendering/shader.h"
|
#include "rendering/shader.h"
|
||||||
#include "datatypes/variant.h"
|
#include "datatypes/meta.h"
|
||||||
|
|
||||||
#define PI 3.14159
|
#define PI 3.14159
|
||||||
#define M_mainWindow dynamic_cast<MainWindow*>(window())
|
|
||||||
|
|
||||||
static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
|
static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
|
||||||
|
|
||||||
MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent), contextMenu(this) {
|
MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent) {
|
||||||
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
}
|
}
|
||||||
|
@ -117,14 +115,12 @@ CFrame snapCFrame(CFrame frame) {
|
||||||
return CFrame(frame.Position(), frame.Position() + closestVec1, closestVec2);
|
return CFrame(frame.Position(), frame.Position() + closestVec1, closestVec2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tryMouseContextMenu = false;
|
|
||||||
bool isMouseDragging = false;
|
bool isMouseDragging = false;
|
||||||
std::weak_ptr<Part> draggingObject;
|
std::weak_ptr<Part> draggingObject;
|
||||||
std::optional<HandleFace> draggingHandle;
|
std::optional<HandleFace> draggingHandle;
|
||||||
Vector3 initialHitPos;
|
Vector3 initialHitPos;
|
||||||
Vector3 initialHitNormal;
|
Vector3 initialHitNormal;
|
||||||
CFrame initialFrame;
|
CFrame initialFrame;
|
||||||
PartAssembly initialAssembly({});
|
|
||||||
void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
||||||
if (!isMouseDragging || draggingObject.expired() || mainWindow()->selectedTool >= TOOL_SMOOTH) return;
|
if (!isMouseDragging || draggingObject.expired() || mainWindow()->selectedTool >= TOOL_SMOOTH) return;
|
||||||
|
|
||||||
|
@ -275,6 +271,7 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
||||||
if (!isMouseDragging || !draggingHandle || !editorToolHandles.active) return;
|
if (!isMouseDragging || !draggingHandle || !editorToolHandles.active) return;
|
||||||
|
|
||||||
glm::vec2 destPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
glm::vec2 destPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||||
|
auto part = getHandleAdornee();
|
||||||
|
|
||||||
// Calculate part pos as screen point
|
// Calculate part pos as screen point
|
||||||
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)width() / (float)height(), 0.1f, 1000.0f);
|
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)width() / (float)height(), 0.1f, 1000.0f);
|
||||||
|
@ -306,8 +303,11 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
||||||
|
|
||||||
glm::vec3 angles = handleNormal * sign * glm::vec3(angle);
|
glm::vec3 angles = handleNormal * sign * glm::vec3(angle);
|
||||||
|
|
||||||
CFrame newFrame = initialFrame * CFrame::FromEulerAnglesXYZ(-angles);
|
part->cframe = initialFrame * CFrame::FromEulerAnglesXYZ(-angles);
|
||||||
initialAssembly.SetOrigin(newFrame);
|
|
||||||
|
gWorkspace()->SyncPartPhysics(part);
|
||||||
|
part->UpdateProperty("Rotation");
|
||||||
|
sendPropertyUpdatedSignal(part, "Rotation", part->cframe.ToEulerAnglesXYZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
||||||
|
@ -342,7 +342,6 @@ void MainGLWidget::wheelEvent(QWheelEvent* evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) {
|
void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) {
|
||||||
tryMouseContextMenu = false;
|
|
||||||
handleCameraRotate(evt);
|
handleCameraRotate(evt);
|
||||||
handleObjectDrag(evt);
|
handleObjectDrag(evt);
|
||||||
handleCursorChange(evt);
|
handleCursorChange(evt);
|
||||||
|
@ -361,7 +360,6 @@ void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
||||||
tryMouseContextMenu = evt->button() == Qt::RightButton;
|
|
||||||
switch(evt->button()) {
|
switch(evt->button()) {
|
||||||
// Camera drag
|
// Camera drag
|
||||||
case Qt::RightButton: {
|
case Qt::RightButton: {
|
||||||
|
@ -377,8 +375,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
||||||
auto handle = raycastHandle(pointDir);
|
auto handle = raycastHandle(pointDir);
|
||||||
if (handle.has_value()) {
|
if (handle.has_value()) {
|
||||||
startPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
startPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||||
initialAssembly = PartAssembly::FromSelection();
|
initialFrame = getHandleAdornee()->cframe;
|
||||||
initialFrame = initialAssembly.assemblyOrigin();
|
|
||||||
isMouseDragging = true;
|
isMouseDragging = true;
|
||||||
draggingHandle = handle;
|
draggingHandle = handle;
|
||||||
startLinearTransform(evt);
|
startLinearTransform(evt);
|
||||||
|
@ -420,10 +417,10 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
||||||
isMouseDragging = true;
|
isMouseDragging = true;
|
||||||
draggingObject = part;
|
draggingObject = part;
|
||||||
if (evt->modifiers() & Qt::ControlModifier) {
|
if (evt->modifiers() & Qt::ControlModifier) {
|
||||||
std::vector<std::shared_ptr<Instance>> currentSelection = getSelection();
|
std::vector<InstanceRefWeak> currentSelection = getSelection();
|
||||||
for (int i = 0; i < currentSelection.size(); i++) {
|
for (int i = 0; i < currentSelection.size(); i++) {
|
||||||
std::shared_ptr<Instance> inst = currentSelection[i];
|
InstanceRefWeak inst = currentSelection[i];
|
||||||
if (inst == part) {
|
if (!inst.expired() && inst.lock() == part) {
|
||||||
currentSelection.erase(currentSelection.begin() + i);
|
currentSelection.erase(currentSelection.begin() + i);
|
||||||
goto skipAddPart;
|
goto skipAddPart;
|
||||||
}
|
}
|
||||||
|
@ -431,8 +428,8 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
||||||
currentSelection.push_back(part);
|
currentSelection.push_back(part);
|
||||||
skipAddPart:
|
skipAddPart:
|
||||||
setSelection(currentSelection);
|
setSelection(currentSelection);
|
||||||
} else
|
}else
|
||||||
setSelection({ part });
|
setSelection(std::vector<InstanceRefWeak> { part });
|
||||||
// Disable bit so that we can ignore the part while raycasting
|
// Disable bit so that we can ignore the part while raycasting
|
||||||
// part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b10);
|
// part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b10);
|
||||||
|
|
||||||
|
@ -448,23 +445,6 @@ void MainGLWidget::mouseReleaseEvent(QMouseEvent* evt) {
|
||||||
isMouseDragging = false;
|
isMouseDragging = false;
|
||||||
draggingObject = {};
|
draggingObject = {};
|
||||||
draggingHandle = std::nullopt;
|
draggingHandle = std::nullopt;
|
||||||
|
|
||||||
// Open context menu
|
|
||||||
if (tryMouseContextMenu)
|
|
||||||
contextMenu.exec(QCursor::pos());
|
|
||||||
tryMouseContextMenu = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainGLWidget::buildContextMenu() {
|
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionDelete);
|
|
||||||
contextMenu.addSeparator();
|
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionCopy);
|
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionCut);
|
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionPaste);
|
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionPasteInto);
|
|
||||||
contextMenu.addSeparator();
|
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionSaveModel);
|
|
||||||
contextMenu.addAction(M_mainWindow->ui->actionInsertModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int moveZ = 0;
|
static int moveZ = 0;
|
||||||
|
@ -508,11 +488,11 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
|
||||||
if (evt->key() == Qt::Key_O)
|
if (evt->key() == Qt::Key_O)
|
||||||
Logger::error("error message");
|
Logger::error("error message");
|
||||||
|
|
||||||
if (evt->key() == Qt::Key_C && getSelection().size() > 0)
|
if (evt->key() == Qt::Key_C && getSelection().size() > 0 && !getSelection()[0].expired())
|
||||||
getSelection()[0]->Clone().value()->SetParent(gWorkspace());
|
getSelection()[0].lock()->Clone().value()->SetParent(gWorkspace());
|
||||||
|
|
||||||
if (evt->key() == Qt::Key_H && getSelection().size() > 0)
|
if (evt->key() == Qt::Key_H && getSelection().size() > 0 && !getSelection()[0].expired())
|
||||||
Logger::infof("Object at: 0x%x\n", getSelection()[0].get());
|
Logger::infof("Object at: 0x%x\n", getSelection()[0].lock().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainGLWidget::keyReleaseEvent(QKeyEvent* evt) {
|
void MainGLWidget::keyReleaseEvent(QKeyEvent* evt) {
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include <QOpenGLWidget>
|
#include <QOpenGLWidget>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <qmenu.h>
|
|
||||||
|
|
||||||
class HandleFace;
|
class HandleFace;
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
@ -17,7 +16,6 @@ public:
|
||||||
void updateCycle();
|
void updateCycle();
|
||||||
std::shared_ptr<Part> lastPart;
|
std::shared_ptr<Part> lastPart;
|
||||||
|
|
||||||
void buildContextMenu();
|
|
||||||
protected:
|
protected:
|
||||||
void initializeGL() override;
|
void initializeGL() override;
|
||||||
void resizeGL(int w, int h) override;
|
void resizeGL(int w, int h) override;
|
||||||
|
@ -38,8 +36,6 @@ protected:
|
||||||
void keyPressEvent(QKeyEvent* evt) override;
|
void keyPressEvent(QKeyEvent* evt) override;
|
||||||
void keyReleaseEvent(QKeyEvent* evt) override;
|
void keyReleaseEvent(QKeyEvent* evt) override;
|
||||||
|
|
||||||
QMenu contextMenu;
|
|
||||||
|
|
||||||
MainWindow* mainWindow();
|
MainWindow* mainWindow();
|
||||||
float snappingFactor();
|
float snappingFactor();
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "objects/datamodel.h"
|
#include "objects/datamodel.h"
|
||||||
#include "objects/model.h"
|
|
||||||
#include "placedocument.h"
|
#include "placedocument.h"
|
||||||
#include "script/scriptdocument.h"
|
#include "script/scriptdocument.h"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -20,7 +19,6 @@
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
#include <qtextcursor.h>
|
#include <qtextcursor.h>
|
||||||
#include <qtextedit.h>
|
#include <qtextedit.h>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#ifdef _NDEBUG
|
#ifdef _NDEBUG
|
||||||
#define NDEBUG
|
#define NDEBUG
|
||||||
|
@ -85,8 +83,6 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
this->close();
|
this->close();
|
||||||
});
|
});
|
||||||
|
|
||||||
ui->explorerView->buildContextMenu();
|
|
||||||
|
|
||||||
connectActionHandlers();
|
connectActionHandlers();
|
||||||
|
|
||||||
// Update properties
|
// Update properties
|
||||||
|
@ -94,17 +90,17 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
if (newSelection.size() == 0) return;
|
if (newSelection.size() == 0) return;
|
||||||
if (newSelection.size() > 1)
|
if (newSelection.size() > 1)
|
||||||
ui->propertiesView->setSelected(std::nullopt);
|
ui->propertiesView->setSelected(std::nullopt);
|
||||||
ui->propertiesView->setSelected(newSelection[0]);
|
ui->propertiesView->setSelected(newSelection[0].lock());
|
||||||
});
|
});
|
||||||
|
|
||||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool __) {
|
addSelectionListener([&](auto oldSelection, auto newSelection, bool __) {
|
||||||
for (std::weak_ptr<Instance> inst : oldSelection) {
|
for (InstanceRefWeak inst : oldSelection) {
|
||||||
if (inst.expired() || inst.lock()->GetClass() != &Part::TYPE) continue;
|
if (inst.expired() || inst.lock()->GetClass() != &Part::TYPE) continue;
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst.lock());
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst.lock());
|
||||||
part->selected = false;
|
part->selected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::weak_ptr<Instance> inst : newSelection) {
|
for (InstanceRefWeak inst : newSelection) {
|
||||||
if (inst.expired() || inst.lock()->GetClass() != &Part::TYPE) continue;
|
if (inst.expired() || inst.lock()->GetClass() != &Part::TYPE) continue;
|
||||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst.lock());
|
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst.lock());
|
||||||
part->selected = true;
|
part->selected = true;
|
||||||
|
@ -145,6 +141,10 @@ void MainWindow::closeEvent(QCloseEvent* evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::connectActionHandlers() {
|
void MainWindow::connectActionHandlers() {
|
||||||
|
// Explorer View
|
||||||
|
|
||||||
|
ui->explorerView->buildContextMenu();
|
||||||
|
|
||||||
connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = TOOL_SELECT; updateToolbars(); });
|
connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = TOOL_SELECT; updateToolbars(); });
|
||||||
connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_MOVE : TOOL_SELECT; updateToolbars(); });
|
connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_MOVE : TOOL_SELECT; updateToolbars(); });
|
||||||
connect(ui->actionToolScale, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_SCALE : TOOL_SELECT; updateToolbars(); });
|
connect(ui->actionToolScale, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_SCALE : TOOL_SELECT; updateToolbars(); });
|
||||||
|
@ -291,16 +291,16 @@ void MainWindow::connectActionHandlers() {
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionDelete, &QAction::triggered, this, [&]() {
|
connect(ui->actionDelete, &QAction::triggered, this, [&]() {
|
||||||
for (std::weak_ptr<Instance> inst : getSelection()) {
|
for (InstanceRefWeak inst : getSelection()) {
|
||||||
if (inst.expired()) continue;
|
if (inst.expired()) continue;
|
||||||
inst.lock()->SetParent(std::nullopt);
|
inst.lock()->SetParent(std::nullopt);
|
||||||
}
|
}
|
||||||
setSelection({});
|
setSelection(std::vector<InstanceRefWeak> {});
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionCopy, &QAction::triggered, this, [&]() {
|
connect(ui->actionCopy, &QAction::triggered, this, [&]() {
|
||||||
pugi::xml_document rootDoc;
|
pugi::xml_document rootDoc;
|
||||||
for (std::weak_ptr<Instance> inst : getSelection()) {
|
for (InstanceRefWeak inst : getSelection()) {
|
||||||
if (inst.expired()) continue;
|
if (inst.expired()) continue;
|
||||||
inst.lock()->Serialize(rootDoc);
|
inst.lock()->Serialize(rootDoc);
|
||||||
}
|
}
|
||||||
|
@ -314,7 +314,7 @@ void MainWindow::connectActionHandlers() {
|
||||||
});
|
});
|
||||||
connect(ui->actionCut, &QAction::triggered, this, [&]() {
|
connect(ui->actionCut, &QAction::triggered, this, [&]() {
|
||||||
pugi::xml_document rootDoc;
|
pugi::xml_document rootDoc;
|
||||||
for (std::weak_ptr<Instance> inst : getSelection()) {
|
for (InstanceRefWeak inst : getSelection()) {
|
||||||
if (inst.expired()) continue;
|
if (inst.expired()) continue;
|
||||||
inst.lock()->Serialize(rootDoc);
|
inst.lock()->Serialize(rootDoc);
|
||||||
inst.lock()->SetParent(std::nullopt);
|
inst.lock()->SetParent(std::nullopt);
|
||||||
|
@ -338,16 +338,16 @@ void MainWindow::connectActionHandlers() {
|
||||||
rootDoc.load_string(encoded.c_str());
|
rootDoc.load_string(encoded.c_str());
|
||||||
|
|
||||||
for (pugi::xml_node instNode : rootDoc.children()) {
|
for (pugi::xml_node instNode : rootDoc.children()) {
|
||||||
result<std::shared_ptr<Instance>, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
result<InstanceRef, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
||||||
if (!inst) { inst.logError(); continue; }
|
if (!inst) { inst.logError(); continue; }
|
||||||
gWorkspace()->AddChild(inst.expect());
|
gWorkspace()->AddChild(inst.expect());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionPasteInto, &QAction::triggered, this, [&]() {
|
connect(ui->actionPasteInto, &QAction::triggered, this, [&]() {
|
||||||
if (getSelection().size() != 1) return;
|
if (getSelection().size() != 1 || getSelection()[0].expired()) return;
|
||||||
|
|
||||||
std::shared_ptr<Instance> selectedParent = getSelection()[0];
|
InstanceRef selectedParent = getSelection()[0].lock();
|
||||||
|
|
||||||
const QMimeData* mimeData = QApplication::clipboard()->mimeData();
|
const QMimeData* mimeData = QApplication::clipboard()->mimeData();
|
||||||
if (!mimeData || !mimeData->hasFormat("application/xml")) return;
|
if (!mimeData || !mimeData->hasFormat("application/xml")) return;
|
||||||
|
@ -358,50 +358,12 @@ void MainWindow::connectActionHandlers() {
|
||||||
rootDoc.load_string(encoded.c_str());
|
rootDoc.load_string(encoded.c_str());
|
||||||
|
|
||||||
for (pugi::xml_node instNode : rootDoc.children()) {
|
for (pugi::xml_node instNode : rootDoc.children()) {
|
||||||
result<std::shared_ptr<Instance>, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
result<InstanceRef, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
||||||
if (!inst) { inst.logError(); continue; }
|
if (!inst) { inst.logError(); continue; }
|
||||||
selectedParent->AddChild(inst.expect());
|
selectedParent->AddChild(inst.expect());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionGroupObjects, &QAction::triggered, this, [&]() {
|
|
||||||
auto model = Model::New();
|
|
||||||
std::shared_ptr<Instance> firstParent;
|
|
||||||
|
|
||||||
for (auto object : getSelection()) {
|
|
||||||
if (firstParent == nullptr && object->GetParent().has_value()) firstParent = object->GetParent().value();
|
|
||||||
object->SetParent(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model->GetChildren().size() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Technically not how it works in the actual studio, but it's not an API-breaking change
|
|
||||||
// and I think this implementation is more useful so I'm sticking with it
|
|
||||||
if (firstParent == nullptr) firstParent = gWorkspace();
|
|
||||||
model->SetParent(firstParent);
|
|
||||||
|
|
||||||
setSelection({ model });
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(ui->actionUngroupObjects, &QAction::triggered, this, [&]() {
|
|
||||||
std::vector<std::shared_ptr<Instance>> newSelection;
|
|
||||||
|
|
||||||
for (auto model : getSelection()) {
|
|
||||||
// Not a model, skip
|
|
||||||
if (!model->IsA<Model>()) { newSelection.push_back(model); continue; }
|
|
||||||
|
|
||||||
for (auto object : model->GetChildren()) {
|
|
||||||
object->SetParent(model->GetParent());
|
|
||||||
newSelection.push_back(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
model->Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelection(newSelection);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(ui->actionSaveModel, &QAction::triggered, this, [&]() {
|
connect(ui->actionSaveModel, &QAction::triggered, this, [&]() {
|
||||||
std::optional<std::string> path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptSave);
|
std::optional<std::string> path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptSave);
|
||||||
if (!path) return;
|
if (!path) return;
|
||||||
|
@ -411,7 +373,7 @@ void MainWindow::connectActionHandlers() {
|
||||||
pugi::xml_document modelDoc;
|
pugi::xml_document modelDoc;
|
||||||
pugi::xml_node modelRoot = modelDoc.append_child("openblocks");
|
pugi::xml_node modelRoot = modelDoc.append_child("openblocks");
|
||||||
|
|
||||||
for (std::weak_ptr<Instance> inst : getSelection()) {
|
for (InstanceRefWeak inst : getSelection()) {
|
||||||
if (inst.expired()) continue;
|
if (inst.expired()) continue;
|
||||||
inst.lock()->Serialize(modelRoot);
|
inst.lock()->Serialize(modelRoot);
|
||||||
}
|
}
|
||||||
|
@ -420,8 +382,8 @@ void MainWindow::connectActionHandlers() {
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->actionInsertModel, &QAction::triggered, this, [&]() {
|
connect(ui->actionInsertModel, &QAction::triggered, this, [&]() {
|
||||||
if (getSelection().size() != 1) return;
|
if (getSelection().size() != 1 || getSelection()[0].expired()) return;
|
||||||
std::shared_ptr<Instance> selectedParent = getSelection()[0];
|
InstanceRef selectedParent = getSelection()[0].lock();
|
||||||
|
|
||||||
std::optional<std::string> path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptOpen);
|
std::optional<std::string> path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptOpen);
|
||||||
if (!path) return;
|
if (!path) return;
|
||||||
|
@ -431,46 +393,13 @@ void MainWindow::connectActionHandlers() {
|
||||||
modelDoc.load(inStream);
|
modelDoc.load(inStream);
|
||||||
|
|
||||||
for (pugi::xml_node instNode : modelDoc.child("openblocks").children("Item")) {
|
for (pugi::xml_node instNode : modelDoc.child("openblocks").children("Item")) {
|
||||||
result<std::shared_ptr<Instance>, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
result<InstanceRef, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
||||||
if (!inst) { inst.logError(); continue; }
|
if (!inst) { inst.logError(); continue; }
|
||||||
selectedParent->AddChild(inst.expect());
|
selectedParent->AddChild(inst.expect());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::openFile(std::string path) {
|
|
||||||
// Don't ask for confirmation if running a debug build (makes development easier)
|
|
||||||
#ifdef NDEBUG
|
|
||||||
// Ask if the user wants to save their changes
|
|
||||||
// https://stackoverflow.com/a/33890731
|
|
||||||
QMessageBox msgBox;
|
|
||||||
msgBox.setText("Save changes before creating new document?");
|
|
||||||
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
|
|
||||||
msgBox.setDefaultButton(QMessageBox::Save);
|
|
||||||
int result = msgBox.exec();
|
|
||||||
|
|
||||||
if (result == QMessageBox::Cancel) return;
|
|
||||||
if (result == QMessageBox::Save) {
|
|
||||||
std::optional<std::string> path;
|
|
||||||
if (!gDataModel->HasFile())
|
|
||||||
path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name));
|
|
||||||
if (!path || path == "") return;
|
|
||||||
|
|
||||||
gDataModel->SaveToFile(path);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path);
|
|
||||||
editModeDataModel = newModel;
|
|
||||||
gDataModel = newModel;
|
|
||||||
newModel->Init();
|
|
||||||
ui->explorerView->updateRoot(newModel);
|
|
||||||
|
|
||||||
// Reset running state
|
|
||||||
placeDocument->setRunState(RUN_STOPPED);
|
|
||||||
updateToolbars();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::updateToolbars() {
|
void MainWindow::updateToolbars() {
|
||||||
ui->actionToolSelect->setChecked(selectedTool == TOOL_SELECT);
|
ui->actionToolSelect->setChecked(selectedTool == TOOL_SELECT);
|
||||||
ui->actionToolMove->setChecked(selectedTool == TOOL_MOVE);
|
ui->actionToolMove->setChecked(selectedTool == TOOL_MOVE);
|
||||||
|
|
|
@ -57,8 +57,6 @@ public:
|
||||||
void openScriptDocument(std::shared_ptr<Script>);
|
void openScriptDocument(std::shared_ptr<Script>);
|
||||||
void closeScriptDocument(std::shared_ptr<Script>);
|
void closeScriptDocument(std::shared_ptr<Script>);
|
||||||
|
|
||||||
void openFile(std::string path);
|
|
||||||
|
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
private:
|
private:
|
||||||
PlaceDocument* placeDocument;
|
PlaceDocument* placeDocument;
|
||||||
|
|
|
@ -176,8 +176,6 @@
|
||||||
<addaction name="actionCut"/>
|
<addaction name="actionCut"/>
|
||||||
<addaction name="actionPaste"/>
|
<addaction name="actionPaste"/>
|
||||||
<addaction name="actionPasteInto"/>
|
<addaction name="actionPasteInto"/>
|
||||||
<addaction name="actionGroupObjects"/>
|
|
||||||
<addaction name="actionUngroupObjects"/>
|
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QToolBar" name="snappingOptions">
|
<widget class="QToolBar" name="snappingOptions">
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -770,40 +768,6 @@
|
||||||
<enum>QAction::MenuRole::NoRole</enum>
|
<enum>QAction::MenuRole::NoRole</enum>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionGroupObjects">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="object-group"/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Group Objects</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Group objects under a Model</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>Ctrl+G</string>
|
|
||||||
</property>
|
|
||||||
<property name="menuRole">
|
|
||||||
<enum>QAction::MenuRole::NoRole</enum>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionUngroupObjects">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="object-ungroup"/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Ungroup Objects</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Ungroup objects inside selected Model</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>Ctrl+U</string>
|
|
||||||
</property>
|
|
||||||
<property name="menuRole">
|
|
||||||
<enum>QAction::MenuRole::NoRole</enum>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
|
|
||||||
std::map<std::string, QIcon> instanceIconCache;
|
std::map<std::string, QIcon> instanceIconCache;
|
||||||
|
|
||||||
ExplorerModel::ExplorerModel(std::shared_ptr<Instance> dataRoot, QWidget *parent)
|
ExplorerModel::ExplorerModel(InstanceRef dataRoot, QWidget *parent)
|
||||||
: QAbstractItemModel(parent)
|
: QAbstractItemModel(parent)
|
||||||
, rootItem(dataRoot) {
|
, rootItem(dataRoot) {
|
||||||
// TODO: Don't use lambdas and handlers like that
|
// TODO: Don't use lambdas and handlers like that
|
||||||
hierarchyPreUpdateHandler = [&](std::shared_ptr<Instance> object, std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) {
|
hierarchyPreUpdateHandler = [&](InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent) {
|
||||||
if (oldParent.has_value()) {
|
if (oldParent.has_value()) {
|
||||||
auto children = oldParent.value()->GetChildren();
|
auto children = oldParent.value()->GetChildren();
|
||||||
size_t idx = std::find(children.begin(), children.end(), object) - children.begin();
|
size_t idx = std::find(children.begin(), children.end(), object) - children.begin();
|
||||||
|
@ -29,7 +29,7 @@ ExplorerModel::ExplorerModel(std::shared_ptr<Instance> dataRoot, QWidget *parent
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
hierarchyPostUpdateHandler = [&](std::shared_ptr<Instance> object, std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) {
|
hierarchyPostUpdateHandler = [&](InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent) {
|
||||||
if (newParent.has_value()) endInsertRows();
|
if (newParent.has_value()) endInsertRows();
|
||||||
if (oldParent.has_value()) endRemoveRows();
|
if (oldParent.has_value()) endRemoveRows();
|
||||||
};
|
};
|
||||||
|
@ -50,11 +50,11 @@ QModelIndex ExplorerModel::index(int row, int column, const QModelIndex &parent)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex ExplorerModel::toIndex(std::shared_ptr<Instance> item) {
|
QModelIndex ExplorerModel::toIndex(InstanceRef item) {
|
||||||
if (item == rootItem || !item->GetParent().has_value())
|
if (item == rootItem || !item->GetParent().has_value())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::shared_ptr<Instance> parentItem = item->GetParent().value();
|
InstanceRef parentItem = item->GetParent().value();
|
||||||
// Check above ensures this item is not root, so value() must be valid
|
// Check above ensures this item is not root, so value() must be valid
|
||||||
for (int i = 0; i < parentItem->GetChildren().size(); i++)
|
for (int i = 0; i < parentItem->GetChildren().size(); i++)
|
||||||
if (parentItem->GetChildren()[i] == item)
|
if (parentItem->GetChildren()[i] == item)
|
||||||
|
@ -62,7 +62,7 @@ QModelIndex ExplorerModel::toIndex(std::shared_ptr<Instance> item) {
|
||||||
return QModelIndex{};
|
return QModelIndex{};
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex ExplorerModel::ObjectToIndex(std::shared_ptr<Instance> item) {
|
QModelIndex ExplorerModel::ObjectToIndex(InstanceRef item) {
|
||||||
return toIndex(item);
|
return toIndex(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,13 +72,13 @@ QModelIndex ExplorerModel::parent(const QModelIndex &index) const {
|
||||||
|
|
||||||
Instance* childItem = static_cast<Instance*>(index.internalPointer());
|
Instance* childItem = static_cast<Instance*>(index.internalPointer());
|
||||||
// NORISK: The parent must exist if the child was obtained from it during this frame
|
// NORISK: The parent must exist if the child was obtained from it during this frame
|
||||||
std::shared_ptr<Instance> parentItem = childItem->GetParent().value();
|
InstanceRef parentItem = childItem->GetParent().value();
|
||||||
|
|
||||||
if (parentItem == rootItem)
|
if (parentItem == rootItem)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
// Check above ensures this item is not root, so value() must be valid
|
// Check above ensures this item is not root, so value() must be valid
|
||||||
std::shared_ptr<Instance> parentParent = parentItem->GetParent().value();
|
InstanceRef parentParent = parentItem->GetParent().value();
|
||||||
for (int i = 0; i < parentParent->GetChildren().size(); i++)
|
for (int i = 0; i < parentParent->GetChildren().size(); i++)
|
||||||
if (parentParent->GetChildren()[i] == parentItem)
|
if (parentParent->GetChildren()[i] == parentItem)
|
||||||
return createIndex(i, 0, parentItem.get());
|
return createIndex(i, 0, parentItem.get());
|
||||||
|
@ -196,13 +196,13 @@ Qt::DropActions ExplorerModel::supportedDropActions() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<Instance> ExplorerModel::fromIndex(const QModelIndex index) const {
|
InstanceRef ExplorerModel::fromIndex(const QModelIndex index) const {
|
||||||
if (!index.isValid()) return rootItem;
|
if (!index.isValid()) return rootItem;
|
||||||
return static_cast<Instance*>(index.internalPointer())->shared_from_this();
|
return static_cast<Instance*>(index.internalPointer())->shared_from_this();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DragDropSlot {
|
struct DragDropSlot {
|
||||||
std::vector<std::shared_ptr<Instance>> instances;
|
std::vector<InstanceRef> instances;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
|
bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
|
||||||
|
@ -216,8 +216,8 @@ bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, i
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Instance> parentInst = fromIndex(parent);
|
InstanceRef parentInst = fromIndex(parent);
|
||||||
for (std::shared_ptr<Instance> instance : slot->instances) {
|
for (InstanceRef instance : slot->instances) {
|
||||||
instance->SetParent(parentInst);
|
instance->SetParent(parentInst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, i
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExplorerModel::updateRoot(std::shared_ptr<Instance> newRoot) {
|
void ExplorerModel::updateRoot(InstanceRef newRoot) {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
rootItem = newRoot;
|
rootItem = newRoot;
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
|
@ -8,7 +8,7 @@ class ExplorerModel : public QAbstractItemModel {
|
||||||
public:
|
public:
|
||||||
Q_DISABLE_COPY_MOVE(ExplorerModel)
|
Q_DISABLE_COPY_MOVE(ExplorerModel)
|
||||||
|
|
||||||
explicit ExplorerModel(std::shared_ptr<Instance> dataRoot, QWidget *parent = nullptr);
|
explicit ExplorerModel(InstanceRef dataRoot, QWidget *parent = nullptr);
|
||||||
~ExplorerModel() override;
|
~ExplorerModel() override;
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
@ -29,15 +29,15 @@ public:
|
||||||
QStringList mimeTypes() const override;
|
QStringList mimeTypes() const override;
|
||||||
Qt::DropActions supportedDragActions() const override;
|
Qt::DropActions supportedDragActions() const override;
|
||||||
Qt::DropActions supportedDropActions() const override;
|
Qt::DropActions supportedDropActions() const override;
|
||||||
std::shared_ptr<Instance> fromIndex(const QModelIndex index) const;
|
InstanceRef fromIndex(const QModelIndex index) const;
|
||||||
QModelIndex ObjectToIndex(std::shared_ptr<Instance> item);
|
QModelIndex ObjectToIndex(InstanceRef item);
|
||||||
|
|
||||||
QIcon iconOf(const InstanceType* type) const;
|
QIcon iconOf(const InstanceType* type) const;
|
||||||
|
|
||||||
void updateRoot(std::shared_ptr<Instance> newRoot);
|
void updateRoot(InstanceRef newRoot);
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Instance> rootItem;
|
InstanceRef rootItem;
|
||||||
QModelIndex toIndex(std::shared_ptr<Instance> item);
|
QModelIndex toIndex(InstanceRef item);
|
||||||
};
|
};
|
||||||
|
|
||||||
// #endif
|
// #endif
|
|
@ -38,11 +38,11 @@ ExplorerView::ExplorerView(QWidget* parent):
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) {
|
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) {
|
||||||
std::vector<std::shared_ptr<Instance>> selectedInstances;
|
std::vector<InstanceRefWeak> selectedInstances;
|
||||||
selectedInstances.reserve(selectedIndexes().count()); // This doesn't reserve everything, but should enhance things anyway
|
selectedInstances.reserve(selectedIndexes().count()); // This doesn't reserve everything, but should enhance things anyway
|
||||||
|
|
||||||
for (auto index : selectedIndexes()) {
|
for (auto index : selectedIndexes()) {
|
||||||
selectedInstances.push_back(reinterpret_cast<Instance*>(index.internalPointer())->shared_from_this());
|
selectedInstances.push_back(reinterpret_cast<Instance*>(index.internalPointer())->weak_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
::setSelection(selectedInstances, true);
|
::setSelection(selectedInstances, true);
|
||||||
|
@ -53,7 +53,7 @@ ExplorerView::ExplorerView(QWidget* parent):
|
||||||
if (fromExplorer) return;
|
if (fromExplorer) return;
|
||||||
|
|
||||||
this->clearSelection();
|
this->clearSelection();
|
||||||
for (std::weak_ptr<Instance> inst : newSelection) {
|
for (InstanceRefWeak inst : newSelection) {
|
||||||
if (inst.expired()) continue;
|
if (inst.expired()) continue;
|
||||||
QModelIndex index = this->model.ObjectToIndex(inst.lock());
|
QModelIndex index = this->model.ObjectToIndex(inst.lock());
|
||||||
this->selectionModel()->select(index, QItemSelectionModel::SelectionFlag::Select);
|
this->selectionModel()->select(index, QItemSelectionModel::SelectionFlag::Select);
|
||||||
|
@ -104,14 +104,14 @@ void ExplorerView::buildContextMenu() {
|
||||||
QAction* instAction = new QAction(model.iconOf(type), QString::fromStdString(type->className));
|
QAction* instAction = new QAction(model.iconOf(type), QString::fromStdString(type->className));
|
||||||
insertObjectMenu->addAction(instAction);
|
insertObjectMenu->addAction(instAction);
|
||||||
connect(instAction, &QAction::triggered, this, [&]() {
|
connect(instAction, &QAction::triggered, this, [&]() {
|
||||||
if (getSelection().size() == 0) return;
|
if (getSelection().size() == 0 || getSelection()[0].expired()) return;
|
||||||
std::shared_ptr<Instance> instParent = getSelection()[0];
|
std::shared_ptr<Instance> instParent = getSelection()[0].lock();
|
||||||
std::shared_ptr<Instance> newInst = type->constructor();
|
std::shared_ptr<Instance> newInst = type->constructor();
|
||||||
newInst->SetParent(instParent);
|
newInst->SetParent(instParent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExplorerView::updateRoot(std::shared_ptr<Instance> newRoot) {
|
void ExplorerView::updateRoot(InstanceRef newRoot) {
|
||||||
model.updateRoot(newRoot);
|
model.updateRoot(newRoot);
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ public:
|
||||||
// void dropEvent(QDropEvent*) override;
|
// void dropEvent(QDropEvent*) override;
|
||||||
|
|
||||||
void buildContextMenu();
|
void buildContextMenu();
|
||||||
void updateRoot(std::shared_ptr<Instance> newRoot);
|
void updateRoot(InstanceRef newRoot);
|
||||||
private:
|
private:
|
||||||
ExplorerModel model;
|
ExplorerModel model;
|
||||||
QMenu contextMenu;
|
QMenu contextMenu;
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#include "panes/propertiesview.h"
|
#include "panes/propertiesview.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "datatypes/base.h"
|
#include "datatypes/base.h"
|
||||||
#include "datatypes/variant.h"
|
#include "datatypes/meta.h"
|
||||||
#include "datatypes/primitives.h"
|
|
||||||
#include "error/data.h"
|
|
||||||
#include "objects/base/member.h"
|
#include "objects/base/member.h"
|
||||||
|
|
||||||
#include <QColorDialog>
|
#include <QColorDialog>
|
||||||
|
@ -40,7 +38,7 @@ public:
|
||||||
if (index.column() == 0) return nullptr;
|
if (index.column() == 0) return nullptr;
|
||||||
|
|
||||||
if (!index.parent().isValid() || view->currentInstance.expired()) return nullptr;
|
if (!index.parent().isValid() || view->currentInstance.expired()) return nullptr;
|
||||||
std::shared_ptr<Instance> inst = view->currentInstance.lock();
|
InstanceRef inst = view->currentInstance.lock();
|
||||||
|
|
||||||
// If the property is deeper than 1 layer, then it is considered composite
|
// If the property is deeper than 1 layer, then it is considered composite
|
||||||
// Handle specially
|
// Handle specially
|
||||||
|
@ -51,10 +49,10 @@ public:
|
||||||
std::string propertyName = !isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString()
|
std::string propertyName = !isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString()
|
||||||
: view->itemFromIndex(index.parent())->data(0, Qt::DisplayRole).toString().toStdString();
|
: view->itemFromIndex(index.parent())->data(0, Qt::DisplayRole).toString().toStdString();
|
||||||
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
||||||
Variant currentValue = inst->GetPropertyValue(propertyName).expect();
|
Data::Variant currentValue = inst->GetPropertyValue(propertyName).expect();
|
||||||
|
|
||||||
if (isComposite) {
|
if (isComposite) {
|
||||||
if (meta.type.descriptor == &Vector3::TYPE) {
|
if (meta.type == &Vector3::TYPE) {
|
||||||
Vector3 vector = currentValue.get<Vector3>();
|
Vector3 vector = currentValue.get<Vector3>();
|
||||||
float value = componentName == "X" ? vector.X() : componentName == "Y" ? vector.Y() : componentName == "Z" ? vector.Z() : 0;
|
float value = componentName == "X" ? vector.X() : componentName == "Y" ? vector.Y() : componentName == "Z" ? vector.Z() : 0;
|
||||||
|
|
||||||
|
@ -67,9 +65,9 @@ public:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.type.descriptor == &FLOAT_TYPE) {
|
if (meta.type == &Data::Float::TYPE) {
|
||||||
QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
|
QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
|
||||||
spinBox->setValue(currentValue.get<float>());
|
spinBox->setValue(currentValue.get<Data::Float>());
|
||||||
|
|
||||||
if (meta.flags & PROP_UNIT_FLOAT) {
|
if (meta.flags & PROP_UNIT_FLOAT) {
|
||||||
spinBox->setMinimum(0);
|
spinBox->setMinimum(0);
|
||||||
|
@ -78,24 +76,24 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
return spinBox;
|
return spinBox;
|
||||||
} else if (meta.type.descriptor == &INT_TYPE) {
|
} else if (meta.type == &Data::Int::TYPE) {
|
||||||
QSpinBox* spinBox = new QSpinBox(parent);
|
QSpinBox* spinBox = new QSpinBox(parent);
|
||||||
spinBox->setValue(currentValue.get<int>());
|
spinBox->setValue(currentValue.get<Data::Int>());
|
||||||
|
|
||||||
return spinBox;
|
return spinBox;
|
||||||
} else if (meta.type.descriptor == &STRING_TYPE) {
|
} else if (meta.type == &Data::String::TYPE) {
|
||||||
QLineEdit* lineEdit = new QLineEdit(parent);
|
QLineEdit* lineEdit = new QLineEdit(parent);
|
||||||
lineEdit->setText(QString::fromStdString(currentValue.get<std::string>()));
|
lineEdit->setText(QString::fromStdString(currentValue.get<Data::String>()));
|
||||||
|
|
||||||
return lineEdit;
|
return lineEdit;
|
||||||
} else if (meta.type.descriptor == &Color3::TYPE) {
|
} else if (meta.type == &Color3::TYPE) {
|
||||||
QColorDialog* colorDialog = new QColorDialog(parent->window());
|
QColorDialog* colorDialog = new QColorDialog(parent->window());
|
||||||
|
|
||||||
Color3 color = currentValue.get<Color3>();
|
Color3 color = currentValue.get<Color3>();
|
||||||
colorDialog->setCurrentColor(QColor::fromRgbF(color.R(), color.G(), color.B()));
|
colorDialog->setCurrentColor(QColor::fromRgbF(color.R(), color.G(), color.B()));
|
||||||
|
|
||||||
return colorDialog;
|
return colorDialog;
|
||||||
} else if (meta.type.descriptor->fromString) {
|
} else if (meta.type->fromString) {
|
||||||
QLineEdit* lineEdit = new QLineEdit(parent);
|
QLineEdit* lineEdit = new QLineEdit(parent);
|
||||||
lineEdit->setText(QString::fromStdString(currentValue.ToString()));
|
lineEdit->setText(QString::fromStdString(currentValue.ToString()));
|
||||||
|
|
||||||
|
@ -109,7 +107,7 @@ public:
|
||||||
if (index.column() == 0) return;
|
if (index.column() == 0) return;
|
||||||
|
|
||||||
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
||||||
std::shared_ptr<Instance> inst = view->currentInstance.lock();
|
InstanceRef inst = view->currentInstance.lock();
|
||||||
|
|
||||||
bool isComposite = index.parent().parent().isValid();
|
bool isComposite = index.parent().parent().isValid();
|
||||||
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
||||||
|
@ -117,10 +115,10 @@ public:
|
||||||
std::string propertyName = !index.parent().parent().isValid() ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString()
|
std::string propertyName = !index.parent().parent().isValid() ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString()
|
||||||
: view->itemFromIndex(index.parent())->data(0, Qt::DisplayRole).toString().toStdString();
|
: view->itemFromIndex(index.parent())->data(0, Qt::DisplayRole).toString().toStdString();
|
||||||
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
||||||
Variant currentValue = inst->GetPropertyValue(propertyName).expect();
|
Data::Variant currentValue = inst->GetPropertyValue(propertyName).expect();
|
||||||
|
|
||||||
if (isComposite) {
|
if (isComposite) {
|
||||||
if (meta.type.descriptor == &Vector3::TYPE) {
|
if (meta.type == &Vector3::TYPE) {
|
||||||
Vector3 vector = currentValue.get<Vector3>();
|
Vector3 vector = currentValue.get<Vector3>();
|
||||||
float value = componentName == "X" ? vector.X() : componentName == "Y" ? vector.Y() : componentName == "Z" ? vector.Z() : 0;
|
float value = componentName == "X" ? vector.X() : componentName == "Y" ? vector.Y() : componentName == "Z" ? vector.Z() : 0;
|
||||||
|
|
||||||
|
@ -133,24 +131,24 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.type.descriptor == &FLOAT_TYPE) {
|
if (meta.type == &Data::Float::TYPE) {
|
||||||
QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(editor);
|
QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(editor);
|
||||||
|
|
||||||
spinBox->setValue(currentValue.get<float>());
|
spinBox->setValue(currentValue.get<Data::Float>());
|
||||||
} else if (meta.type.descriptor == &INT_TYPE) {
|
} else if (meta.type == &Data::Int::TYPE) {
|
||||||
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(editor);
|
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(editor);
|
||||||
|
|
||||||
spinBox->setValue(currentValue.get<int>());
|
spinBox->setValue(currentValue.get<Data::Int>());
|
||||||
} else if (meta.type.descriptor == &STRING_TYPE) {
|
} else if (meta.type == &Data::String::TYPE) {
|
||||||
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
||||||
|
|
||||||
lineEdit->setText(QString::fromStdString((std::string)currentValue.get<std::string>()));
|
lineEdit->setText(QString::fromStdString((std::string)currentValue.get<Data::String>()));
|
||||||
} else if (meta.type.descriptor == &Color3::TYPE) {
|
} else if (meta.type == &Color3::TYPE) {
|
||||||
QColorDialog* colorDialog = dynamic_cast<QColorDialog*>(editor);
|
QColorDialog* colorDialog = dynamic_cast<QColorDialog*>(editor);
|
||||||
|
|
||||||
Color3 color = currentValue.get<Color3>();
|
Color3 color = currentValue.get<Color3>();
|
||||||
colorDialog->setCurrentColor(QColor::fromRgbF(color.R(), color.G(), color.B()));
|
colorDialog->setCurrentColor(QColor::fromRgbF(color.R(), color.G(), color.B()));
|
||||||
} else if (meta.type.descriptor->fromString) {
|
} else if (meta.type->fromString) {
|
||||||
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
||||||
|
|
||||||
lineEdit->setText(QString::fromStdString((std::string)currentValue.ToString()));
|
lineEdit->setText(QString::fromStdString((std::string)currentValue.ToString()));
|
||||||
|
@ -161,7 +159,7 @@ public:
|
||||||
if (index.column() == 0) return;
|
if (index.column() == 0) return;
|
||||||
|
|
||||||
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
||||||
std::shared_ptr<Instance> inst = view->currentInstance.lock();
|
InstanceRef inst = view->currentInstance.lock();
|
||||||
|
|
||||||
bool isComposite = index.parent().parent().isValid();
|
bool isComposite = index.parent().parent().isValid();
|
||||||
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
||||||
|
@ -171,7 +169,7 @@ public:
|
||||||
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
||||||
|
|
||||||
if (isComposite) {
|
if (isComposite) {
|
||||||
if (meta.type.descriptor == &Vector3::TYPE) {
|
if (meta.type == &Vector3::TYPE) {
|
||||||
QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(editor);
|
QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(editor);
|
||||||
float value = spinBox->value();
|
float value = spinBox->value();
|
||||||
|
|
||||||
|
@ -188,22 +186,22 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.type.descriptor == &FLOAT_TYPE) {
|
if (meta.type == &Data::Float::TYPE) {
|
||||||
QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(editor);
|
QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(editor);
|
||||||
|
|
||||||
inst->SetPropertyValue(propertyName, (float)spinBox->value()).expect();
|
inst->SetPropertyValue(propertyName, Data::Float((float)spinBox->value())).expect();
|
||||||
model->setData(index, spinBox->value());
|
model->setData(index, spinBox->value());
|
||||||
} else if (meta.type.descriptor == &INT_TYPE) {
|
} else if (meta.type == &Data::Int::TYPE) {
|
||||||
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(editor);
|
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(editor);
|
||||||
|
|
||||||
inst->SetPropertyValue(propertyName, (int)spinBox->value()).expect();
|
inst->SetPropertyValue(propertyName, Data::Int((float)spinBox->value())).expect();
|
||||||
model->setData(index, spinBox->value());
|
model->setData(index, spinBox->value());
|
||||||
} else if (meta.type.descriptor == &STRING_TYPE) {
|
} else if (meta.type == &Data::String::TYPE) {
|
||||||
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
||||||
|
|
||||||
inst->SetPropertyValue(propertyName, lineEdit->text().toStdString()).expect();
|
inst->SetPropertyValue(propertyName, Data::String(lineEdit->text().toStdString())).expect();
|
||||||
model->setData(index, lineEdit->text());
|
model->setData(index, lineEdit->text());
|
||||||
} else if (meta.type.descriptor == &Color3::TYPE) {
|
} else if (meta.type == &Color3::TYPE) {
|
||||||
QColorDialog* colorDialog = dynamic_cast<QColorDialog*>(editor);
|
QColorDialog* colorDialog = dynamic_cast<QColorDialog*>(editor);
|
||||||
|
|
||||||
QColor color = colorDialog->currentColor();
|
QColor color = colorDialog->currentColor();
|
||||||
|
@ -211,15 +209,14 @@ public:
|
||||||
inst->SetPropertyValue(propertyName, color3).expect();
|
inst->SetPropertyValue(propertyName, color3).expect();
|
||||||
model->setData(index, QString::fromStdString(color3.ToString()), Qt::DisplayRole);
|
model->setData(index, QString::fromStdString(color3.ToString()), Qt::DisplayRole);
|
||||||
model->setData(index, color, Qt::DecorationRole);
|
model->setData(index, color, Qt::DecorationRole);
|
||||||
} else if (meta.type.descriptor->fromString) {
|
} else if (meta.type->fromString) {
|
||||||
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
||||||
|
|
||||||
result<Variant, DataParseError> parsedResult = meta.type.descriptor->fromString(lineEdit->text().toStdString(), meta.type);
|
std::optional<Data::Variant> parsedResult = meta.type->fromString(lineEdit->text().toStdString());
|
||||||
if (!parsedResult) return;
|
if (!parsedResult) return;
|
||||||
Variant parsedValue = parsedResult.expect();
|
inst->SetPropertyValue(propertyName, parsedResult.value()).expect();
|
||||||
inst->SetPropertyValue(propertyName, parsedValue).expect();
|
model->setData(index, QString::fromStdString(parsedResult.value().ToString()));
|
||||||
model->setData(index, QString::fromStdString(parsedValue.ToString()));
|
view->rebuildCompositeProperty(view->itemFromIndex(index), meta.type, parsedResult.value());
|
||||||
view->rebuildCompositeProperty(view->itemFromIndex(index), meta.type.descriptor, parsedValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -271,11 +268,11 @@ void PropertiesView::drawBranches(QPainter *painter, const QRect &rect, const QM
|
||||||
QTreeWidget::drawBranches(painter, rect, index);
|
QTreeWidget::drawBranches(painter, rect, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesView::setSelected(std::optional<std::shared_ptr<Instance>> instance) {
|
void PropertiesView::setSelected(std::optional<InstanceRef> instance) {
|
||||||
clear();
|
clear();
|
||||||
currentInstance = {};
|
currentInstance = {};
|
||||||
if (!instance) return;
|
if (!instance) return;
|
||||||
std::shared_ptr<Instance> inst = instance.value();
|
InstanceRef inst = instance.value();
|
||||||
currentInstance = inst;
|
currentInstance = inst;
|
||||||
|
|
||||||
std::map<PropertyCategory, QTreeWidgetItem*> propertyCategories;
|
std::map<PropertyCategory, QTreeWidgetItem*> propertyCategories;
|
||||||
|
@ -299,35 +296,35 @@ void PropertiesView::setSelected(std::optional<std::shared_ptr<Instance>> instan
|
||||||
|
|
||||||
for (std::string property : properties) {
|
for (std::string property : properties) {
|
||||||
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
||||||
Variant currentValue = inst->GetPropertyValue(property).expect();
|
Data::Variant currentValue = inst->GetPropertyValue(property).expect();
|
||||||
|
|
||||||
if (meta.type.descriptor == &CFrame::TYPE || meta.flags & PROP_HIDDEN) continue;
|
if (meta.type == &CFrame::TYPE || meta.flags & PROP_HIDDEN) continue;
|
||||||
|
|
||||||
QTreeWidgetItem* item = new QTreeWidgetItem;
|
QTreeWidgetItem* item = new QTreeWidgetItem;
|
||||||
item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
||||||
item->setData(0, Qt::DisplayRole, QString::fromStdString(property));
|
item->setData(0, Qt::DisplayRole, QString::fromStdString(property));
|
||||||
|
|
||||||
if (meta.type.descriptor == &BOOL_TYPE) {
|
if (meta.type == &Data::Bool::TYPE) {
|
||||||
item->setCheckState(1, (bool)currentValue.get<bool>() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
item->setCheckState(1, (bool)currentValue.get<Data::Bool>() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||||
} else if (meta.type.descriptor == &Color3::TYPE) {
|
} else if (meta.type == &Color3::TYPE) {
|
||||||
Color3 color = currentValue.get<Color3>();
|
Color3 color = currentValue.get<Color3>();
|
||||||
item->setData(1, Qt::DecorationRole, QColor::fromRgbF(color.R(), color.G(), color.B()));
|
item->setData(1, Qt::DecorationRole, QColor::fromRgbF(color.R(), color.G(), color.B()));
|
||||||
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
} else if (meta.type.descriptor == &Vector3::TYPE) {
|
} else if (meta.type == &Vector3::TYPE) {
|
||||||
Vector3 vector = currentValue.get<Vector3>();
|
Vector3 vector = currentValue.get<Vector3>();
|
||||||
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
// } else if (meta.type.descriptor == &CFrame::TYPE) {
|
// } else if (meta.type == &CFrame::TYPE) {
|
||||||
// Vector3 vector = currentValue.get<CFrame>().Position();
|
// Vector3 vector = currentValue.get<CFrame>().Position();
|
||||||
// item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
// item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
} else {
|
} else {
|
||||||
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.type.descriptor != &Color3::TYPE && (!meta.type.descriptor->fromString || meta.flags & PROP_READONLY)) {
|
if (meta.type != &Color3::TYPE && (!meta.type->fromString || meta.flags & PROP_READONLY)) {
|
||||||
item->setDisabled(true);
|
item->setDisabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuildCompositeProperty(item, meta.type.descriptor, currentValue);
|
rebuildCompositeProperty(item, meta.type, currentValue);
|
||||||
|
|
||||||
propertyCategories[meta.category]->addChild(item);
|
propertyCategories[meta.category]->addChild(item);
|
||||||
propertyCategories[meta.category]->setExpanded(true);
|
propertyCategories[meta.category]->setExpanded(true);
|
||||||
|
@ -350,17 +347,17 @@ void PropertiesView::propertyChanged(QTreeWidgetItem *item, int column) {
|
||||||
// Necessary because otherwise this will catch setCheckState from onPropertyUpdated
|
// Necessary because otherwise this will catch setCheckState from onPropertyUpdated
|
||||||
if (ignorePropertyUpdates) return;
|
if (ignorePropertyUpdates) return;
|
||||||
if (!item->parent() || (item->parent() && item->parent()->parent()) || currentInstance.expired()) return;
|
if (!item->parent() || (item->parent() && item->parent()->parent()) || currentInstance.expired()) return;
|
||||||
std::shared_ptr<Instance> inst = currentInstance.lock();
|
InstanceRef inst = currentInstance.lock();
|
||||||
|
|
||||||
std::string propertyName = item->data(0, Qt::DisplayRole).toString().toStdString();
|
std::string propertyName = item->data(0, Qt::DisplayRole).toString().toStdString();
|
||||||
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
||||||
|
|
||||||
if (meta.type.descriptor == &BOOL_TYPE) {
|
if (meta.type == &Data::Bool::TYPE) {
|
||||||
inst->SetPropertyValue(propertyName, item->checkState(1) == Qt::Checked).expect();
|
inst->SetPropertyValue(propertyName, Data::Bool(item->checkState(1) == Qt::Checked)).expect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const TypeDesc* type, Variant value) {
|
void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo* type, Data::Variant value) {
|
||||||
if (type == &Vector3::TYPE) {
|
if (type == &Vector3::TYPE) {
|
||||||
// https://forum.qt.io/post/266837
|
// https://forum.qt.io/post/266837
|
||||||
foreach(auto i, item->takeChildren()) delete i;
|
foreach(auto i, item->takeChildren()) delete i;
|
||||||
|
@ -387,15 +384,15 @@ void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const TypeD
|
||||||
}
|
}
|
||||||
|
|
||||||
// static auto lastUpdateTime = std::chrono::steady_clock::now();
|
// static auto lastUpdateTime = std::chrono::steady_clock::now();
|
||||||
void PropertiesView::onPropertyUpdated(std::shared_ptr<Instance> inst, std::string property, Variant newValue) {
|
void PropertiesView::onPropertyUpdated(InstanceRef inst, std::string property, Data::Variant newValue) {
|
||||||
// if (!currentInstance || currentInstance->expired() || instance != currentInstance->lock()) return;
|
// if (!currentInstance || currentInstance->expired() || instance != currentInstance->lock()) return;
|
||||||
// if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - lastUpdateTime).count() < 1000) return;
|
// if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - lastUpdateTime).count() < 1000) return;
|
||||||
// lastUpdateTime = std::chrono::steady_clock::now();
|
// lastUpdateTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
||||||
Variant currentValue = inst->GetPropertyValue(property).expect();
|
Data::Variant currentValue = inst->GetPropertyValue(property).expect();
|
||||||
|
|
||||||
if (meta.type.descriptor == &CFrame::TYPE) return;
|
if (meta.type == &CFrame::TYPE) return;
|
||||||
|
|
||||||
for (int categoryItemIdx = 0; categoryItemIdx < topLevelItemCount(); categoryItemIdx++) {
|
for (int categoryItemIdx = 0; categoryItemIdx < topLevelItemCount(); categoryItemIdx++) {
|
||||||
QTreeWidgetItem* categoryItem = topLevelItem(categoryItemIdx);
|
QTreeWidgetItem* categoryItem = topLevelItem(categoryItemIdx);
|
||||||
|
@ -404,27 +401,27 @@ void PropertiesView::onPropertyUpdated(std::shared_ptr<Instance> inst, std::stri
|
||||||
|
|
||||||
if (item->data(0, Qt::DisplayRole).toString().toStdString() != property) continue;
|
if (item->data(0, Qt::DisplayRole).toString().toStdString() != property) continue;
|
||||||
|
|
||||||
if (meta.type.descriptor == &BOOL_TYPE) {
|
if (meta.type == &Data::Bool::TYPE) {
|
||||||
// This is done because otherwise propertyChanged will catch this change erroneously
|
// This is done because otherwise propertyChanged will catch this change erroneously
|
||||||
ignorePropertyUpdates = true;
|
ignorePropertyUpdates = true;
|
||||||
item->setCheckState(1, (bool)currentValue.get<bool>() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
item->setCheckState(1, (bool)currentValue.get<Data::Bool>() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||||
ignorePropertyUpdates = false;
|
ignorePropertyUpdates = false;
|
||||||
} else if (meta.type.descriptor == &Color3::TYPE) {
|
} else if (meta.type == &Color3::TYPE) {
|
||||||
Color3 color = currentValue.get<Color3>();
|
Color3 color = currentValue.get<Color3>();
|
||||||
item->setData(1, Qt::DecorationRole, QColor::fromRgbF(color.R(), color.G(), color.B()));
|
item->setData(1, Qt::DecorationRole, QColor::fromRgbF(color.R(), color.G(), color.B()));
|
||||||
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
} else if (meta.type.descriptor == &Vector3::TYPE) {
|
} else if (meta.type == &Vector3::TYPE) {
|
||||||
Vector3 vector = currentValue.get<Vector3>();
|
Vector3 vector = currentValue.get<Vector3>();
|
||||||
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
} else {
|
} else {
|
||||||
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.type.descriptor != &Color3::TYPE && (!meta.type.descriptor->fromString || meta.flags & PROP_READONLY)) {
|
if (meta.type != &Color3::TYPE && (!meta.type->fromString || meta.flags & PROP_READONLY)) {
|
||||||
item->setDisabled(true);
|
item->setDisabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuildCompositeProperty(item, meta.type.descriptor, currentValue);
|
rebuildCompositeProperty(item, meta.type, currentValue);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,11 @@ class PropertiesView : public QTreeWidget {
|
||||||
Q_DECLARE_PRIVATE(QTreeView)
|
Q_DECLARE_PRIVATE(QTreeView)
|
||||||
|
|
||||||
bool ignorePropertyUpdates = false;
|
bool ignorePropertyUpdates = false;
|
||||||
std::weak_ptr<Instance> currentInstance;
|
InstanceRefWeak currentInstance;
|
||||||
void propertyChanged(QTreeWidgetItem *item, int column);
|
void propertyChanged(QTreeWidgetItem *item, int column);
|
||||||
void activateProperty(QTreeWidgetItem *item, int column);
|
void activateProperty(QTreeWidgetItem *item, int column);
|
||||||
void rebuildCompositeProperty(QTreeWidgetItem *item, const TypeDesc*, Variant);
|
void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant);
|
||||||
void onPropertyUpdated(std::shared_ptr<Instance> instance, std::string property, Variant newValue);
|
void onPropertyUpdated(InstanceRef instance, std::string property, Data::Variant newValue);
|
||||||
|
|
||||||
friend PropertiesItemDelegate;
|
friend PropertiesItemDelegate;
|
||||||
protected:
|
protected:
|
||||||
|
@ -26,5 +26,5 @@ public:
|
||||||
PropertiesView(QWidget* parent = nullptr);
|
PropertiesView(QWidget* parent = nullptr);
|
||||||
~PropertiesView() override;
|
~PropertiesView() override;
|
||||||
|
|
||||||
void setSelected(std::optional<std::shared_ptr<Instance>> instance);
|
void setSelected(std::optional<InstanceRef> instance);
|
||||||
};
|
};
|
|
@ -9,17 +9,14 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <qboxlayout.h>
|
#include <qboxlayout.h>
|
||||||
#include <qdebug.h>
|
|
||||||
#include <qevent.h>
|
#include <qevent.h>
|
||||||
#include <qmargins.h>
|
#include <qmargins.h>
|
||||||
#include <qmdisubwindow.h>
|
#include <qmdisubwindow.h>
|
||||||
#include <qlayout.h>
|
#include <qlayout.h>
|
||||||
#include <qmimedata.h>
|
|
||||||
|
|
||||||
PlaceDocument::PlaceDocument(QWidget* parent):
|
PlaceDocument::PlaceDocument(QWidget* parent):
|
||||||
QMdiSubWindow(parent) {
|
QMdiSubWindow(parent) {
|
||||||
placeWidget = new MainGLWidget;
|
placeWidget = new MainGLWidget;
|
||||||
setAcceptDrops(true);
|
|
||||||
setWidget(placeWidget);
|
setWidget(placeWidget);
|
||||||
setWindowTitle("Place");
|
setWindowTitle("Place");
|
||||||
|
|
||||||
|
@ -77,7 +74,6 @@ void PlaceDocument::timerEvent(QTimerEvent* evt) {
|
||||||
|
|
||||||
void PlaceDocument::init() {
|
void PlaceDocument::init() {
|
||||||
timer.start(33, this);
|
timer.start(33, this);
|
||||||
placeWidget->buildContextMenu();
|
|
||||||
|
|
||||||
std::shared_ptr<Part> lastPart;
|
std::shared_ptr<Part> lastPart;
|
||||||
// Baseplate
|
// Baseplate
|
||||||
|
@ -138,19 +134,4 @@ void PlaceDocument::init() {
|
||||||
gWorkspace()->AddChild(script);
|
gWorkspace()->AddChild(script);
|
||||||
MainWindow* mainWnd = dynamic_cast<MainWindow*>(window());
|
MainWindow* mainWnd = dynamic_cast<MainWindow*>(window());
|
||||||
// mainWnd->openScriptDocument(script);
|
// mainWnd->openScriptDocument(script);
|
||||||
}
|
|
||||||
|
|
||||||
void PlaceDocument::dragEnterEvent(QDragEnterEvent* evt) {
|
|
||||||
// https://stackoverflow.com/a/14895393/16255372
|
|
||||||
if (evt->mimeData()->hasUrls()) {
|
|
||||||
evt->acceptProposedAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlaceDocument::dropEvent(QDropEvent* evt) {
|
|
||||||
auto urls = evt->mimeData()->urls();
|
|
||||||
if (urls.size() == 0) return;
|
|
||||||
QString fileName = urls[0].toLocalFile();
|
|
||||||
MainWindow* mainWnd = dynamic_cast<MainWindow*>(window());
|
|
||||||
mainWnd->openFile(fileName.toStdString());
|
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "mainglwidget.h"
|
#include "mainglwidget.h"
|
||||||
#include <qevent.h>
|
|
||||||
#include <qmdisubwindow.h>
|
#include <qmdisubwindow.h>
|
||||||
#include <QBasicTimer>
|
#include <QBasicTimer>
|
||||||
|
|
||||||
|
@ -26,7 +25,4 @@ public:
|
||||||
|
|
||||||
void closeEvent(QCloseEvent *closeEvent) override;
|
void closeEvent(QCloseEvent *closeEvent) override;
|
||||||
void init();
|
void init();
|
||||||
protected:
|
|
||||||
void dragEnterEvent(QDragEnterEvent*) override;
|
|
||||||
void dropEvent(QDropEvent*) override;
|
|
||||||
};
|
};
|
|
@ -16,7 +16,7 @@
|
||||||
#include <qtextformat.h>
|
#include <qtextformat.h>
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "objects/script.h"
|
#include "objects/script.h"
|
||||||
#include "datatypes/variant.h"
|
#include "datatypes/meta.h"
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
#include <QStyleHints>
|
#include <QStyleHints>
|
||||||
|
|
||||||
|
@ -115,9 +115,9 @@ ScriptDocument::ScriptDocument(std::shared_ptr<Script> script, QWidget* parent):
|
||||||
setWindowTitle(QString::fromStdString(script->name));
|
setWindowTitle(QString::fromStdString(script->name));
|
||||||
|
|
||||||
// Add detector for script deletion to automatically close this document
|
// Add detector for script deletion to automatically close this document
|
||||||
scriptDeletionHandler = script->AncestryChanged->Connect([this, script](std::vector<Variant> args) {
|
scriptDeletionHandler = script->AncestryChanged->Connect([this, script](std::vector<Data::Variant> args) {
|
||||||
std::weak_ptr<Instance> child = args[0].get<InstanceRef>();
|
std::weak_ptr<Instance> child = args[0].get<Data::InstanceRef>();
|
||||||
std::weak_ptr<Instance> newParent = args[1].get<InstanceRef>();
|
std::weak_ptr<Instance> newParent = args[1].get<Data::InstanceRef>();
|
||||||
if (child.expired() || child.lock() != script || !newParent.expired()) return;
|
if (child.expired() || child.lock() != script || !newParent.expired()) return;
|
||||||
|
|
||||||
dynamic_cast<MainWindow*>(window())->closeScriptDocument(script);
|
dynamic_cast<MainWindow*>(window())->closeScriptDocument(script);
|
||||||
|
|
Loading…
Add table
Reference in a new issue