3d: Match MOT bones to POL bones by array index

The original {Reign,Tapir,Seal}Engine apply motion bones to model bones
purely by array position, never consulting bone name or bone_id.

This fixes bone animation of "Shizuka & Nagi" model in Rance 9.
This commit is contained in:
kichikuou
2026-05-29 07:17:41 +09:00
parent 1fcccb766c
commit 916daec6b2
2 changed files with 12 additions and 30 deletions
+3 -3
View File
@@ -40,9 +40,9 @@ struct model {
int nr_materials;
struct material *materials;
int nr_bones;
struct bone *bones;
struct hash_table *bone_map; // bone id in POL/MOT -> struct bone *
struct hash_table *bone_name_map; // bone name -> (struct bone * | NULL)
struct bone *bones; // ordered so that parents precede their children
struct bone **bones_by_pol_index; // POL bone array index -> struct bone *
struct hash_table *bone_map; // bone id in POL -> struct bone *
struct hash_table *mot_cache; // name -> struct mot *
struct collider *collider;
vec3 aabb[2]; // axis-aligned bounding box
+9 -27
View File
@@ -485,14 +485,6 @@ static struct bone *add_bone(struct model *model, struct pol *pol, struct pol_bo
glm_quat_mat4(pol_bone->rotq, bone->inverse_bind_matrix);
glm_translate(bone->inverse_bind_matrix, pol_bone->pos);
// Update bone_name_map. If the bone name is not unique in the POL, set the
// map value to NULL so that ID matching will be used.
struct ht_slot *slot = ht_put(model->bone_name_map, pol_bone->name, bone);
if (slot->value != bone) {
NOTICE("%s: non-unique bone %s", model->path, pol_bone->name);
slot->value = NULL;
}
model->nr_bones++;
return bone;
}
@@ -546,11 +538,11 @@ struct model *model_load(struct archive *aar, const char *path)
if (pol->nr_bones > MAX_BONES)
ERROR("%s: Too many bones (%u)", model->path, pol->nr_bones);
model->bone_map = ht_create(pol->nr_bones * 3 / 2);
model->bone_name_map = ht_create(pol->nr_bones * 3 / 2);
model->mot_cache = ht_create(16);
model->bones = xcalloc_aligned(pol->nr_bones, struct bone);
model->bones_by_pol_index = xcalloc(pol->nr_bones, sizeof(struct bone *));
for (uint32_t i = 0; i < pol->nr_bones; i++) {
add_bone(model, pol, &pol->bones[i]);
model->bones_by_pol_index[i] = add_bone(model, pol, &pol->bones[i]);
}
if (model->nr_bones != (int)pol->nr_bones)
ERROR("%s: Broken bone data", model->path);
@@ -636,10 +628,9 @@ void model_free(struct model *model)
for (int i = 0; i < model->nr_bones; i++)
destroy_bone(&model->bones[i]);
xfree_aligned(model->bones);
free(model->bones_by_pol_index);
if (model->bone_map)
ht_free_int(model->bone_map);
if (model->bone_name_map)
ht_free(model->bone_name_map);
if (model->mot_cache) {
ht_foreach_value(model->mot_cache, (void(*)(void*))mot_free);
ht_free(model->mot_cache);
@@ -746,11 +737,6 @@ struct model *model_create_sphere(int r, int g, int b, int a)
return model;
}
static int cmp_motions_by_bone_id(const void *lhs, const void *rhs)
{
return (*(struct mot_bone **)lhs)->id - (*(struct mot_bone **)rhs)->id;
}
static struct mot *mot_load(const char *name, struct model *model, struct archive *aar)
{
struct archive_data *mot_file = RE_get_aar_entry(aar, model->path, name, ".MOT");
@@ -770,18 +756,14 @@ static struct mot *mot_load(const char *name, struct model *model, struct archiv
ERROR("%s: wrong number of bones. Expected %d but got %d", name, model->nr_bones, mot->nr_bones);
// Reorder mot->motions so that motion for model->bones[i] can be
// accessed by mot->motions[i].
// accessed by mot->motions[i]. MOT bones are applied to POL bones purely
// by array index; bone name and bone id are never used for matching.
struct mot_bone **reordered = xmalloc(mot->nr_bones * sizeof(struct mot_bone *));
for (uint32_t i = 0; i < mot->nr_bones; i++) {
// Match by name first, since some MOT have wrong bone IDs (e.g. maidsan_ahoge_*).
struct bone *bone = ht_get(model->bone_name_map, mot->motions[i]->name, NULL);
// If it is not found or is NULL (non-unique bone name), match by bone ID.
if (!bone)
bone = ht_get_int(model->bone_map, mot->motions[i]->id, NULL);
if (!bone)
ERROR("%s: invalid bone \"%s\" (%d)", name, mot->motions[i]->name, mot->motions[i]->id);
mot->motions[i]->id = bone->index;
reordered[model->bones_by_pol_index[i]->index] = mot->motions[i];
}
qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id);
memcpy(mot->motions, reordered, mot->nr_bones * sizeof(struct mot_bone *));
free(reordered);
// Load optional sidecar file.
if (re_plugin_version <= RE_TAPIR_PLUGIN) {