Compare commits
No commits in common. "10d69ce7ac10b490080eba674181af1b18c518ef" and "3a3b2d12c9d1fd7fa007cf6b54b42850ea7632d0" have entirely different histories.
10d69ce7ac
...
3a3b2d12c9
74 changed files with 1010 additions and 1567 deletions
Binary file not shown.
Before Width: | Height: | Size: 537 B |
Binary file not shown.
Before Width: | Height: | Size: 825 B |
Binary file not shown.
Before Width: | Height: | Size: 802 B |
Binary file not shown.
Before Width: | Height: | Size: 774 B |
|
@ -174,25 +174,6 @@ static bool hasMethod(CXCursor cur, std::string methodName) {
|
|||
return found;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -203,8 +184,6 @@ 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,8 +37,6 @@ 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,6 +9,10 @@
|
|||
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" },
|
||||
};
|
||||
|
||||
|
@ -26,36 +30,16 @@ 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
|
||||
|
@ -85,7 +69,7 @@ static void writeLuaTestArgument(std::ofstream& out, std::string type, int narg,
|
|||
}
|
||||
|
||||
static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
||||
std::string fqn = "" + state.name;
|
||||
std::string fqn = "Data::" + state.name;
|
||||
|
||||
// Collect all method names to account for overloaded functions
|
||||
std::map<std::string, std::vector<MethodAnalysis>> methods;
|
||||
|
@ -148,7 +132,11 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
|||
|
||||
// Return result
|
||||
if (methodImpl.returnType != "void") {
|
||||
out << " " << pushLuaValue(methodImpl.returnType, "result") << ";\n";
|
||||
std::string mappedType = MAPPED_TYPE[methodImpl.returnType];
|
||||
if (mappedType == "")
|
||||
out << " result.PushLuaValue(L);\n";
|
||||
else
|
||||
out << " " << mappedType << "(result).PushLuaValue(L);\n";
|
||||
}
|
||||
|
||||
if (methodImpl.returnType == "void")
|
||||
|
@ -217,7 +205,11 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
|||
|
||||
// Return result
|
||||
if (methodImpl.returnType != "void") {
|
||||
out << " " << pushLuaValue(methodImpl.returnType, "result") << ";\n";
|
||||
std::string mappedType = MAPPED_TYPE[methodImpl.returnType];
|
||||
if (mappedType == "")
|
||||
out << " result.PushLuaValue(L);\n";
|
||||
else
|
||||
out << " " << mappedType << "(result).PushLuaValue(L);\n";
|
||||
}
|
||||
|
||||
if (methodImpl.returnType == "void")
|
||||
|
@ -236,42 +228,45 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
|||
}
|
||||
|
||||
static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||
std::string fqn = state.name;
|
||||
std::string fqn = "Data::" + state.name;
|
||||
|
||||
out << "static int data_" << state.name << "_gc(lua_State*);\n"
|
||||
"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"
|
||||
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"
|
||||
" {NULL, NULL} /* end of array */\n"
|
||||
"};\n\n";
|
||||
|
||||
out << "void " << state.name << "::PushLuaValue(lua_State* L) const {\n"
|
||||
out << "void Data::" << state.name << "::PushLuaValue(lua_State* L) const {\n"
|
||||
" int n = lua_gettop(L);\n"
|
||||
|
||||
// " // 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, " << state.name << "_metatable);\n"
|
||||
" luaL_register(L, NULL, metatable);\n"
|
||||
|
||||
" lua_setmetatable(L, n+1);\n"
|
||||
"}\n\n";
|
||||
|
||||
|
||||
out << "result<Variant, LuaCastError> " << state.name << "::FromLuaValue(lua_State* L, int idx) {\n"
|
||||
out << "result<Data::Variant, LuaCastError> Data::" << state.name << "::FromLuaValue(lua_State* L, int idx) {\n"
|
||||
" " << fqn << "** userdata = (" << fqn << "**) luaL_testudata(L, idx, \"" << getMtName(state.name) << "\");\n"
|
||||
" if (userdata == nullptr)\n"
|
||||
" return LuaCastError(lua_typename(L, idx), \"" << state.name << "\");\n"
|
||||
" return Variant(**userdata);\n"
|
||||
" return Data::Variant(**userdata);\n"
|
||||
"}\n\n";
|
||||
|
||||
// Indexing methods and properties
|
||||
|
||||
out << "static int data_" << state.name << "_index(lua_State* L) {\n"
|
||||
out << "static int data_index(lua_State* L) {\n"
|
||||
" " << fqn << "* this_ = *(" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n"
|
||||
"\n"
|
||||
" std::string key(lua_tostring(L, 2));\n"
|
||||
|
@ -297,7 +292,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
|||
valueExpr = "this_->" + prop.backingSymbol + "()";
|
||||
|
||||
// This largely depends on the type
|
||||
out << " " << pushLuaValue(type, valueExpr) << ";\n";
|
||||
out << " " << type << "(" << valueExpr << ").PushLuaValue(L);\n";
|
||||
out << " return 1;\n";
|
||||
|
||||
out << " }";
|
||||
|
@ -323,7 +318,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
|||
|
||||
// ToString
|
||||
|
||||
out << "\nint data_" << state.name << "_tostring(lua_State* L) {\n"
|
||||
out << "\nint data_tostring(lua_State* L) {\n"
|
||||
" " << fqn << "* this_ = *(" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n"
|
||||
" lua_pushstring(L, std::string(this_->ToString()).c_str());\n"
|
||||
" return 1;\n"
|
||||
|
@ -331,7 +326,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
|||
|
||||
// Destructor
|
||||
|
||||
out << "\nint data_" << state.name << "_gc(lua_State* L) {\n"
|
||||
out << "\nint data_gc(lua_State* L) {\n"
|
||||
" " << fqn << "** userdata = (" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n"
|
||||
" delete *userdata;\n"
|
||||
" return 0;\n"
|
||||
|
@ -339,18 +334,15 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
|||
}
|
||||
|
||||
static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||
std::string fqn = state.name;
|
||||
std::string fqn = "Data::" + state.name;
|
||||
|
||||
// If there are no static methods or properties, no need to create a library
|
||||
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"
|
||||
out << "static int lib_index(lua_State*);\n"
|
||||
"static const struct luaL_Reg lib_metatable [] = {\n"
|
||||
" {\"__index\", lib_index},\n"
|
||||
" {NULL, NULL} /* end of array */\n"
|
||||
"};\n\n";
|
||||
|
||||
out << "void " << state.name << "::PushLuaLibrary(lua_State* L) {\n"
|
||||
out << "void Data::" << state.name << "::PushLuaLibrary(lua_State* L) {\n"
|
||||
" lua_getglobal(L, \"_G\");\n"
|
||||
" lua_pushstring(L, \"" << state.name << "\");\n"
|
||||
"\n"
|
||||
|
@ -358,7 +350,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_" << state.name << "_metatable);\n"
|
||||
" luaL_register(L, NULL, lib_metatable);\n"
|
||||
" lua_setmetatable(L, -2);\n"
|
||||
"\n"
|
||||
" lua_rawset(L, -3);\n"
|
||||
|
@ -367,7 +359,7 @@ static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
|||
|
||||
// Indexing methods and properties
|
||||
|
||||
out << "static int lib_" << state.name << "_index(lua_State* L) {\n"
|
||||
out << "static int lib_index(lua_State* L) {\n"
|
||||
" std::string key(lua_tostring(L, 2));\n"
|
||||
" lua_pop(L, 2);\n"
|
||||
"\n";
|
||||
|
@ -390,7 +382,7 @@ static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
|||
else if (prop.backingType == PropertyBackingType::Method)
|
||||
valueExpr = fqn + "::" + prop.backingSymbol + "()";
|
||||
|
||||
out << " " << pushLuaValue(type, valueExpr) << ";\n";
|
||||
out << " " << type << "(" << valueExpr << ").PushLuaValue(L);\n";
|
||||
out << " return 1;\n";
|
||||
|
||||
out << " }";
|
||||
|
@ -416,29 +408,22 @@ 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/variant.h\"\n";
|
||||
out << "#include \"datatypes/meta.h\"\n";
|
||||
out << "#include <pugixml.hpp>\n";
|
||||
out << "#include \"lua.h\"\n\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"
|
||||
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"
|
||||
<< "};\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);
|
||||
analyzeClasses(cursor, srcRoot, &dataAnlyState);
|
||||
data::analyzeClasses(cursor, srcRoot, &dataAnlyState);
|
||||
|
||||
fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it
|
||||
|
||||
|
@ -72,7 +72,7 @@ int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
|
|||
}
|
||||
|
||||
for (auto& [_, clazz] : dataAnlyState.classes) {
|
||||
writeCodeForClass(outStream, relpath, clazz);
|
||||
data::writeCodeForClass(outStream, relpath, clazz);
|
||||
}
|
||||
|
||||
outStream.close();
|
||||
|
|
|
@ -16,17 +16,13 @@ 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() }
|
||||
};
|
||||
|
@ -44,7 +40,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<int>()";
|
||||
return "(SurfaceType)(int)" + valueStr + ".get<Data::Int>()";
|
||||
}
|
||||
|
||||
std::string mappedType = MAPPED_TYPE[fieldType];
|
||||
|
@ -54,13 +50,13 @@ static std::string castFromVariant(std::string valueStr, std::string fieldType)
|
|||
static std::string castToVariant(std::string valueStr, std::string fieldType) {
|
||||
// Manual exception for now, enums will get their own system eventually
|
||||
if (fieldType == "SurfaceType") {
|
||||
return "(int)" + valueStr;
|
||||
return "Data::Int((int)" + valueStr + ")";
|
||||
}
|
||||
|
||||
// std::shared_ptr<Instance>
|
||||
// InstanceRef
|
||||
std::string subtype = parseWeakPtr(fieldType);
|
||||
if (!subtype.empty()) {
|
||||
return "Variant(" + valueStr + ".expired() ? InstanceRef() : InstanceRef(std::dynamic_pointer_cast<Instance>(" + valueStr + ".lock())))";
|
||||
return "Data::Variant(" + valueStr + ".expired() ? Data::InstanceRef() : Data::InstanceRef(std::dynamic_pointer_cast<Instance>(" + valueStr + ".lock())))";
|
||||
}
|
||||
|
||||
std::string mappedType = MAPPED_TYPE[fieldType];
|
||||
|
@ -71,13 +67,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, Variant value) {";
|
||||
out << "fallible<MemberNotFound, AssignToReadOnlyMember> " << state.name << "::InternalSetPropertyValue(std::string name, Data::Variant value) {";
|
||||
|
||||
out << "\n ";
|
||||
bool first = true;
|
||||
for (auto& prop : state.properties) {
|
||||
out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {";
|
||||
// std::shared_ptr<Instance>
|
||||
// InstanceRef
|
||||
std::string subtype = parseWeakPtr(prop.backingFieldType);
|
||||
|
||||
if (prop.flags & PropertyFlag_Readonly) {
|
||||
|
@ -87,7 +83,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<InstanceRef>();"
|
||||
out << "\n std::weak_ptr<Instance> ref = value.get<Data::InstanceRef>();"
|
||||
<< "\n this->" << prop.fieldName << " = ref.expired() ? std::weak_ptr<" << subtype << ">() : std::dynamic_pointer_cast<" << subtype << ">(ref.lock());";
|
||||
} else {
|
||||
out << "\n this->" << prop.fieldName << " = " << castFromVariant("value", prop.backingFieldType) << ";";
|
||||
|
@ -132,7 +128,7 @@ static void writePropertyUpdateHandler(std::ofstream& out, ClassAnalysis state)
|
|||
}
|
||||
|
||||
static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||
out << "result<Variant, MemberNotFound> " << state.name << "::InternalGetPropertyValue(std::string name) {";
|
||||
out << "result<Data::Variant, MemberNotFound> " << state.name << "::InternalGetPropertyValue(std::string name) {";
|
||||
|
||||
out << "\n ";
|
||||
bool first = true;
|
||||
|
@ -140,11 +136,11 @@ static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
|||
out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {";
|
||||
|
||||
if (prop.cframeMember == CFrameMember_Position) {
|
||||
out << "\n return Variant(" << prop.fieldName << ".Position());";
|
||||
out << "\n return Data::Variant(" << prop.fieldName << ".Position());";
|
||||
} else if (prop.cframeMember == CFrameMember_Rotation) {
|
||||
out << "\n return Variant(" << prop.fieldName << ".ToEulerAnglesXYZ());";
|
||||
out << "\n return Data::Variant(" << prop.fieldName << ".ToEulerAnglesXYZ());";
|
||||
} else {
|
||||
out << "\n return Variant(" << castToVariant(prop.fieldName, prop.backingFieldType) << ");";
|
||||
out << "\n return Data::Variant(" << castToVariant(prop.fieldName, prop.backingFieldType) << ");";
|
||||
}
|
||||
|
||||
out << "\n }";
|
||||
|
@ -157,7 +153,7 @@ static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
|||
for (auto& signal : state.signals) {
|
||||
out << (first ? "" : " else ") << "if (name == \"" << signal.name << "\") {";
|
||||
|
||||
out << "\n return Variant(SignalRef(" << signal.sourceFieldName << "));";
|
||||
out << "\n return Data::Variant(Data::SignalRef(" << signal.sourceFieldName << "));";
|
||||
|
||||
out << "\n }";
|
||||
first = false;
|
||||
|
@ -190,10 +186,10 @@ static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
|
|||
for (auto& prop : state.properties) {
|
||||
out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {";
|
||||
|
||||
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 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 strFlags;
|
||||
if (prop.flags & PropertyFlag_Readonly)
|
||||
|
@ -210,7 +206,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 { &" << typeInfo << ", " << strFlags << ", " << category << " };";
|
||||
out << "\n return PropertyMeta { &" << type << "::TYPE, " << strFlags << ", " << category << " };";
|
||||
|
||||
out << "\n }";
|
||||
first = false;
|
||||
|
@ -224,8 +220,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 << " };";
|
||||
|
||||
|
@ -256,8 +252,7 @@ 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/variant.h\"\n\n";
|
||||
out << "#include \"datatypes/primitives.h\"\n\n";
|
||||
out << "#include \"datatypes/meta.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 (std::shared_ptr<Instance> inst : gWorkspace()->GetChildren()) {
|
||||
for (InstanceRef 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/variant.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include "common.h"
|
||||
#include <memory>
|
||||
|
||||
|
@ -14,11 +14,11 @@ std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
|||
Handles editorToolHandles;
|
||||
|
||||
|
||||
std::vector<std::shared_ptr<Instance>> currentSelection;
|
||||
std::vector<InstanceRefWeak> currentSelection;
|
||||
std::vector<SelectionUpdateHandler> selectionUpdateListeners;
|
||||
std::vector<PropertyUpdateHandler> propertyUpdatelisteners;
|
||||
|
||||
void setSelection(std::vector<std::shared_ptr<Instance>> newSelection, bool fromExplorer) {
|
||||
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer) {
|
||||
for (SelectionUpdateHandler handler : selectionUpdateListeners) {
|
||||
handler(currentSelection, newSelection, fromExplorer);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ void setSelection(std::vector<std::shared_ptr<Instance>> newSelection, bool from
|
|||
currentSelection = newSelection;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Instance>> getSelection() {
|
||||
const std::vector<InstanceRefWeak> getSelection() {
|
||||
return currentSelection;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ void addSelectionListener(SelectionUpdateHandler handler) {
|
|||
selectionUpdateListeners.push_back(handler);
|
||||
}
|
||||
|
||||
void sendPropertyUpdatedSignal(std::shared_ptr<Instance> instance, std::string property, Variant newValue) {
|
||||
void sendPropertyUpdatedSignal(InstanceRef instance, std::string property, Data::Variant newValue) {
|
||||
for (PropertyUpdateHandler handler : propertyUpdatelisteners) {
|
||||
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(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;
|
||||
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;
|
||||
|
||||
// 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<std::shared_ptr<Instance>> newSelection, bool fromExplorer = false);
|
||||
const std::vector<std::shared_ptr<Instance>> getSelection();
|
||||
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer = false);
|
||||
const std::vector<InstanceRefWeak> getSelection();
|
||||
void addSelectionListener(SelectionUpdateHandler handler);
|
||||
|
||||
void sendPropertyUpdatedSignal(std::shared_ptr<Instance> instance, std::string property, Variant newValue);
|
||||
void sendPropertyUpdatedSignal(InstanceRef instance, std::string property, Data::Variant newValue);
|
||||
void addPropertyUpdateListener(PropertyUpdateHandler handler);
|
|
@ -26,7 +26,8 @@
|
|||
|
||||
#define AUTOGEN_PREAMBLE_DATA \
|
||||
public: \
|
||||
static const TypeDesc TYPE; \
|
||||
virtual void PushLuaValue(lua_State*) const; \
|
||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int idx); \
|
||||
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); \
|
||||
private:
|
||||
|
|
162
core/src/datatypes/base.cpp
Normal file
162
core/src/datatypes/base.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
#include "base.h"
|
||||
#include "error/data.h"
|
||||
#include "meta.h"
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <pugixml.hpp>
|
||||
#include "lua.h"
|
||||
|
||||
#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE, TYPE_NAME) Data::CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : value(in) {} \
|
||||
Data::CLASS_NAME::~CLASS_NAME() = default; \
|
||||
Data::CLASS_NAME::operator const WRAPPED_TYPE() const { return value; } \
|
||||
const Data::TypeInfo Data::CLASS_NAME::TYPE = { \
|
||||
.name = TYPE_NAME, \
|
||||
.deserializer = &Data::CLASS_NAME::Deserialize, \
|
||||
.fromString = &Data::CLASS_NAME::FromString, \
|
||||
.fromLuaValue = &Data::CLASS_NAME::FromLuaValue, \
|
||||
}; \
|
||||
const Data::TypeInfo& Data::CLASS_NAME::GetType() const { return Data::CLASS_NAME::TYPE; }; \
|
||||
void Data::CLASS_NAME::Serialize(pugi::xml_node node) const { node.text().set(std::string(this->ToString())); }
|
||||
|
||||
Data::Base::~Base() {};
|
||||
|
||||
Data::Null::Null() {};
|
||||
Data::Null::~Null() = default;
|
||||
const Data::TypeInfo Data::Null::TYPE = {
|
||||
.name = "null",
|
||||
.deserializer = &Data::Null::Deserialize,
|
||||
};
|
||||
const Data::TypeInfo& Data::Null::GetType() const { return Data::Null::TYPE; };
|
||||
|
||||
const Data::String Data::Null::ToString() const {
|
||||
return Data::String("null");
|
||||
}
|
||||
|
||||
void Data::Null::Serialize(pugi::xml_node node) const {
|
||||
node.text().set("null");
|
||||
}
|
||||
|
||||
Data::Variant Data::Null::Deserialize(pugi::xml_node node) {
|
||||
return Data::Null();
|
||||
}
|
||||
|
||||
void Data::Null::PushLuaValue(lua_State* L) const {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::Null::FromLuaValue(lua_State* L, int idx) {
|
||||
return Data::Variant(Data::Null());
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
IMPL_WRAPPER_CLASS(Bool, bool, "bool")
|
||||
IMPL_WRAPPER_CLASS(Int, int, "int")
|
||||
IMPL_WRAPPER_CLASS(Float, float, "float")
|
||||
IMPL_WRAPPER_CLASS(String, std::string, "string")
|
||||
|
||||
// ToString
|
||||
|
||||
const Data::String Data::Bool::ToString() const {
|
||||
return Data::String(value ? "true" : "false");
|
||||
}
|
||||
|
||||
const Data::String Data::Int::ToString() const {
|
||||
return Data::String(std::to_string(value));
|
||||
}
|
||||
|
||||
const Data::String Data::Float::ToString() const {
|
||||
std::stringstream stream;
|
||||
stream << std::noshowpoint << value;
|
||||
return Data::String(stream.str());
|
||||
}
|
||||
|
||||
const Data::String Data::String::ToString() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Deserialize
|
||||
|
||||
Data::Variant Data::Bool::Deserialize(pugi::xml_node node) {
|
||||
return Data::Bool(node.text().as_bool());
|
||||
}
|
||||
|
||||
Data::Variant Data::Int::Deserialize(pugi::xml_node node) {
|
||||
return Data::Int(node.text().as_int());
|
||||
}
|
||||
|
||||
Data::Variant Data::Float::Deserialize(pugi::xml_node node) {
|
||||
return Data::Float(node.text().as_float());
|
||||
}
|
||||
|
||||
Data::Variant Data::String::Deserialize(pugi::xml_node node) {
|
||||
return Data::String(node.text().as_string());
|
||||
}
|
||||
|
||||
// FromString
|
||||
|
||||
std::optional<Data::Variant> Data::Bool::FromString(std::string string) {
|
||||
return Data::Bool(string[0] == 't' || string[0] == 'T' || string[0] == '1' || string[0] == 'y' || string[0] == 'Y');
|
||||
}
|
||||
|
||||
std::optional<Data::Variant> Data::Int::FromString(std::string string) {
|
||||
char* endPos;
|
||||
int value = (int)std::strtol(string.c_str(), &endPos, 10);
|
||||
if (endPos == string.c_str()) return std::nullopt;
|
||||
return Data::Int(value);
|
||||
}
|
||||
|
||||
std::optional<Data::Variant> Data::Float::FromString(std::string string) {
|
||||
char* endPos;
|
||||
float value = std::strtof(string.c_str(), &endPos);
|
||||
if (endPos == string.c_str()) return std::nullopt;
|
||||
return Data::Float(value);
|
||||
}
|
||||
|
||||
std::optional<Data::Variant> Data::String::FromString(std::string string) {
|
||||
return Data::String(string);
|
||||
}
|
||||
|
||||
// PushLuaValue
|
||||
|
||||
void Data::Bool::PushLuaValue(lua_State* L) const {
|
||||
lua_pushboolean(L, *this);
|
||||
}
|
||||
|
||||
void Data::Int::PushLuaValue(lua_State* L) const {
|
||||
lua_pushinteger(L, *this);
|
||||
}
|
||||
|
||||
void Data::Float::PushLuaValue(lua_State* L) const {
|
||||
lua_pushnumber(L, *this);
|
||||
}
|
||||
|
||||
void Data::String::PushLuaValue(lua_State* L) const {
|
||||
lua_pushstring(L, value.c_str());
|
||||
}
|
||||
|
||||
// FromLuaValue
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::Bool::FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_isboolean(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "boolean");
|
||||
return Data::Variant(Data::Bool(lua_toboolean(L, idx)));
|
||||
}
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::Int::FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_isnumber(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "integer");
|
||||
return Data::Variant(Data::Int((int)lua_tonumber(L, idx)));
|
||||
}
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::Float::FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_isnumber(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "float");
|
||||
return Data::Variant(Data::Float((float)lua_tonumber(L, idx)));
|
||||
}
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::String::FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_tostring(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "string");
|
||||
return Data::Variant(Data::String(lua_tostring(L, idx)));
|
||||
}
|
|
@ -10,39 +10,67 @@ extern "C" { typedef struct lua_State lua_State; }
|
|||
|
||||
namespace pugi { class xml_node; };
|
||||
|
||||
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;
|
||||
#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 Enum;
|
||||
struct InstanceType;
|
||||
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;
|
||||
|
||||
// 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
|
||||
struct TypeInfo {
|
||||
std::string name;
|
||||
Deserializer deserializer;
|
||||
FromString fromString;
|
||||
FromLuaValue fromLuaValue;
|
||||
};
|
||||
|
||||
inline TypeMeta(const TypeDesc* descriptor) : descriptor(descriptor) {}
|
||||
TypeMeta(Enum*);
|
||||
TypeMeta(InstanceType*);
|
||||
};
|
||||
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
|
|
@ -1,24 +1,23 @@
|
|||
#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/variant.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include <pugixml.hpp>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
// #include "variant.h" // IWYU pragma: keep
|
||||
// #include "meta.h" // IWYU pragma: keep
|
||||
|
||||
const CFrame CFrame::IDENTITY(glm::vec3(0, 0, 0), glm::mat3(1.f));
|
||||
const CFrame CFrame::YToZ(glm::vec3(0, 0, 0), glm::mat3(glm::vec3(1, 0, 0), glm::vec3(0, 0, 1), glm::vec3(0, 1, 0)));
|
||||
const Data::CFrame Data::CFrame::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)));
|
||||
|
||||
CFrame::CFrame() : CFrame::CFrame(glm::vec3(0, 0, 0), glm::mat3(1.f)) {}
|
||||
Data::CFrame::CFrame() : Data::CFrame::CFrame(glm::vec3(0, 0, 0), glm::mat3(1.f)) {}
|
||||
|
||||
CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22)
|
||||
Data::CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22)
|
||||
: translation(x, y, z)
|
||||
, rotation({
|
||||
// { R00, R01, R02 },
|
||||
|
@ -30,17 +29,17 @@ CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float
|
|||
}) {
|
||||
}
|
||||
|
||||
CFrame::CFrame(glm::vec3 translation, glm::mat3 rotation)
|
||||
Data::CFrame::CFrame(glm::vec3 translation, glm::mat3 rotation)
|
||||
: translation(translation)
|
||||
, rotation(rotation) {
|
||||
}
|
||||
|
||||
CFrame::CFrame(Vector3 position, glm::quat quat)
|
||||
Data::CFrame::CFrame(Vector3 position, glm::quat quat)
|
||||
: translation(position)
|
||||
, rotation(quat) {
|
||||
}
|
||||
|
||||
CFrame::CFrame(const rp::Transform& transform) : CFrame::CFrame(rpToGlm(transform.getPosition()), rpToGlm(transform.getOrientation())) {
|
||||
Data::CFrame::CFrame(const rp::Transform& transform) : Data::CFrame::CFrame(rpToGlm(transform.getPosition()), rpToGlm(transform.getOrientation())) {
|
||||
}
|
||||
|
||||
glm::mat3 lookAt(Vector3 position, Vector3 lookAt, Vector3 up) {
|
||||
|
@ -53,35 +52,35 @@ glm::mat3 lookAt(Vector3 position, Vector3 lookAt, Vector3 up) {
|
|||
return { s, u, -f };
|
||||
}
|
||||
|
||||
CFrame::CFrame(Vector3 position, Vector3 lookAt, Vector3 up)
|
||||
Data::CFrame::CFrame(Vector3 position, Vector3 lookAt, Vector3 up)
|
||||
: translation(position)
|
||||
, rotation(::lookAt(position, lookAt, up)) {
|
||||
}
|
||||
|
||||
CFrame::~CFrame() = default;
|
||||
Data::CFrame::~CFrame() = default;
|
||||
|
||||
const std::string CFrame::ToString() const {
|
||||
const Data::String Data::CFrame::ToString() const {
|
||||
return std::to_string(X()) + ", " + std::to_string(Y()) + ", " + std::to_string(Z());
|
||||
}
|
||||
|
||||
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::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::pointAligned(Vector3 position, Vector3 toward, Vector3 up, Vector3 right) {
|
||||
return CFrame(position, position + toward, (abs(toward.Dot(up)) > 0.999) ? right : up);
|
||||
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::operator glm::mat4() const {
|
||||
Data::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);
|
||||
}
|
||||
|
||||
CFrame::operator rp::Transform() const {
|
||||
Data::CFrame::operator rp::Transform() const {
|
||||
return rp::Transform(glmToRp(translation), glmToRp(rotation));
|
||||
}
|
||||
|
||||
Vector3 CFrame::ToEulerAnglesXYZ() {
|
||||
Vector3 Data::CFrame::ToEulerAnglesXYZ() {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
|
@ -89,37 +88,37 @@ Vector3 CFrame::ToEulerAnglesXYZ() {
|
|||
return Vector3(x, y, z);
|
||||
}
|
||||
|
||||
CFrame CFrame::FromEulerAnglesXYZ(Vector3 vector) {
|
||||
Data::CFrame Data::CFrame::FromEulerAnglesXYZ(Vector3 vector) {
|
||||
glm::mat3 mat = glm::eulerAngleXYZ(vector.X(), vector.Y(), vector.Z());
|
||||
return CFrame((glm::vec3)Vector3::ZERO, mat);
|
||||
return Data::CFrame((glm::vec3)Vector3::ZERO, mat);
|
||||
}
|
||||
|
||||
CFrame CFrame::Inverse() const {
|
||||
Data::CFrame Data::CFrame::Inverse() const {
|
||||
return CFrame { -translation * glm::transpose(glm::inverse(rotation)), glm::inverse(rotation) };
|
||||
}
|
||||
|
||||
|
||||
// Operators
|
||||
|
||||
CFrame CFrame::operator *(CFrame otherFrame) const {
|
||||
Data::CFrame Data::CFrame::operator *(Data::CFrame otherFrame) const {
|
||||
return CFrame { this->translation + this->rotation * otherFrame.translation, this->rotation * otherFrame.rotation };
|
||||
}
|
||||
|
||||
Vector3 CFrame::operator *(Vector3 vector) const {
|
||||
Vector3 Data::CFrame::operator *(Vector3 vector) const {
|
||||
return this->translation + this->rotation * vector;
|
||||
}
|
||||
|
||||
CFrame CFrame::operator +(Vector3 vector) const {
|
||||
Data::CFrame Data::CFrame::operator +(Vector3 vector) const {
|
||||
return CFrame { this->translation + glm::vec3(vector), this->rotation };
|
||||
}
|
||||
|
||||
CFrame CFrame::operator -(Vector3 vector) const {
|
||||
Data::CFrame Data::CFrame::operator -(Vector3 vector) const {
|
||||
return *this + -vector;
|
||||
}
|
||||
|
||||
// Serialization
|
||||
|
||||
void CFrame::Serialize(pugi::xml_node node) const {
|
||||
void Data::CFrame::Serialize(pugi::xml_node node) const {
|
||||
node.append_child("X").text().set(std::to_string(this->X()));
|
||||
node.append_child("Y").text().set(std::to_string(this->Y()));
|
||||
node.append_child("Z").text().set(std::to_string(this->Z()));
|
||||
|
@ -135,8 +134,8 @@ void CFrame::Serialize(pugi::xml_node node) const {
|
|||
}
|
||||
|
||||
|
||||
result<CFrame, DataParseError> CFrame::Deserialize(pugi::xml_node node) {
|
||||
return CFrame(
|
||||
Data::Variant Data::CFrame::Deserialize(pugi::xml_node node) {
|
||||
return Data::CFrame(
|
||||
node.child("X").text().as_float(),
|
||||
node.child("Y").text().as_float(),
|
||||
node.child("Z").text().as_float(),
|
||||
|
|
|
@ -3,69 +3,72 @@
|
|||
#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; };
|
||||
|
||||
class DEF_DATA_(name="CoordinateFrame") CFrame {
|
||||
AUTOGEN_PREAMBLE_DATA
|
||||
namespace Data {
|
||||
class DEF_DATA_(name="CoordinateFrame") CFrame : public Base {
|
||||
AUTOGEN_PREAMBLE_DATA
|
||||
|
||||
glm::vec3 translation;
|
||||
glm::mat3 rotation;
|
||||
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();
|
||||
|
||||
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();
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
DEF_DATA_PROP static const CFrame IDENTITY;
|
||||
static const CFrame YToZ;
|
||||
|
||||
DEF_DATA_PROP static const CFrame IDENTITY;
|
||||
static const CFrame YToZ;
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node parent) const override;
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
|
||||
virtual const std::string ToString() const;
|
||||
virtual void Serialize(pugi::xml_node parent) const;
|
||||
static result<CFrame, DataParseError> Deserialize(pugi::xml_node node);
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
operator glm::mat4() const;
|
||||
operator reactphysics3d::Transform() const;
|
||||
|
||||
operator glm::mat4() const;
|
||||
operator reactphysics3d::Transform() const;
|
||||
//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; }
|
||||
|
||||
//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_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); }
|
||||
|
||||
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); }
|
||||
DEF_DATA_METHOD Vector3 ToEulerAnglesXYZ();
|
||||
DEF_DATA_METHOD static CFrame FromEulerAnglesXYZ(Vector3);
|
||||
|
||||
DEF_DATA_METHOD Vector3 ToEulerAnglesXYZ();
|
||||
DEF_DATA_METHOD static CFrame FromEulerAnglesXYZ(Vector3);
|
||||
// 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;
|
||||
};
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
||||
using Data::CFrame;
|
|
@ -1,22 +1,21 @@
|
|||
#include "color3.h"
|
||||
#include "datatypes/variant.h"
|
||||
#include "error/data.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include <pugixml.hpp>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
Color3::Color3(float r, float g, float b) : r(std::clamp(r, 0.f, 1.f)), g(std::clamp(g, 0.f, 1.f)), b(std::clamp(b, 0.f, 1.f)) {};
|
||||
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(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() = default;
|
||||
Data::Color3::~Color3() = default;
|
||||
|
||||
const std::string Color3::ToString() const {
|
||||
const Data::String Data::Color3::ToString() const {
|
||||
return std::to_string(int(r*256)) + ", " + std::to_string(int(g*256)) + ", " + std::to_string(int(b*256));
|
||||
}
|
||||
|
||||
Color3::operator glm::vec3() const { return glm::vec3(r, g, b); };
|
||||
Data::Color3::operator glm::vec3() const { return glm::vec3(r, g, b); };
|
||||
|
||||
std::string Color3::ToHex() const {
|
||||
std::string Data::Color3::ToHex() const {
|
||||
std::stringstream ss;
|
||||
ss << "FF" << std::hex << std::uppercase << std::setfill('0')
|
||||
<< std::setw(2) << int(r*255)
|
||||
|
@ -26,7 +25,7 @@ std::string Color3::ToHex() const {
|
|||
}
|
||||
|
||||
|
||||
Color3 Color3::FromHex(std::string hex) {
|
||||
Data::Color3 Data::Color3::FromHex(std::string hex) {
|
||||
float r = float(std::stoi(hex.substr(2, 2), nullptr, 16)) / 255;
|
||||
float g = float(std::stoi(hex.substr(4, 2), nullptr, 16)) / 255;
|
||||
float b = float(std::stoi(hex.substr(6, 2), nullptr, 16)) / 255;
|
||||
|
@ -36,10 +35,10 @@ Color3 Color3::FromHex(std::string hex) {
|
|||
|
||||
// Serialization
|
||||
|
||||
void Color3::Serialize(pugi::xml_node node) const {
|
||||
void Data::Color3::Serialize(pugi::xml_node node) const {
|
||||
node.text().set(this->ToHex());
|
||||
}
|
||||
|
||||
result<Color3, DataParseError> Color3::Deserialize(pugi::xml_node node) {
|
||||
Data::Variant Data::Color3::Deserialize(pugi::xml_node node) {
|
||||
return Color3::FromHex(node.text().get());
|
||||
}
|
|
@ -2,33 +2,36 @@
|
|||
|
||||
#include "base.h"
|
||||
#include "datatypes/annotation.h"
|
||||
#include "error/data.h"
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
|
||||
class DEF_DATA Color3 {
|
||||
AUTOGEN_PREAMBLE_DATA
|
||||
namespace Data {
|
||||
class DEF_DATA Color3 : public Base {
|
||||
AUTOGEN_PREAMBLE_DATA
|
||||
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
|
||||
public:
|
||||
DEF_DATA_CTOR Color3(float r, float g, float b);
|
||||
Color3(const glm::vec3&);
|
||||
~Color3();
|
||||
|
||||
public:
|
||||
DEF_DATA_CTOR Color3(float r, float g, float b);
|
||||
Color3(const glm::vec3&);
|
||||
~Color3();
|
||||
DEF_DATA_METHOD static Color3 FromHex(std::string hex);
|
||||
|
||||
DEF_DATA_METHOD static Color3 FromHex(std::string hex);
|
||||
virtual const Data::String ToString() const override;
|
||||
DEF_DATA_METHOD std::string ToHex() const;
|
||||
virtual void Serialize(pugi::xml_node node) const override;
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
|
||||
virtual const std::string ToString() const;
|
||||
DEF_DATA_METHOD std::string ToHex() const;
|
||||
virtual void Serialize(pugi::xml_node node) const;
|
||||
static result<Color3, DataParseError> Deserialize(pugi::xml_node node);
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
operator glm::vec3() const;
|
||||
|
||||
operator glm::vec3() const;
|
||||
DEF_DATA_PROP inline float R() const { return r; }
|
||||
DEF_DATA_PROP inline float G() const { return g; }
|
||||
DEF_DATA_PROP inline float B() const { return b; }
|
||||
};
|
||||
}
|
||||
|
||||
DEF_DATA_PROP inline float R() const { return r; }
|
||||
DEF_DATA_PROP inline float G() const { return g; }
|
||||
DEF_DATA_PROP inline float B() const { return b; }
|
||||
};
|
||||
using Data::Color3;
|
|
@ -1,66 +0,0 @@
|
|||
#include "enum.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/variant.h"
|
||||
#include "error/data.h"
|
||||
#include <pugixml.hpp>
|
||||
|
||||
TypeMeta::TypeMeta(Enum* enum_) : enum_(enum_), descriptor(&EnumItem::TYPE) {}
|
||||
|
||||
Enum::Enum(_EnumData* data) : data(data) {}
|
||||
|
||||
std::vector<EnumItem> Enum::GetEnumItems() const {
|
||||
std::vector<EnumItem> enumItems;
|
||||
|
||||
for (int i = 0; i < data->count; i++) {
|
||||
enumItems.push_back(EnumItem(data, data->values[i].second, data->values[i].first));
|
||||
}
|
||||
|
||||
return enumItems;
|
||||
}
|
||||
|
||||
std::optional<EnumItem> Enum::FromName(std::string name) const {
|
||||
for (int i = 0; i < data->count; i++) {
|
||||
if (data->values[i].second == name)
|
||||
return EnumItem(data, name, data->values[i].first);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<EnumItem> Enum::FromValue(int value) const {
|
||||
for (int i = 0; i < data->count; i++) {
|
||||
if (data->values[i].first == value)
|
||||
return EnumItem(data, data->values[i].second, value);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
EnumItem::EnumItem(_EnumData* parentData, std::string name, int value) : parentData(parentData), name(name), value(value) {}
|
||||
|
||||
//
|
||||
|
||||
std::string Enum::ToString() const {
|
||||
return "Enum." + this->data->name;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
std::string EnumItem::ToString() const {
|
||||
return "Enum." + parentData->name + "." + name;
|
||||
}
|
||||
|
||||
void EnumItem::Serialize(pugi::xml_node node) const {
|
||||
node.set_name("token");
|
||||
node.text().set(value);
|
||||
}
|
||||
|
||||
result<EnumItem, DataParseError> EnumItem::Deserialize(pugi::xml_node node, const TypeMeta info) {
|
||||
auto result = info.enum_->FromValue(node.text().as_int());
|
||||
if (result.has_value()) return result.value();
|
||||
return DataParseError(node.text().as_string(), "EnumItem");
|
||||
}
|
||||
|
||||
result<EnumItem, DataParseError> EnumItem::FromString(std::string string, const TypeMeta info) {
|
||||
auto result = info.enum_->FromName(string);
|
||||
if (result.has_value()) return result.value();
|
||||
return DataParseError(string, "EnumItem");
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "datatypes/annotation.h"
|
||||
#include "error/data.h"
|
||||
#include "lua.h"
|
||||
|
||||
struct _EnumData {
|
||||
std::pair<int, std::string>* values;
|
||||
std::string name;
|
||||
int count;
|
||||
};
|
||||
|
||||
class EnumItem;
|
||||
|
||||
class DEF_DATA Enum {
|
||||
_EnumData* data;
|
||||
public:
|
||||
Enum(_EnumData*);
|
||||
|
||||
static const TypeDesc TYPE;
|
||||
|
||||
inline _EnumData* InternalType() const { return this->data; };
|
||||
std::vector<EnumItem> GetEnumItems() const;
|
||||
std::optional<EnumItem> FromName(std::string) const;
|
||||
std::optional<EnumItem> FromValue(int) const;
|
||||
|
||||
std::string ToString() const;
|
||||
void PushLuaValue(lua_State*) const;
|
||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int);
|
||||
};
|
||||
|
||||
class DEF_DATA EnumItem {
|
||||
_EnumData* parentData;
|
||||
std::string name;
|
||||
int value;
|
||||
public:
|
||||
EnumItem(_EnumData*, std::string, int);
|
||||
|
||||
static const TypeDesc TYPE;
|
||||
|
||||
inline std::string Name() const { return this->name; }
|
||||
inline int Value() const { return this->value; }
|
||||
inline Enum EnumType() const { return Enum(this->parentData); }
|
||||
|
||||
static result<EnumItem, DataParseError> FromString(std::string, const TypeMeta);
|
||||
std::string ToString() const;
|
||||
void Serialize(pugi::xml_node) const;
|
||||
static result<EnumItem, DataParseError> Deserialize(pugi::xml_node, const TypeMeta);
|
||||
void PushLuaValue(lua_State*) const;
|
||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int);
|
||||
};
|
48
core/src/datatypes/meta.cpp
Normal file
48
core/src/datatypes/meta.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include "meta.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/ref.h"
|
||||
#include "logger.h"
|
||||
#include "panic.h"
|
||||
#include <pugixml.hpp>
|
||||
#include <variant>
|
||||
|
||||
Data::String Data::Variant::ToString() const {
|
||||
return std::visit([](auto&& it) {
|
||||
return it.ToString();
|
||||
}, this->wrapped);
|
||||
}
|
||||
|
||||
void Data::Variant::Serialize(pugi::xml_node node) const {
|
||||
std::visit([&](auto&& it) {
|
||||
it.Serialize(node);
|
||||
}, this->wrapped);
|
||||
}
|
||||
|
||||
void Data::Variant::PushLuaValue(lua_State* state) const {
|
||||
return std::visit([&](auto&& it) {
|
||||
return it.PushLuaValue(state);
|
||||
}, this->wrapped);
|
||||
}
|
||||
|
||||
Data::Variant Data::Variant::Deserialize(pugi::xml_node node) {
|
||||
if (Data::TYPE_MAP.count(node.name()) == 0) {
|
||||
Logger::fatalErrorf("Unknown type for property: '%s'", node.name());
|
||||
panic();
|
||||
}
|
||||
|
||||
const Data::TypeInfo* type = Data::TYPE_MAP[node.name()];
|
||||
return type->deserializer(node);
|
||||
}
|
||||
|
||||
std::map<std::string, const Data::TypeInfo*> Data::TYPE_MAP = {
|
||||
{ "null", &Data::Null::TYPE },
|
||||
{ "bool", &Data::Bool::TYPE },
|
||||
{ "int", &Data::Int::TYPE },
|
||||
{ "float", &Data::Float::TYPE },
|
||||
{ "string", &Data::String::TYPE },
|
||||
{ "Vector3", &Data::Vector3::TYPE },
|
||||
{ "CoordinateFrame", &Data::CFrame::TYPE },
|
||||
{ "Color3", &Data::Color3::TYPE },
|
||||
{ "Ref", &Data::InstanceRef::TYPE },
|
||||
};
|
49
core/src/datatypes/meta.h
Normal file
49
core/src/datatypes/meta.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
#include <map>
|
||||
#include "base.h"
|
||||
#include "datatypes/color3.h"
|
||||
#include "datatypes/ref.h"
|
||||
#include "datatypes/signal.h"
|
||||
#include "vector.h"
|
||||
#include "cframe.h"
|
||||
|
||||
// #define __VARIANT_TYPE std::variant< \
|
||||
// Null, \
|
||||
// Bool, \
|
||||
// Int, \
|
||||
// Float, \
|
||||
// String \
|
||||
// >
|
||||
|
||||
namespace Data {
|
||||
typedef std::variant<
|
||||
Null,
|
||||
Bool,
|
||||
Int,
|
||||
Float,
|
||||
String,
|
||||
Vector3,
|
||||
CFrame,
|
||||
Color3,
|
||||
InstanceRef,
|
||||
SignalRef,
|
||||
SignalConnectionRef
|
||||
> __VARIANT_TYPE;
|
||||
|
||||
class Variant {
|
||||
__VARIANT_TYPE wrapped;
|
||||
public:
|
||||
template <typename T> Variant(T obj) : wrapped(obj) {}
|
||||
template <typename T> T get() { return std::get<T>(wrapped); }
|
||||
Data::String ToString() const;
|
||||
|
||||
void Serialize(pugi::xml_node node) const;
|
||||
void PushLuaValue(lua_State* state) const;
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
};
|
||||
|
||||
// Map of all data types to their type names
|
||||
extern std::map<std::string, const TypeInfo*> TYPE_MAP;
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
#include "primitives.h"
|
||||
#include "error/data.h"
|
||||
#include "variant.h"
|
||||
#include <pugixml.hpp>
|
||||
#include "lua.h"
|
||||
#include <sstream>
|
||||
|
||||
// null
|
||||
|
||||
void Null_Serialize(Variant self, pugi::xml_node node) {
|
||||
node.text().set("null");
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> Null_Deserialize(pugi::xml_node node, const TypeMeta) {
|
||||
return std::monostate();
|
||||
}
|
||||
|
||||
const std::string Null_ToString(Variant self) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> Null_FromString(std::string string, const TypeMeta) {
|
||||
return std::monostate();
|
||||
}
|
||||
|
||||
void Null_PushLuaValue(Variant self, lua_State* L) {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
result<Variant, LuaCastError> Null_FromLuaValue(lua_State* L, int idx) {
|
||||
return Variant(std::monostate());
|
||||
}
|
||||
|
||||
const TypeDesc NULL_TYPE {
|
||||
"null",
|
||||
Null_Serialize,
|
||||
Null_Deserialize,
|
||||
Null_ToString,
|
||||
Null_FromString,
|
||||
Null_PushLuaValue,
|
||||
Null_FromLuaValue,
|
||||
};
|
||||
|
||||
// /null
|
||||
|
||||
// bool
|
||||
|
||||
void Bool_Serialize(Variant self, pugi::xml_node node) {
|
||||
node.text().set(self.get<bool>() ? "true" : "false");
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> Bool_Deserialize(pugi::xml_node node, const TypeMeta) {
|
||||
return node.text().as_bool();
|
||||
}
|
||||
|
||||
const std::string Bool_ToString(Variant self) {
|
||||
return self.get<bool>() ? "true" : "false";
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> Bool_FromString(std::string string, const TypeMeta) {
|
||||
return string[0] == 't' || string[0] == 'T' || string[0] == '1' || string[0] == 'y' || string[0] == 'Y';
|
||||
}
|
||||
|
||||
void Bool_PushLuaValue(Variant self, lua_State* L) {
|
||||
lua_pushboolean(L, self.get<bool>());
|
||||
}
|
||||
|
||||
result<Variant, LuaCastError> Bool_FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_isboolean(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "boolean");
|
||||
return Variant(lua_toboolean(L, idx));
|
||||
}
|
||||
|
||||
const TypeDesc BOOL_TYPE {
|
||||
"bool",
|
||||
Bool_Serialize,
|
||||
Bool_Deserialize,
|
||||
Bool_ToString,
|
||||
Bool_FromString,
|
||||
Bool_PushLuaValue,
|
||||
Bool_FromLuaValue,
|
||||
};
|
||||
|
||||
// /bool
|
||||
|
||||
// int
|
||||
|
||||
void Int_Serialize(Variant self, pugi::xml_node node) {
|
||||
node.text().set(self.get<int>());
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> Int_Deserialize(pugi::xml_node node, const TypeMeta) {
|
||||
return node.text().as_int();
|
||||
}
|
||||
|
||||
const std::string Int_ToString(Variant self) {
|
||||
return std::to_string(self.get<int>());
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> Int_FromString(std::string string, const TypeMeta) {
|
||||
char* endPos;
|
||||
int value = (int)std::strtol(string.c_str(), &endPos, 10);
|
||||
if (endPos == string.c_str()) return DataParseError(string, "int");
|
||||
return value;
|
||||
}
|
||||
|
||||
void Int_PushLuaValue(Variant self, lua_State* L) {
|
||||
lua_pushinteger(L, self.get<int>());
|
||||
}
|
||||
|
||||
result<Variant, LuaCastError> Int_FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_isnumber(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "integer");
|
||||
return Variant((int)lua_tonumber(L, idx));
|
||||
}
|
||||
|
||||
const TypeDesc INT_TYPE {
|
||||
"int",
|
||||
Int_Serialize,
|
||||
Int_Deserialize,
|
||||
Int_ToString,
|
||||
Int_FromString,
|
||||
Int_PushLuaValue,
|
||||
Int_FromLuaValue,
|
||||
};
|
||||
|
||||
// /int
|
||||
|
||||
// float
|
||||
|
||||
void Float_Serialize(Variant self, pugi::xml_node node) {
|
||||
node.text().set(self.get<float>());
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> Float_Deserialize(pugi::xml_node node, const TypeMeta) {
|
||||
return node.text().as_float();
|
||||
}
|
||||
|
||||
const std::string Float_ToString(Variant self) {
|
||||
std::stringstream stream;
|
||||
stream << std::noshowpoint << self.get<float>();
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> Float_FromString(std::string string, const TypeMeta) {
|
||||
char* endPos;
|
||||
float value = std::strtof(string.c_str(), &endPos);
|
||||
if (endPos == string.c_str()) return DataParseError(string, "float");
|
||||
return value;
|
||||
}
|
||||
|
||||
void Float_PushLuaValue(Variant self, lua_State* L) {
|
||||
lua_pushnumber(L, self.get<float>());
|
||||
}
|
||||
|
||||
result<Variant, LuaCastError> Float_FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_isnumber(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "float");
|
||||
return Variant((float)lua_tonumber(L, idx));
|
||||
}
|
||||
|
||||
const TypeDesc FLOAT_TYPE {
|
||||
"float",
|
||||
Float_Serialize,
|
||||
Float_Deserialize,
|
||||
Float_ToString,
|
||||
Float_FromString,
|
||||
Float_PushLuaValue,
|
||||
Float_FromLuaValue,
|
||||
};
|
||||
|
||||
// /float
|
||||
|
||||
// string
|
||||
|
||||
void String_Serialize(Variant self, pugi::xml_node node) {
|
||||
node.text().set(self.get<std::string>());
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> String_Deserialize(pugi::xml_node node, const TypeMeta) {
|
||||
return node.text().as_string();
|
||||
}
|
||||
|
||||
const std::string String_ToString(Variant self) {
|
||||
return self.get<std::string>();
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> String_FromString(std::string string, const TypeMeta) {
|
||||
return string;
|
||||
}
|
||||
|
||||
void String_PushLuaValue(Variant self, lua_State* L) {
|
||||
lua_pushstring(L, self.get<std::string>().c_str());
|
||||
}
|
||||
|
||||
result<Variant, LuaCastError> String_FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_tostring(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "string");
|
||||
return Variant(lua_tostring(L, idx));
|
||||
}
|
||||
|
||||
const TypeDesc STRING_TYPE {
|
||||
"string",
|
||||
String_Serialize,
|
||||
String_Deserialize,
|
||||
String_ToString,
|
||||
String_FromString,
|
||||
String_PushLuaValue,
|
||||
String_FromLuaValue,
|
||||
};
|
||||
|
||||
// /string
|
|
@ -1,9 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
extern const TypeDesc NULL_TYPE;
|
||||
extern const TypeDesc BOOL_TYPE;
|
||||
extern const TypeDesc INT_TYPE;
|
||||
extern const TypeDesc FLOAT_TYPE;
|
||||
extern const TypeDesc STRING_TYPE;
|
|
@ -2,7 +2,7 @@
|
|||
#include "datatypes/base.h"
|
||||
#include "error/data.h"
|
||||
#include "logger.h"
|
||||
#include "variant.h" // IWYU pragma: keep
|
||||
#include "meta.h" // IWYU pragma: keep
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include "objects/base/instance.h"
|
||||
|
@ -10,40 +10,34 @@
|
|||
#include "objects/base/member.h"
|
||||
#include <pugixml.hpp>
|
||||
|
||||
TypeMeta::TypeMeta(InstanceType* instType) : instType(instType), descriptor(&InstanceRef::TYPE) {}
|
||||
Data::InstanceRef::InstanceRef() {};
|
||||
Data::InstanceRef::InstanceRef(std::weak_ptr<Instance> instance) : ref(instance) {};
|
||||
Data::InstanceRef::~InstanceRef() = default;
|
||||
|
||||
InstanceRef::InstanceRef() {};
|
||||
InstanceRef::InstanceRef(std::weak_ptr<Instance> instance) : ref(instance) {};
|
||||
InstanceRef::~InstanceRef() = default;
|
||||
|
||||
const TypeDesc InstanceRef::TYPE = {
|
||||
const Data::TypeInfo Data::InstanceRef::TYPE = {
|
||||
.name = "Ref",
|
||||
.serialize = toVariantFunction(&InstanceRef::Serialize),
|
||||
.deserialize = toVariantGeneratorNoMeta(&InstanceRef::Deserialize),
|
||||
.toString = toVariantFunction(&InstanceRef::ToString),
|
||||
.fromString = nullptr,
|
||||
.pushLuaValue = toVariantFunction(&InstanceRef::PushLuaValue),
|
||||
.fromLuaValue = &InstanceRef::FromLuaValue,
|
||||
.deserializer = &Data::InstanceRef::Deserialize,
|
||||
.fromLuaValue = &Data::InstanceRef::FromLuaValue,
|
||||
};
|
||||
|
||||
const std::string InstanceRef::ToString() const {
|
||||
const Data::TypeInfo& Data::InstanceRef::GetType() const { return Data::InstanceRef::TYPE; };
|
||||
|
||||
const Data::String Data::InstanceRef::ToString() const {
|
||||
return ref.expired() ? "" : ref.lock()->name;
|
||||
}
|
||||
|
||||
InstanceRef::operator std::weak_ptr<Instance>() {
|
||||
Data::InstanceRef::operator std::weak_ptr<Instance>() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
// Serialization
|
||||
|
||||
void InstanceRef::Serialize(pugi::xml_node node) const {
|
||||
// Handled by Instance
|
||||
panic();
|
||||
void Data::InstanceRef::Serialize(pugi::xml_node node) const {
|
||||
// node.text().set(this->ToHex());
|
||||
}
|
||||
|
||||
result<InstanceRef, DataParseError> InstanceRef::Deserialize(pugi::xml_node node) {
|
||||
// Handled by Instance
|
||||
panic();
|
||||
Data::Variant Data::InstanceRef::Deserialize(pugi::xml_node node) {
|
||||
return Data::InstanceRef();
|
||||
}
|
||||
|
||||
static int inst_gc(lua_State*);
|
||||
|
@ -56,7 +50,7 @@ static const struct luaL_Reg metatable [] = {
|
|||
{NULL, NULL} /* end of array */
|
||||
};
|
||||
|
||||
void InstanceRef::PushLuaValue(lua_State* L) const {
|
||||
void Data::InstanceRef::PushLuaValue(lua_State* L) const {
|
||||
if (ref.expired()) return lua_pushnil(L);
|
||||
|
||||
int n = lua_gettop(L);
|
||||
|
@ -74,14 +68,14 @@ void InstanceRef::PushLuaValue(lua_State* L) const {
|
|||
lua_setmetatable(L, n+1);
|
||||
}
|
||||
|
||||
result<Variant, LuaCastError> InstanceRef::FromLuaValue(lua_State* L, int idx) {
|
||||
result<Data::Variant, LuaCastError> Data::InstanceRef::FromLuaValue(lua_State* L, int idx) {
|
||||
if (lua_isnil(L, idx))
|
||||
return Variant(InstanceRef());
|
||||
return Data::Variant(Data::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 Variant(InstanceRef(**userdata));
|
||||
return Data::Variant(Data::InstanceRef(**userdata));
|
||||
}
|
||||
|
||||
static int inst_gc(lua_State* L) {
|
||||
|
@ -103,7 +97,7 @@ static int inst_index(lua_State* L) {
|
|||
// Read property
|
||||
std::optional<PropertyMeta> meta = inst->GetPropertyMeta(key);
|
||||
if (meta) {
|
||||
Variant value = inst->GetPropertyValue(key).expect();
|
||||
Data::Variant value = inst->GetPropertyValue(key).expect();
|
||||
value.PushLuaValue(L);
|
||||
return 1;
|
||||
}
|
||||
|
@ -111,7 +105,7 @@ static int inst_index(lua_State* L) {
|
|||
// Look for child
|
||||
std::optional<std::shared_ptr<Instance>> child = inst->FindFirstChild(key);
|
||||
if (child) {
|
||||
InstanceRef(child.value()).PushLuaValue(L);
|
||||
Data::InstanceRef(child.value()).PushLuaValue(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -133,8 +127,7 @@ 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());
|
||||
|
||||
// TODO: Make this work for enums, this is not a solution!!
|
||||
result<Variant, LuaCastError> value = meta->type.descriptor->fromLuaValue(L, -1);
|
||||
result<Data::Variant, LuaCastError> value = meta->type->fromLuaValue(L, -1);
|
||||
lua_pop(L, 3);
|
||||
|
||||
if (value.isError())
|
||||
|
|
|
@ -6,20 +6,23 @@
|
|||
|
||||
class Instance;
|
||||
|
||||
class InstanceRef {
|
||||
std::weak_ptr<Instance> ref;
|
||||
public:
|
||||
InstanceRef();
|
||||
InstanceRef(std::weak_ptr<Instance>);
|
||||
~InstanceRef();
|
||||
namespace Data {
|
||||
class InstanceRef : public Base {
|
||||
std::weak_ptr<Instance> ref;
|
||||
public:
|
||||
InstanceRef();
|
||||
InstanceRef(std::weak_ptr<Instance>);
|
||||
~InstanceRef();
|
||||
|
||||
static const TypeDesc TYPE;
|
||||
virtual const TypeInfo& GetType() const override;
|
||||
static const TypeInfo TYPE;
|
||||
|
||||
operator std::weak_ptr<Instance>();
|
||||
operator std::weak_ptr<Instance>();
|
||||
|
||||
virtual const std::string ToString() const;
|
||||
virtual 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);
|
||||
};
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "signal.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "variant.h"
|
||||
#include "meta.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<Variant> args) {
|
||||
void LuaSignalConnection::Call(std::vector<Data::Variant> args) {
|
||||
lua_State* thread = lua_newthread(state);
|
||||
|
||||
// Push function
|
||||
lua_rawgeti(thread, LUA_REGISTRYINDEX, function);
|
||||
|
||||
for (Variant arg : args) {
|
||||
for (Data::Variant arg : args) {
|
||||
arg.PushLuaValue(thread);
|
||||
}
|
||||
|
||||
|
@ -74,11 +74,11 @@ void LuaSignalConnection::Call(std::vector<Variant> args) {
|
|||
|
||||
//
|
||||
|
||||
CSignalConnection::CSignalConnection(std::function<void(std::vector<Variant>)> func, std::weak_ptr<Signal> parent) : SignalConnection(parent) {
|
||||
CSignalConnection::CSignalConnection(std::function<void(std::vector<Data::Variant>)> func, std::weak_ptr<Signal> parent) : SignalConnection(parent) {
|
||||
this->function = func;
|
||||
}
|
||||
|
||||
void CSignalConnection::Call(std::vector<Variant> args) {
|
||||
void CSignalConnection::Call(std::vector<Data::Variant> args) {
|
||||
function(args);
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ void CSignalConnection::Call(std::vector<Variant> args) {
|
|||
|
||||
SignalConnectionHolder::SignalConnectionHolder() : heldConnection() {}
|
||||
SignalConnectionHolder::SignalConnectionHolder(std::shared_ptr<SignalConnection> connection) : heldConnection(connection) {}
|
||||
SignalConnectionHolder::SignalConnectionHolder(SignalConnectionRef other) : heldConnection(other) {}
|
||||
SignalConnectionHolder::SignalConnectionHolder(Data::SignalConnectionRef other) : heldConnection(other) {}
|
||||
|
||||
SignalConnectionHolder::~SignalConnectionHolder() {
|
||||
// printf("Prediscon!\n");
|
||||
|
@ -97,7 +97,7 @@ SignalConnectionHolder::~SignalConnectionHolder() {
|
|||
|
||||
//
|
||||
|
||||
SignalConnectionRef Signal::Connect(std::function<void(std::vector<Variant>)> callback) {
|
||||
SignalConnectionRef Signal::Connect(std::function<void(std::vector<Data::Variant>)> callback) {
|
||||
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<CSignalConnection>(callback, weak_from_this()));
|
||||
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<Variant>)> callback) {
|
||||
SignalConnectionRef Signal::Once(std::function<void(std::vector<Data::Variant>)> callback) {
|
||||
auto conn = std::dynamic_pointer_cast<SignalConnection>(std::make_shared<CSignalConnection>(callback, weak_from_this()));
|
||||
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<Variant> args) {
|
||||
void Signal::Fire(std::vector<Data::Variant> args) {
|
||||
for (std::shared_ptr<SignalConnection> connection : connections) {
|
||||
connection->Call(args);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ void Signal::Fire(std::vector<Variant> args) {
|
|||
auto prevThreads = std::move(waitingThreads);
|
||||
waitingThreads = std::vector<std::pair<int, lua_State*>>();
|
||||
for (auto& [threadId, thread] : prevThreads) {
|
||||
for (Variant arg : args) {
|
||||
for (Data::Variant arg : args) {
|
||||
arg.PushLuaValue(thread);
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ void Signal::Fire(std::vector<Variant> args) {
|
|||
}
|
||||
|
||||
void Signal::Fire() {
|
||||
return Fire(std::vector<Variant> {});
|
||||
return Fire(std::vector<Data::Variant> {});
|
||||
}
|
||||
|
||||
void Signal::DisconnectAll() {
|
||||
|
@ -222,29 +222,29 @@ static const struct luaL_Reg signal_metatable [] = {
|
|||
{NULL, NULL} /* end of array */
|
||||
};
|
||||
|
||||
SignalRef::SignalRef(std::weak_ptr<Signal> ref) : signal(ref) {}
|
||||
SignalRef::~SignalRef() = default;
|
||||
Data::SignalRef::SignalRef(std::weak_ptr<Signal> ref) : signal(ref) {}
|
||||
Data::SignalRef::~SignalRef() = default;
|
||||
|
||||
const TypeDesc SignalRef::TYPE = {
|
||||
const Data::TypeInfo Data::SignalRef::TYPE = {
|
||||
.name = "Signal",
|
||||
.toString = toVariantFunction(&SignalRef::ToString),
|
||||
.pushLuaValue = toVariantFunction(&SignalRef::PushLuaValue),
|
||||
.fromLuaValue = &SignalRef::FromLuaValue,
|
||||
.fromLuaValue = &Data::SignalRef::FromLuaValue,
|
||||
};
|
||||
|
||||
const std::string SignalRef::ToString() const {
|
||||
return "Signal";
|
||||
const Data::TypeInfo& Data::SignalRef::GetType() const { return Data::SignalRef::TYPE; };
|
||||
|
||||
const Data::String Data::SignalRef::ToString() const {
|
||||
return Data::String("Signal");
|
||||
}
|
||||
|
||||
SignalRef::operator std::weak_ptr<Signal>() {
|
||||
Data::SignalRef::operator std::weak_ptr<Signal>() {
|
||||
return signal;
|
||||
}
|
||||
|
||||
void SignalRef::Serialize(pugi::xml_node node) const {
|
||||
void Data::SignalRef::Serialize(pugi::xml_node node) const {
|
||||
// Not serializable
|
||||
}
|
||||
|
||||
void SignalRef::PushLuaValue(lua_State* L) const {
|
||||
void Data::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 SignalRef::PushLuaValue(lua_State* L) const {
|
|||
lua_setmetatable(L, n+1);
|
||||
}
|
||||
|
||||
result<Variant, LuaCastError> SignalRef::FromLuaValue(lua_State* L, int idx) {
|
||||
result<Data::Variant, LuaCastError> Data::SignalRef::FromLuaValue(lua_State* L, int idx) {
|
||||
auto userdata = (std::weak_ptr<Signal>**)luaL_checkudata(L, 1, "__mt_signal");
|
||||
lua_pop(L, 1);
|
||||
return Variant(SignalRef(**userdata));
|
||||
return Data::Variant(Data::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 */
|
||||
};
|
||||
|
||||
SignalConnectionRef::SignalConnectionRef(std::weak_ptr<SignalConnection> ref) : signalConnection(ref) {}
|
||||
SignalConnectionRef::~SignalConnectionRef() = default;
|
||||
Data::SignalConnectionRef::SignalConnectionRef(std::weak_ptr<SignalConnection> ref) : signalConnection(ref) {}
|
||||
Data::SignalConnectionRef::~SignalConnectionRef() = default;
|
||||
|
||||
const TypeDesc SignalConnectionRef::TYPE = {
|
||||
const Data::TypeInfo Data::SignalConnectionRef::TYPE = {
|
||||
.name = "Signal",
|
||||
.toString = toVariantFunction(&SignalConnectionRef::ToString),
|
||||
.pushLuaValue = toVariantFunction(&SignalConnectionRef::PushLuaValue),
|
||||
.fromLuaValue = &SignalConnectionRef::FromLuaValue,
|
||||
.fromLuaValue = &Data::SignalConnectionRef::FromLuaValue,
|
||||
};
|
||||
|
||||
const std::string SignalConnectionRef::ToString() const {
|
||||
return "Connection";
|
||||
const Data::TypeInfo& Data::SignalConnectionRef::GetType() const { return Data::SignalConnectionRef::TYPE; };
|
||||
|
||||
const Data::String Data::SignalConnectionRef::ToString() const {
|
||||
return Data::String("Connection");
|
||||
}
|
||||
|
||||
SignalConnectionRef::operator std::weak_ptr<SignalConnection>() {
|
||||
Data::SignalConnectionRef::operator std::weak_ptr<SignalConnection>() {
|
||||
return signalConnection;
|
||||
}
|
||||
|
||||
void SignalConnectionRef::Serialize(pugi::xml_node node) const {
|
||||
void Data::SignalConnectionRef::Serialize(pugi::xml_node node) const {
|
||||
// Not serializable
|
||||
}
|
||||
|
||||
void SignalConnectionRef::PushLuaValue(lua_State* L) const {
|
||||
void Data::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 SignalConnectionRef::PushLuaValue(lua_State* L) const {
|
|||
lua_setmetatable(L, n+1);
|
||||
}
|
||||
|
||||
result<Variant, LuaCastError> SignalConnectionRef::FromLuaValue(lua_State* L, int idx) {
|
||||
result<Data::Variant, LuaCastError> Data::SignalConnectionRef::FromLuaValue(lua_State* L, int idx) {
|
||||
auto userdata = (std::weak_ptr<SignalConnection>**)luaL_checkudata(L, 1, "__mt_signalconnection");
|
||||
lua_pop(L, 1);
|
||||
return Variant(SignalConnectionRef(**userdata));
|
||||
return Data::Variant(Data::SignalConnectionRef(**userdata));
|
||||
}
|
||||
|
||||
static int signalconnection_tostring(lua_State* L) {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
class Instance;
|
||||
class Signal;
|
||||
|
||||
class SignalConnectionRef;
|
||||
namespace Data { 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<Variant>) = 0;
|
||||
virtual void Call(std::vector<Data::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<Variant>)> function;
|
||||
std::function<void(std::vector<Data::Variant>)> function;
|
||||
|
||||
friend Signal;
|
||||
protected:
|
||||
void Call(std::vector<Variant>) override;
|
||||
void Call(std::vector<Data::Variant>) override;
|
||||
public:
|
||||
CSignalConnection(std::function<void(std::vector<Variant>)>, std::weak_ptr<Signal> parent);
|
||||
CSignalConnection(std::function<void(std::vector<Data::Variant>)>, std::weak_ptr<Signal> parent);
|
||||
};
|
||||
|
||||
class LuaSignalConnection : public SignalConnection {
|
||||
|
@ -48,7 +48,7 @@ class LuaSignalConnection : public SignalConnection {
|
|||
|
||||
friend Signal;
|
||||
protected:
|
||||
void Call(std::vector<Variant>) override;
|
||||
void Call(std::vector<Data::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(SignalConnectionRef other);
|
||||
SignalConnectionHolder(Data::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<Variant> args);
|
||||
void Fire(std::vector<Data::Variant> args);
|
||||
void Fire();
|
||||
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*);
|
||||
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*);
|
||||
int Wait(lua_State*);
|
||||
};
|
||||
|
||||
|
@ -105,36 +105,43 @@ public:
|
|||
virtual ~SignalSource();
|
||||
};
|
||||
|
||||
class SignalRef {
|
||||
std::weak_ptr<Signal> signal;
|
||||
namespace Data {
|
||||
class SignalRef : public Data::Base {
|
||||
std::weak_ptr<Signal> signal;
|
||||
|
||||
public:
|
||||
SignalRef(std::weak_ptr<Signal>);
|
||||
~SignalRef();
|
||||
public:
|
||||
SignalRef(std::weak_ptr<Signal>);
|
||||
~SignalRef();
|
||||
|
||||
static const TypeDesc TYPE;
|
||||
virtual const TypeInfo& GetType() const override;
|
||||
static const TypeInfo TYPE;
|
||||
|
||||
operator std::weak_ptr<Signal>();
|
||||
operator std::weak_ptr<Signal>();
|
||||
|
||||
virtual const std::string ToString() const;
|
||||
virtual void Serialize(pugi::xml_node node) const;
|
||||
virtual void PushLuaValue(lua_State*) const;
|
||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||
};
|
||||
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);
|
||||
};
|
||||
|
||||
class SignalConnectionRef {
|
||||
std::weak_ptr<SignalConnection> signalConnection;
|
||||
class SignalConnectionRef : public Data::Base {
|
||||
std::weak_ptr<SignalConnection> signalConnection;
|
||||
|
||||
public:
|
||||
SignalConnectionRef(std::weak_ptr<SignalConnection>);
|
||||
~SignalConnectionRef();
|
||||
public:
|
||||
SignalConnectionRef(std::weak_ptr<SignalConnection>);
|
||||
~SignalConnectionRef();
|
||||
|
||||
static const TypeDesc TYPE;
|
||||
virtual const TypeInfo& GetType() const override;
|
||||
static const TypeInfo TYPE;
|
||||
|
||||
operator std::weak_ptr<SignalConnection>();
|
||||
operator std::weak_ptr<SignalConnection>();
|
||||
|
||||
virtual const std::string ToString() const;
|
||||
virtual void Serialize(pugi::xml_node node) const;
|
||||
virtual void PushLuaValue(lua_State*) const;
|
||||
static result<Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||
};
|
||||
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;
|
|
@ -1,88 +0,0 @@
|
|||
#include "variant.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/enum.h"
|
||||
#include "datatypes/primitives.h"
|
||||
#include "datatypes/ref.h"
|
||||
#include "datatypes/signal.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include "error/data.h"
|
||||
#include "logger.h"
|
||||
#include "panic.h"
|
||||
#include <pugixml.hpp>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
[[noreturn]] inline void unreachable() {
|
||||
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
|
||||
__assume(false);
|
||||
#else // GCC, Clang
|
||||
__builtin_unreachable();
|
||||
#endif
|
||||
}
|
||||
|
||||
const TypeDesc* VARIANT_TYPES[] {
|
||||
&NULL_TYPE,
|
||||
&BOOL_TYPE,
|
||||
&INT_TYPE,
|
||||
&FLOAT_TYPE,
|
||||
&STRING_TYPE,
|
||||
&Vector3::TYPE,
|
||||
&CFrame::TYPE,
|
||||
&Color3::TYPE,
|
||||
&InstanceRef::TYPE,
|
||||
&SignalRef::TYPE,
|
||||
&SignalConnectionRef::TYPE,
|
||||
&Enum::TYPE,
|
||||
&EnumItem::TYPE,
|
||||
};
|
||||
|
||||
const TypeMeta Variant::GetTypeMeta() const {
|
||||
return VARIANT_TYPES[wrapped.index()];
|
||||
}
|
||||
|
||||
const TypeDesc* Variant::GetType() const {
|
||||
return VARIANT_TYPES[wrapped.index()];
|
||||
}
|
||||
|
||||
std::string Variant::ToString() const {
|
||||
if (!VARIANT_TYPES[wrapped.index()]->pushLuaValue) {
|
||||
Logger::fatalErrorf("Data type %s does not implement toString", VARIANT_TYPES[wrapped.index()]->name.c_str());
|
||||
}
|
||||
return VARIANT_TYPES[wrapped.index()]->toString(*this);
|
||||
}
|
||||
|
||||
void Variant::Serialize(pugi::xml_node node) const {
|
||||
if (!VARIANT_TYPES[wrapped.index()]->pushLuaValue) {
|
||||
Logger::fatalErrorf("Data type %s does not implement serializer", VARIANT_TYPES[wrapped.index()]->name.c_str());
|
||||
}
|
||||
VARIANT_TYPES[wrapped.index()]->serialize(*this, node);
|
||||
}
|
||||
|
||||
void Variant::PushLuaValue(lua_State* state) const {
|
||||
if (!VARIANT_TYPES[wrapped.index()]->pushLuaValue) {
|
||||
Logger::fatalErrorf("Data type %s does not implement pushLuaValue", VARIANT_TYPES[wrapped.index()]->name.c_str());
|
||||
}
|
||||
VARIANT_TYPES[wrapped.index()]->pushLuaValue(*this, state);
|
||||
}
|
||||
|
||||
result<Variant, DataParseError> Variant::Deserialize(pugi::xml_node node, const TypeMeta type) {
|
||||
if (!type.descriptor->deserialize) {
|
||||
Logger::fatalErrorf("Data type %s does not implement deserialize", type.descriptor->name.c_str());
|
||||
return DataParseError(node.text().as_string(), type.descriptor->name);
|
||||
}
|
||||
return type.descriptor->deserialize(node, type);
|
||||
}
|
||||
|
||||
std::map<std::string, const TypeDesc*> TYPE_MAP = {
|
||||
{ "null", &NULL_TYPE },
|
||||
{ "bool", &BOOL_TYPE },
|
||||
{ "int", &INT_TYPE },
|
||||
{ "float", &FLOAT_TYPE },
|
||||
{ "string", &STRING_TYPE },
|
||||
{ "Vector3", &Vector3::TYPE },
|
||||
{ "CoordinateFrame", &CFrame::TYPE },
|
||||
{ "Color3", &Color3::TYPE },
|
||||
{ "Ref", &InstanceRef::TYPE },
|
||||
{ "token", &EnumItem::TYPE },
|
||||
};
|
|
@ -1,94 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <map>
|
||||
#include "base.h"
|
||||
#include "datatypes/color3.h"
|
||||
#include "datatypes/enum.h"
|
||||
#include "datatypes/ref.h"
|
||||
#include "datatypes/signal.h"
|
||||
#include "error/data.h"
|
||||
#include "vector.h"
|
||||
#include "cframe.h"
|
||||
|
||||
// #define __VARIANT_TYPE std::variant< \
|
||||
// Null, \
|
||||
// Bool, \
|
||||
// Int, \
|
||||
// Float, \
|
||||
// String \
|
||||
// >
|
||||
|
||||
typedef std::variant<
|
||||
std::monostate,
|
||||
bool,
|
||||
int,
|
||||
float,
|
||||
std::string,
|
||||
Vector3,
|
||||
CFrame,
|
||||
Color3,
|
||||
InstanceRef,
|
||||
SignalRef,
|
||||
SignalConnectionRef,
|
||||
Enum,
|
||||
EnumItem
|
||||
> __VARIANT_TYPE;
|
||||
|
||||
class Variant {
|
||||
__VARIANT_TYPE wrapped;
|
||||
public:
|
||||
template <typename T, std::enable_if_t<std::is_constructible_v<__VARIANT_TYPE, T>, int> = 0> Variant(T obj) : wrapped(obj) {}
|
||||
template <typename T, std::enable_if_t<std::is_constructible_v<__VARIANT_TYPE, T>, int> = 0> T get() { return std::get<T>(wrapped); }
|
||||
std::string ToString() const;
|
||||
|
||||
const TypeMeta GetTypeMeta() const;
|
||||
const TypeDesc* GetType() const;
|
||||
|
||||
void Serialize(pugi::xml_node node) const;
|
||||
void PushLuaValue(lua_State* state) const;
|
||||
static result<Variant, DataParseError> Deserialize(pugi::xml_node node, const TypeMeta);
|
||||
};
|
||||
|
||||
template <typename T, typename R, typename ...Args>
|
||||
std::function<R(Variant, Args...)> toVariantFunction(R(T::*f)(Args...)) {
|
||||
return [f](Variant var, Args... args) {
|
||||
return (var.get<T>().*f)(args...);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T, typename R, typename ...Args>
|
||||
std::function<R(Variant, Args...)> toVariantFunction(R(T::*f)(Args...) const) {
|
||||
return [f](Variant var, Args... args) {
|
||||
return (var.get<T>().*f)(args...);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
std::function<Variant(Args...)> toVariantGenerator(T(f)(Args...)) {
|
||||
return [f](Args... args) {
|
||||
return (Variant)f(args...);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T, typename ...Args, typename ...E>
|
||||
std::function<result<Variant, E...>(Args...)> toVariantGenerator(result<T, E...>(f)(Args...)) {
|
||||
return [f](Args... args) -> result<Variant, E...> {
|
||||
auto result = f(args...);
|
||||
if (result.isSuccess()) return (Variant)(result.success().value());
|
||||
return result.error().value();
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T, typename ...Args, typename ...E>
|
||||
std::function<result<Variant, E...>(Args..., const TypeMeta)> toVariantGeneratorNoMeta(result<T, E...>(f)(Args...)) {
|
||||
return [f](Args... args, const TypeMeta) -> result<Variant, E...> {
|
||||
auto result = f(args...);
|
||||
if (result.isSuccess()) return (Variant)(result.success().value());
|
||||
return result.error().value();
|
||||
};
|
||||
}
|
||||
|
||||
// Map of all data types to their type names
|
||||
extern std::map<std::string, const TypeDesc*> TYPE_MAP;
|
|
@ -7,96 +7,95 @@
|
|||
#include <string>
|
||||
#include <pugixml.hpp>
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/variant.h"
|
||||
#include "error/data.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include <sstream>
|
||||
|
||||
namespace rp = reactphysics3d;
|
||||
|
||||
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() : 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() = default;
|
||||
Data::Vector3::~Vector3() = default;
|
||||
|
||||
Vector3 Vector3::ZERO(0, 0, 0);
|
||||
Vector3 Vector3::ONE(1, 1, 1);
|
||||
Data::Vector3 Data::Vector3::ZERO(0, 0, 0);
|
||||
Data::Vector3 Data::Vector3::ONE(1, 1, 1);
|
||||
|
||||
const std::string Vector3::ToString() const {
|
||||
const Data::String Data::Vector3::ToString() const {
|
||||
// https://stackoverflow.com/a/46424921/16255372
|
||||
std::stringstream stream;
|
||||
stream << std::setprecision(8) << std::noshowpoint << X() << ", " << Y() << ", " << Z();
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
Vector3::operator glm::vec3() const { return vector; };
|
||||
Vector3::operator rp::Vector3() const { return rp::Vector3(X(), Y(), Z()); };
|
||||
Data::Vector3::operator glm::vec3() const { return vector; };
|
||||
Data::Vector3::operator rp::Vector3() const { return rp::Vector3(X(), Y(), Z()); };
|
||||
|
||||
// Operators
|
||||
|
||||
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);
|
||||
Data::Vector3 Data::Vector3::operator /(float scale) const {
|
||||
return Data::Vector3(this->X() / scale, this->Y() / scale, this->Z() / scale);
|
||||
}
|
||||
|
||||
// Component-wise
|
||||
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 -(Data::Vector3 other) const {
|
||||
return Data::Vector3(this->X() - other.X(), this->Y() - other.Y(), this->Z() - other.Z());
|
||||
}
|
||||
|
||||
Vector3 Vector3::operator -() const {
|
||||
return Vector3(-this->X(), -this->Y(), -this->Z());
|
||||
Data::Vector3 Data::Vector3::operator -() const {
|
||||
return Data::Vector3(-this->X(), -this->Y(), -this->Z());
|
||||
}
|
||||
|
||||
bool Vector3::operator ==(Vector3 other) const {
|
||||
bool Data::Vector3::operator ==(Data::Vector3 other) const {
|
||||
return this->X() == other.X() && this->Y() == other.Y() && this->Z() == other.Z();
|
||||
}
|
||||
|
||||
bool Vector3::operator <(Vector3 other) const {
|
||||
bool Data::Vector3::operator <(Data::Vector3 other) const {
|
||||
return X() < other.X() && Y() < other.Y() && Z() < other.Z();
|
||||
}
|
||||
|
||||
bool Vector3::operator >(Vector3 other) const {
|
||||
bool Data::Vector3::operator >(Data::Vector3 other) const {
|
||||
return X() > other.X() && Y() > other.Y() && Z() > other.Z();
|
||||
}
|
||||
|
||||
Vector3 Vector3::Cross(Vector3 other) const {
|
||||
Data::Vector3 Data::Vector3::Cross(Data::Vector3 other) const {
|
||||
return glm::cross(this->vector, other.vector);
|
||||
}
|
||||
|
||||
float Vector3::Dot(Vector3 other) const {
|
||||
float Data::Vector3::Dot(Data::Vector3 other) const {
|
||||
return glm::dot(this->vector, other.vector);
|
||||
}
|
||||
|
||||
// Serialization
|
||||
|
||||
void Vector3::Serialize(pugi::xml_node node) const {
|
||||
void Data::Vector3::Serialize(pugi::xml_node node) const {
|
||||
node.append_child("X").text().set(std::to_string(this->X()));
|
||||
node.append_child("Y").text().set(std::to_string(this->Y()));
|
||||
node.append_child("Z").text().set(std::to_string(this->Z()));
|
||||
}
|
||||
|
||||
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());
|
||||
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::FromString(std::string string) {
|
||||
std::optional<Data::Variant> Data::Vector3::FromString(std::string string) {
|
||||
float components[3];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (string.length() == 0) return DataParseError(string, "Vector3");
|
||||
if (string.length() == 0) return std::nullopt;
|
||||
while (string[0] == ' ' && string.length() > 0) string.erase(0, 1);
|
||||
size_t nextPos = string.find(",");
|
||||
if (nextPos == -1) nextPos = string.length();
|
||||
|
@ -105,10 +104,10 @@ result<Vector3, DataParseError> Vector3::FromString(std::string string) {
|
|||
|
||||
char* cpos;
|
||||
float value = std::strtof(term.c_str(), &cpos);
|
||||
if (cpos == term.c_str()) return DataParseError(string, "Vector3");
|
||||
if (cpos == term.c_str()) return std::nullopt;
|
||||
|
||||
components[i] = value;
|
||||
}
|
||||
|
||||
return Vector3(components[0], components[1], components[2]);
|
||||
return Data::Vector3(components[0], components[1], components[2]);
|
||||
}
|
|
@ -2,61 +2,64 @@
|
|||
|
||||
#include "base.h"
|
||||
#include "datatypes/annotation.h"
|
||||
#include "error/data.h"
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <glm/geometric.hpp>
|
||||
|
||||
namespace reactphysics3d { class Vector3; };
|
||||
|
||||
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);
|
||||
namespace Data {
|
||||
class DEF_DATA Vector3 : public Base {
|
||||
AUTOGEN_PREAMBLE_DATA
|
||||
glm::vec3 vector;
|
||||
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
public:
|
||||
DEF_DATA_CTOR Vector3();
|
||||
DEF_DATA_CTOR Vector3(float x, float y, float z);
|
||||
Vector3(const glm::vec3&);
|
||||
Vector3(const reactphysics3d::Vector3&);
|
||||
~Vector3();
|
||||
|
||||
operator glm::vec3() const;
|
||||
operator reactphysics3d::Vector3() const;
|
||||
DEF_DATA_PROP static Data::Vector3 ZERO;
|
||||
DEF_DATA_PROP static Data::Vector3 ONE;
|
||||
|
||||
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); }
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node node) const override;
|
||||
|
||||
DEF_DATA_METHOD Vector3 Cross(Vector3) const;
|
||||
DEF_DATA_METHOD float Dot(Vector3) const;
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
static std::optional<Data::Variant> FromString(std::string);
|
||||
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
|
||||
// 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;
|
||||
operator glm::vec3() const;
|
||||
operator reactphysics3d::Vector3() const;
|
||||
|
||||
DEF_DATA_OP bool operator <(Vector3) const;
|
||||
DEF_DATA_OP bool operator >(Vector3) 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_METHOD Data::Vector3 Cross(Data::Vector3) const;
|
||||
DEF_DATA_METHOD float Dot(Data::Vector3) const;
|
||||
|
||||
// Operators
|
||||
DEF_DATA_OP Data::Vector3 operator *(float) const;
|
||||
DEF_DATA_OP Data::Vector3 operator /(float) const;
|
||||
DEF_DATA_OP Data::Vector3 operator *(Data::Vector3) const; // Component-wise
|
||||
DEF_DATA_OP Data::Vector3 operator +(Data::Vector3) const;
|
||||
DEF_DATA_OP Data::Vector3 operator -(Data::Vector3) const;
|
||||
DEF_DATA_OP Data::Vector3 operator -() const;
|
||||
|
||||
inline void printVec(Vector3 vec) {
|
||||
DEF_DATA_OP bool operator <(Data::Vector3) const;
|
||||
DEF_DATA_OP bool operator >(Data::Vector3) const;
|
||||
|
||||
DEF_DATA_OP bool operator ==(Data::Vector3) const;
|
||||
};
|
||||
}
|
||||
|
||||
using Data::Vector3;
|
||||
|
||||
inline void printVec(Data::Vector3 vec) {
|
||||
printf("(%f, %f, %f)\n", vec.X(), vec.Y(), vec.Z());
|
||||
}
|
|
@ -4,10 +4,5 @@
|
|||
|
||||
class LuaCastError : public Error {
|
||||
public:
|
||||
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) {}
|
||||
inline LuaCastError(std::string sourceType, std::string targetType) : Error("InstanceCastError", "Attempt to cast " + sourceType + " to " + targetType) {}
|
||||
};
|
|
@ -24,8 +24,6 @@ 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>
|
||||
|
@ -63,7 +61,7 @@ public:
|
|||
|
||||
// Equivalent to .success
|
||||
operator std::optional<T_Result>() { return success(); }
|
||||
explicit operator bool() const { return isSuccess(); } // Explicity is necessary to prevent auto-casting from result to Variant, for instance
|
||||
operator bool() { return isSuccess(); }
|
||||
bool operator !() { return isError(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -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, Variant value) override; \
|
||||
virtual result<Variant, MemberNotFound> InternalGetPropertyValue(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 void InternalUpdateProperty(std::string name) override; \
|
||||
virtual std::vector<std::string> InternalGetProperties() override; \
|
||||
public: \
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "instance.h"
|
||||
#include "common.h"
|
||||
#include "datatypes/primitives.h"
|
||||
#include "datatypes/variant.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/ref.h"
|
||||
#include "error/instance.h"
|
||||
|
@ -97,7 +96,7 @@ void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedCh
|
|||
}
|
||||
|
||||
OnAncestryChanged(updatedChild, newParent);
|
||||
AncestryChanged->Fire({updatedChild.has_value() ? InstanceRef(updatedChild.value()) : InstanceRef(), newParent.has_value() ? InstanceRef(newParent.value()) : InstanceRef()});
|
||||
AncestryChanged->Fire({updatedChild.has_value() ? Data::InstanceRef(updatedChild.value()) : Data::InstanceRef(), newParent.has_value() ? Data::InstanceRef(newParent.value()) : Data::InstanceRef()});
|
||||
|
||||
// Old workspace used to exist, and workspaces differ
|
||||
if (!oldWorkspace.expired() && oldWorkspace != _workspace) {
|
||||
|
@ -110,7 +109,7 @@ void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedCh
|
|||
}
|
||||
|
||||
// Update ancestry in descendants
|
||||
for (std::shared_ptr<Instance> child : children) {
|
||||
for (InstanceRef child : children) {
|
||||
child->updateAncestry(updatedChild, newParent);
|
||||
}
|
||||
}
|
||||
|
@ -180,11 +179,11 @@ void Instance::OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) {
|
|||
|
||||
// Properties
|
||||
|
||||
result<Variant, MemberNotFound> Instance::GetPropertyValue(std::string name) {
|
||||
result<Data::Variant, MemberNotFound> Instance::GetPropertyValue(std::string name) {
|
||||
return InternalGetPropertyValue(name);
|
||||
}
|
||||
|
||||
fallible<MemberNotFound, AssignToReadOnlyMember> Instance::SetPropertyValue(std::string name, Variant value, bool sendUpdateEvent) {
|
||||
fallible<MemberNotFound, AssignToReadOnlyMember> Instance::SetPropertyValue(std::string name, Data::Variant value, bool sendUpdateEvent) {
|
||||
auto result = InternalSetPropertyValue(name, value);
|
||||
if (result.isSuccess() && sendUpdateEvent) {
|
||||
InternalUpdateProperty(name);
|
||||
|
@ -198,33 +197,33 @@ result<PropertyMeta, MemberNotFound> Instance::GetPropertyMeta(std::string name)
|
|||
}
|
||||
|
||||
|
||||
result<Variant, MemberNotFound> Instance::InternalGetPropertyValue(std::string name) {
|
||||
result<Data::Variant, MemberNotFound> Instance::InternalGetPropertyValue(std::string name) {
|
||||
if (name == "Name") {
|
||||
return Variant(this->name);
|
||||
return Data::Variant(Data::String(this->name));
|
||||
} else if (name == "Parent") {
|
||||
return Variant(InstanceRef(this->parent));
|
||||
return Data::Variant(Data::InstanceRef(this->parent));
|
||||
} else if (name == "ClassName") {
|
||||
return Variant(GetClass()->className);
|
||||
return Data::Variant(Data::String(GetClass()->className));
|
||||
}
|
||||
return MemberNotFound(GetClass()->className, name);
|
||||
}
|
||||
|
||||
result<PropertyMeta, MemberNotFound> Instance::InternalGetPropertyMeta(std::string name) {
|
||||
if (name == "Name") {
|
||||
return PropertyMeta { &STRING_TYPE };
|
||||
return PropertyMeta { &Data::String::TYPE };
|
||||
} else if (name == "Parent") {
|
||||
return PropertyMeta { &InstanceRef::TYPE, PROP_NOSAVE };
|
||||
return PropertyMeta { &Data::InstanceRef::TYPE, };
|
||||
} else if (name == "ClassName") {
|
||||
return PropertyMeta { &STRING_TYPE, PROP_NOSAVE | PROP_READONLY };
|
||||
return PropertyMeta { &Data::String::TYPE, PROP_NOSAVE | PROP_READONLY };
|
||||
}
|
||||
return MemberNotFound(GetClass()->className, name);
|
||||
}
|
||||
|
||||
fallible<MemberNotFound, AssignToReadOnlyMember> Instance::InternalSetPropertyValue(std::string name, Variant value) {
|
||||
fallible<MemberNotFound, AssignToReadOnlyMember> Instance::InternalSetPropertyValue(std::string name, Data::Variant value) {
|
||||
if (name == "Name") {
|
||||
this->name = (std::string)value.get<std::string>();
|
||||
this->name = (std::string)value.get<Data::String>();
|
||||
} else if (name == "Parent") {
|
||||
std::weak_ptr<Instance> ref = value.get<InstanceRef>();
|
||||
std::weak_ptr<Instance> ref = value.get<Data::InstanceRef>();
|
||||
SetParent(ref.expired() ? std::nullopt : std::make_optional(ref.lock()));
|
||||
} else if (name == "ClassName") {
|
||||
return AssignToReadOnlyMember(GetClass()->className, name);
|
||||
|
@ -260,7 +259,7 @@ std::vector<std::string> Instance::GetProperties() {
|
|||
|
||||
// Serialization
|
||||
|
||||
void Instance::Serialize(pugi::xml_node parent, RefStateSerialize state) {
|
||||
void Instance::Serialize(pugi::xml_node parent) {
|
||||
pugi::xml_node node = parent.append_child("Item");
|
||||
node.append_attribute("class").set_value(this->GetClass()->className);
|
||||
|
||||
|
@ -270,55 +269,24 @@ void Instance::Serialize(pugi::xml_node parent, RefStateSerialize state) {
|
|||
PropertyMeta meta = GetPropertyMeta(name).expect("Meta of declared property is missing");
|
||||
if (meta.flags & (PROP_NOSAVE | PROP_READONLY)) continue; // This property should not be serialized. Skip...
|
||||
|
||||
pugi::xml_node propertyNode = propertiesNode.append_child(meta.type.descriptor->name);
|
||||
pugi::xml_node propertyNode = propertiesNode.append_child(meta.type->name);
|
||||
propertyNode.append_attribute("name").set_value(name);
|
||||
|
||||
// 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);
|
||||
}
|
||||
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 (std::shared_ptr<Instance> child : this->children) {
|
||||
child->Serialize(node, state);
|
||||
for (InstanceRef child : this->children) {
|
||||
child->Serialize(node);
|
||||
}
|
||||
}
|
||||
|
||||
result<std::shared_ptr<Instance>, NoSuchInstance> Instance::Deserialize(pugi::xml_node node, RefStateDeserialize state) {
|
||||
result<InstanceRef, NoSuchInstance> Instance::Deserialize(pugi::xml_node node) {
|
||||
std::string className = node.attribute("class").value();
|
||||
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.
|
||||
std::shared_ptr<Instance> object = INSTANCE_MAP[className]->constructor();
|
||||
InstanceRef object = INSTANCE_MAP[className]->constructor();
|
||||
object->GetChildren();
|
||||
|
||||
// const InstanceType* type = INSTANCE_MAP.at(className);
|
||||
|
@ -332,51 +300,13 @@ result<std::shared_ptr<Instance>, NoSuchInstance> Instance::Deserialize(pugi::xm
|
|||
Logger::fatalErrorf("Attempt to set unknown property '%s' of %s", propertyName.c_str(), object->GetClass()->className.c_str());
|
||||
continue;
|
||||
}
|
||||
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");
|
||||
}
|
||||
Data::Variant value = Data::Variant::Deserialize(propertyNode);
|
||||
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<std::shared_ptr<Instance>, NoSuchInstance> child = Instance::Deserialize(childNode, state);
|
||||
result<InstanceRef, NoSuchInstance> child = Instance::Deserialize(childNode);
|
||||
if (child.isError()) {
|
||||
std::get<NoSuchInstance>(child.error().value()).logMessage();
|
||||
continue;
|
||||
|
@ -432,7 +362,7 @@ DescendantsIterator::self_type DescendantsIterator::operator++(int _) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Instance>> Instance::Clone(RefStateClone state) {
|
||||
std::optional<std::shared_ptr<Instance>> Instance::Clone(RefState<_RefStatePropertyCell> state) {
|
||||
std::shared_ptr<Instance> newInstance = GetClass()->constructor();
|
||||
|
||||
// Copy properties
|
||||
|
@ -441,9 +371,9 @@ std::optional<std::shared_ptr<Instance>> Instance::Clone(RefStateClone state) {
|
|||
|
||||
if (meta.flags & (PROP_READONLY | PROP_NOSAVE)) continue;
|
||||
|
||||
// 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>();
|
||||
// Update InstanceRef properties using map above
|
||||
if (meta.type == &Data::InstanceRef::TYPE) {
|
||||
std::weak_ptr<Instance> refWeak = GetPropertyValue(property).expect().get<Data::InstanceRef>();
|
||||
if (refWeak.expired()) continue;
|
||||
|
||||
auto ref = refWeak.lock();
|
||||
|
@ -451,17 +381,17 @@ std::optional<std::shared_ptr<Instance>> Instance::Clone(RefStateClone state) {
|
|||
|
||||
if (remappedRef) {
|
||||
// If the instance has already been remapped, set the new value
|
||||
newInstance->SetPropertyValue(property, InstanceRef(remappedRef)).expect();
|
||||
newInstance->SetPropertyValue(property, Data::InstanceRef(remappedRef)).expect();
|
||||
} else {
|
||||
// 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, InstanceRef(ref)).expect();
|
||||
newInstance->SetPropertyValue(property, Data::InstanceRef(ref)).expect();
|
||||
}
|
||||
} else {
|
||||
Variant value = GetPropertyValue(property).expect();
|
||||
Data::Variant value = GetPropertyValue(property).expect();
|
||||
newInstance->SetPropertyValue(property, value).expect();
|
||||
}
|
||||
}
|
||||
|
@ -471,9 +401,8 @@ std::optional<std::shared_ptr<Instance>> Instance::Clone(RefStateClone state) {
|
|||
|
||||
// Remap queued properties
|
||||
for (std::pair<std::shared_ptr<Instance>, std::string> ref : state->refsAwaitingRemap[shared_from_this()]) {
|
||||
ref.first->SetPropertyValue(ref.second, InstanceRef(newInstance)).expect();
|
||||
ref.first->SetPropertyValue(ref.second, Data::InstanceRef(newInstance)).expect();
|
||||
}
|
||||
state->refsAwaitingRemap[shared_from_this()].clear();
|
||||
|
||||
// Clone children
|
||||
for (std::shared_ptr<Instance> child : GetChildren()) {
|
||||
|
@ -492,9 +421,9 @@ std::vector<std::pair<std::string, std::shared_ptr<Instance>>> Instance::GetRefe
|
|||
|
||||
for (std::string property : propertyNames) {
|
||||
PropertyMeta meta = GetPropertyMeta(property).expect();
|
||||
if (meta.type.descriptor != &InstanceRef::TYPE) continue;
|
||||
if (meta.type != &Data::InstanceRef::TYPE) continue;
|
||||
|
||||
std::weak_ptr<Instance> ref = GetPropertyValue(property).expect().get<InstanceRef>();
|
||||
std::weak_ptr<Instance> ref = GetPropertyValue(property).expect().get<Data::InstanceRef>();
|
||||
if (ref.expired()) continue;
|
||||
referenceProperties.push_back(std::make_pair(property, ref.lock()));
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ struct InstanceType {
|
|||
InstanceFlags flags;
|
||||
};
|
||||
|
||||
typedef std::pair<std::shared_ptr<Instance>, std::string> _RefStatePropertyCell;
|
||||
|
||||
class DescendantsIterator;
|
||||
class JointInstance;
|
||||
|
||||
|
@ -69,8 +71,8 @@ protected:
|
|||
Instance(const InstanceType*);
|
||||
virtual ~Instance();
|
||||
|
||||
virtual result<Variant, MemberNotFound> InternalGetPropertyValue(std::string name);
|
||||
virtual fallible<MemberNotFound, AssignToReadOnlyMember> InternalSetPropertyValue(std::string name, Variant value);
|
||||
virtual result<Data::Variant, MemberNotFound> InternalGetPropertyValue(std::string name);
|
||||
virtual fallible<MemberNotFound, AssignToReadOnlyMember> InternalSetPropertyValue(std::string name, Data::Variant value);
|
||||
virtual result<PropertyMeta, MemberNotFound> InternalGetPropertyMeta(std::string name);
|
||||
virtual void InternalUpdateProperty(std::string name);
|
||||
virtual std::vector<std::string> InternalGetProperties();
|
||||
|
@ -114,8 +116,8 @@ public:
|
|||
std::string GetFullName();
|
||||
|
||||
// Properties
|
||||
result<Variant, MemberNotFound> GetPropertyValue(std::string name);
|
||||
fallible<MemberNotFound, AssignToReadOnlyMember> SetPropertyValue(std::string name, Variant value, bool sendUpdateEvent = true);
|
||||
result<Data::Variant, MemberNotFound> GetPropertyValue(std::string name);
|
||||
fallible<MemberNotFound, AssignToReadOnlyMember> SetPropertyValue(std::string name, Data::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);
|
||||
|
@ -133,11 +135,14 @@ public:
|
|||
}
|
||||
|
||||
// Serialization
|
||||
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>());
|
||||
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>>());
|
||||
};
|
||||
|
||||
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 TypeMeta type;
|
||||
const Data::TypeInfo* type;
|
||||
PropertyFlags flags;
|
||||
PropertyCategory category = PROP_CATEGORY_DATA;
|
||||
};
|
||||
|
|
|
@ -2,26 +2,16 @@
|
|||
|
||||
// Helper struct used for remapping reference when cloning/serializing
|
||||
|
||||
#include "datatypes/base.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
class Instance;
|
||||
|
||||
template <typename T, typename U, typename K>
|
||||
template <typename T>
|
||||
struct __RefState {
|
||||
std::map<K, U> remappedInstances;
|
||||
std::map<K, std::vector<T>> refsAwaitingRemap;
|
||||
int count = 0;
|
||||
std::map<std::shared_ptr<Instance>, std::shared_ptr<Instance>> remappedInstances;
|
||||
std::map<std::shared_ptr<Instance>, std::vector<T>> refsAwaitingRemap;
|
||||
};
|
||||
|
||||
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;
|
||||
template <typename T>
|
||||
using RefState = std::shared_ptr<__RefState<T>>;
|
|
@ -5,7 +5,7 @@
|
|||
#include "objects/base/service.h"
|
||||
#include "objects/meta.h"
|
||||
#include "objects/script/serverscriptservice.h"
|
||||
#include "datatypes/variant.h"
|
||||
#include "datatypes/meta.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 (std::shared_ptr<Instance> child : this->GetChildren()) {
|
||||
for (InstanceRef child : this->GetChildren()) {
|
||||
child->Serialize(root);
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,50 @@ 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;
|
||||
|
@ -66,28 +110,9 @@ 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")) {
|
||||
// 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->DeserializeService(childNode);
|
||||
}
|
||||
|
||||
newModel->Init();
|
||||
|
@ -121,7 +146,7 @@ result<std::optional<std::shared_ptr<Service>>, NoSuchService> DataModel::FindSe
|
|||
}
|
||||
|
||||
std::shared_ptr<DataModel> DataModel::CloneModel() {
|
||||
RefStateClone state = std::make_shared<__RefStateClone>();
|
||||
RefState<_RefStatePropertyCell> state = std::make_shared<__RefState<_RefStatePropertyCell>>();
|
||||
std::shared_ptr<DataModel> newModel = DataModel::New();
|
||||
|
||||
// Copy properties
|
||||
|
@ -130,9 +155,9 @@ std::shared_ptr<DataModel> DataModel::CloneModel() {
|
|||
|
||||
if (meta.flags & (PROP_READONLY | PROP_NOSAVE)) continue;
|
||||
|
||||
// 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>();
|
||||
// Update InstanceRef properties using map above
|
||||
if (meta.type == &Data::InstanceRef::TYPE) {
|
||||
std::weak_ptr<Instance> refWeak = GetPropertyValue(property).expect().get<Data::InstanceRef>();
|
||||
if (refWeak.expired()) continue;
|
||||
|
||||
auto ref = refWeak.lock();
|
||||
|
@ -140,17 +165,17 @@ std::shared_ptr<DataModel> DataModel::CloneModel() {
|
|||
|
||||
if (remappedRef) {
|
||||
// If the instance has already been remapped, set the new value
|
||||
newModel->SetPropertyValue(property, InstanceRef(remappedRef)).expect();
|
||||
newModel->SetPropertyValue(property, Data::InstanceRef(remappedRef)).expect();
|
||||
} else {
|
||||
// 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, InstanceRef(ref)).expect();
|
||||
newModel->SetPropertyValue(property, Data::InstanceRef(ref)).expect();
|
||||
}
|
||||
} else {
|
||||
Variant value = GetPropertyValue(property).expect();
|
||||
Data::Variant value = GetPropertyValue(property).expect();
|
||||
newModel->SetPropertyValue(property, value).expect();
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +185,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, InstanceRef(newModel)).expect();
|
||||
ref.first->SetPropertyValue(ref.second, Data::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, RefStateDeserialize);
|
||||
static void cloneService(std::shared_ptr<DataModel> target, std::shared_ptr<Service>, RefStateClone);
|
||||
void DeserializeService(pugi::xml_node node);
|
||||
static void cloneService(std::shared_ptr<DataModel> target, std::shared_ptr<Service>, RefState<_RefStatePropertyCell>);
|
||||
public:
|
||||
std::map<std::string, std::shared_ptr<Service>> services;
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#include "folder.h"
|
||||
|
||||
Folder::Folder(): Instance(&TYPE) {}
|
||||
Folder::~Folder() = default;
|
|
@ -1,19 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "objects/annotation.h"
|
||||
#include "objects/base/instance.h"
|
||||
#include <memory>
|
||||
|
||||
// The simplest instance
|
||||
// Has no functionality of its own, used purely for organizational/grouping purposes
|
||||
|
||||
class DEF_INST_(explorer_icon="folder") Folder : public Instance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
public:
|
||||
Folder();
|
||||
~Folder();
|
||||
|
||||
static inline std::shared_ptr<Folder> New() { return std::make_shared<Folder>(); };
|
||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Folder>(); };
|
||||
};
|
|
@ -1,11 +1,9 @@
|
|||
#include "meta.h"
|
||||
#include "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"
|
||||
|
@ -25,8 +23,6 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
|||
{ "RotateV", &RotateV::TYPE },
|
||||
{ "JointInstance", &JointInstance::TYPE },
|
||||
{ "Script", &Script::TYPE },
|
||||
{ "Model", &Model::TYPE },
|
||||
// { "Folder", &Folder::TYPE },
|
||||
|
||||
// Services
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#include "model.h"
|
||||
|
||||
Model::Model(): Instance(&TYPE) {}
|
||||
Model::~Model() = default;
|
|
@ -1,18 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "objects/annotation.h"
|
||||
#include "objects/base/instance.h"
|
||||
#include <memory>
|
||||
|
||||
// Group object for Parts
|
||||
|
||||
class DEF_INST_(explorer_icon="model") Model : public Instance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
public:
|
||||
Model();
|
||||
~Model();
|
||||
|
||||
static inline std::shared_ptr<Model> New() { return std::make_shared<Model>(); };
|
||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Model>(); };
|
||||
};
|
|
@ -224,7 +224,7 @@ void Part::MakeJoints() {
|
|||
// TEMPORARY
|
||||
// TODO: Use more efficient algorithm to *actually* find nearby parts)
|
||||
for (auto it = workspace().value()->GetDescendantsStart(); it != workspace().value()->GetDescendantsEnd(); it++) {
|
||||
std::shared_ptr<Instance> obj = *it;
|
||||
InstanceRef obj = *it;
|
||||
if (obj == shared_from_this()) continue; // Skip ourselves
|
||||
if (obj->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 std::shared_ptr<Instance> Create() { return std::make_shared<Part>(); };
|
||||
static inline InstanceRef Create() { return std::make_shared<Part>(); };
|
||||
|
||||
inline Vector3 position() { return cframe.Position(); }
|
||||
|
||||
|
|
|
@ -41,10 +41,10 @@ void Script::Run() {
|
|||
// Initialize script globals
|
||||
lua_getglobal(Lt, "_G");
|
||||
|
||||
InstanceRef(dataModel().value()).PushLuaValue(Lt);
|
||||
Data::InstanceRef(dataModel().value()).PushLuaValue(Lt);
|
||||
lua_setfield(Lt, -2, "game");
|
||||
|
||||
InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(Lt);
|
||||
Data::InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(Lt);
|
||||
lua_setfield(Lt, -2, "workspace");
|
||||
|
||||
lua_pushlightuserdata(Lt, scriptContext.get());
|
||||
|
|
|
@ -43,9 +43,9 @@ void ScriptContext::InitService() {
|
|||
// luaopen_debug(state);
|
||||
luaopen_bit(state);
|
||||
|
||||
Vector3::PushLuaLibrary(state);
|
||||
CFrame::PushLuaLibrary(state);
|
||||
Color3::PushLuaLibrary(state);
|
||||
Data::Vector3::PushLuaLibrary(state);
|
||||
Data::CFrame::PushLuaLibrary(state);
|
||||
Data::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_(explorer_icon="server-scripts") ServerScriptService : public Service {
|
||||
class DEF_INST_SERVICE ServerScriptService : public Service {
|
||||
AUTOGEN_PREAMBLE
|
||||
protected:
|
||||
void InitService() override;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "workspace.h"
|
||||
#include "datatypes/variant.h"
|
||||
#include "datatypes/meta.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({ (Variant)InstanceRef(part1) });
|
||||
part1->Touched->Fire({ (Variant)InstanceRef(part0) });
|
||||
part0->Touched->Fire({ (Data::Variant)Data::InstanceRef(part1) });
|
||||
part1->Touched->Fire({ (Data::Variant)Data::InstanceRef(part0) });
|
||||
} else if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactExit) {
|
||||
part0->TouchEnded->Fire({ (Variant)InstanceRef(part1) });
|
||||
part1->TouchEnded->Fire({ (Variant)InstanceRef(part0) });
|
||||
part0->TouchEnded->Fire({ (Data::Variant)Data::InstanceRef(part1) });
|
||||
part1->TouchEnded->Fire({ (Data::Variant)Data::InstanceRef(part0) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ void Workspace::InitService() {
|
|||
|
||||
// Sync all parts
|
||||
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
|
||||
std::shared_ptr<Instance> obj = *it;
|
||||
InstanceRef 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++) {
|
||||
std::shared_ptr<Instance> obj = *it;
|
||||
InstanceRef obj = *it;
|
||||
if (!obj->IsA<JointInstance>()) continue;
|
||||
std::shared_ptr<JointInstance> joint = obj->CastTo<JointInstance>().expect();
|
||||
joint->UpdateProperty("Part0");
|
||||
|
@ -127,19 +127,17 @@ 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++) {
|
||||
std::shared_ptr<Instance> obj = *it;
|
||||
if (!obj->IsA<Part>()) continue;
|
||||
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<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;
|
||||
|
||||
|
@ -148,16 +146,6 @@ 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,8 +66,6 @@ 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,7 +1,6 @@
|
|||
#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"
|
||||
|
@ -33,7 +32,7 @@ PartAssembly::PartAssembly(std::vector<std::shared_ptr<Part>> parts, bool worldM
|
|||
_bounds = size;
|
||||
}
|
||||
|
||||
PartAssembly PartAssembly::FromSelection(std::vector<std::shared_ptr<Instance>> newSelection) {
|
||||
PartAssembly PartAssembly::FromSelection(std::vector<std::weak_ptr<Instance>> newSelection) {
|
||||
std::vector<std::shared_ptr<Part>> selection;
|
||||
|
||||
for (std::weak_ptr<Instance> obj : newSelection) {
|
||||
|
@ -49,7 +48,6 @@ 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;
|
||||
|
@ -59,7 +57,6 @@ 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;
|
||||
|
@ -69,7 +66,6 @@ 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;
|
||||
}
|
||||
|
@ -82,10 +78,8 @@ 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::shared_ptr<Instance>> getSelection();
|
||||
const std::vector<std::weak_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::shared_ptr<Instance>> selection = getSelection());
|
||||
static PartAssembly FromSelection(std::vector<std::weak_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++) {
|
||||
std::shared_ptr<Instance> inst = *it;
|
||||
InstanceRef 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++) {
|
||||
std::shared_ptr<Instance> inst = *it;
|
||||
InstanceRef 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 (std::shared_ptr<Instance> inst : gWorkspace()->GetChildren()) {
|
||||
for (InstanceRef 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 (std::shared_ptr<Instance> inst : gWorkspace()->GetChildren()) {
|
||||
for (InstanceRef 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++) {
|
||||
std::shared_ptr<Instance> inst = *it;
|
||||
InstanceRef 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);
|
||||
|
||||
PartAssembly assembly = PartAssembly::FromSelection();
|
||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(getSelection()[0].lock());
|
||||
|
||||
for (HandleFace face : HandleFace::Faces) {
|
||||
if (glm::any(glm::lessThan(face.normal, glm::vec3(0)))) continue;
|
||||
glm::mat4 model = assembly.assemblyOrigin() * CFrame(glm::vec3(0), face.normal, glm::vec3(0, 1.01, 0.1));
|
||||
glm::mat4 model = part->cframe * CFrame(glm::vec3(0), face.normal, glm::vec3(0, 1.01, 0.1));
|
||||
handleShader->set("model", model);
|
||||
|
||||
float radius = glm::max(assembly.bounds().X(), assembly.bounds().Y(), assembly.bounds().Z()) / 2.f + 2.f;
|
||||
float radius = glm::max(part->size.x, part->size.y, part->size.z) / 2.f + 2.f;
|
||||
|
||||
handleShader->set("material", Material {
|
||||
.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(CFrame);
|
||||
void addDebugRenderCFrame(CFrame, Color3);
|
||||
void addDebugRenderCFrame(Data::CFrame);
|
||||
void addDebugRenderCFrame(Data::CFrame, Data::Color3);
|
|
@ -20,6 +20,6 @@ enum SurfaceType {
|
|||
SurfaceMotor = 7,
|
||||
};
|
||||
|
||||
class Vector3;
|
||||
namespace Data { class Vector3; } using Data::Vector3;
|
||||
NormalId faceFromNormal(Vector3);
|
||||
Vector3 normalFromFace(NormalId);
|
|
@ -1,4 +1,3 @@
|
|||
#include "error/data.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "logger.h"
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <qnamespace.h>
|
||||
#include <qsoundeffect.h>
|
||||
#include <string>
|
||||
#include "./ui_mainwindow.h"
|
||||
#include "mainglwidget.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include "handles.h"
|
||||
|
@ -17,14 +16,13 @@
|
|||
#include "physics/util.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include "rendering/shader.h"
|
||||
#include "datatypes/variant.h"
|
||||
#include "datatypes/meta.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), contextMenu(this) {
|
||||
MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent) {
|
||||
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
@ -117,14 +115,12 @@ 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;
|
||||
|
||||
|
@ -275,6 +271,7 @@ 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);
|
||||
|
@ -306,8 +303,11 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
|||
|
||||
glm::vec3 angles = handleNormal * sign * glm::vec3(angle);
|
||||
|
||||
CFrame newFrame = initialFrame * CFrame::FromEulerAnglesXYZ(-angles);
|
||||
initialAssembly.SetOrigin(newFrame);
|
||||
part->cframe = initialFrame * CFrame::FromEulerAnglesXYZ(-angles);
|
||||
|
||||
gWorkspace()->SyncPartPhysics(part);
|
||||
part->UpdateProperty("Rotation");
|
||||
sendPropertyUpdatedSignal(part, "Rotation", part->cframe.ToEulerAnglesXYZ());
|
||||
}
|
||||
|
||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
||||
|
@ -342,7 +342,6 @@ void MainGLWidget::wheelEvent(QWheelEvent* evt) {
|
|||
}
|
||||
|
||||
void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) {
|
||||
tryMouseContextMenu = false;
|
||||
handleCameraRotate(evt);
|
||||
handleObjectDrag(evt);
|
||||
handleCursorChange(evt);
|
||||
|
@ -361,7 +360,6 @@ void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) {
|
|||
}
|
||||
|
||||
void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
||||
tryMouseContextMenu = evt->button() == Qt::RightButton;
|
||||
switch(evt->button()) {
|
||||
// Camera drag
|
||||
case Qt::RightButton: {
|
||||
|
@ -377,8 +375,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
|||
auto handle = raycastHandle(pointDir);
|
||||
if (handle.has_value()) {
|
||||
startPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||
initialAssembly = PartAssembly::FromSelection();
|
||||
initialFrame = initialAssembly.assemblyOrigin();
|
||||
initialFrame = getHandleAdornee()->cframe;
|
||||
isMouseDragging = true;
|
||||
draggingHandle = handle;
|
||||
startLinearTransform(evt);
|
||||
|
@ -420,10 +417,10 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
|||
isMouseDragging = true;
|
||||
draggingObject = part;
|
||||
if (evt->modifiers() & Qt::ControlModifier) {
|
||||
std::vector<std::shared_ptr<Instance>> currentSelection = getSelection();
|
||||
std::vector<InstanceRefWeak> currentSelection = getSelection();
|
||||
for (int i = 0; i < currentSelection.size(); i++) {
|
||||
std::shared_ptr<Instance> inst = currentSelection[i];
|
||||
if (inst == part) {
|
||||
InstanceRefWeak inst = currentSelection[i];
|
||||
if (!inst.expired() && inst.lock() == part) {
|
||||
currentSelection.erase(currentSelection.begin() + i);
|
||||
goto skipAddPart;
|
||||
}
|
||||
|
@ -431,8 +428,8 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
|||
currentSelection.push_back(part);
|
||||
skipAddPart:
|
||||
setSelection(currentSelection);
|
||||
} else
|
||||
setSelection({ part });
|
||||
}else
|
||||
setSelection(std::vector<InstanceRefWeak> { part });
|
||||
// Disable bit so that we can ignore the part while raycasting
|
||||
// part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b10);
|
||||
|
||||
|
@ -448,23 +445,6 @@ 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;
|
||||
|
@ -508,11 +488,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]->Clone().value()->SetParent(gWorkspace());
|
||||
if (evt->key() == Qt::Key_C && getSelection().size() > 0 && !getSelection()[0].expired())
|
||||
getSelection()[0].lock()->Clone().value()->SetParent(gWorkspace());
|
||||
|
||||
if (evt->key() == Qt::Key_H && getSelection().size() > 0)
|
||||
Logger::infof("Object at: 0x%x\n", getSelection()[0].get());
|
||||
if (evt->key() == Qt::Key_H && getSelection().size() > 0 && !getSelection()[0].expired())
|
||||
Logger::infof("Object at: 0x%x\n", getSelection()[0].lock().get());
|
||||
}
|
||||
|
||||
void MainGLWidget::keyReleaseEvent(QKeyEvent* evt) {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <QOpenGLWidget>
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
#include <qmenu.h>
|
||||
|
||||
class HandleFace;
|
||||
class MainWindow;
|
||||
|
@ -17,7 +16,6 @@ public:
|
|||
void updateCycle();
|
||||
std::shared_ptr<Part> lastPart;
|
||||
|
||||
void buildContextMenu();
|
||||
protected:
|
||||
void initializeGL() override;
|
||||
void resizeGL(int w, int h) override;
|
||||
|
@ -38,8 +36,6 @@ protected:
|
|||
void keyPressEvent(QKeyEvent* evt) override;
|
||||
void keyReleaseEvent(QKeyEvent* evt) override;
|
||||
|
||||
QMenu contextMenu;
|
||||
|
||||
MainWindow* mainWindow();
|
||||
float snappingFactor();
|
||||
};
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "common.h"
|
||||
#include "logger.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "objects/model.h"
|
||||
#include "placedocument.h"
|
||||
#include "script/scriptdocument.h"
|
||||
#include <cstdio>
|
||||
|
@ -20,7 +19,6 @@
|
|||
#include <pugixml.hpp>
|
||||
#include <qtextcursor.h>
|
||||
#include <qtextedit.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _NDEBUG
|
||||
#define NDEBUG
|
||||
|
@ -85,8 +83,6 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
this->close();
|
||||
});
|
||||
|
||||
ui->explorerView->buildContextMenu();
|
||||
|
||||
connectActionHandlers();
|
||||
|
||||
// Update properties
|
||||
|
@ -94,17 +90,17 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
if (newSelection.size() == 0) return;
|
||||
if (newSelection.size() > 1)
|
||||
ui->propertiesView->setSelected(std::nullopt);
|
||||
ui->propertiesView->setSelected(newSelection[0]);
|
||||
ui->propertiesView->setSelected(newSelection[0].lock());
|
||||
});
|
||||
|
||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool __) {
|
||||
for (std::weak_ptr<Instance> inst : oldSelection) {
|
||||
for (InstanceRefWeak inst : oldSelection) {
|
||||
if (inst.expired() || inst.lock()->GetClass() != &Part::TYPE) continue;
|
||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst.lock());
|
||||
part->selected = false;
|
||||
}
|
||||
|
||||
for (std::weak_ptr<Instance> inst : newSelection) {
|
||||
for (InstanceRefWeak 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;
|
||||
|
@ -145,6 +141,10 @@ 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 (std::weak_ptr<Instance> inst : getSelection()) {
|
||||
for (InstanceRefWeak inst : getSelection()) {
|
||||
if (inst.expired()) continue;
|
||||
inst.lock()->SetParent(std::nullopt);
|
||||
}
|
||||
setSelection({});
|
||||
setSelection(std::vector<InstanceRefWeak> {});
|
||||
});
|
||||
|
||||
connect(ui->actionCopy, &QAction::triggered, this, [&]() {
|
||||
pugi::xml_document rootDoc;
|
||||
for (std::weak_ptr<Instance> inst : getSelection()) {
|
||||
for (InstanceRefWeak 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 (std::weak_ptr<Instance> inst : getSelection()) {
|
||||
for (InstanceRefWeak 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<std::shared_ptr<Instance>, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
||||
result<InstanceRef, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
||||
if (!inst) { inst.logError(); continue; }
|
||||
gWorkspace()->AddChild(inst.expect());
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->actionPasteInto, &QAction::triggered, this, [&]() {
|
||||
if (getSelection().size() != 1) return;
|
||||
if (getSelection().size() != 1 || getSelection()[0].expired()) return;
|
||||
|
||||
std::shared_ptr<Instance> selectedParent = getSelection()[0];
|
||||
InstanceRef selectedParent = getSelection()[0].lock();
|
||||
|
||||
const QMimeData* mimeData = QApplication::clipboard()->mimeData();
|
||||
if (!mimeData || !mimeData->hasFormat("application/xml")) return;
|
||||
|
@ -358,50 +358,12 @@ void MainWindow::connectActionHandlers() {
|
|||
rootDoc.load_string(encoded.c_str());
|
||||
|
||||
for (pugi::xml_node instNode : rootDoc.children()) {
|
||||
result<std::shared_ptr<Instance>, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
||||
result<InstanceRef, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
||||
if (!inst) { inst.logError(); continue; }
|
||||
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;
|
||||
|
@ -411,7 +373,7 @@ void MainWindow::connectActionHandlers() {
|
|||
pugi::xml_document modelDoc;
|
||||
pugi::xml_node modelRoot = modelDoc.append_child("openblocks");
|
||||
|
||||
for (std::weak_ptr<Instance> inst : getSelection()) {
|
||||
for (InstanceRefWeak inst : getSelection()) {
|
||||
if (inst.expired()) continue;
|
||||
inst.lock()->Serialize(modelRoot);
|
||||
}
|
||||
|
@ -420,8 +382,8 @@ void MainWindow::connectActionHandlers() {
|
|||
});
|
||||
|
||||
connect(ui->actionInsertModel, &QAction::triggered, this, [&]() {
|
||||
if (getSelection().size() != 1) return;
|
||||
std::shared_ptr<Instance> selectedParent = getSelection()[0];
|
||||
if (getSelection().size() != 1 || getSelection()[0].expired()) return;
|
||||
InstanceRef selectedParent = getSelection()[0].lock();
|
||||
|
||||
std::optional<std::string> path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptOpen);
|
||||
if (!path) return;
|
||||
|
@ -431,46 +393,13 @@ void MainWindow::connectActionHandlers() {
|
|||
modelDoc.load(inStream);
|
||||
|
||||
for (pugi::xml_node instNode : modelDoc.child("openblocks").children("Item")) {
|
||||
result<std::shared_ptr<Instance>, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
||||
result<InstanceRef, NoSuchInstance> inst = Instance::Deserialize(instNode);
|
||||
if (!inst) { inst.logError(); continue; }
|
||||
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,8 +57,6 @@ 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,8 +176,6 @@
|
|||
<addaction name="actionCut"/>
|
||||
<addaction name="actionPaste"/>
|
||||
<addaction name="actionPasteInto"/>
|
||||
<addaction name="actionGroupObjects"/>
|
||||
<addaction name="actionUngroupObjects"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="snappingOptions">
|
||||
<property name="windowTitle">
|
||||
|
@ -770,40 +768,6 @@
|
|||
<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(std::shared_ptr<Instance> dataRoot, QWidget *parent)
|
||||
ExplorerModel::ExplorerModel(InstanceRef dataRoot, QWidget *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, rootItem(dataRoot) {
|
||||
// TODO: Don't use lambdas and handlers like that
|
||||
hierarchyPreUpdateHandler = [&](std::shared_ptr<Instance> object, std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) {
|
||||
hierarchyPreUpdateHandler = [&](InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent) {
|
||||
if (oldParent.has_value()) {
|
||||
auto children = oldParent.value()->GetChildren();
|
||||
size_t idx = std::find(children.begin(), children.end(), object) - children.begin();
|
||||
|
@ -29,7 +29,7 @@ ExplorerModel::ExplorerModel(std::shared_ptr<Instance> dataRoot, QWidget *parent
|
|||
}
|
||||
};
|
||||
|
||||
hierarchyPostUpdateHandler = [&](std::shared_ptr<Instance> object, std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) {
|
||||
hierarchyPostUpdateHandler = [&](InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent) {
|
||||
if (newParent.has_value()) endInsertRows();
|
||||
if (oldParent.has_value()) endRemoveRows();
|
||||
};
|
||||
|
@ -50,11 +50,11 @@ QModelIndex ExplorerModel::index(int row, int column, const QModelIndex &parent)
|
|||
return {};
|
||||
}
|
||||
|
||||
QModelIndex ExplorerModel::toIndex(std::shared_ptr<Instance> item) {
|
||||
QModelIndex ExplorerModel::toIndex(InstanceRef item) {
|
||||
if (item == rootItem || !item->GetParent().has_value())
|
||||
return {};
|
||||
|
||||
std::shared_ptr<Instance> parentItem = item->GetParent().value();
|
||||
InstanceRef parentItem = item->GetParent().value();
|
||||
// Check above ensures this item is not root, so value() must be valid
|
||||
for (int i = 0; i < parentItem->GetChildren().size(); i++)
|
||||
if (parentItem->GetChildren()[i] == item)
|
||||
|
@ -62,7 +62,7 @@ QModelIndex ExplorerModel::toIndex(std::shared_ptr<Instance> item) {
|
|||
return QModelIndex{};
|
||||
}
|
||||
|
||||
QModelIndex ExplorerModel::ObjectToIndex(std::shared_ptr<Instance> item) {
|
||||
QModelIndex ExplorerModel::ObjectToIndex(InstanceRef 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
|
||||
std::shared_ptr<Instance> parentItem = childItem->GetParent().value();
|
||||
InstanceRef parentItem = childItem->GetParent().value();
|
||||
|
||||
if (parentItem == rootItem)
|
||||
return {};
|
||||
|
||||
// Check above ensures this item is not root, so value() must be valid
|
||||
std::shared_ptr<Instance> parentParent = parentItem->GetParent().value();
|
||||
InstanceRef parentParent = parentItem->GetParent().value();
|
||||
for (int i = 0; i < parentParent->GetChildren().size(); i++)
|
||||
if (parentParent->GetChildren()[i] == parentItem)
|
||||
return createIndex(i, 0, parentItem.get());
|
||||
|
@ -196,13 +196,13 @@ Qt::DropActions ExplorerModel::supportedDropActions() const {
|
|||
}
|
||||
|
||||
|
||||
std::shared_ptr<Instance> ExplorerModel::fromIndex(const QModelIndex index) const {
|
||||
InstanceRef ExplorerModel::fromIndex(const QModelIndex index) const {
|
||||
if (!index.isValid()) return rootItem;
|
||||
return static_cast<Instance*>(index.internalPointer())->shared_from_this();
|
||||
}
|
||||
|
||||
struct DragDropSlot {
|
||||
std::vector<std::shared_ptr<Instance>> instances;
|
||||
std::vector<InstanceRef> instances;
|
||||
};
|
||||
|
||||
bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
|
||||
|
@ -216,8 +216,8 @@ bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, i
|
|||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Instance> parentInst = fromIndex(parent);
|
||||
for (std::shared_ptr<Instance> instance : slot->instances) {
|
||||
InstanceRef parentInst = fromIndex(parent);
|
||||
for (InstanceRef 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(std::shared_ptr<Instance> newRoot) {
|
||||
void ExplorerModel::updateRoot(InstanceRef newRoot) {
|
||||
beginResetModel();
|
||||
rootItem = newRoot;
|
||||
endResetModel();
|
||||
|
|
|
@ -8,7 +8,7 @@ class ExplorerModel : public QAbstractItemModel {
|
|||
public:
|
||||
Q_DISABLE_COPY_MOVE(ExplorerModel)
|
||||
|
||||
explicit ExplorerModel(std::shared_ptr<Instance> dataRoot, QWidget *parent = nullptr);
|
||||
explicit ExplorerModel(InstanceRef 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;
|
||||
std::shared_ptr<Instance> fromIndex(const QModelIndex index) const;
|
||||
QModelIndex ObjectToIndex(std::shared_ptr<Instance> item);
|
||||
InstanceRef fromIndex(const QModelIndex index) const;
|
||||
QModelIndex ObjectToIndex(InstanceRef item);
|
||||
|
||||
QIcon iconOf(const InstanceType* type) const;
|
||||
|
||||
void updateRoot(std::shared_ptr<Instance> newRoot);
|
||||
void updateRoot(InstanceRef newRoot);
|
||||
private:
|
||||
std::shared_ptr<Instance> rootItem;
|
||||
QModelIndex toIndex(std::shared_ptr<Instance> item);
|
||||
InstanceRef rootItem;
|
||||
QModelIndex toIndex(InstanceRef item);
|
||||
};
|
||||
|
||||
// #endif
|
|
@ -38,11 +38,11 @@ ExplorerView::ExplorerView(QWidget* parent):
|
|||
});
|
||||
|
||||
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) {
|
||||
std::vector<std::shared_ptr<Instance>> selectedInstances;
|
||||
std::vector<InstanceRefWeak> selectedInstances;
|
||||
selectedInstances.reserve(selectedIndexes().count()); // This doesn't reserve everything, but should enhance things anyway
|
||||
|
||||
for (auto index : selectedIndexes()) {
|
||||
selectedInstances.push_back(reinterpret_cast<Instance*>(index.internalPointer())->shared_from_this());
|
||||
selectedInstances.push_back(reinterpret_cast<Instance*>(index.internalPointer())->weak_from_this());
|
||||
}
|
||||
|
||||
::setSelection(selectedInstances, true);
|
||||
|
@ -53,7 +53,7 @@ ExplorerView::ExplorerView(QWidget* parent):
|
|||
if (fromExplorer) return;
|
||||
|
||||
this->clearSelection();
|
||||
for (std::weak_ptr<Instance> inst : newSelection) {
|
||||
for (InstanceRefWeak 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) return;
|
||||
std::shared_ptr<Instance> instParent = getSelection()[0];
|
||||
if (getSelection().size() == 0 || getSelection()[0].expired()) return;
|
||||
std::shared_ptr<Instance> instParent = getSelection()[0].lock();
|
||||
std::shared_ptr<Instance> newInst = type->constructor();
|
||||
newInst->SetParent(instParent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ExplorerView::updateRoot(std::shared_ptr<Instance> newRoot) {
|
||||
void ExplorerView::updateRoot(InstanceRef newRoot) {
|
||||
model.updateRoot(newRoot);
|
||||
}
|
|
@ -16,7 +16,7 @@ public:
|
|||
// void dropEvent(QDropEvent*) override;
|
||||
|
||||
void buildContextMenu();
|
||||
void updateRoot(std::shared_ptr<Instance> newRoot);
|
||||
void updateRoot(InstanceRef newRoot);
|
||||
private:
|
||||
ExplorerModel model;
|
||||
QMenu contextMenu;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#include "panes/propertiesview.h"
|
||||
#include "common.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/variant.h"
|
||||
#include "datatypes/primitives.h"
|
||||
#include "error/data.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include "objects/base/member.h"
|
||||
|
||||
#include <QColorDialog>
|
||||
|
@ -40,7 +38,7 @@ public:
|
|||
if (index.column() == 0) return nullptr;
|
||||
|
||||
if (!index.parent().isValid() || view->currentInstance.expired()) return nullptr;
|
||||
std::shared_ptr<Instance> inst = view->currentInstance.lock();
|
||||
InstanceRef inst = view->currentInstance.lock();
|
||||
|
||||
// If the property is deeper than 1 layer, then it is considered composite
|
||||
// Handle specially
|
||||
|
@ -51,10 +49,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();
|
||||
Variant currentValue = inst->GetPropertyValue(propertyName).expect();
|
||||
Data::Variant currentValue = inst->GetPropertyValue(propertyName).expect();
|
||||
|
||||
if (isComposite) {
|
||||
if (meta.type.descriptor == &Vector3::TYPE) {
|
||||
if (meta.type == &Vector3::TYPE) {
|
||||
Vector3 vector = currentValue.get<Vector3>();
|
||||
float value = componentName == "X" ? vector.X() : componentName == "Y" ? vector.Y() : componentName == "Z" ? vector.Z() : 0;
|
||||
|
||||
|
@ -67,9 +65,9 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (meta.type.descriptor == &FLOAT_TYPE) {
|
||||
if (meta.type == &Data::Float::TYPE) {
|
||||
QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
|
||||
spinBox->setValue(currentValue.get<float>());
|
||||
spinBox->setValue(currentValue.get<Data::Float>());
|
||||
|
||||
if (meta.flags & PROP_UNIT_FLOAT) {
|
||||
spinBox->setMinimum(0);
|
||||
|
@ -78,24 +76,24 @@ public:
|
|||
}
|
||||
|
||||
return spinBox;
|
||||
} else if (meta.type.descriptor == &INT_TYPE) {
|
||||
} else if (meta.type == &Data::Int::TYPE) {
|
||||
QSpinBox* spinBox = new QSpinBox(parent);
|
||||
spinBox->setValue(currentValue.get<int>());
|
||||
spinBox->setValue(currentValue.get<Data::Int>());
|
||||
|
||||
return spinBox;
|
||||
} else if (meta.type.descriptor == &STRING_TYPE) {
|
||||
} else if (meta.type == &Data::String::TYPE) {
|
||||
QLineEdit* lineEdit = new QLineEdit(parent);
|
||||
lineEdit->setText(QString::fromStdString(currentValue.get<std::string>()));
|
||||
lineEdit->setText(QString::fromStdString(currentValue.get<Data::String>()));
|
||||
|
||||
return lineEdit;
|
||||
} else if (meta.type.descriptor == &Color3::TYPE) {
|
||||
} else if (meta.type == &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.descriptor->fromString) {
|
||||
} else if (meta.type->fromString) {
|
||||
QLineEdit* lineEdit = new QLineEdit(parent);
|
||||
lineEdit->setText(QString::fromStdString(currentValue.ToString()));
|
||||
|
||||
|
@ -109,7 +107,7 @@ public:
|
|||
if (index.column() == 0) return;
|
||||
|
||||
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
||||
std::shared_ptr<Instance> inst = view->currentInstance.lock();
|
||||
InstanceRef inst = view->currentInstance.lock();
|
||||
|
||||
bool isComposite = index.parent().parent().isValid();
|
||||
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
||||
|
@ -117,10 +115,10 @@ public:
|
|||
std::string propertyName = !index.parent().parent().isValid() ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString()
|
||||
: view->itemFromIndex(index.parent())->data(0, Qt::DisplayRole).toString().toStdString();
|
||||
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
||||
Variant currentValue = inst->GetPropertyValue(propertyName).expect();
|
||||
Data::Variant currentValue = inst->GetPropertyValue(propertyName).expect();
|
||||
|
||||
if (isComposite) {
|
||||
if (meta.type.descriptor == &Vector3::TYPE) {
|
||||
if (meta.type == &Vector3::TYPE) {
|
||||
Vector3 vector = currentValue.get<Vector3>();
|
||||
float value = componentName == "X" ? vector.X() : componentName == "Y" ? vector.Y() : componentName == "Z" ? vector.Z() : 0;
|
||||
|
||||
|
@ -133,24 +131,24 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
if (meta.type.descriptor == &FLOAT_TYPE) {
|
||||
if (meta.type == &Data::Float::TYPE) {
|
||||
QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(editor);
|
||||
|
||||
spinBox->setValue(currentValue.get<float>());
|
||||
} else if (meta.type.descriptor == &INT_TYPE) {
|
||||
spinBox->setValue(currentValue.get<Data::Float>());
|
||||
} else if (meta.type == &Data::Int::TYPE) {
|
||||
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(editor);
|
||||
|
||||
spinBox->setValue(currentValue.get<int>());
|
||||
} else if (meta.type.descriptor == &STRING_TYPE) {
|
||||
spinBox->setValue(currentValue.get<Data::Int>());
|
||||
} else if (meta.type == &Data::String::TYPE) {
|
||||
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
||||
|
||||
lineEdit->setText(QString::fromStdString((std::string)currentValue.get<std::string>()));
|
||||
} else if (meta.type.descriptor == &Color3::TYPE) {
|
||||
lineEdit->setText(QString::fromStdString((std::string)currentValue.get<Data::String>()));
|
||||
} else if (meta.type == &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.descriptor->fromString) {
|
||||
} else if (meta.type->fromString) {
|
||||
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
||||
|
||||
lineEdit->setText(QString::fromStdString((std::string)currentValue.ToString()));
|
||||
|
@ -161,7 +159,7 @@ public:
|
|||
if (index.column() == 0) return;
|
||||
|
||||
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
||||
std::shared_ptr<Instance> inst = view->currentInstance.lock();
|
||||
InstanceRef inst = view->currentInstance.lock();
|
||||
|
||||
bool isComposite = index.parent().parent().isValid();
|
||||
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
||||
|
@ -171,7 +169,7 @@ public:
|
|||
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
||||
|
||||
if (isComposite) {
|
||||
if (meta.type.descriptor == &Vector3::TYPE) {
|
||||
if (meta.type == &Vector3::TYPE) {
|
||||
QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(editor);
|
||||
float value = spinBox->value();
|
||||
|
||||
|
@ -188,22 +186,22 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
if (meta.type.descriptor == &FLOAT_TYPE) {
|
||||
if (meta.type == &Data::Float::TYPE) {
|
||||
QDoubleSpinBox* spinBox = dynamic_cast<QDoubleSpinBox*>(editor);
|
||||
|
||||
inst->SetPropertyValue(propertyName, (float)spinBox->value()).expect();
|
||||
inst->SetPropertyValue(propertyName, Data::Float((float)spinBox->value())).expect();
|
||||
model->setData(index, spinBox->value());
|
||||
} else if (meta.type.descriptor == &INT_TYPE) {
|
||||
} else if (meta.type == &Data::Int::TYPE) {
|
||||
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(editor);
|
||||
|
||||
inst->SetPropertyValue(propertyName, (int)spinBox->value()).expect();
|
||||
inst->SetPropertyValue(propertyName, Data::Int((float)spinBox->value())).expect();
|
||||
model->setData(index, spinBox->value());
|
||||
} else if (meta.type.descriptor == &STRING_TYPE) {
|
||||
} else if (meta.type == &Data::String::TYPE) {
|
||||
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
||||
|
||||
inst->SetPropertyValue(propertyName, lineEdit->text().toStdString()).expect();
|
||||
inst->SetPropertyValue(propertyName, Data::String(lineEdit->text().toStdString())).expect();
|
||||
model->setData(index, lineEdit->text());
|
||||
} else if (meta.type.descriptor == &Color3::TYPE) {
|
||||
} else if (meta.type == &Color3::TYPE) {
|
||||
QColorDialog* colorDialog = dynamic_cast<QColorDialog*>(editor);
|
||||
|
||||
QColor color = colorDialog->currentColor();
|
||||
|
@ -211,15 +209,14 @@ 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.descriptor->fromString) {
|
||||
} else if (meta.type->fromString) {
|
||||
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editor);
|
||||
|
||||
result<Variant, DataParseError> parsedResult = meta.type.descriptor->fromString(lineEdit->text().toStdString(), meta.type);
|
||||
std::optional<Data::Variant> parsedResult = meta.type->fromString(lineEdit->text().toStdString());
|
||||
if (!parsedResult) return;
|
||||
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);
|
||||
inst->SetPropertyValue(propertyName, parsedResult.value()).expect();
|
||||
model->setData(index, QString::fromStdString(parsedResult.value().ToString()));
|
||||
view->rebuildCompositeProperty(view->itemFromIndex(index), meta.type, parsedResult.value());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -271,11 +268,11 @@ void PropertiesView::drawBranches(QPainter *painter, const QRect &rect, const QM
|
|||
QTreeWidget::drawBranches(painter, rect, index);
|
||||
}
|
||||
|
||||
void PropertiesView::setSelected(std::optional<std::shared_ptr<Instance>> instance) {
|
||||
void PropertiesView::setSelected(std::optional<InstanceRef> instance) {
|
||||
clear();
|
||||
currentInstance = {};
|
||||
if (!instance) return;
|
||||
std::shared_ptr<Instance> inst = instance.value();
|
||||
InstanceRef inst = instance.value();
|
||||
currentInstance = inst;
|
||||
|
||||
std::map<PropertyCategory, QTreeWidgetItem*> propertyCategories;
|
||||
|
@ -299,35 +296,35 @@ void PropertiesView::setSelected(std::optional<std::shared_ptr<Instance>> instan
|
|||
|
||||
for (std::string property : properties) {
|
||||
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
||||
Variant currentValue = inst->GetPropertyValue(property).expect();
|
||||
Data::Variant currentValue = inst->GetPropertyValue(property).expect();
|
||||
|
||||
if (meta.type.descriptor == &CFrame::TYPE || meta.flags & PROP_HIDDEN) continue;
|
||||
if (meta.type == &CFrame::TYPE || meta.flags & PROP_HIDDEN) continue;
|
||||
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem;
|
||||
item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
||||
item->setData(0, Qt::DisplayRole, QString::fromStdString(property));
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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.descriptor == &Vector3::TYPE) {
|
||||
} else if (meta.type == &Vector3::TYPE) {
|
||||
Vector3 vector = currentValue.get<Vector3>();
|
||||
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||
// } else if (meta.type.descriptor == &CFrame::TYPE) {
|
||||
// } else if (meta.type == &CFrame::TYPE) {
|
||||
// Vector3 vector = currentValue.get<CFrame>().Position();
|
||||
// item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||
} else {
|
||||
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||
}
|
||||
|
||||
if (meta.type.descriptor != &Color3::TYPE && (!meta.type.descriptor->fromString || meta.flags & PROP_READONLY)) {
|
||||
if (meta.type != &Color3::TYPE && (!meta.type->fromString || meta.flags & PROP_READONLY)) {
|
||||
item->setDisabled(true);
|
||||
}
|
||||
|
||||
rebuildCompositeProperty(item, meta.type.descriptor, currentValue);
|
||||
rebuildCompositeProperty(item, meta.type, currentValue);
|
||||
|
||||
propertyCategories[meta.category]->addChild(item);
|
||||
propertyCategories[meta.category]->setExpanded(true);
|
||||
|
@ -350,17 +347,17 @@ void PropertiesView::propertyChanged(QTreeWidgetItem *item, int column) {
|
|||
// Necessary because otherwise this will catch setCheckState from onPropertyUpdated
|
||||
if (ignorePropertyUpdates) return;
|
||||
if (!item->parent() || (item->parent() && item->parent()->parent()) || currentInstance.expired()) return;
|
||||
std::shared_ptr<Instance> inst = currentInstance.lock();
|
||||
InstanceRef inst = currentInstance.lock();
|
||||
|
||||
std::string propertyName = item->data(0, Qt::DisplayRole).toString().toStdString();
|
||||
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
||||
|
||||
if (meta.type.descriptor == &BOOL_TYPE) {
|
||||
inst->SetPropertyValue(propertyName, item->checkState(1) == Qt::Checked).expect();
|
||||
if (meta.type == &Data::Bool::TYPE) {
|
||||
inst->SetPropertyValue(propertyName, Data::Bool(item->checkState(1) == Qt::Checked)).expect();
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const TypeDesc* type, Variant value) {
|
||||
void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo* type, Data::Variant value) {
|
||||
if (type == &Vector3::TYPE) {
|
||||
// https://forum.qt.io/post/266837
|
||||
foreach(auto i, item->takeChildren()) delete i;
|
||||
|
@ -387,15 +384,15 @@ void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const TypeD
|
|||
}
|
||||
|
||||
// static auto lastUpdateTime = std::chrono::steady_clock::now();
|
||||
void PropertiesView::onPropertyUpdated(std::shared_ptr<Instance> inst, std::string property, Variant newValue) {
|
||||
void PropertiesView::onPropertyUpdated(InstanceRef inst, std::string property, Data::Variant newValue) {
|
||||
// if (!currentInstance || currentInstance->expired() || instance != currentInstance->lock()) return;
|
||||
// if (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();
|
||||
Variant currentValue = inst->GetPropertyValue(property).expect();
|
||||
Data::Variant currentValue = inst->GetPropertyValue(property).expect();
|
||||
|
||||
if (meta.type.descriptor == &CFrame::TYPE) return;
|
||||
if (meta.type == &CFrame::TYPE) return;
|
||||
|
||||
for (int categoryItemIdx = 0; categoryItemIdx < topLevelItemCount(); categoryItemIdx++) {
|
||||
QTreeWidgetItem* categoryItem = topLevelItem(categoryItemIdx);
|
||||
|
@ -404,27 +401,27 @@ void PropertiesView::onPropertyUpdated(std::shared_ptr<Instance> inst, std::stri
|
|||
|
||||
if (item->data(0, Qt::DisplayRole).toString().toStdString() != property) continue;
|
||||
|
||||
if (meta.type.descriptor == &BOOL_TYPE) {
|
||||
if (meta.type == &Data::Bool::TYPE) {
|
||||
// This is done because otherwise propertyChanged will catch this change erroneously
|
||||
ignorePropertyUpdates = true;
|
||||
item->setCheckState(1, (bool)currentValue.get<bool>() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||
item->setCheckState(1, (bool)currentValue.get<Data::Bool>() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||
ignorePropertyUpdates = false;
|
||||
} else if (meta.type.descriptor == &Color3::TYPE) {
|
||||
} else if (meta.type == &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.descriptor == &Vector3::TYPE) {
|
||||
} else if (meta.type == &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.descriptor != &Color3::TYPE && (!meta.type.descriptor->fromString || meta.flags & PROP_READONLY)) {
|
||||
if (meta.type != &Color3::TYPE && (!meta.type->fromString || meta.flags & PROP_READONLY)) {
|
||||
item->setDisabled(true);
|
||||
}
|
||||
|
||||
rebuildCompositeProperty(item, meta.type.descriptor, currentValue);
|
||||
rebuildCompositeProperty(item, meta.type, currentValue);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -12,11 +12,11 @@ class PropertiesView : public QTreeWidget {
|
|||
Q_DECLARE_PRIVATE(QTreeView)
|
||||
|
||||
bool ignorePropertyUpdates = false;
|
||||
std::weak_ptr<Instance> currentInstance;
|
||||
InstanceRefWeak currentInstance;
|
||||
void propertyChanged(QTreeWidgetItem *item, int column);
|
||||
void activateProperty(QTreeWidgetItem *item, int column);
|
||||
void rebuildCompositeProperty(QTreeWidgetItem *item, const TypeDesc*, Variant);
|
||||
void onPropertyUpdated(std::shared_ptr<Instance> instance, std::string property, Variant newValue);
|
||||
void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant);
|
||||
void onPropertyUpdated(InstanceRef instance, std::string property, Data::Variant newValue);
|
||||
|
||||
friend PropertiesItemDelegate;
|
||||
protected:
|
||||
|
@ -26,5 +26,5 @@ public:
|
|||
PropertiesView(QWidget* parent = nullptr);
|
||||
~PropertiesView() override;
|
||||
|
||||
void setSelected(std::optional<std::shared_ptr<Instance>> instance);
|
||||
void setSelected(std::optional<InstanceRef> instance);
|
||||
};
|
|
@ -9,17 +9,14 @@
|
|||
#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");
|
||||
|
||||
|
@ -77,7 +74,6 @@ void PlaceDocument::timerEvent(QTimerEvent* evt) {
|
|||
|
||||
void PlaceDocument::init() {
|
||||
timer.start(33, this);
|
||||
placeWidget->buildContextMenu();
|
||||
|
||||
std::shared_ptr<Part> lastPart;
|
||||
// Baseplate
|
||||
|
@ -138,19 +134,4 @@ 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,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "mainglwidget.h"
|
||||
#include <qevent.h>
|
||||
#include <qmdisubwindow.h>
|
||||
#include <QBasicTimer>
|
||||
|
||||
|
@ -26,7 +25,4 @@ 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/variant.h"
|
||||
#include "datatypes/meta.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<Variant> args) {
|
||||
std::weak_ptr<Instance> child = args[0].get<InstanceRef>();
|
||||
std::weak_ptr<Instance> newParent = args[1].get<InstanceRef>();
|
||||
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>();
|
||||
if (child.expired() || child.lock() != script || !newParent.expired()) return;
|
||||
|
||||
dynamic_cast<MainWindow*>(window())->closeScriptDocument(script);
|
||||
|
|
Loading…
Add table
Reference in a new issue