mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
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:
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user