#include "MujocoAttributeHandler.h" #include "Enums/MujocoEnums.h" #include "LuckyMujocoEditor.h" using AttributeHandler = TFunction; void HandleEnumAttribute(const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (FEnumProperty* EnumProp = CastField(Prop)) { UEnum* EnumDef = EnumProp->GetEnum(); FName Search = FName(*ValueStr); int64 EnumIndex = EnumDef->GetIndexByName(Search); if (EnumIndex == INDEX_NONE) { FName SearchLover = FName(*ValueStr.ToLower()); EnumIndex = EnumDef->GetIndexByName(SearchLover); if (EnumIndex == INDEX_NONE) { UE_LOG(LogMujoco, Warning, TEXT("Invalid enum value: %s"), *ValueStr); return; } } EnumProp->GetUnderlyingProperty()->SetIntPropertyValue(EnumProp->ContainerPtrToValuePtr(DataPtr), EnumIndex); } else { UE_LOG(LogMujoco, Warning, TEXT("Failed to set enum value: %s"), *ValueStr); } } template void HandleOptionalEnumAttribute(const FString& ValueStr, void* DataPtr, FProperty* Prop) { UE_LOG(LogMujoco, Warning, TEXT("Unhandled optional enum attribute: %s"), *ValueStr); } template <> void HandleOptionalEnumAttribute(const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (auto* OptionalEnum = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { EDynType EnumValue = EDynType::None; if (ValueStr == TEXT("integrator")) { EnumValue = EDynType::Integrator; } else if (ValueStr == TEXT("filter")) { EnumValue = EDynType::Filter; } else if (ValueStr == TEXT("filterexact")) { EnumValue = EDynType::FilterExact; } else if (ValueStr == TEXT("muscle")) { EnumValue = EDynType::Muscle; } else if (ValueStr == TEXT("user")) { EnumValue = EDynType::User; } *OptionalEnum = TOptional{ EnumValue }; } else { UE_LOG(LogMujoco, Warning, TEXT("Failed to set optional enum value: %s"), *ValueStr); } } template <> void HandleOptionalEnumAttribute(const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (auto* OptionalEnum = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { EGainType EnumValue = EGainType::Fixed; if (ValueStr == TEXT("affine")) { EnumValue = EGainType::Affine; } else if (ValueStr == TEXT("muscle")) { EnumValue = EGainType::Muscle; } else if (ValueStr == TEXT("user")) { EnumValue = EGainType::User; } *OptionalEnum = TOptional{ EnumValue }; } else { UE_LOG(LogMujoco, Warning, TEXT("Failed to set optional enum value: %s"), *ValueStr); } } template <> void HandleOptionalEnumAttribute(const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (auto* OptionalEnum = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { EBiasType EnumValue = EBiasType::None; if (ValueStr == TEXT("affine")) { EnumValue = EBiasType::Affine; } else if (ValueStr == TEXT("muscle")) { EnumValue = EBiasType::Muscle; } else if (ValueStr == TEXT("user")) { EnumValue = EBiasType::User; } *OptionalEnum = TOptional{ EnumValue }; } else { UE_LOG(LogMujoco, Warning, TEXT("Failed to set optional enum value: %s"), *ValueStr); } } template void HandleOptionalEnum(const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (auto* OptionalEnum = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalEnum = TOptional{ static_cast(FCString::Atoi(*ValueStr)) }; } else { UE_LOG(LogMujoco, Warning, TEXT("Failed to set optional enum value: %s"), *ValueStr); } } void HandleOptionalInt(const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (auto* OptionalInt = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalInt = TOptional{ FCString::Atoi(*ValueStr) }; } else { UE_LOG(LogMujoco, Warning, TEXT("Failed to set optional int value: %s"), *ValueStr); } } template void HandleOptionalNumber(const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (auto* OptionalFloat = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalFloat = TOptional{ FCString::Atod(*ValueStr) }; } else { UE_LOG(LogMujoco, Warning, TEXT("Failed to set optional float value: %s"), *ValueStr); } } void HandleVector2D(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); if (Components.Num() >= 2) { FVector2D Range(FCString::Atod(*Components[0]), FCString::Atod(*Components[1])); if (auto* OptionalRange = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalRange = TOptional{ Range }; return; } } UE_LOG(LogMujoco, Warning, TEXT("Invalid Vector2D value: %s"), *ValueStr); } void HandleVector3D(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); if (Components.Num() >= 3) { FVector Vector(FCString::Atod(*Components[0]), FCString::Atod(*Components[1]), FCString::Atod(*Components[2])); if (auto* OptionalVector = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalVector = TOptional{ Vector }; return; } } UE_LOG(LogMujoco, Warning, TEXT("Invalid Vector3D value: %s"), *ValueStr); } void HandleVector4D(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); if (Components.Num() >= 4) { FVector4 Vector(FCString::Atod(*Components[0]), FCString::Atod(*Components[1]), FCString::Atod(*Components[2]), FCString::Atod(*Components[3])); if (auto* OptionalVector = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalVector = TOptional{ Vector }; return; } } UE_LOG(LogMujoco, Warning, TEXT("Invalid Vector4D value: %s"), *ValueStr); } void HandleOptionalBool(const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (auto* OptionalBool = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalBool = TOptional{ FCString::ToBool(*ValueStr) }; } else { UE_LOG(LogMujoco, Warning, TEXT("Failed to set optional bool value: %s"), *ValueStr); } } void HandleFName(const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (auto* NamePtr = static_cast(Prop->ContainerPtrToValuePtr(DataPtr))) { *NamePtr = FName(*ValueStr); } else { UE_LOG(LogMujoco, Warning, TEXT("Failed to set FName value: %s"), *ValueStr); } } void HandleGear(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); if (Components.Num() >= 6) { FMujocoGear Gear; Gear.G0.X = FCString::Atod(*Components[0]); Gear.G0.Y = FCString::Atod(*Components[1]); Gear.G0.Z = FCString::Atod(*Components[2]); Gear.G1.X = FCString::Atod(*Components[3]); Gear.G1.Y = FCString::Atod(*Components[4]); Gear.G1.Z = FCString::Atod(*Components[5]); if (auto* OptionalGear = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalGear = TOptional{ Gear }; return; } } UE_LOG(LogMujoco, Warning, TEXT("Invalid Gear value: %s"), *ValueStr); } void HandleSolImp(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); FMujocoSolImp SolImp; float* SolImpPtr = &SolImp.DMin; for (int i = 0; i < 5 && i < Components.Num(); ++i) { SolImpPtr[i] = FCString::Atod(*Components[i]); } if (auto* OptionalSolImp = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalSolImp = TOptional{ SolImp }; return; } } void HandleSolRef(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); if (Components.Num() >= 2) { FMujocoSolRef SolRef; SolRef.TimeConst = FCString::Atod(*Components[0]); SolRef.DampRatio = FCString::Atod(*Components[1]); if (auto* OptionalSolRef = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalSolRef = TOptional{ SolRef }; return; } } UE_LOG(LogMujoco, Warning, TEXT("Invalid SolRef value: %s"), *ValueStr); } void HandlePolyCoef(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); FMujocoPolyCoef PolyCoef; float* PolyCoefPtr = &PolyCoef.A0; for (int i = 0; i < 5 && i < Components.Num(); ++i) { PolyCoefPtr[i] = FCString::Atod(*Components[i]); } if (auto* OptionalPolyCoef = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalPolyCoef = TOptional{ PolyCoef }; return; } } void HandleRelPose(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); FMujocoRelPose RelPose; double* RelPosePtr[] = { &RelPose.Pos.X, &RelPose.Pos.Y, &RelPose.Pos.Z, &RelPose.Rot.X, &RelPose.Rot.Y, &RelPose.Rot.Z }; for (int i = 0; i < 6 && i < Components.Num(); ++i) { *RelPosePtr[i] = FCString::Atod(*Components[i]); } if (auto* OptionalRelPose = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalRelPose = TOptional{ RelPose }; return; } UE_LOG(LogMujoco, Warning, TEXT("Invalid RelPose value: %s"), *ValueStr); } void HandleFriction(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); FMujocoFriction Friction; float* FrictionPtr = &Friction.Sliding; for (int i = 0; i < 3 && i < Components.Num(); ++i) { FrictionPtr[i] = FCString::Atod(*Components[i]); } if (auto* OptionalFriction = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { *OptionalFriction = TOptional{ Friction }; return; } UE_LOG(LogMujoco, Warning, TEXT("Invalid Friction value: %s"), *ValueStr); } template void HandleNumberArray(const FString& ValueStr, void* DataPtr, FProperty* Prop) { TArray Components; ValueStr.ParseIntoArray(Components, TEXT(" "), true); if (auto* FloatArrayPtr = static_cast*>(Prop->ContainerPtrToValuePtr(DataPtr))) { TArray FloatArray; for (const FString& Component : Components) { FloatArray.Add(FCString::Atod(*Component)); } *FloatArrayPtr = FloatArray; return; } UE_LOG(LogMujoco, Warning, TEXT("Invalid FloatArray value: %s"), *ValueStr); } static const TMap AttributeHandlers = { { TEXT("type"), &HandleEnumAttribute }, { TEXT("dyntype"), &HandleOptionalEnumAttribute }, { TEXT("biastype"), &HandleOptionalEnumAttribute }, { TEXT("gaintype"), &HandleOptionalEnumAttribute }, { TEXT("group"), &HandleOptionalInt }, { TEXT("priority"), &HandleOptionalInt }, { TEXT("conaffinity"), &HandleOptionalInt }, { TEXT("contype"), &HandleOptionalInt }, { TEXT("condim"), &HandleOptionalEnum }, { TEXT("ctrllimited"), &HandleOptionalEnum }, { TEXT("forcelimited"), &HandleOptionalEnum }, { TEXT("actlimited"), &HandleOptionalEnum }, { TEXT("ctrlrange"), &HandleVector2D }, { TEXT("forcerange"), &HandleVector2D }, { TEXT("actrange"), &HandleVector2D }, { TEXT("lengthrange"), &HandleVector2D }, { TEXT("gear"), &HandleGear }, { TEXT("cranklength"), &HandleOptionalNumber }, { TEXT("kp"), &HandleOptionalNumber }, { TEXT("kv"), &HandleOptionalNumber }, { TEXT("dampratio"), &HandleOptionalNumber }, { TEXT("timeconst"), &HandleOptionalNumber }, { TEXT("inheritrange"), &HandleOptionalNumber }, { TEXT("area"), &HandleOptionalNumber }, { TEXT("diameter"), &HandleOptionalNumber }, { TEXT("tausmooth"), &HandleOptionalNumber }, { TEXT("force"), &HandleOptionalNumber }, { TEXT("scale"), &HandleOptionalNumber }, { TEXT("vmax"), &HandleOptionalNumber }, { TEXT("fpmax"), &HandleOptionalNumber }, { TEXT("fvmax"), &HandleOptionalNumber }, { TEXT("lmin"), &HandleOptionalNumber }, { TEXT("lmax"), &HandleOptionalNumber }, { TEXT("gain"), &HandleOptionalNumber }, { TEXT("range"), &HandleVector2D }, { TEXT("muscle_timeconsts"), &HandleVector2D }, { TEXT("actuatorfrcrange"), &HandleVector2D }, { TEXT("name"), &HandleFName }, { TEXT("joint"), &HandleFName }, { TEXT("body1"), &HandleFName }, { TEXT("body2"), &HandleFName }, { TEXT("site1"), &HandleFName }, { TEXT("site2"), &HandleFName }, { TEXT("joint1"), &HandleFName }, { TEXT("joint2"), &HandleFName }, { TEXT("jointinparent"), &HandleFName }, { TEXT("site"), &HandleFName }, { TEXT("refsite"), &HandleFName }, { TEXT("body"), &HandleFName }, { TEXT("tendon"), &HandleFName }, { TEXT("tendon1"), &HandleFName }, { TEXT("tendon2"), &HandleFName }, { TEXT("cranksite"), &HandleFName }, { TEXT("slidersite"), &HandleFName }, { TEXT("pos"), &HandleVector3D }, { TEXT("axis"), &HandleVector3D }, { TEXT("diaginertia"), &HandleVector3D }, { TEXT("size"), &HandleVector3D }, { TEXT("bias"), &HandleVector3D }, { TEXT("rgba"), &HandleVector4D }, { TEXT("armature"), &HandleOptionalNumber }, { TEXT("springref"), &HandleOptionalNumber }, { TEXT("gravcomp"), &HandleOptionalNumber }, { TEXT("solreflimit"), &HandleOptionalNumber }, { TEXT("solimplimit"), &HandleOptionalNumber }, { TEXT("solreffriction"), &HandleOptionalNumber }, { TEXT("solimpfriction"), &HandleOptionalNumber }, { TEXT("margin"), &HandleOptionalNumber }, { TEXT("frictionloss"), &HandleOptionalNumber }, { TEXT("width"), &HandleOptionalNumber }, { TEXT("stiffness"), &HandleOptionalNumber }, { TEXT("damping"), &HandleOptionalNumber }, { TEXT("mass"), &HandleOptionalNumber }, { TEXT("density"), &HandleOptionalNumber }, { TEXT("shellinertia"), &HandleOptionalNumber }, { TEXT("solmix"), &HandleOptionalNumber }, { TEXT("gap"), &HandleOptionalNumber }, { TEXT("torquescale"), &HandleOptionalNumber }, { TEXT("actearly"), &HandleOptionalBool }, { TEXT("dynprm"), &HandleNumberArray }, { TEXT("gainprm"), &HandleNumberArray }, { TEXT("biasprm"), &HandleNumberArray }, { TEXT("solimp"), &HandleSolImp }, { TEXT("solref"), &HandleSolRef }, { TEXT("polycoef"), &HandlePolyCoef }, { TEXT("relpose"), &HandleRelPose }, { TEXT("friction"), &HandleFriction }, { TEXT("fullinertia"), &HandleGear }, { TEXT("active"), &HandleOptionalBool }, }; void ProcessAttribute(const FString& AttrKey, const FString& ValueStr, void* DataPtr, FProperty* Prop) { if (const AttributeHandler* Handler = AttributeHandlers.Find(AttrKey)) { (*Handler)(ValueStr, DataPtr, Prop); } else { UE_LOG(LogMujoco, Error, TEXT("Unhandled attribute: %s"), *AttrKey); } } void ParseMujocoElementAttribute(const tinyxml2::XMLElement* Element, const FString& AttrKey, void* DataPtr, FProperty* Prop) { if (!Element) { UE_LOG(LogMujoco, Warning, TEXT("ParseXmlElementAttribute: Element is null.")); return; } const char* XmlVal = Element->Attribute(TCHAR_TO_UTF8(*AttrKey)); if (XmlVal) { ProcessAttribute(AttrKey, UTF8_TO_TCHAR(XmlVal), DataPtr, Prop); } else { UE_LOG(LogMujoco, Warning, TEXT("Attribute %s not found"), *AttrKey); } }