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