349 lines
34 KiB
Markdown
349 lines
34 KiB
Markdown
|
|
Fiche U7b-T2 — Niveau N3 — Modèle prévu lp-cart | utilisé claude-opus-5-5 — Agent lp-cart — Skills sk-protocol, lp-commandlets (préchargés aussi : ue-cpp-build, lp-paths)
|
|||
|
|
|
|||
|
|
# U7b-T2 : portage de `RetargetPack.py` (étape 4 de la chaîne AP) à l'API Python de l'IK Retargeter UE 5.8
|
|||
|
|
|
|||
|
|
Date : 2026-10-09. Lecture seule : rien n'a été modifié, lancé ni commité. Ce livrable est le seul fichier écrit.
|
|||
|
|
- Addon : worktree `D:\Game\LifePlay-Sandbox-5.8`, branche `ue5.8`, HEAD `d1efdd1`. `git status --porcelain` est vide.
|
|||
|
|
- Script : `Tools\anim\RetargetPack.py` (463 lignes). Il est identique sur `lot/DC3-atelier`, le checkout 5.5 : `git -C D:/Game/LifePlay-Sandbox diff --stat HEAD ue5.8 -- Tools/anim/RetargetPack.py` ne sort rien.
|
|||
|
|
- Moteurs : `UE_5.8` (5.8.3, CL 58210709, `Engine/Build/Build.version`) et `UE_5.5`. Les sources IKRig et PythonScriptPlugin sont présentes dans les deux.
|
|||
|
|
- Abréviations : `K8/` = `D:\Game\Launcher\Engine\UE_5.8\Engine\Plugins\Animation\IKRig\Source\` ; `K5/` = idem sous `UE_5.5` ; `P8/` = `D:\Game\Launcher\Engine\UE_5.8\Engine\Plugins\Experimental\PythonScriptPlugin\Source\PythonScriptPlugin\Private\` ; `RP` = `RetargetPack.py`.
|
|||
|
|
- Niveaux de preuve : établi = lu à la source ou rejoué ; estimé = déduit ; incertain = non vérifié, avec l'essai qui tranche.
|
|||
|
|
|
|||
|
|
## Résumé
|
|||
|
|
|
|||
|
|
1. Il y a **trois** ruptures, pas deux. Les deux du report U5 sont confirmées. La troisième, absente du report, est la plus grave.
|
|||
|
|
2. **R1 (établi, code)** : `duplicate_and_retarget(..., True)` (RP:396-397) lève une `TypeError` en 5.8.
|
|||
|
|
- Le 9e paramètre est désormais `TargetPath: FString` (`K8/IKRigEditor/Public/RetargetEditor/IKRetargetBatchOperation.h:206-218`).
|
|||
|
|
- Un `bool` Python n'est converti ni en `str` ni en `Name` (`P8/PyConversion.cpp:307-326`) : l'appel échoue sur « Failed to convert parameter » (`P8/PyWrapperObject.cpp:300-311`).
|
|||
|
|
- La fonction est en plus dépréciée en 5.8 (h:196-205). La remplaçante est `run_batch_retarget(IKRetargetBatchOperationInputs)` (h:116-177, 193-194 ; cpp:964-1010).
|
|||
|
|
3. **R2 (établi, code + essai U5)** : `get/set_retarget_chain_settings` (RP:279, 286) sont dépréciés depuis 5.6 (`IKRetargeterController.h:758-766`).
|
|||
|
|
- Ils n'agissent que sur `ChainSettings_DEPRECATED`, vide sur un retargeter 5.8 (cpp:2593-2621).
|
|||
|
|
- `set` renvoie False (`Saved\U5T3b\resultats_v3.txt:12`). La voie qui marche passe par l'op « FK Chains » : `get_op_controller(i).get_settings()/set_settings()` (`FKChainsOp.h:369-384`, cpp:1222-1230).
|
|||
|
|
4. **R3 (établi, code ; nouveau)** : en 5.8, un retargeter créé par `IKRetargetFactory` n'a **aucune op** (`IKRetargetFactory.cpp:22-31` ; constructeur `IKRetargeter.cpp:30-46`). `set_ik_rig` n'en crée pas non plus (`IKRetargeterController.cpp:76-121`).
|
|||
|
|
- Conséquence : `auto_map_chains` (RP:274) ne fait rien, car il n'agit que sur le mappage de chaque op (cpp:908-939). Le réglage des chaînes n'a rien à modifier.
|
|||
|
|
- Effet estimé : clips figés en pose de retarget. Incertain : aucun avertissement n'existe dans le code pour une pile vide (recherche vide).
|
|||
|
|
5. Les RTG existants n'ont **pas** ce défaut. Leur pile de 6 ops a été construite au chargement par la conversion 5.5 → 5.8 (`IKRetargeter.cpp:261-473`). Le dump après resave montre : Pelvis Motion, FK Chains, Stride Warp (désactivée), Speed Plant (sans chaîne), Run IK Rig, Pole Vector Alignment.
|
|||
|
|
6. Correctif proposé (§ 5, diff non appliqué), en trois points :
|
|||
|
|
- ajouter 3 ops (Pelvis Motion, FK Chains, Run IK Rig) juste après `set_ik_rig` ;
|
|||
|
|
- régler les chaînes par l'op FK Chains (`FKChainRotationMode`) ;
|
|||
|
|
- remplacer l'appel par lot par `run_batch_retarget`, avec les mêmes valeurs qu'en 5.5.
|
|||
|
|
7. Garde de version : **pas nécessaire**. Le checkout 5.5 garde sa propre copie du script (branche `lot/DC3-atelier`). Le correctif ne va que sur `ue5.8`. Une variante par détection de fonctionnalité est donnée au § 6.
|
|||
|
|
8. Le point le plus risqué : lancer le script **non corrigé** en 5.8 supprime les références avant d'échouer (§ 8, risque 1). Il remplace les IK/RTG par des RTG sans op, puis efface les clips cuits B_/M_ (RP:438), puis lève la `TypeError` (RP:396). Ces dossiers sont ignorés par git.
|
|||
|
|
9. Essai décisif (§ 7) : tout se joue en 5.8, sur une destination temporaire.
|
|||
|
|
- Recréer les RTG avec le script corrigé, puis recibler les mêmes clips avec le RTG de référence et avec le nouveau.
|
|||
|
|
- Critère : 0 valeur différente, bit pour bit, plus un témoin qui doit réagir.
|
|||
|
|
|
|||
|
|
## 1. Recherche web (avant la vérification locale)
|
|||
|
|
|
|||
|
|
Les pages ont été lues par `curl` et sont traitées comme des données. Toutes les valeurs ont ensuite été relues dans les sources installées.
|
|||
|
|
- API `IKRetargetBatchOperation`, à trois versions :
|
|||
|
|
- 5.6 : `duplicate_and_retarget(..., suffix='', include_referenced_assets=True)`, 9 paramètres. https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/IKRetargetBatchOperation?application_version=5.6
|
|||
|
|
- 5.7 : ajoute `overwrite_existing_files` en 10e position ; le 9e reste le booléen, donc l'appel du script y marchait encore. https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/IKRetargetBatchOperation?application_version=5.7
|
|||
|
|
- 5.8 : `(…, suffix='', target_path='', use_source_path=False, include_referenced_assets=True, overwrite_existing_files=False)`, marquée « deprecated: Use RunBatchRetarget instead », plus `run_batch_retarget(inputs)`. https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/IKRetargetBatchOperation?application_version=5.8
|
|||
|
|
- Structure d'entrée : https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/IKRetargetBatchOperationInputs?application_version=5.8. Champs Python : `assets_to_retarget`, `source_mesh`, `target_mesh`, `ik_retarget_asset`, `override_set_names`, `search`, `replace`, `prefix`, `suffix`, `target_path`, `use_source_path`, `include_referenced_assets`, `overwrite_existing_files`, `retain_additive_flags`.
|
|||
|
|
- La signature de la page affiche `include_referenced_assets=False` et `retain_additive_flags=False`. Les défauts C++ valent `true` (h:167, 176).
|
|||
|
|
- Il faut donc tout régler explicitement (estimé : la page montre des défauts de type, pas ceux du constructeur).
|
|||
|
|
- Contrôleur et op FK, en 5.6 et en 5.8 :
|
|||
|
|
- https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/IKRetargeterController?application_version=5.6 et `…=5.8` : `get_op_controller`, `get_index_of_op_by_name`, `add_retarget_op`, `add_default_ops`, `auto_map_chains(auto_map_type, force_remap, op_name='None')`. `set_retarget_chain_settings` reste listé, sans mention de dépréciation côté Python.
|
|||
|
|
- https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/IKRetargetFKChainsController?application_version=5.8 : `get_settings()` et `set_settings()`.
|
|||
|
|
- https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/RetargetFKChainSettings?application_version=5.8 : `target_chain_name`, `enable_fk`, `rotation_mode`, `rotation_alpha`, `translation_mode`, `translation_alpha`.
|
|||
|
|
- https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/FKChainRotationMode?application_version=5.8 : `INTERPOLATED=0`, `ONE_TO_ONE=1`, etc.
|
|||
|
|
- https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/RetargetRotationMode?application_version=5.8 : « NOTE: Replaced by FK Chain Op », fichier `IKRetargetDeprecated.h`.
|
|||
|
|
- Notes de version 5.8 : https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5-8-release-notes
|
|||
|
|
- « Expose bRetainAdditiveFlags to RunBatchRetarget Blueprint/Python API ».
|
|||
|
|
- « Blend to Source, Scale Goals, and Offset Goals are split out in the IK Op Stack ».
|
|||
|
|
- Rien sur le 9e argument de `DuplicateAndRetarget`.
|
|||
|
|
- Les pages de notes 5.6 et 5.7 et la page « retargeting scripting » sont générées en JavaScript : `curl` ne rapporte que la table des matières, contenu non lu.
|
|||
|
|
|
|||
|
|
## 2. Chaîne d'appel (pilote)
|
|||
|
|
|
|||
|
|
- `AnimPipeline.ps1` étape 4 `Step-Conversion` (l. 262-291) écrit `anim_retarget.json` (mode bake, `pose_dir = <Work>/pose`), puis lance `BuildMod.ps1 -Retarget bake -RetargetOnly -RetargetConfig` par `cmd /c start /wait`.
|
|||
|
|
- Il exige ensuite la ligne `RETARGET bridget n michael n`, avec n = nombre de clips source (l. 286-290).
|
|||
|
|
- `BuildMod.ps1` `Invoke-AnimToGenesis` (l. 84-148) :
|
|||
|
|
- il copie la configuration vers `Saved\anim_retarget.json` en ajoutant un jeton ;
|
|||
|
|
- il lance `UnrealEditor.exe "<projet>" -ExecutePythonScript="<addon>\Tools\anim\RetargetPack.py" -nosplash -unattended` (l. 113) ;
|
|||
|
|
- il attend `RETARGET … jeton <j>` ou `RETARGET echec … jeton <j>` (l. 73-77, 131-140).
|
|||
|
|
- Le seul consommateur de ces API dans l'addon est `RP`. Commande : `grep -rn "IKRetargeterController|IKRetargetBatchOperation|RetargetRotationMode|get_retarget_chain_settings"` sur les `.py/.cpp/.h` du worktree, hors `Content` → 4 lignes, toutes dans RP (269, 279, 281, 396). Aucun autre script n'importe RP : `LP_RETARGET_NOMAIN` n'apparaît qu'en RP:462.
|
|||
|
|
|
|||
|
|
## 3. Appel par appel (RP:ligne → état 5.8 → équivalent 5.8)
|
|||
|
|
|
|||
|
|
Recomptage : `grep -o "unreal\.[A-Za-z_]*\(\.[A-Za-z_]*\)\?" Tools/anim/RetargetPack.py | sort | uniq -c` → 37 symboles distincts, dont `log_warning` et `log_error`. `grep -o "ctrl\.[a-z_]*"` → 9 méthodes de contrôleur.
|
|||
|
|
|
|||
|
|
| RP:ligne | Appel | État 5.8 | Équivalent / preuve 5.8 (5.5 si utile) |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| 185 | `factory.set_editor_property("edit_after_new", False)` | inchangé | `UFactory::bEditAfterNew` ; factories `IKRigDefinitionFactory.cpp:23-33`, `IKRetargetFactory.cpp:15-31` |
|
|||
|
|
| 190, 194, 202 | `MathLibrary.subtract_vector_vector`, `normal`, `cross_vector_vector` | inchangé | `Engine/Source/Runtime/Engine/Classes/Kismet/KismetMathLibrary.h:1381, 1703, 1441` (5.5 : 1384, 1702, 1444) |
|
|||
|
|
| 206 | `mesh.skeleton.get_reference_pose()` | inchangé | `Engine/Source/Editor/AnimationBlueprintLibrary/Public/AnimPose.h:329` |
|
|||
|
|
| 207, 345 | `AnimPoseExtensions.get_bone_names`, `get_bone_pose` | inchangé | `AnimPose.h:181, 193` |
|
|||
|
|
| 328-337 | `AnimPoseEvaluationOptions` (`should_retarget`), `get_anim_pose_at_time` | inchangé | `AnimPose.h:62, 288` |
|
|||
|
|
| 229, 270-273 | `RetargetSourceOrTarget.SOURCE/TARGET` | inchangé | `ERetargetSourceOrTarget`, signatures du contrôleur h:76, 93 |
|
|||
|
|
| 243, 265, 311, 370 / 244, 266, 312 | `EditorAssetLibrary.does_asset_exist` / `delete_asset` | inchangé | `…/EditorScriptingUtilities/Public/EditorAssetLibrary.h:70 / 144` |
|
|||
|
|
| 259, 287 / 294 / 301 / 403 | `save_loaded_asset` / `list_assets` / `find_asset_data` / `rename_asset` | inchangé | `EditorAssetLibrary.h:262 / 328 / 62 / 206` |
|
|||
|
|
| 249, 267 | `AssetTools.create_asset(name, path, class, factory)` | inchangé (compatible) | `Engine/Source/Developer/AssetTools/Public/IAssetTools.h:349` : paramètre optionnel `bOverwriteExisting` ajouté en fin |
|
|||
|
|
| 251-255 | `IKRigController.get_controller`, `set_skeletal_mesh`, `set_retarget_root`, `add_retarget_chain(chain, start, end, Name())` | inchangé | `K8/IKRigEditor/Public/RigEditor/IKRigController.h:56-57, 61-62, 349-350, 309-310`. `SetRetargetRoot` porte `DisplayName="SetPelvisBone"`, mais le nom Python vient de `ScriptName` (`P8/PyGenUtil.cpp:3213-3216`) et la doc 5.8 liste `set_retarget_root` : établi |
|
|||
|
|
| 268 | `create_asset(…, IKRetargeter, IKRetargetFactory)` | **comportement changé (R3)** | `IKRetargetFactory.cpp:22-31` : `NewObject` seul, pile `RetargetOps` vide. Remplacer par `add_retarget_op(type)` ×3 (h:106-109, cpp:197-207, 780-835). `OnAddedToStack` lit alors les IK Rigs du retargeter (`FKChainsOp.cpp:990-1000`, `PelvisMotionOp.cpp:137-149`, `RunIKRigOp.cpp:358-370`) |
|
|||
|
|
| 269 | `IKRetargeterController.get_controller` | inchangé | h:70 |
|
|||
|
|
| 270-271 | `set_ik_rig` | signature inchangée | h:76, cpp:76-121 : ne met à jour que les mappages des ops déjà présentes, côté source seulement (l. 99-116). Les ops doivent être ajoutées **après** |
|
|||
|
|
| 272-273 | `set_preview_mesh` | inchangé | h:93 |
|
|||
|
|
| 274 | `auto_map_chains(EXACT, True)` | **changé (sémantique, R3)** | h:285-286 (`op_name=None` ajouté) ; cpp:908-939 : boucle sur les ops. Sans op, aucun effet. À appeler après l'ajout des ops |
|
|||
|
|
| 236 | `set_rotation_offset_for_retarget_pose_bone(bone, quat, TARGET)` | signature inchangée | h:413-414, cpp:1359-1379. Le tri hiérarchique se fait par le squelette du maillage de prévisualisation (5.5 : par le squelette de l'IK Rig, `K5/…/IKRetargeterController.cpp:889-899`). Effet sur la sortie : estimé nul ; ordre de sérialisation : incertain (essai § 7, md5 de `TargetRetargetPoses`) |
|
|||
|
|
| 279 | `get_retarget_chain_settings(chain)` | **déprécié 5.6, inerte (R2)** | h:758-761 ; cpp:2593-2604 renvoie `FTargetChainSettings()` par défaut. Remplacer par `get_index_of_op_by_name("FK Chains")` (h:153-154), `get_op_controller(i)` (h:209-210) et `IKRetargetFKChainsController.get_settings()` (`K8/IKRig/Public/Retargeter/RetargetOps/FKChainsOp.h:377-378`) |
|
|||
|
|
| 280, 285 | propriété `fk` de `TargetChainSettings` | déprécié | `IKRetargetDeprecated.h` (doc 5.8 : « Property 'FK' is deprecated ») → `RetargetFKChainSettings` (`FKChainsOp.h:176-230`) |
|
|||
|
|
| 281 | `RetargetRotationMode.INTERPOLATED / ONE_TO_ONE` | déprécié | `K8/IKRig/Public/Retargeter/IKRetargetDeprecated.h:49` → `FKChainRotationMode` (`FKChainsOp.h:164-174` : `Interpolated=0`, `OneToOne=1`), même valeur numérique (conversion `IKRetargeter.cpp:342`) |
|
|||
|
|
| 283 | `RetargetTranslationMode.NONE` | déprécié | `IKRetargetDeprecated.h:38` → `FKChainTranslationMode.NONE` (`FKChainsOp.h:153-162`), défaut l. 219 |
|
|||
|
|
| 284 | `pole_vector_matching = 0.0` | déprécié | Remplacé par l'op Pole Vector (`AlignPoleVectorOp.h`, `bEnabled=false` par défaut l. 73). 0,0 est le défaut 5.5 (`K5/IKRig/Public/Retargeter/IKRetargetSettings.h:98`), et la conversion ne crée aucune chaîne active à 0 (`IKRetargeter.cpp:456`) : **pas d'op nécessaire** |
|
|||
|
|
| 286 | `set_retarget_chain_settings(chain, settings)` | **déprécié 5.6, sans effet (R2)** | h:763-766 ; cpp:2606-2621 renvoie False. Remplacer par `IKRetargetFKChainsController.set_settings(settings)` (`FKChainsOp.h:382-383`, cpp:1227-1230 → `CopySettingsAtRuntime` cpp:787-800, recopie par nom de chaîne `IKRetargetOpUtils.h:164-178`) |
|
|||
|
|
| 396-397 | `IKRetargetBatchOperation.duplicate_and_retarget(clips, src, mesh, rtg, "", "", prefixe, "", True)` | **déprécié 5.8 et cassé (R1)** | 5.5 : 9e = `bIncludeReferencedAssets` (`K5/IKRigEditor/Public/RetargetEditor/IKRetargetBatchOperation.h:129-139`). 5.8 : 9e = `TargetPath` (h:206-218), meta `DeprecatedFunction` (h:197-205) ; échec de conversion bool → FString, voir § 4. Remplacer par `run_batch_retarget(IKRetargetBatchOperationInputs)` (h:193-194 ; struct h:116-177 ; cpp:964-1010) |
|
|||
|
|
| 321, 407 | `EditorLoadingAndSavingUtils.save_dirty_packages(True, True)` | inchangé | `Engine/Source/Editor/UnrealEd/Public/FileHelpers.h:108` |
|
|||
|
|
| 323, 455, 458 | `SystemLibrary.quit_editor()` | inchangé | `KismetSystemLibrary.h:656` (5.5 : 606) |
|
|||
|
|
| 43, 224, 245…, 296, 342, 413 | `Name`, `Quat`, `load_asset`, `AnimSequence`, `AnimPoseSpaces.WORLD`, `Paths.project_saved_dir` | estimé inchangé | non relus un par un (types et fonctions de base) |
|
|||
|
|
|
|||
|
|
Bilan (comptage manuel du tableau) :
|
|||
|
|
- 4 appels rompus ou sans effet : RP:396, 286, 279, 274 ;
|
|||
|
|
- 4 types ou propriétés dépréciés, tous dans le bloc RP:279-286 ;
|
|||
|
|
- 2 comportements changés sans échec : RP:268 (retargeter sans op) et RP:236 (tri de la pose) ;
|
|||
|
|
- 25 inchangés établis et 2 lignes de tableau « estimé inchangé ».
|
|||
|
|
|
|||
|
|
## 4. Détail des trois ruptures
|
|||
|
|
|
|||
|
|
**R1, appel par lot (RP:396-397).**
|
|||
|
|
- Comportement en 5.8, établi par le code :
|
|||
|
|
- avertissement `DeprecationWarning` « Function 'duplicate_and_retarget' on 'IKRetargetBatchOperation' is deprecated » (`P8/PyWrapperObject.cpp:288-296`) ;
|
|||
|
|
- conversion de `True` vers `FString` : échec, seuls `str` et `unreal.Name` sont acceptés (`P8/PyConversion.cpp:307-326`, `NATIVIZE_INLINE_PROPERTY(FStrProperty)` l. 761) ;
|
|||
|
|
- `TypeError` « Failed to convert parameter 'target_path' when calling function 'IKRetargetBatchOperation.DuplicateAndRetarget' on 'Default__IKRetargetBatchOperation' » (l. 309). Texte exact : estimé, car le nom de l'objet n'a pas été vérifié.
|
|||
|
|
- Le script attrape l'exception et écrit `RETARGET echec : … jeton <j>` (RP:456-459). BuildMod lève alors « Conversion en echec » (`BuildMod.ps1:139`).
|
|||
|
|
- Équivalence 5.5 → 5.8 des valeurs :
|
|||
|
|
- 5.5 ne réglait pas le dossier : `FNameDuplicationRule::FolderPath` vaut `/Game` par défaut (`Engine/Source/Editor/UnrealEd/Public/EditorAnimUtils.h:27`, identique en 5.5 et 5.8). Les copies naissaient donc sous `/Game/<préfixe><clip>`, puis RP:399-406 les déplaçait vers `out_dir`. C'est pour cela que `delete_copies` vise aussi `/Game/` (RP:309).
|
|||
|
|
- 5.8 garde ce défaut si `target_path` est vide (cpp:985-988).
|
|||
|
|
- Les défauts de contexte sont identiques : `bRetainAdditiveFlags=true`, `bExportOnlyAnimatedBones=true` (5.5 h:79-83 ; 5.8 h:82-86, struct h:176).
|
|||
|
|
|
|||
|
|
**R2, réglage des chaînes (RP:278-286).**
|
|||
|
|
- Avec un retargeter neuf, `get` renvoie le défaut (Interpolated) et `set` renvoie False, sans effet (essai U5, `D:\Game\LifePlay-master 5.8\Saved\U5T3b\resultats_v3.txt:12-13`).
|
|||
|
|
- La voie op FK est prouvée par les témoins U5 (l. 16-22) : Spine relu `ONE_TO_ONE`, 930 valeurs sur 59 210 différentes, 8,91°.
|
|||
|
|
- Les 24 chaînes `OneToOne` du script seraient perdues : seules Spine et Neck sont voulues `Interpolated`, le défaut (`FKChainsOp.h:203`).
|
|||
|
|
|
|||
|
|
**R3, retargeter neuf sans op (RP:268-274).**
|
|||
|
|
- Établi : ni la factory, ni le constructeur, ni `set_ik_rig` n'ajoutent d'op. `AddDefaultOps` n'est appelé que par l'UI : `SRetargetAnimAssetsWindow.cpp:687` et `SRetargetOpStack.cpp:1106` (grep sur les plugins 5.8).
|
|||
|
|
- Le flux d'Epic pour l'auto-retarget enchaîne `SetIKRig` ×2, `RemoveAllOps`, `AddDefaultOps`, puis le mappage exact (`SRetargetAnimAssetsWindow.cpp:680-692`).
|
|||
|
|
- Pourquoi ne pas reprendre `add_default_ops()` tel quel :
|
|||
|
|
- il ajoute aussi **Root Motion** et **Curve Remap** (`IKRetargeterController.cpp:383-396`), absents des RTG de référence (dump, § 7) ;
|
|||
|
|
- sans op de courbe, 5.8 garde toutes les courbes copiées à la duplication (`IKRetargetBatchOperation.cpp:593-614`), comme 5.5 sans op Curve Remap (`K5/…/IKRetargetBatchOperation.cpp:497-523`) ;
|
|||
|
|
- Root Motion modifierait l'os racine (estimé).
|
|||
|
|
- D'où la pile minimale : Pelvis Motion, FK Chains, Run IK Rig.
|
|||
|
|
- Les réglages de Pelvis Motion par défaut (`PelvisMotionOp.h:31-125`) sont ceux de l'op convertie : tous les champs du dump sont égaux aux défauts. Ses os de bassin sont lus sur les IK Rigs (`PelvisMotionOp.cpp:137-149`) : `pelvis` → `hip` dans la référence.
|
|||
|
|
- Les 3 ops omises de la référence sont inertes (estimé) :
|
|||
|
|
- Stride Warp `bEnabled=False` ;
|
|||
|
|
- Speed Plant sans chaîne ;
|
|||
|
|
- Pole Vector avec `bEnabled` par chaîne à false par défaut (`AlignPoleVectorOp.h:73`).
|
|||
|
|
- Chemins de type des ops (`AddRetargetOp(FString)` = `FindObject<UScriptStruct>(nullptr, path)`, cpp:197-206 ; exemple d'en-tête h:107 ; noms vus au dump) : `/Script/IKRig.IKRetargetPelvisMotionOp`, `/Script/IKRig.IKRetargetFKChainsOp`, `/Script/IKRig.IKRetargetRunIKRigOp`.
|
|||
|
|
|
|||
|
|
Référence lue : la pile des RTG convertis, par `python -I D:\Game\LifePlay-master 5.8\Saved\U5T3b\scripts\ops.py <section>` sur `D:\Game\LifePlay-master 5.8\Saved\Logs\portage\U5_assets_apres.txt:1117300-1117315` (RTG_Bridget Quaternius) → `ops: 6`. Contenu :
|
|||
|
|
- FK `ChainsToRetarget` : 24 `OneToOne` ; Spine et Neck à défaut ;
|
|||
|
|
- `ChainMapping` exact par nom (26 paires selon U5-T3b § 3.1) ;
|
|||
|
|
- Run IK Rig `Chains=` vide ;
|
|||
|
|
- Pole Vector : entrées sans `bEnabled`.
|
|||
|
|
|
|||
|
|
## 5. Correctif proposé (diff non appliqué, branche `ue5.8`, base `d1efdd1`)
|
|||
|
|
|
|||
|
|
Ordre imposé :
|
|||
|
|
1. `set_ik_rig` ×2 (les ops lisent les rigs à l'ajout) ;
|
|||
|
|
2. `set_preview_mesh` ×2 (tri de la pose) ;
|
|||
|
|
3. ops ;
|
|||
|
|
4. `auto_map_chains` ;
|
|||
|
|
5. pose ;
|
|||
|
|
6. réglages FK.
|
|||
|
|
|
|||
|
|
Les lignes `RETARGET …` attendues par BuildMod et AnimPipeline ne changent pas.
|
|||
|
|
|
|||
|
|
```diff
|
|||
|
|
--- a/Tools/anim/RetargetPack.py
|
|||
|
|
+++ b/Tools/anim/RetargetPack.py
|
|||
|
|
@@ -178,6 +178,12 @@
|
|||
|
|
|
|||
|
|
|
|||
|
|
INTERPOLATED = ("Spine", "Neck")
|
|||
|
|
+# UE 5.8 : un retargeter neuf n'a aucune op (IKRetargetFactory.cpp:30). Pile minimale equivalente a celle que la
|
|||
|
|
+# conversion 5.5 -> 5.8 a donnee aux RTG existants (IKRetargeter.cpp:309-446) : Pelvis Motion, FK Chains, Run IK Rig.
|
|||
|
|
+# Stride Warp (desactivee), Speed Plant (sans chaine) et Pole Vector (aucune chaine active) sont inertes : omises.
|
|||
|
|
+RETARGET_OPS = ("/Script/IKRig.IKRetargetPelvisMotionOp",
|
|||
|
|
+ "/Script/IKRig.IKRetargetFKChainsOp",
|
|||
|
|
+ "/Script/IKRig.IKRetargetRunIKRigOp")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def make_factory(factory_class):
|
|||
|
|
@@ -260,30 +266,57 @@
|
|||
|
|
return rig, mesh, rig_chains
|
|||
|
|
|
|||
|
|
|
|||
|
|
+def set_fk_chains(ctrl, name, source_chains):
|
|||
|
|
+ # UE 5.6+ : les reglages de chaine vivent dans l'op FK Chains ; set_retarget_chain_settings est inerte en 5.8
|
|||
|
|
+ # (IKRetargeterController.cpp:2606-2621). Spine et Neck : Interpolated (defaut) ; les autres : OneToOne ;
|
|||
|
|
+ # translation None (defaut). pole_vector_matching 0 (defaut 5.5) = pas d'op Pole Vector.
|
|||
|
|
+ index = ctrl.get_index_of_op_by_name("FK Chains")
|
|||
|
|
+ fk_op = ctrl.get_op_controller(index) if index >= 0 else None
|
|||
|
|
+ if fk_op is None:
|
|||
|
|
+ raise RuntimeError("op FK Chains absente %s" % name)
|
|||
|
|
+ wanted = {}
|
|||
|
|
+ for chain_name, _, _ in source_chains:
|
|||
|
|
+ wanted[chain_name] = (unreal.FKChainRotationMode.INTERPOLATED if chain_name in INTERPOLATED
|
|||
|
|
+ else unreal.FKChainRotationMode.ONE_TO_ONE)
|
|||
|
|
+ settings = fk_op.get_settings()
|
|||
|
|
+ chains = list(settings.get_editor_property("chains_to_retarget"))
|
|||
|
|
+ for chain in chains:
|
|||
|
|
+ mode = wanted.get(str(chain.get_editor_property("target_chain_name")))
|
|||
|
|
+ if mode is None:
|
|||
|
|
+ continue
|
|||
|
|
+ chain.set_editor_property("enable_fk", True)
|
|||
|
|
+ chain.set_editor_property("rotation_mode", mode)
|
|||
|
|
+ chain.set_editor_property("translation_mode", unreal.FKChainTranslationMode.NONE)
|
|||
|
|
+ settings.set_editor_property("chains_to_retarget", chains)
|
|||
|
|
+ fk_op.set_settings(settings)
|
|||
|
|
+ read = {str(c.get_editor_property("target_chain_name")): c.get_editor_property("rotation_mode")
|
|||
|
|
+ for c in fk_op.get_settings().get_editor_property("chains_to_retarget")}
|
|||
|
|
+ wrong = sorted(n for n, m in wanted.items() if read.get(n) != m)
|
|||
|
|
+ if wrong:
|
|||
|
|
+ raise RuntimeError("reglage FK non applique %s : %s" % (name, ", ".join(wrong)))
|
|||
|
|
+
|
|||
|
|
+
|
|||
|
|
def make_retargeter(name, source_rig, target_rig, source_mesh, target_mesh, body, source_chains):
|
|||
|
|
asset_path = CFG["dest"] + "/" + name
|
|||
|
|
if unreal.EditorAssetLibrary.does_asset_exist(asset_path):
|
|||
|
|
unreal.EditorAssetLibrary.delete_asset(asset_path)
|
|||
|
|
retargeter = unreal.AssetToolsHelpers.get_asset_tools().create_asset(
|
|||
|
|
name, CFG["dest"], unreal.IKRetargeter, make_factory(unreal.IKRetargetFactory))
|
|||
|
|
ctrl = unreal.IKRetargeterController.get_controller(retargeter)
|
|||
|
|
ctrl.set_ik_rig(unreal.RetargetSourceOrTarget.SOURCE, source_rig)
|
|||
|
|
ctrl.set_ik_rig(unreal.RetargetSourceOrTarget.TARGET, target_rig)
|
|||
|
|
ctrl.set_preview_mesh(unreal.RetargetSourceOrTarget.SOURCE, source_mesh)
|
|||
|
|
ctrl.set_preview_mesh(unreal.RetargetSourceOrTarget.TARGET, target_mesh)
|
|||
|
|
+ # Un retargeter neuf n'a aucune op en 5.8 ; les ops lisent les IK Rigs a l'ajout (OnAddedToStack) :
|
|||
|
|
+ # apres set_ik_rig, avant auto_map_chains (qui n'agit que sur le mappage des ops).
|
|||
|
|
+ for op_type in RETARGET_OPS:
|
|||
|
|
+ if ctrl.add_retarget_op(op_type) < 0:
|
|||
|
|
+ raise RuntimeError("op impossible %s %s" % (name, op_type))
|
|||
|
|
ctrl.auto_map_chains(unreal.AutoMapChainType.EXACT, True)
|
|||
|
|
# Pose de retarget : la pose ajustee de la sandbox, et non plus l'alignement par le maillage,
|
|||
|
|
# les reglages Daz et les demi-tours d'epaule (pole / roll180), qu'elle remplace.
|
|||
|
|
apply_pose(ctrl, target_mesh, body)
|
|||
|
|
- for chain_name, _, _ in source_chains:
|
|||
|
|
- settings = ctrl.get_retarget_chain_settings(chain_name)
|
|||
|
|
- fk = settings.get_editor_property("fk")
|
|||
|
|
- mode = unreal.RetargetRotationMode.INTERPOLATED if chain_name in INTERPOLATED else unreal.RetargetRotationMode.ONE_TO_ONE
|
|||
|
|
- fk.set_editor_property("rotation_mode", mode)
|
|||
|
|
- fk.set_editor_property("translation_mode", unreal.RetargetTranslationMode.NONE)
|
|||
|
|
- fk.set_editor_property("pole_vector_matching", 0.0)
|
|||
|
|
- settings.set_editor_property("fk", fk)
|
|||
|
|
- ctrl.set_retarget_chain_settings(chain_name, settings)
|
|||
|
|
+ set_fk_chains(ctrl, name, source_chains)
|
|||
|
|
unreal.EditorAssetLibrary.save_loaded_asset(retargeter)
|
|||
|
|
return retargeter
|
|||
|
|
|
|||
|
|
@@ -392,7 +425,15 @@
|
|||
|
|
def bake_named(clips, source_mesh, bodies):
|
|||
|
|
# bodies : [(corps, maillage, retargeter)] ; renvoie (nombre de copies par corps, nombre deplaces).
|
|||
|
|
+ # UE 5.8 : duplicate_and_retarget est deprecie et son 9e argument est devenu target_path (FString) :
|
|||
|
|
+ # run_batch_retarget et sa structure. Memes valeurs qu'en 5.5 : copies dans /Game (target_path vide), puis deplacees.
|
|||
|
|
made = []
|
|||
|
|
for body, mesh, retargeter in bodies:
|
|||
|
|
- made.append(unreal.IKRetargetBatchOperation.duplicate_and_retarget(
|
|||
|
|
- clips, source_mesh, mesh, retargeter, "", "", body["prefixe"], "", True))
|
|||
|
|
+ inputs = unreal.IKRetargetBatchOperationInputs()
|
|||
|
|
+ for prop, value in (("assets_to_retarget", clips), ("source_mesh", source_mesh), ("target_mesh", mesh),
|
|||
|
|
+ ("ik_retarget_asset", retargeter), ("search", ""), ("replace", ""),
|
|||
|
|
+ ("prefix", body["prefixe"]), ("suffix", ""), ("target_path", ""),
|
|||
|
|
+ ("use_source_path", False), ("include_referenced_assets", True),
|
|||
|
|
+ ("overwrite_existing_files", False), ("retain_additive_flags", True)):
|
|||
|
|
+ inputs.set_editor_property(prop, value)
|
|||
|
|
+ made.append(unreal.IKRetargetBatchOperation.run_batch_retarget(inputs))
|
|||
|
|
moved = 0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Notes sur le diff :
|
|||
|
|
- Diff vérifié sur une copie du script hors projet (scratchpad de session) : `git apply --check -v p.diff` passe, `git apply` est appliqué, et `ast.parse` du résultat donne « syntaxe OK ». Après application, aucune occurrence de `duplicate_and_retarget(` ni de `set_retarget_chain_settings(` en code ; il ne reste que des commentaires. L'addon reste intact : md5 `3c6491c6…`, `git status --porcelain` à 0 ligne.
|
|||
|
|
- Comparer les valeurs d'enum Python (`read.get(n) != m`) : estimé correct, l'essai U5 l. 17 relit `<FKChainRotationMode.ONE_TO_ONE: 1>`.
|
|||
|
|
- `get_op_controller` renvoie l'objet de classe réelle `IKRetargetFKChainsController` : établi par l'essai U5, qui utilisait `get_settings` et `set_settings`.
|
|||
|
|
- Variante possible pour `target_path` : passer `CFG["out_dir"]` pour écrire directement dans `out_dir`. `bake_named` gère déjà `package == dest` (RP:403).
|
|||
|
|
- Cela s'écarte de 5.5 : la correction du chemin de réimport s'active quand le dossier n'est pas vide (5.8 cpp:180-…).
|
|||
|
|
- À n'adopter qu'après l'essai.
|
|||
|
|
|
|||
|
|
## 6. Garde de version
|
|||
|
|
|
|||
|
|
- **Pas nécessaire** dans l'organisation actuelle.
|
|||
|
|
- L'addon 5.5 = `D:\Game\LifePlay-Sandbox`, branche `lot/DC3-atelier` ; l'addon 5.8 = worktree `ue5.8` (`git worktree list`).
|
|||
|
|
- Chaque moteur exécute le script de son checkout. Le correctif ne se pose que sur `ue5.8`.
|
|||
|
|
- Elle le deviendrait si une seule copie devait servir les deux moteurs, par exemple en fusionnant `ue5.8` dans la branche 5.5 avant le retrait de 5.5.
|
|||
|
|
- Le code 5.8 ne tourne pas en 5.5 : aucune op ni `FKChainsOp` dans `K5/` (recherche vide), pas de `run_batch_retarget` en 5.5 (h:129-139), et `AddRetargetOp` y prend une classe `URetargetOpBase` (`K5/…/IKRetargeterController.h:114`).
|
|||
|
|
- Variante proposée, par détection de fonctionnalité plutôt que par numéro de version :
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
OP_STACK = hasattr(unreal, "IKRetargetFKChainsController") # pile d'ops : UE 5.6+
|
|||
|
|
BATCH_INPUTS = hasattr(unreal.IKRetargetBatchOperation, "run_batch_retarget") # structure d'entree : UE 5.8
|
|||
|
|
# make_retargeter : if OP_STACK -> ops + set_fk_chains ; else -> ancien bloc RP:278-286.
|
|||
|
|
# bake_named : if BATCH_INPUTS -> run_batch_retarget ; else -> duplicate_and_retarget(..., True).
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 7. Essai décisif (à blanc, sous 5.8, références intactes)
|
|||
|
|
|
|||
|
|
**Références**, copie 5.8 `D:\Game\LifePlay-master 5.8\Content\` ; toutes ignorées par git (U5-T1-CART l. 80) :
|
|||
|
|
- méthode ue4 : `Quaternius\Retarget\{IK_Quaternius, IK_Bridget, IK_Michael, RTG_Bridget, RTG_Michael}.uasset` ;
|
|||
|
|
- méthode ue5 : `Retarget_ue5\{IK_Source_ue5, IK_Bridget_ue5, IK_Michael_ue5, RTG_Bridget_ue5, RTG_Michael_ue5}.uasset` ;
|
|||
|
|
- clips cuits en 5.5 : `Quaternius\UAL\B_*`, `M_*` (43 + 43, `ls | grep -c`) et `AnimPacks\FreeAnimationsPack\B_*`, `M_*` (10 + 10) ;
|
|||
|
|
- originaux 5.5 d'avant le resave : `D:\Game\LifePlay_Backup_5.5\U5\Content\{Quaternius\Retarget, Retarget_ue5}` ;
|
|||
|
|
- poses : `Saved\Measurement\retarget_pose_{Bridget,Michael}.txt` (ue4) et `Saved\AnimPipeline\freeanims\pose\…` (ue5). Les md5 sont identiques à la copie 5.5.
|
|||
|
|
- Banc de comparaison réutilisable : `D:\Game\LifePlay-master 5.8\Saved\U5T3b\scripts\U5T3b_func.py`. Il contient `retarget` (en mémoire, `run_batch_retarget`), `sample` (toutes images, tous os, RAW, local) et `compare` (égalité exacte des 10 composantes). Résultats de la base : `resultats_v3.txt`.
|
|||
|
|
|
|||
|
|
**Étapes** (éditeur fermé ; l'exécution relève d'une fiche IMPL ou VAL, pas de celle-ci) :
|
|||
|
|
0. Instantané `Snapshot.ps1` des dossiers de référence ci-dessus. Ils sont ignorés par git : sans instantané, aucun retour arrière.
|
|||
|
|
1. Appliquer le diff sur `ue5.8`.
|
|||
|
|
2. Rigs sous une destination temporaire, sans cuisson :
|
|||
|
|
- commande : `BuildMod.ps1 -GameRoot "D:\Game\LifePlay-master 5.8" -Retarget rigs -RetargetOnly -RetargetConfig <json>` ;
|
|||
|
|
- JSON ue4 : `{"mode":"rigs","methode":"ue4","dest":"/Game/_TempU7b/Retarget"}` ;
|
|||
|
|
- JSON ue5 : `source_mesh` SKM_Manny, `clips_dir` `/Game/FreeAnimationsPack/Animations`, `dest` `/Game/_TempU7b/Retarget_ue5`, `pose_dir` = **copie 5.8** `…/Saved/AnimPipeline/freeanims/pose`. Le JSON existant de `freeanims` pointe vers la copie 5.5.
|
|||
|
|
- Attendu : la ligne `RETARGET rigs prets jeton <j>`.
|
|||
|
|
3. Structure : `LPDumpAssets` sur les nouveaux RTG et les références, puis `ops.py`.
|
|||
|
|
- Attendus : `TargetRetargetPoses` au même md5 ; FK `ChainsToRetarget` avec 24 `OneToOne` aux mêmes noms ; `ChainMapping` identique.
|
|||
|
|
- Seul écart admis : 3 ops au lieu de 6.
|
|||
|
|
4. Sortie, par un commandlet `pythonscript` dérivé d'`U5T3b_func.py` : recibler en mémoire les mêmes clips avec le RTG de référence et avec le nouveau.
|
|||
|
|
- ue4 : UAL1_Standard → bridget_gp et michael, au moins Dance_Loop et 2 autres clips ; les 43 si le temps le permet.
|
|||
|
|
- ue5 : SKM_Manny → bridget_gp et michael, au moins AS_AirKiss.
|
|||
|
|
- **Critère : 0 valeur différente sur chaque comparaison.**
|
|||
|
|
- Témoin obligatoire : Spine passée en `ONE_TO_ONE` sur le nouveau RTG doit diverger. Référence U5 : 930 sur 59 210, 8,91°.
|
|||
|
|
5. Facultatif, informatif seulement : cuisson complète vers un `out_dir` temporaire (`/Game/_TempU7b/UAL`), puis comparaison avec les B_/M_ cuits en 5.5.
|
|||
|
|
- Ce n'est pas un critère du portage : cela mesure l'équivalence d'algorithme 5.5 → 5.8, restée incertaine (U5-T3b § 7, essai N4).
|
|||
|
|
6. Rangement : `/Game/_TempU7b` est **déplacé** vers `D:\Game\LifePlay_Backup_5.5\U7b\`, jamais supprimé.
|
|||
|
|
|
|||
|
|
## 8. Risques
|
|||
|
|
|
|||
|
|
1. **Script non corrigé lancé en 5.8 : perte des références** (établi par l'ordre du code RP:428-441).
|
|||
|
|
- `make_rig` et `make_retargeter` suppriment puis recréent IK et RTG dans `dest` (RP:243-244, 265-266). On obtient des RTG sans op.
|
|||
|
|
- `delete_copies` efface ensuite les B_/M_ de `out_dir` et de `/Game` (RP:438, 305-312), avant que `bake_named` ne lève la `TypeError` (RP:396).
|
|||
|
|
- Avec la configuration par défaut, cela touche `Quaternius/Retarget` et 86 clips de `Quaternius/UAL`, dossiers ignorés par git.
|
|||
|
|
- Ne lancer ni AnimPipeline étape 4 ni `BuildMod -Retarget` sur `ue5.8` avant le correctif.
|
|||
|
|
2. Même corrigé, la configuration par défaut écrase les références : l'essai se fait sur une destination temporaire et après un instantané.
|
|||
|
|
3. `add_default_ops()` est tentant mais ajoute Root Motion et Curve Remap, que la référence n'a pas. Effet sur la racine estimé non nul.
|
|||
|
|
4. L'ordre d'appel compte : des ops ajoutées avant `set_ik_rig(TARGET)` ne verraient pas la cible (cpp:99-116, `OnAddedToStack`), et `auto_map_chains` appelé avant les ops ne fait rien.
|
|||
|
|
5. Tri de la pose : 5.8 trie par le squelette du maillage de prévisualisation (`set_preview_mesh` reste avant `apply_pose`). Différence de sérialisation possible, à vérifier par le md5 de l'étape 3.
|
|||
|
|
6. Valeurs par défaut de la structure d'entrée : la doc Python et le C++ divergent (§ 1). Le diff règle les 13 champs explicitement.
|
|||
|
|
7. Une recuisson complète en 5.8 peut ne pas égaler les clips cuits en 5.5, à cause de l'algorithme (incertain).
|
|||
|
|
- Recuire les packs relève d'une décision de l'utilisateur : DeliverAnims, mods figés en V2.0.
|
|||
|
|
- Après toute recuisson, la chaîne lance `LPAnimTwist` (étape 4).
|
|||
|
|
8. Pas d'avertissement moteur pour un retargeter sans op. `set_fk_chains` lève une erreur si l'op FK manque ou si un réglage n'est pas relu : l'échec devient visible (`RETARGET echec`).
|
|||
|
|
9. `include_referenced_assets=True` est repris de 5.5. Si un clip référence d'autres assets, le compte `bridget n` dépasse n et AnimPipeline échoue (l. 289). Comportement identique en 5.5.
|
|||
|
|
|
|||
|
|
## 9. Points d'appel à casser (pour la fiche IMPL)
|
|||
|
|
|
|||
|
|
- `RP:268-274` : insérer l'ajout des 3 ops entre `set_preview_mesh` et `auto_map_chains`.
|
|||
|
|
- `RP:278-286` : remplacer le bloc déprécié par `set_fk_chains`.
|
|||
|
|
- `RP:396-397` : remplacer par `run_batch_retarget`.
|
|||
|
|
- Rien à changer dans `BuildMod.ps1` ni `AnimPipeline.ps1` : les formats de ligne sont conservés.
|
|||
|
|
- Documentation à mettre à jour après validation : `D:\Game\LifePlay-master 5.8\Docs\Migration\21_Sandbox_Addon.md:206` (report U5), à compléter par R3.
|
|||
|
|
|
|||
|
|
## 10. Écarts, fichiers, état
|
|||
|
|
|
|||
|
|
- Écarts à la fiche :
|
|||
|
|
- Recherche web faite par `curl`, faute d'outil WebSearch ou WebFetch. Les pages de notes 5.6 et 5.7 n'ont pas été lues, faute de rendu JavaScript.
|
|||
|
|
- Rupture R3 ajoutée : hors du report U5, mais dans l'objectif de la fiche.
|
|||
|
|
- Fichier écrit : ce livrable seul, `D:\Game\LifePlay-master 5.5\Docs\Migration\etat\U58\U7b\U7b-T2-CART.md`.
|
|||
|
|
- Pages web téléchargées dans le dossier temporaire de session (scratchpad), hors projet.
|
|||
|
|
- Aucun lancement d'éditeur ni de commandlet, aucun commit.
|