LifePlay-Revival/Docs/Migration/etat/U58/U7b/U7b-T2-CART.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

34 KiB
Raw Blame History

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.

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.

--- 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 :
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.