From e45272137f976e0c952b3bf8664a522b171bb4ef Mon Sep 17 00:00:00 2001 From: Ash <109132519+uhhashe@users.noreply.github.com> Date: Sun, 12 Jul 2026 20:17:11 +0800 Subject: [PATCH] feat: T4 and T5 physconstraints dumping (#879) * feat: T4 and T5 physconstraints dumping * chore: small style adjustments --------- Co-authored-by: Jan Laupetin --- docs/SupportedAssetTypes.md | 4 +- src/Common/Game/T4/T4.h | 7 ++ src/Common/Game/T4/T4_Assets.h | 3 +- src/Common/Game/T6/T6_Assets.h | 3 +- src/ObjCommon/Game/T4/ObjConstantsT4.h | 2 + .../PhysConstraints/PhysConstraintsFields.h | 97 ++++++++++++++++++ src/ObjCommon/Game/T5/ObjConstantsT5.h | 2 + .../PhysConstraints/PhysConstraintsFields.h | 98 +++++++++++++++++++ .../PhysConstraints/PhysConstraintsFields.h | 1 + src/ObjWriting/Game/T4/ObjWriterT4.cpp | 2 + .../PhysConstraintsInfoStringDumperT4.cpp | 87 ++++++++++++++++ .../PhysConstraintsInfoStringDumperT4.h | 13 +++ src/ObjWriting/Game/T5/ObjWriterT5.cpp | 3 +- .../PhysConstraintsInfoStringDumperT5.cpp | 87 ++++++++++++++++ .../PhysConstraintsInfoStringDumperT5.h | 13 +++ 15 files changed, 417 insertions(+), 5 deletions(-) create mode 100644 src/ObjCommon/Game/T4/PhysConstraints/PhysConstraintsFields.h create mode 100644 src/ObjCommon/Game/T5/PhysConstraints/PhysConstraintsFields.h create mode 100644 src/ObjWriting/Game/T4/PhysConstraints/PhysConstraintsInfoStringDumperT4.cpp create mode 100644 src/ObjWriting/Game/T4/PhysConstraints/PhysConstraintsInfoStringDumperT4.h create mode 100644 src/ObjWriting/Game/T5/PhysConstraints/PhysConstraintsInfoStringDumperT5.cpp create mode 100644 src/ObjWriting/Game/T5/PhysConstraints/PhysConstraintsInfoStringDumperT5.h diff --git a/docs/SupportedAssetTypes.md b/docs/SupportedAssetTypes.md index 5a110ce2..3dbdf688 100644 --- a/docs/SupportedAssetTypes.md +++ b/docs/SupportedAssetTypes.md @@ -128,7 +128,7 @@ using `Linker`): | Asset Type | Dumping Support | Loading Support | Notes | |----------------------|-----------------|-----------------|------------------------------------------------------------------------------| | PhysPreset | ❌ | ❌ | | -| PhysConstraints | ❌ | ❌ | | +| PhysConstraints | ✅ | ❌ | | | DestructibleDef | ❌ | ❌ | | | XAnimParts | ✅ | ✅ | | | XModel | ✅ | ❌ | Model data can be exported to `XMODEL_EXPORT/XMODEL_BIN`, `OBJ`, `GLB/GLTF`. | @@ -161,7 +161,7 @@ using `Linker`): | Asset Type | Dumping Support | Loading Support | Notes | |----------------------|-----------------|-----------------|------------------------------------------------------------------------------| | PhysPreset | ✅ | ✅ | | -| PhysConstraints | ❌ | ❌ | | +| PhysConstraints | ✅ | ❌ | | | DestructibleDef | ❌ | ❌ | | | XAnimParts | ✅ | ✅ | | | XModel | ✅ | ✅ | Model data can be exported to `XMODEL_EXPORT/XMODEL_BIN`, `OBJ`, `GLB/GLTF`. | diff --git a/src/Common/Game/T4/T4.h b/src/Common/Game/T4/T4.h index 48d117e2..bf1cbf87 100644 --- a/src/Common/Game/T4/T4.h +++ b/src/Common/Game/T4/T4.h @@ -142,6 +142,13 @@ namespace T4 WFT_NUM_FIELD_TYPES, }; + enum constraintsFieldType_t + { + CFT_TYPE = CSPFT_NUM_BASE_FIELD_TYPES, + + CFT_NUM_FIELD_TYPES + }; + struct AccuracyGraph { vec2_t* graphKnots; diff --git a/src/Common/Game/T4/T4_Assets.h b/src/Common/Game/T4/T4_Assets.h index 064ade88..30c0d428 100644 --- a/src/Common/Game/T4/T4_Assets.h +++ b/src/Common/Game/T4/T4_Assets.h @@ -151,7 +151,8 @@ namespace T4 CONSTRAINT_FAKE_SHAKE = 0x6, CONSTRAINT_LAUNCH = 0x7, CONSTRAINT_ROPE = 0x8, - NUM_CONSTRAINT_TYPES = 0x9, + + NUM_CONSTRAINT_TYPES, }; enum AttachPointType : int diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index bad6882a..db3f68bf 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -405,7 +405,8 @@ namespace T6 CONSTRAINT_LAUNCH = 0x7, CONSTRAINT_ROPE = 0x8, CONSTRAINT_LIGHT = 0x9, - NUM_CONSTRAINT_TYPES = 0xA, + + NUM_CONSTRAINT_TYPES }; enum AttachPointType diff --git a/src/ObjCommon/Game/T4/ObjConstantsT4.h b/src/ObjCommon/Game/T4/ObjConstantsT4.h index ec1436be..5aff1828 100644 --- a/src/ObjCommon/Game/T4/ObjConstantsT4.h +++ b/src/ObjCommon/Game/T4/ObjConstantsT4.h @@ -3,7 +3,9 @@ namespace T4 { static constexpr auto INFO_STRING_PREFIX_FLAME_TABLE = "FLAMETABLEFILE"; + static constexpr auto INFO_STRING_PREFIX_PHYS_CONSTRAINTS = "PHYSCONSTRAINTS"; static constexpr auto INFO_STRING_PREFIX_WEAPON = "WEAPONFILE"; + static constexpr auto GDF_FILENAME_PHYS_CONSTRAINTS = "physconstraints.gdf"; static constexpr auto GDF_FILENAME_WEAPON = "weapon.gdf"; } // namespace T4 diff --git a/src/ObjCommon/Game/T4/PhysConstraints/PhysConstraintsFields.h b/src/ObjCommon/Game/T4/PhysConstraints/PhysConstraintsFields.h new file mode 100644 index 00000000..b6d7b603 --- /dev/null +++ b/src/ObjCommon/Game/T4/PhysConstraints/PhysConstraintsFields.h @@ -0,0 +1,97 @@ +#pragma once +#include "Game/T4/T4.h" + +namespace T4 +{ + inline cspField_t phys_constraints_fields[]{ + {"c1_type", offsetof(PhysConstraints, data[0].type), CFT_TYPE }, + {"c1_bone1_name", offsetof(PhysConstraints, data[0].target_bone1), CSPFT_STRING}, + {"c1_bone2_name", offsetof(PhysConstraints, data[0].target_bone2), CSPFT_STRING}, + {"c1_offsetX", offsetof(PhysConstraints, data[0].offset[1]), CSPFT_FLOAT }, + {"c1_offsetY", offsetof(PhysConstraints, data[0].offset[2]), CSPFT_FLOAT }, + {"c1_offsetZ", offsetof(PhysConstraints, data[0].offset[3]), CSPFT_FLOAT }, + {"c1_timeout", offsetof(PhysConstraints, data[0].timeout), CSPFT_INT }, + {"c1_min_health", offsetof(PhysConstraints, data[0].min_health), CSPFT_INT }, + {"c1_max_health", offsetof(PhysConstraints, data[0].max_health), CSPFT_INT }, + {"c1_damp", offsetof(PhysConstraints, data[0].damp), CSPFT_FLOAT }, + {"c1_power", offsetof(PhysConstraints, data[0].power), CSPFT_FLOAT }, + {"c1_spin_scale", offsetof(PhysConstraints, data[0].spin_scale), CSPFT_FLOAT }, + {"c1_shakescalex", offsetof(PhysConstraints, data[0].scale[1]), CSPFT_FLOAT }, + {"c1_shakescaley", offsetof(PhysConstraints, data[0].scale[2]), CSPFT_FLOAT }, + {"c1_shakescalez", offsetof(PhysConstraints, data[0].scale[3]), CSPFT_FLOAT }, + {"c1_min_angle", offsetof(PhysConstraints, data[0].minAngle), CSPFT_FLOAT }, + {"c1_max_angle", offsetof(PhysConstraints, data[0].maxAngle), CSPFT_FLOAT }, + {"c1_yaw", offsetof(PhysConstraints, data[0].scale[1]), CSPFT_FLOAT }, + {"c1_pitch", offsetof(PhysConstraints, data[0].scale[2]), CSPFT_FLOAT }, + {"c2_type", offsetof(PhysConstraints, data[1].type), CFT_TYPE }, + {"c2_bone1_name", offsetof(PhysConstraints, data[1].target_bone1), CSPFT_STRING}, + {"c2_bone2_name", offsetof(PhysConstraints, data[1].target_bone2), CSPFT_STRING}, + {"c2_offsetX", offsetof(PhysConstraints, data[1].offset[1]), CSPFT_FLOAT }, + {"c2_offsetY", offsetof(PhysConstraints, data[1].offset[2]), CSPFT_FLOAT }, + {"c2_offsetZ", offsetof(PhysConstraints, data[1].offset[3]), CSPFT_FLOAT }, + {"c2_timeout", offsetof(PhysConstraints, data[1].timeout), CSPFT_INT }, + {"c2_min_health", offsetof(PhysConstraints, data[1].min_health), CSPFT_INT }, + {"c2_max_health", offsetof(PhysConstraints, data[1].max_health), CSPFT_INT }, + {"c2_damp", offsetof(PhysConstraints, data[1].damp), CSPFT_FLOAT }, + {"c2_power", offsetof(PhysConstraints, data[1].power), CSPFT_FLOAT }, + {"c2_spin_scale", offsetof(PhysConstraints, data[1].spin_scale), CSPFT_FLOAT }, + {"c2_shakescalex", offsetof(PhysConstraints, data[1].scale[1]), CSPFT_FLOAT }, + {"c2_shakescaley", offsetof(PhysConstraints, data[1].scale[2]), CSPFT_FLOAT }, + {"c2_shakescalez", offsetof(PhysConstraints, data[1].scale[3]), CSPFT_FLOAT }, + {"c2_min_angle", offsetof(PhysConstraints, data[1].minAngle), CSPFT_FLOAT }, + {"c2_max_angle", offsetof(PhysConstraints, data[1].maxAngle), CSPFT_FLOAT }, + {"c2_yaw", offsetof(PhysConstraints, data[1].scale[1]), CSPFT_FLOAT }, + {"c2_pitch", offsetof(PhysConstraints, data[1].scale[2]), CSPFT_FLOAT }, + {"c3_type", offsetof(PhysConstraints, data[2].type), CFT_TYPE }, + {"c3_bone1_name", offsetof(PhysConstraints, data[2].target_bone1), CSPFT_STRING}, + {"c3_bone2_name", offsetof(PhysConstraints, data[2].target_bone2), CSPFT_STRING}, + {"c3_offsetX", offsetof(PhysConstraints, data[2].offset[1]), CSPFT_FLOAT }, + {"c3_offsetY", offsetof(PhysConstraints, data[2].offset[2]), CSPFT_FLOAT }, + {"c3_offsetZ", offsetof(PhysConstraints, data[2].offset[3]), CSPFT_FLOAT }, + {"c3_timeout", offsetof(PhysConstraints, data[2].timeout), CSPFT_INT }, + {"c3_min_health", offsetof(PhysConstraints, data[2].min_health), CSPFT_INT }, + {"c3_max_health", offsetof(PhysConstraints, data[2].max_health), CSPFT_INT }, + {"c3_damp", offsetof(PhysConstraints, data[2].damp), CSPFT_FLOAT }, + {"c3_power", offsetof(PhysConstraints, data[2].power), CSPFT_FLOAT }, + {"c3_spin_scale", offsetof(PhysConstraints, data[2].spin_scale), CSPFT_FLOAT }, + {"c3_shakescalex", offsetof(PhysConstraints, data[2].scale[1]), CSPFT_FLOAT }, + {"c3_shakescaley", offsetof(PhysConstraints, data[2].scale[2]), CSPFT_FLOAT }, + {"c3_shakescalez", offsetof(PhysConstraints, data[2].scale[3]), CSPFT_FLOAT }, + {"c3_min_angle", offsetof(PhysConstraints, data[2].minAngle), CSPFT_FLOAT }, + {"c3_max_angle", offsetof(PhysConstraints, data[2].maxAngle), CSPFT_FLOAT }, + {"c3_yaw", offsetof(PhysConstraints, data[2].scale[1]), CSPFT_FLOAT }, + {"c3_pitch", offsetof(PhysConstraints, data[2].scale[2]), CSPFT_FLOAT }, + {"c4_type", offsetof(PhysConstraints, data[3].type), CFT_TYPE }, + {"c4_bone1_name", offsetof(PhysConstraints, data[3].target_bone1), CSPFT_STRING}, + {"c4_bone2_name", offsetof(PhysConstraints, data[3].target_bone2), CSPFT_STRING}, + {"c4_offsetX", offsetof(PhysConstraints, data[3].offset[1]), CSPFT_FLOAT }, + {"c4_offsetY", offsetof(PhysConstraints, data[3].offset[2]), CSPFT_FLOAT }, + {"c4_offsetZ", offsetof(PhysConstraints, data[3].offset[3]), CSPFT_FLOAT }, + {"c4_timeout", offsetof(PhysConstraints, data[3].timeout), CSPFT_INT }, + {"c4_min_health", offsetof(PhysConstraints, data[3].min_health), CSPFT_INT }, + {"c4_max_health", offsetof(PhysConstraints, data[3].max_health), CSPFT_INT }, + {"c4_damp", offsetof(PhysConstraints, data[3].damp), CSPFT_FLOAT }, + {"c4_power", offsetof(PhysConstraints, data[3].power), CSPFT_FLOAT }, + {"c4_spin_scale", offsetof(PhysConstraints, data[3].spin_scale), CSPFT_FLOAT }, + {"c4_shakescalex", offsetof(PhysConstraints, data[3].scale[1]), CSPFT_FLOAT }, + {"c4_shakescaley", offsetof(PhysConstraints, data[3].scale[2]), CSPFT_FLOAT }, + {"c4_shakescalez", offsetof(PhysConstraints, data[3].scale[3]), CSPFT_FLOAT }, + {"c4_min_angle", offsetof(PhysConstraints, data[3].minAngle), CSPFT_FLOAT }, + {"c4_max_angle", offsetof(PhysConstraints, data[3].maxAngle), CSPFT_FLOAT }, + {"c4_yaw", offsetof(PhysConstraints, data[3].scale[1]), CSPFT_FLOAT }, + {"c4_pitch", offsetof(PhysConstraints, data[3].scale[2]), CSPFT_FLOAT }, + }; + + inline const char* s_constraintTypeNames[]{ + "none", + "point", + "distance", + "hinge", + "joint", + "actuator", + "fake_shake", + "launch", + "rope", + }; + static_assert(std::extent_v == NUM_CONSTRAINT_TYPES); +} // namespace T4 diff --git a/src/ObjCommon/Game/T5/ObjConstantsT5.h b/src/ObjCommon/Game/T5/ObjConstantsT5.h index 870c4773..9a1f45b5 100644 --- a/src/ObjCommon/Game/T5/ObjConstantsT5.h +++ b/src/ObjCommon/Game/T5/ObjConstantsT5.h @@ -3,9 +3,11 @@ namespace T5 { static constexpr auto INFO_STRING_PREFIX_FLAME_TABLE = "FLAMETABLEFILE"; + static constexpr auto INFO_STRING_PREFIX_PHYS_CONSTRAINTS = "PHYSCONSTRAINTS"; static constexpr auto INFO_STRING_PREFIX_PHYS_PRESET = "PHYSIC"; static constexpr auto INFO_STRING_PREFIX_WEAPON = "WEAPONFILE"; + static constexpr auto GDF_FILENAME_PHYS_CONSTRAINTS = "physconstraints.gdf"; static constexpr auto GDF_FILENAME_PHYS_PRESET = "physpreset.gdf"; static constexpr auto GDF_FILENAME_WEAPON = "weapon.gdf"; diff --git a/src/ObjCommon/Game/T5/PhysConstraints/PhysConstraintsFields.h b/src/ObjCommon/Game/T5/PhysConstraints/PhysConstraintsFields.h new file mode 100644 index 00000000..97bb0305 --- /dev/null +++ b/src/ObjCommon/Game/T5/PhysConstraints/PhysConstraintsFields.h @@ -0,0 +1,98 @@ +#pragma once +#include "Game/T5/T5.h" + +namespace T5 +{ + inline cspField_t phys_constraints_fields[]{ + {"c1_type", offsetof(PhysConstraints, data[0].type), CFT_TYPE }, + {"c1_bone1_name", offsetof(PhysConstraints, data[0].target_bone1), CSPFT_STRING}, + {"c1_bone2_name", offsetof(PhysConstraints, data[0].target_bone2), CSPFT_STRING}, + {"c1_offsetX", offsetof(PhysConstraints, data[0].offset.x), CSPFT_FLOAT }, + {"c1_offsetY", offsetof(PhysConstraints, data[0].offset.y), CSPFT_FLOAT }, + {"c1_offsetZ", offsetof(PhysConstraints, data[0].offset.z), CSPFT_FLOAT }, + {"c1_timeout", offsetof(PhysConstraints, data[0].timeout), CSPFT_INT }, + {"c1_min_health", offsetof(PhysConstraints, data[0].min_health), CSPFT_INT }, + {"c1_max_health", offsetof(PhysConstraints, data[0].max_health), CSPFT_INT }, + {"c1_damp", offsetof(PhysConstraints, data[0].damp), CSPFT_FLOAT }, + {"c1_power", offsetof(PhysConstraints, data[0].power), CSPFT_FLOAT }, + {"c1_spin_scale", offsetof(PhysConstraints, data[0].spin_scale), CSPFT_FLOAT }, + {"c1_shakescalex", offsetof(PhysConstraints, data[0].scale.x), CSPFT_FLOAT }, + {"c1_shakescaley", offsetof(PhysConstraints, data[0].scale.y), CSPFT_FLOAT }, + {"c1_shakescalez", offsetof(PhysConstraints, data[0].scale.z), CSPFT_FLOAT }, + {"c1_min_angle", offsetof(PhysConstraints, data[0].minAngle), CSPFT_FLOAT }, + {"c1_max_angle", offsetof(PhysConstraints, data[0].maxAngle), CSPFT_FLOAT }, + {"c1_yaw", offsetof(PhysConstraints, data[0].scale.y), CSPFT_FLOAT }, + {"c1_pitch", offsetof(PhysConstraints, data[0].scale.x), CSPFT_FLOAT }, + {"c2_type", offsetof(PhysConstraints, data[1].type), CFT_TYPE }, + {"c2_bone1_name", offsetof(PhysConstraints, data[1].target_bone1), CSPFT_STRING}, + {"c2_bone2_name", offsetof(PhysConstraints, data[1].target_bone2), CSPFT_STRING}, + {"c2_offsetX", offsetof(PhysConstraints, data[1].offset.x), CSPFT_FLOAT }, + {"c2_offsetY", offsetof(PhysConstraints, data[1].offset.y), CSPFT_FLOAT }, + {"c2_offsetZ", offsetof(PhysConstraints, data[1].offset.z), CSPFT_FLOAT }, + {"c2_timeout", offsetof(PhysConstraints, data[1].timeout), CSPFT_INT }, + {"c2_min_health", offsetof(PhysConstraints, data[1].min_health), CSPFT_INT }, + {"c2_max_health", offsetof(PhysConstraints, data[1].max_health), CSPFT_INT }, + {"c2_damp", offsetof(PhysConstraints, data[1].damp), CSPFT_FLOAT }, + {"c2_power", offsetof(PhysConstraints, data[1].power), CSPFT_FLOAT }, + {"c2_spin_scale", offsetof(PhysConstraints, data[1].spin_scale), CSPFT_FLOAT }, + {"c2_shakescalex", offsetof(PhysConstraints, data[1].scale.x), CSPFT_FLOAT }, + {"c2_shakescaley", offsetof(PhysConstraints, data[1].scale.y), CSPFT_FLOAT }, + {"c2_shakescalez", offsetof(PhysConstraints, data[1].scale.z), CSPFT_FLOAT }, + {"c2_min_angle", offsetof(PhysConstraints, data[1].minAngle), CSPFT_FLOAT }, + {"c2_max_angle", offsetof(PhysConstraints, data[1].maxAngle), CSPFT_FLOAT }, + {"c2_yaw", offsetof(PhysConstraints, data[1].scale.y), CSPFT_FLOAT }, + {"c2_pitch", offsetof(PhysConstraints, data[1].scale.x), CSPFT_FLOAT }, + {"c3_type", offsetof(PhysConstraints, data[2].type), CFT_TYPE }, + {"c3_bone1_name", offsetof(PhysConstraints, data[2].target_bone1), CSPFT_STRING}, + {"c3_bone2_name", offsetof(PhysConstraints, data[2].target_bone2), CSPFT_STRING}, + {"c3_offsetX", offsetof(PhysConstraints, data[2].offset.x), CSPFT_FLOAT }, + {"c3_offsetY", offsetof(PhysConstraints, data[2].offset.y), CSPFT_FLOAT }, + {"c3_offsetZ", offsetof(PhysConstraints, data[2].offset.z), CSPFT_FLOAT }, + {"c3_timeout", offsetof(PhysConstraints, data[2].timeout), CSPFT_INT }, + {"c3_min_health", offsetof(PhysConstraints, data[2].min_health), CSPFT_INT }, + {"c3_max_health", offsetof(PhysConstraints, data[2].max_health), CSPFT_INT }, + {"c3_damp", offsetof(PhysConstraints, data[2].damp), CSPFT_FLOAT }, + {"c3_power", offsetof(PhysConstraints, data[2].power), CSPFT_FLOAT }, + {"c3_spin_scale", offsetof(PhysConstraints, data[2].spin_scale), CSPFT_FLOAT }, + {"c3_shakescalex", offsetof(PhysConstraints, data[2].scale.x), CSPFT_FLOAT }, + {"c3_shakescaley", offsetof(PhysConstraints, data[2].scale.y), CSPFT_FLOAT }, + {"c3_shakescalez", offsetof(PhysConstraints, data[2].scale.z), CSPFT_FLOAT }, + {"c3_min_angle", offsetof(PhysConstraints, data[2].minAngle), CSPFT_FLOAT }, + {"c3_max_angle", offsetof(PhysConstraints, data[2].maxAngle), CSPFT_FLOAT }, + {"c3_yaw", offsetof(PhysConstraints, data[2].scale.y), CSPFT_FLOAT }, + {"c3_pitch", offsetof(PhysConstraints, data[2].scale.x), CSPFT_FLOAT }, + {"c4_type", offsetof(PhysConstraints, data[3].type), CFT_TYPE }, + {"c4_bone1_name", offsetof(PhysConstraints, data[3].target_bone1), CSPFT_STRING}, + {"c4_bone2_name", offsetof(PhysConstraints, data[3].target_bone2), CSPFT_STRING}, + {"c4_offsetX", offsetof(PhysConstraints, data[3].offset.x), CSPFT_FLOAT }, + {"c4_offsetY", offsetof(PhysConstraints, data[3].offset.y), CSPFT_FLOAT }, + {"c4_offsetZ", offsetof(PhysConstraints, data[3].offset.z), CSPFT_FLOAT }, + {"c4_timeout", offsetof(PhysConstraints, data[3].timeout), CSPFT_INT }, + {"c4_min_health", offsetof(PhysConstraints, data[3].min_health), CSPFT_INT }, + {"c4_max_health", offsetof(PhysConstraints, data[3].max_health), CSPFT_INT }, + {"c4_damp", offsetof(PhysConstraints, data[3].damp), CSPFT_FLOAT }, + {"c4_power", offsetof(PhysConstraints, data[3].power), CSPFT_FLOAT }, + {"c4_spin_scale", offsetof(PhysConstraints, data[3].spin_scale), CSPFT_FLOAT }, + {"c4_shakescalex", offsetof(PhysConstraints, data[3].scale.x), CSPFT_FLOAT }, + {"c4_shakescaley", offsetof(PhysConstraints, data[3].scale.y), CSPFT_FLOAT }, + {"c4_shakescalez", offsetof(PhysConstraints, data[3].scale.z), CSPFT_FLOAT }, + {"c4_min_angle", offsetof(PhysConstraints, data[3].minAngle), CSPFT_FLOAT }, + {"c4_max_angle", offsetof(PhysConstraints, data[3].maxAngle), CSPFT_FLOAT }, + {"c4_yaw", offsetof(PhysConstraints, data[3].scale.y), CSPFT_FLOAT }, + {"c4_pitch", offsetof(PhysConstraints, data[3].scale.x), CSPFT_FLOAT }, + }; + + inline const char* s_constraintTypeNames[]{ + "none", + "point", + "distance", + "hinge", + "joint", + "actuator", + "fake_shake", + "launch", + "rope", + "light", + }; + static_assert(std::extent_v == NUM_CONSTRAINT_TYPES); +} // namespace T5 diff --git a/src/ObjCommon/Game/T6/PhysConstraints/PhysConstraintsFields.h b/src/ObjCommon/Game/T6/PhysConstraints/PhysConstraintsFields.h index a161fd83..0aed357c 100644 --- a/src/ObjCommon/Game/T6/PhysConstraints/PhysConstraintsFields.h +++ b/src/ObjCommon/Game/T6/PhysConstraints/PhysConstraintsFields.h @@ -94,4 +94,5 @@ namespace T6 "rope", "light", }; + static_assert(std::extent_v == NUM_CONSTRAINT_TYPES); } // namespace T6 diff --git a/src/ObjWriting/Game/T4/ObjWriterT4.cpp b/src/ObjWriting/Game/T4/ObjWriterT4.cpp index 408aab3d..ff466d2d 100644 --- a/src/ObjWriting/Game/T4/ObjWriterT4.cpp +++ b/src/ObjWriting/Game/T4/ObjWriterT4.cpp @@ -7,6 +7,7 @@ #include "Game/T4/XModel/XModelDumperT4.h" #include "Localize/LocalizeDumperT4.h" #include "Maps/MapEntsDumperT4.h" +#include "PhysConstraints/PhysConstraintsInfoStringDumperT4.h" #include "RawFile/RawFileDumperT4.h" #include "Sound/LoadedSoundDumperT4.h" #include "StringTable/StringTableDumperT4.h" @@ -16,6 +17,7 @@ using namespace T4; void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context) { + RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); diff --git a/src/ObjWriting/Game/T4/PhysConstraints/PhysConstraintsInfoStringDumperT4.cpp b/src/ObjWriting/Game/T4/PhysConstraints/PhysConstraintsInfoStringDumperT4.cpp new file mode 100644 index 00000000..bfbc7393 --- /dev/null +++ b/src/ObjWriting/Game/T4/PhysConstraints/PhysConstraintsInfoStringDumperT4.cpp @@ -0,0 +1,87 @@ +#include "PhysConstraintsInfoStringDumperT4.h" + +#include "Game/T4/InfoString/InfoStringFromStructConverter.h" +#include "Game/T4/ObjConstantsT4.h" +#include "Game/T4/PhysConstraints/PhysConstraintsFields.h" +#include "PhysConstraints/PhysConstraintsCommon.h" + +#include +#include + +using namespace T4; + +namespace +{ + class InfoStringFromPhysConstraintsConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + switch (static_cast(field.iFieldType)) + { + case CFT_TYPE: + FillFromEnumInt(std::string(field.szName), field.iOffset, s_constraintTypeNames, std::extent_v); + break; + + default: + assert(false); + break; + } + } + + public: + InfoStringFromPhysConstraintsConverter(const PhysConstraints* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateInfoString(const XAssetInfo& asset) + { + assert(asset.Asset()->count <= 4); + + InfoStringFromPhysConstraintsConverter converter(asset.Asset(), + phys_constraints_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset.m_zone->m_script_strings.Count()); + if (scrStr >= asset.m_zone->m_script_strings.Count()) + return ""; + + return asset.m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace phys_constraints +{ + void InfoStringDumperT4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset.m_name, GDF_FILENAME_PHYS_CONSTRAINTS); + infoString.ToGdtProperties(INFO_STRING_PREFIX_PHYS_CONSTRAINTS, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(INFO_STRING_PREFIX_PHYS_CONSTRAINTS); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace phys_constraints diff --git a/src/ObjWriting/Game/T4/PhysConstraints/PhysConstraintsInfoStringDumperT4.h b/src/ObjWriting/Game/T4/PhysConstraints/PhysConstraintsInfoStringDumperT4.h new file mode 100644 index 00000000..06bcee7a --- /dev/null +++ b/src/ObjWriting/Game/T4/PhysConstraints/PhysConstraintsInfoStringDumperT4.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T4/T4.h" + +namespace phys_constraints +{ + class InfoStringDumperT4 final : public AbstractAssetDumper + { + protected: + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; + }; +} // namespace phys_constraints diff --git a/src/ObjWriting/Game/T5/ObjWriterT5.cpp b/src/ObjWriting/Game/T5/ObjWriterT5.cpp index d791f91c..6233c9b4 100644 --- a/src/ObjWriting/Game/T5/ObjWriterT5.cpp +++ b/src/ObjWriting/Game/T5/ObjWriterT5.cpp @@ -9,6 +9,7 @@ #include "LightDef/LightDefDumperT5.h" #include "Localize/LocalizeDumperT5.h" #include "Maps/MapEntsDumperT5.h" +#include "PhysConstraints/PhysConstraintsInfoStringDumperT5.h" #include "PhysPreset/PhysPresetInfoStringDumperT5.h" #include "RawFile/RawFileDumperT5.h" #include "StringTable/StringTableDumperT5.h" @@ -19,7 +20,7 @@ using namespace T5; void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context) { RegisterAssetDumper(std::make_unique()); - // REGISTER_DUMPER(AssetDumperPhysConstraints, m_phys_constraints) + RegisterAssetDumper(std::make_unique()); // REGISTER_DUMPER(AssetDumperDestructibleDef, m_destructible_def) RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); diff --git a/src/ObjWriting/Game/T5/PhysConstraints/PhysConstraintsInfoStringDumperT5.cpp b/src/ObjWriting/Game/T5/PhysConstraints/PhysConstraintsInfoStringDumperT5.cpp new file mode 100644 index 00000000..20cc53cf --- /dev/null +++ b/src/ObjWriting/Game/T5/PhysConstraints/PhysConstraintsInfoStringDumperT5.cpp @@ -0,0 +1,87 @@ +#include "PhysConstraintsInfoStringDumperT5.h" + +#include "Game/T5/InfoString/InfoStringFromStructConverter.h" +#include "Game/T5/ObjConstantsT5.h" +#include "Game/T5/PhysConstraints/PhysConstraintsFields.h" +#include "PhysConstraints/PhysConstraintsCommon.h" + +#include +#include + +using namespace T5; + +namespace +{ + class InfoStringFromPhysConstraintsConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + switch (static_cast(field.iFieldType)) + { + case CFT_TYPE: + FillFromEnumInt(std::string(field.szName), field.iOffset, s_constraintTypeNames, std::extent_v); + break; + + default: + assert(false); + break; + } + } + + public: + InfoStringFromPhysConstraintsConverter(const PhysConstraints* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateInfoString(const XAssetInfo& asset) + { + assert(asset.Asset()->count <= 4); + + InfoStringFromPhysConstraintsConverter converter(asset.Asset(), + phys_constraints_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset.m_zone->m_script_strings.Count()); + if (scrStr >= asset.m_zone->m_script_strings.Count()) + return ""; + + return asset.m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace phys_constraints +{ + void InfoStringDumperT5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset.m_name, GDF_FILENAME_PHYS_CONSTRAINTS); + infoString.ToGdtProperties(INFO_STRING_PREFIX_PHYS_CONSTRAINTS, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(INFO_STRING_PREFIX_PHYS_CONSTRAINTS); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace phys_constraints diff --git a/src/ObjWriting/Game/T5/PhysConstraints/PhysConstraintsInfoStringDumperT5.h b/src/ObjWriting/Game/T5/PhysConstraints/PhysConstraintsInfoStringDumperT5.h new file mode 100644 index 00000000..4050328f --- /dev/null +++ b/src/ObjWriting/Game/T5/PhysConstraints/PhysConstraintsInfoStringDumperT5.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T5/T5.h" + +namespace phys_constraints +{ + class InfoStringDumperT5 final : public AbstractAssetDumper + { + protected: + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; + }; +} // namespace phys_constraints