diff --git a/autogen/src/analysis.cpp b/autogen/src/analysis.cpp index 45cc3a1..4d887a5 100644 --- a/autogen/src/analysis.cpp +++ b/autogen/src/analysis.cpp @@ -163,6 +163,40 @@ void processField(CXCursor cur, ClassAnalysis* state) { anly.backingFieldType = x_clang_toString(clang_getTypeSpelling(type)).c_str(); state->properties.push_back(anly); + + // For cframe, add member fields + + std::optional cframePositionDef = findAnnotation(cur, "OB::cframe_position_prop"); + if (cframePositionDef) { + auto cframePosition = parseAnnotationString(cframePositionDef.value()); + + PropertyAnalysis cframeProp; + cframeProp.backingFieldType = anly.backingFieldType; + cframeProp.fieldName = anly.fieldName; + cframeProp.name = cframePosition["name"]; + cframeProp.category = anly.category; + cframeProp.cframeMember = CFrameMember_Position; + cframeProp.onUpdateCallback = anly.onUpdateCallback; + cframeProp.flags = PropertyFlag_NoSave; + + state->properties.push_back(cframeProp); + }; + + std::optional cframeRotationDef = findAnnotation(cur, "OB::cframe_rotation_prop"); + if (cframeRotationDef) { + auto cframeRotation = parseAnnotationString(cframeRotationDef.value()); + + PropertyAnalysis cframeProp; + cframeProp.backingFieldType = anly.backingFieldType; + cframeProp.fieldName = anly.fieldName; + cframeProp.name = cframeRotation["name"]; + cframeProp.category = anly.category; + cframeProp.cframeMember = CFrameMember_Rotation; + cframeProp.onUpdateCallback = anly.onUpdateCallback; + cframeProp.flags = PropertyFlag_NoSave; + + state->properties.push_back(cframeProp); + }; } void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) { diff --git a/autogen/src/analysis.h b/autogen/src/analysis.h index 3a48e5b..88e4319 100644 --- a/autogen/src/analysis.h +++ b/autogen/src/analysis.h @@ -17,13 +17,20 @@ enum PropertyFlags { PropertyFlag_Readonly = 1 << 3, }; +enum CFrameMember { + CFrameMember_None, // Not part of CFrame + CFrameMember_Position, + CFrameMember_Rotation +}; + struct PropertyAnalysis { std::string name; std::string fieldName; + CFrameMember cframeMember = CFrameMember_None; // for cframe_position_prop etc. std::string backingFieldType; std::string onUpdateCallback; std::string category; - PropertyFlags flags; + PropertyFlags flags = (PropertyFlags)0; }; // https://stackoverflow.com/a/1448478/16255372 diff --git a/autogen/src/codegen.cpp b/autogen/src/codegen.cpp index 21157ab..bef3ea4 100644 --- a/autogen/src/codegen.cpp +++ b/autogen/src/codegen.cpp @@ -4,6 +4,14 @@ #include #include +std::map CATEGORY_STR = { + { "appearance", "PROP_CATEGORY_APPEARENCE" }, + { "data", "PROP_CATEGORY_DATA" }, + { "behavior", "PROP_CATEGORY_BEHAVIOR" }, + { "part", "PROP_CATEGORY_PART" }, + { "surface", "PROP_CATEGORY_SURFACE" }, +}; + std::map MAPPED_TYPE = { { "bool", "Data::Bool" }, { "int", "Data::Int" }, @@ -27,6 +35,19 @@ std::string castFromVariant(std::string valueStr, std::string fieldType) { return valueStr + ".get<" + (!mappedType.empty() ? mappedType : fieldType) + ">()"; } +std::string castToVariant(std::string valueStr, std::string fieldType) { + // Manual exception for now, enums will get their own system eventually + if (fieldType == "SurfaceType") { + return "Data::Int((int)" + valueStr + ")"; + } + + std::string mappedType = MAPPED_TYPE[fieldType]; + if (!mappedType.empty()) { + return mappedType + "(" + valueStr + ")"; + } + return valueStr; +} + void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) { out << "fallible " << state.name << "::InternalSetPropertyValue(std::string name, Data::Variant value) {"; @@ -37,8 +58,14 @@ void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) { if (prop.flags & PropertyFlag_Readonly) { out << "\n return AssignToReadOnlyMember(\"" << state.name << "\", name)"; + } else if (prop.cframeMember == CFrameMember_Position) { + out << "\n this->" << prop.fieldName << " = this->" << prop.fieldName << ".Rotation() + value.get();"; + } else if (prop.cframeMember == CFrameMember_Rotation) { + out << "\n this->" << prop.fieldName << " = CFrame::FromEulerAnglesXYZ(value.get()) + this->" << prop.fieldName << ".Position();"; } else { out << "\n this->" << prop.fieldName << " = " << castFromVariant("value", prop.backingFieldType) << ";"; + if (!prop.onUpdateCallback.empty()) + out << "\n " << prop.onUpdateCallback << "(name);"; } out << "\n }"; @@ -50,6 +77,69 @@ void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) { out << "\n};\n\n"; } +void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) { + out << "result " << state.name << "::InternalGetPropertyValue(std::string name) {"; + + out << "\n "; + bool first = true; + for (auto& prop : state.properties) { + out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {"; + + if (prop.cframeMember == CFrameMember_Position) { + out << "\n return Data::Variant(" << prop.fieldName << ".Position());"; + } else if (prop.cframeMember == CFrameMember_Rotation) { + out << "\n return Data::Variant(" << prop.fieldName << ".ToEulerAnglesXYZ());"; + } else { + out << "\n return Data::Variant(" << castToVariant(prop.fieldName, prop.backingFieldType) << ");"; + } + + out << "\n }"; + first = false; + } + + out << "\n return MemberNotFound(\"" << state.name << "\", name);"; + + out << "\n};\n\n"; +} + +void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) { + out << "result " << state.name << "::InternalGetPropertyMeta(std::string name) {"; + + out << "\n "; + bool first = true; + for (auto& prop : state.properties) { + out << (first ? "" : " else ") << "if (name == \"" << prop.name << "\") {"; + + std::string type = MAPPED_TYPE[prop.backingFieldType]; + if (type.empty()) type = prop.backingFieldType; + if (type == "SurfaceType") type = "Data::Int"; + + std::string strFlags; + if (prop.flags & PropertyFlag_Readonly) + strFlags += " | PROP_READONLY"; + if (prop.flags & PropertyFlag_Hidden) + strFlags += " | PROP_HIDDEN"; + if (prop.flags & PropertyFlag_NoSave) + strFlags += " | PROP_NOSAVE"; + if (prop.flags & PropertyFlag_UnitFloat) + strFlags += " | PROP_UNIT_FLOAT"; + if (!strFlags.empty()) strFlags = strFlags.substr(3); // Remove leading pipe + else strFlags = "0"; // 0 == No option + + std::string category = CATEGORY_STR[prop.category]; + if (category.empty()) category = "PROP_CATEGORY_DATA"; + + out << "\n return PropertyMeta { &" << type << "::TYPE, " << strFlags << ", " << category << " };"; + + out << "\n }"; + first = false; + } + + out << "\n return MemberNotFound(\"" << state.name << "\", name);"; + + out << "\n};\n\n"; +} + void writeCodeForClass(std::ofstream& out, ClassAnalysis& state) { std::string strFlags; if (state.flags & ClassFlag_NotCreatable) @@ -61,6 +151,10 @@ void writeCodeForClass(std::ofstream& out, ClassAnalysis& state) { if (!strFlags.empty()) strFlags = strFlags.substr(3); // Remove leading pipe else strFlags = "0"; // 0 == No option + out << "/////////////////////////////////////////////////////////////////////////////////////////\n"; + out << "// This file was automatically generated by autogen, and should not be edited manually //\n"; + out << "/////////////////////////////////////////////////////////////////////////////////////////\n\n"; + out << "#include \"" << state.headerPath << "\"\n\n"; out << "const InstanceType " << state.name << "::TYPE = {\n" << " .super = &" << state.baseClass << "::TYPE,\n" @@ -75,4 +169,6 @@ void writeCodeForClass(std::ofstream& out, ClassAnalysis& state) { << "};\n\n"; writePropertySetHandler(out, state); + writePropertyGetHandler(out, state); + writePropertyMetaHandler(out, state); } \ No newline at end of file diff --git a/core/src/objects/base/instance.cpp b/core/src/objects/base/instance.cpp index 6f750af..34983f3 100644 --- a/core/src/objects/base/instance.cpp +++ b/core/src/objects/base/instance.cpp @@ -245,6 +245,11 @@ result Instance::GetPropertyMeta(std::string name) } } + +result Instance::InternalGetPropertyValue(std::string name) { return MemberNotFound(GetClass()->className, name); } +fallible Instance::InternalSetPropertyValue(std::string name, Data::Variant value) { return MemberNotFound(GetClass()->className, name); } +result Instance::InternalGetPropertyMeta(std::string name) { return MemberNotFound(GetClass()->className, name); } + void Instance::UpdateProperty(std::string name) { PropertyMeta meta = GetPropertyMeta(name).expect(); if (!meta.updateCallback) return; // Nothing to update, exit. @@ -282,7 +287,7 @@ void Instance::Serialize(pugi::xml_node parent) { pugi::xml_node propertiesNode = node.append_child("Properties"); for (std::string name : GetProperties()) { PropertyMeta meta = GetPropertyMeta(name).expect("Meta of declared property is missing"); - if (meta.flags & (PropertyFlags::PROP_NOSAVE | PropertyFlags::PROP_READONLY)) continue; // This property should not be serialized. Skip... + if (meta.flags & (PROP_NOSAVE | PROP_READONLY)) continue; // This property should not be serialized. Skip... pugi::xml_node propertyNode = propertiesNode.append_child(meta.type->name); propertyNode.append_attribute("name").set_value(name); diff --git a/core/src/objects/base/member.h b/core/src/objects/base/member.h index e0f42ac..2f4dc97 100644 --- a/core/src/objects/base/member.h +++ b/core/src/objects/base/member.h @@ -44,12 +44,11 @@ std::function memberFunctionOf(void(T::*func)(std::strin return std::bind(func, obj, std::placeholders::_1); } -enum PropertyFlags { - PROP_HIDDEN = 1 << 0, // Hidden from the editor - PROP_NOSAVE = 1 << 1, // Do not serialize - PROP_UNIT_FLOAT = 1 << 2, // Float between 0 and 1 - PROP_READONLY = 1 << 3, // Read only property, do not write -}; +typedef int PropertyFlags; +const PropertyFlags PROP_HIDDEN = 1 << 0; // Hidden from the editor +const PropertyFlags PROP_NOSAVE = 1 << 1; // Do not serialize +const PropertyFlags PROP_UNIT_FLOAT = 1 << 2; // Float between 0 and 1 +const PropertyFlags PROP_READONLY = 1 << 3; // Read only property, do not write enum PropertyCategory { PROP_CATEGORY_APPEARENCE, @@ -62,10 +61,7 @@ enum PropertyCategory { const int PROPERTY_CATEGORY_MAX = PROP_CATEGORY_SURFACE; struct PropertyMeta { - void* backingField; const Data::TypeInfo* type; - FieldCodec codec; - std::optional> updateCallback; PropertyFlags flags; PropertyCategory category = PROP_CATEGORY_DATA; }; diff --git a/core/src/objects/part.cpp b/core/src/objects/part.cpp index 3fc00a4..644c864 100644 --- a/core/src/objects/part.cpp +++ b/core/src/objects/part.cpp @@ -74,13 +74,13 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(CFrame::FromEule .type = &Vector3::TYPE, .codec = cframePositionCodec(), .updateCallback = memberFunctionOf(&Part::onUpdated, this), - .flags = PropertyFlags::PROP_NOSAVE + .flags = PROP_NOSAVE }}, { "Rotation", { .backingField = &cframe, .type = &Vector3::TYPE, .codec = cframeRotationCodec(), .updateCallback = memberFunctionOf(&Part::onUpdated, this), - .flags = PropertyFlags::PROP_NOSAVE + .flags = PROP_NOSAVE }}, { "Velocity", { .backingField = &velocity, .type = &Vector3::TYPE, diff --git a/core/src/objects/part.h b/core/src/objects/part.h index 131fc5d..dd061dc 100644 --- a/core/src/objects/part.h +++ b/core/src/objects/part.h @@ -52,7 +52,7 @@ public: const static InstanceType TYPE; Vector3 velocity; - [[ def_prop(name="cframe"), cframe_position_prop(name="Position"), cframe_rotation_prop(name="Rotation") ]] + [[ def_prop(name="CFrame"), cframe_position_prop(name="Position"), cframe_rotation_prop(name="Rotation") ]] CFrame cframe; [[ def_prop(name="Size", category=PART) ]] glm::vec3 size;