diff --git a/assets/blender/characters/transfer_shape_keys.py b/assets/blender/characters/transfer_shape_keys.py index cedaa98..32b9ae1 100644 --- a/assets/blender/characters/transfer_shape_keys.py +++ b/assets/blender/characters/transfer_shape_keys.py @@ -522,9 +522,9 @@ def enforce_side_constraint(pos, surface_point, reference_normal, target_side, c # Very small offset to stay near surface offset = abs((surface_point - pos).length) * 0.2 if target_side > 0: - return proj_point - reference_normal * offset - else: return proj_point + reference_normal * offset + else: + return proj_point - reference_normal * offset return pos # Regular vertices - stronger enforcement @@ -534,9 +534,9 @@ def enforce_side_constraint(pos, surface_point, reference_normal, target_side, c offset = abs((surface_point - pos).length) * 0.95 if target_side > 0: - corrected_pos = proj_point - reference_normal * offset - else: corrected_pos = proj_point + reference_normal * offset + else: + corrected_pos = proj_point - reference_normal * offset return corrected_pos @@ -879,7 +879,7 @@ def set_shape_key_with_side_preservation(target_obj, sk_name, mapping, source_da smooth_penetration_areas(target_obj, sk_name, mapping, source_data) def test_shape_key_quality(target_obj, sk_name, mapping, source_data): - """Test shape key quality with side tracking""" + """Test shape key quality with side tracking (READ-ONLY - does not modify shape key data)""" sk = target_obj.data.shape_keys.key_blocks[sk_name] @@ -889,6 +889,9 @@ def test_shape_key_quality(target_obj, sk_name, mapping, source_data): prev_positions = None boundary_vertices = mapping.get('boundary_vertices', set()) + # Save original shape key data so we can restore it after testing + original_positions = [v.co.copy() for v in sk.data] + for val in test_values: if val == 0.0: sk.value = 0.0 @@ -932,6 +935,13 @@ def test_shape_key_quality(target_obj, sk_name, mapping, source_data): prev_positions = [v.co.copy() for v in target_obj.data.vertices] + # Restore original shape key data (the test should not modify the shape key) + target_obj.data.shape_keys.use_relative = False + for i, v in enumerate(sk.data): + if i < len(original_positions): + v.co = original_positions[i] + target_obj.data.shape_keys.use_relative = True + sk.value = 0.0 target_obj.data.update_tag() bpy.context.view_layer.update() diff --git a/assets/blender/scripts/export_models2.py b/assets/blender/scripts/export_models2.py index efb72bc..08e6a29 100644 --- a/assets/blender/scripts/export_models2.py +++ b/assets/blender/scripts/export_models2.py @@ -376,10 +376,14 @@ for mapping in[CommandLineMapping()]: save_data["tags"] = unique_tags # Export shape keys if present + # IMPORTANT: Skip "Basis" to match OGRE's pose indexing. + # OGRE's blender2ogre exporter skips the Basis shape key (index 0), + # so pose index 0 in OGRE = first non-Basis shape key. shape_keys = [] if obj.data.shape_keys and obj.data.shape_keys.key_blocks: for sk in obj.data.shape_keys.key_blocks: - shape_keys.append(sk.name) + if sk.name != 'Basis': + shape_keys.append(sk.name) save_data["shape_keys"] = shape_keys save_data["mesh"] = obj.data.name + ".mesh" diff --git a/src/features/editScene/systems/CharacterSlotSystem.cpp b/src/features/editScene/systems/CharacterSlotSystem.cpp index e1d4b2e..463e245 100644 --- a/src/features/editScene/systems/CharacterSlotSystem.cpp +++ b/src/features/editScene/systems/CharacterSlotSystem.cpp @@ -614,7 +614,13 @@ void CharacterSlotSystem::applyShapeKeys(flecs::entity e, Ogre::Entity *ent, as->setEnabled(true); as->setLoop(false); - /* Build name -> pose index map from catalog */ + /* Build name -> pose index map from catalog. + * + * IMPORTANT: The catalog now skips "Basis" to match OGRE's pose indexing. + * OGRE's blender2ogre exporter skips the Basis shape key (index 0), + * so pose index 0 in OGRE = first non-Basis shape key in the catalog. + * This means catalog index i directly maps to OGRE pose index i. + */ const auto &shapeKeys = entry->value("shape_keys", nlohmann::json::array()); std::unordered_map nameToIndex;