2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2026-05-12 21:31:43 +00:00

feat: preserve root bone name when dumping xmodels with omitted default armature

This commit is contained in:
Jan Laupetin
2026-05-03 11:44:56 +02:00
parent 618592e411
commit d6a736eb5d
2 changed files with 44 additions and 22 deletions
@@ -56,6 +56,7 @@ namespace GAME
public: public:
std::optional<JsonXModelType> type; std::optional<JsonXModelType> type;
std::vector<JsonXModelLod> lods; std::vector<JsonXModelLod> lods;
std::optional<std::string> rootBoneName;
std::optional<int> collLod; std::optional<int> collLod;
std::optional<std::string> physPreset; std::optional<std::string> physPreset;
#if defined(FEATURE_IW4) || defined(FEATURE_IW5) #if defined(FEATURE_IW4) || defined(FEATURE_IW5)
@@ -74,6 +75,7 @@ namespace GAME
JsonXModel, JsonXModel,
type, type,
lods, lods,
rootBoneName,
collLod, collLod,
physPreset, physPreset,
#if defined(FEATURE_IW4) || defined(FEATURE_IW5) #if defined(FEATURE_IW4) || defined(FEATURE_IW5)
+42 -22
View File
@@ -161,28 +161,28 @@ namespace
return GetImageFromTextureDef(*potentialTextureDefs[0]); return GetImageFromTextureDef(*potentialTextureDefs[0]);
} }
bool GetSurfaces(const XModel* model, const unsigned lod, XSurface*& surfs, unsigned& surfCount) bool GetSurfaces(const XModel& model, const unsigned lod, XSurface*& surfs, unsigned& surfCount)
{ {
#if defined(FEATURE_IW4) || defined(FEATURE_IW5) #if defined(FEATURE_IW4) || defined(FEATURE_IW5)
if (!model->lodInfo[lod].modelSurfs || !model->lodInfo[lod].modelSurfs->surfs) if (!model.lodInfo[lod].modelSurfs || !model.lodInfo[lod].modelSurfs->surfs)
return false; return false;
surfs = model->lodInfo[lod].modelSurfs->surfs; surfs = model.lodInfo[lod].modelSurfs->surfs;
surfCount = model->lodInfo[lod].modelSurfs->numsurfs; surfCount = model.lodInfo[lod].modelSurfs->numsurfs;
#else #else
if (!model->surfs) if (!model.surfs)
return false; return false;
surfs = &model->surfs[model->lodInfo[lod].surfIndex]; surfs = &model.surfs[model.lodInfo[lod].surfIndex];
surfCount = model->lodInfo[lod].numsurfs; surfCount = model.lodInfo[lod].numsurfs;
#endif #endif
return true; return true;
} }
bool HasDefaultArmatureForLod(const XModel* model, const unsigned lod) bool HasDefaultArmatureForLod(const XModel& model, const unsigned lod)
{ {
if (model->numRootBones != 1 || model->numBones != 1) if (model.numRootBones != 1 || model.numBones != 1)
return false; return false;
XSurface* surfs; XSurface* surfs;
@@ -213,9 +213,9 @@ namespace
return true; return true;
} }
bool HasDefaultArmatureForAllLods(const XModel* model) bool HasDefaultArmatureForAllLods(const XModel& model)
{ {
for (auto lod = 0u; lod < model->numLods; lod++) for (auto lod = 0u; lod < model.numLods; lod++)
{ {
if (!HasDefaultArmatureForLod(model, lod)) if (!HasDefaultArmatureForLod(model, lod))
return false; return false;
@@ -348,7 +348,7 @@ namespace
{ {
XSurface* surfs; XSurface* surfs;
unsigned surfCount; unsigned surfCount;
if (!GetSurfaces(model, lod, surfs, surfCount)) if (!GetSurfaces(*model, lod, surfs, surfCount))
return; return;
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
@@ -376,7 +376,7 @@ namespace
{ {
XSurface* surfs; XSurface* surfs;
unsigned surfCount; unsigned surfCount;
if (!GetSurfaces(model, lod, surfs, surfCount)) if (!GetSurfaces(*model, lod, surfs, surfCount))
return; return;
auto totalWeightCount = 0u; auto totalWeightCount = 0u;
@@ -410,7 +410,7 @@ namespace
{ {
XSurface* surfs; XSurface* surfs;
unsigned surfCount; unsigned surfCount;
if (!GetSurfaces(model, lod, surfs, surfCount)) if (!GetSurfaces(*model, lod, surfs, surfCount))
return; return;
auto& weightCollection = out.m_bone_weight_data; auto& weightCollection = out.m_bone_weight_data;
@@ -529,7 +529,7 @@ namespace
{ {
XSurface* surfs; XSurface* surfs;
unsigned surfCount; unsigned surfCount;
if (!GetSurfaces(model, lod, surfs, surfCount)) if (!GetSurfaces(*model, lod, surfs, surfCount))
return; return;
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
@@ -570,7 +570,7 @@ namespace
// Keep armature handling consistent across all LODs so dumped GLTF/GLB round-trips // Keep armature handling consistent across all LODs so dumped GLTF/GLB round-trips
// preserve the same bone layout when re-imported. // preserve the same bone layout when re-imported.
if (!CanOmitDefaultArmature() || !HasDefaultArmatureForAllLods(model)) if (!CanOmitDefaultArmature() || !HasDefaultArmatureForAllLods(*model))
{ {
AddXModelBones(out, context, model); AddXModelBones(out, context, model);
AddXModelVertexBoneWeights(out, model, lod); AddXModelVertexBoneWeights(out, model, lod);
@@ -698,15 +698,15 @@ namespace
class JsonDumper class JsonDumper
{ {
public: public:
JsonDumper(AssetDumpingContext& context, std::ostream& stream) explicit JsonDumper(std::ostream& stream)
: m_stream(stream) : m_stream(stream)
{ {
} }
void Dump(const XModel* xmodel) const void Dump(AssetDumpingContext& context, const XModel* xmodel) const
{ {
JsonXModel jsonXModel; JsonXModel jsonXModel;
CreateJsonXModel(jsonXModel, *xmodel); CreateJsonXModel(context, jsonXModel, *xmodel);
nlohmann::json jRoot = jsonXModel; nlohmann::json jRoot = jsonXModel;
jRoot["$schema"] = "http://openassettools.dev/schema/xmodel.v1.json"; jRoot["$schema"] = "http://openassettools.dev/schema/xmodel.v1.json";
@@ -819,13 +819,33 @@ namespace
return JsonXModelType::ANIMATED; return JsonXModelType::ANIMATED;
} }
static void SetJsonRootBoneName(AssetDumpingContext& context, JsonXModel& jXModel, const XModel& xmodel)
{
assert(xmodel.boneNames);
assert(xmodel.boneNames[0] < context.m_zone.m_script_strings.Count());
assert(xmodel.numRootBones <= 1);
if (!xmodel.boneNames || xmodel.boneNames[0] >= context.m_zone.m_script_strings.Count() || xmodel.numRootBones == 0)
return;
jXModel.rootBoneName = context.m_zone.m_script_strings[xmodel.boneNames[0]];
}
static void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel) static void CreateJsonXModel(AssetDumpingContext& context, JsonXModel& jXModel, const XModel& xmodel)
{ {
if (xmodel.collLod >= 0) if (xmodel.collLod >= 0)
jXModel.collLod = xmodel.collLod; jXModel.collLod = xmodel.collLod;
jXModel.type = GetType(xmodel); jXModel.type = GetType(xmodel);
if (CanOmitDefaultArmature() && HasDefaultArmatureForAllLods(xmodel))
{
// If we are going to omit the armature, we need to make sure we remember the root bone name.
// It does not follow a specific pattern, may not be identical to the xmodel name and
// may be required for attaching the xmodel.
SetJsonRootBoneName(context, jXModel, xmodel);
}
for (auto lodNumber = 0u; lodNumber < xmodel.numLods; lodNumber++) for (auto lodNumber = 0u; lodNumber < xmodel.numLods; lodNumber++)
{ {
@@ -868,8 +888,8 @@ namespace
if (!assetFile) if (!assetFile)
return; return;
const JsonDumper dumper(context, *assetFile); const JsonDumper dumper(*assetFile);
dumper.Dump(asset.Asset()); dumper.Dump(context, asset.Asset());
} }
} // namespace } // namespace