> Depuis le lot DC3 (2026-10-07), ces scripts, `ModSources/` et `AnimSources/` sont dans le dépôt addon `LifePlay-Sandbox` (même disposition relative ; lancer depuis l'addon, `D:\Game\LifePlay-Sandbox`).
Objectif : qu'un pack d'animations, quel que soit son squelette d'origine, soit converti pour les corps du jeu
(Genesis 8 : Bridget, Michael), évalué et livré en mod **sans intervention manuelle**, avec un contrôle bloquant à
chaque étape. Base technique : la méthode validée dans la sandbox Mesure (CHG-188 à 204, mémoire « transfert de
pose ») et le mécanisme des mods d'assets (16_H3_Mods_Paquets.md).
---
## 1. État actuel de la chaîne
| Question | Situation aujourd'hui | Source |
|---|---|---|
| **Comment les animations entrent** | Un seul pack, importé à la main : `Tools/ImportQuaternius.py` lit un `.glb` à un chemin fixe (`D:/Game/Universal Animation Library[Standard]/…`) et écrit dans `/Game/Quaternius/UAL`. Un second pack (`Content/FreeAnimationsPack`, mannequin UE5) est présent mais n'est pas traité. | `ImportQuaternius.py` |
| **Comment le modèle est identifié** | **Levé (AP7)** : `-run=LPAnimIdentify` (et `LPAnimMeasure::Identify`) compare les os du squelette aux profils `Tools/anim/profiles/*.json` (score par profil, os manquants, méthode associée) ; à égalité, le profil le plus complet l'emporte (le mannequin UE5 contient tous les noms UE4). Les tables ne sont plus en dur. | CHG-221 |
| **Le pack contient-il un modèle de référence** | Quaternius : oui, `UAL1_Standard` (maillage, squelette, repos en pose T). FreeAnimationsPack : oui, `SKM_Manny` / `SKM_Quinn`, plus un clip `MM_T_Pose` et son propre `RTG_Mannequin`. | `Content/Quaternius/UAL`, `Content/FreeAnimationsPack/Demo` |
| **Calques** | Le calque de pose (CHG-203) est le squelette cible posé par la formule, à partir du squelette source du pack : il utilise déjà le modèle de référence du pack (son squelette au repos), pas un modèle à nous. | mode Mesure du plugin `LPSandbox` (`LPSandboxMeasure*.cpp`, ex-`LPPairSandbox.cpp`) |
| **Conversion** | Retargeter généré par `Tools/anim/RetargetPack.py` (ex-`RetargetQuaternius.py`), piloté par `Saved/anim_retarget.json` ; la méthode (`ue4` pose T, `ue5` pose A, `genesis_direct`) est choisie par `-Methode=<id ou auto>` ; pose de retarget écrite par `-run=LPAnimEval` (`-PoseDir`, `Saved/Measurement` par défaut) ou la sandbox Mesure ; os *Twist* répartis après cuisson (`-run=LPAnimTwist`). Module de mesure : `LPAnimMeasure` (addon `LifePlay-Sandbox`, `21_Sandbox_Addon.md`). | CHG-204, CHG-221, CHG-222 |
| **Évaluation** | **Interactive :** tournée sandbox Mesure (`tour.csv`, captures, vignettes, planches ; CHG-202, AP6). **Headless :**`-run=LPAnimEval` — rapports sous `Saved/AnimPipeline/<run>/`, contrôles AP4 (`controles_*.csv`, `rapport.json`). **Enchaînée (AP7) :**`Tools/anim/AnimPipeline.ps1` (`BuildMod.ps1 -Anim <pack>`) exécute identification, calque, conversion, évaluation, livraison et catalogue ; passé sur Quaternius et FreeAnimationsPack. | CHG-206 à CHG-208, CHG-223 |
| **Livraison** | Les clips convertis sont dans le **contenu du jeu** (`/Game/Quaternius/UAL/B_*`, `M_*`), adressés par un chemin en dur (`LPAnimRedirect::ClipPath`). Ils ne passent pas par un mod. `BuildMod.ps1 -Retarget` ne vérifie que la présence d'une ligne de journal. | `LPAnimRedirect.cpp`, `BuildMod.ps1` |
| **Appel depuis un scénario** | Un scénario désigne une animation par une clé courte : `ANIMATION: write` (`.lpaction`), `Actor.animate(martialart)` (`.lpscene`). Le jeu lui associe les assets `<clé>_F` (corps féminin) et `<clé>_M` (corps masculin), par exemple `Content/Heavy/New3/Anims/Daily/martialart_F` / `_M` ; la même convention sert aux paires de couple (`LPShowCoupleSubsystem`). Les clips convertis, nommés `B_<clip>` / `M_<clip>`, ne sont donc **pas appelables** par un scénario. Le jeu résout la clé par `Person::PlayDaily`, qui cherche dans `Room.Dailies` (tableau fixe de clips, sans registre d'assets) ; les clips des mods y sont ajoutés par `ULPModDailiesSubsystem` (CHG-209). | `vin_Base/Actions`, `vin_Base/Scenes` |
| **Information pour le moddeur** | Aucune : pour connaître la durée, la posture ou l'usage d'un clip, il faut l'ouvrir dans l'éditeur. | |
1.~~Pas d'identification du squelette : un pack Mixamo, UE5, CC4 ou Genesis 9 serait converti avec une table fausse.~~**Levé (AP7)** : `LPAnimIdentify` et profils (CHG-221).
2.~~Correspondance dupliquée (C++ et Python) : une modification oubliée d'un côté fausse le résultat sans erreur.~~**Levé (AP1, CHG-205)** : profils JSON lus par le C++ et le Python.
3.~~La pose de retarget dépend d'une ouverture manuelle de la sandbox.~~**Levé (AP2 / S6, confirmé AP7)** : `-run=LPAnimEval` écrit les mêmes fichiers sans fenêtre ; la sandbox reste utile pour le contrôle visuel.
**Méthodes de transformation (AP7).** Le convertisseur choisit un profil de transformation (`Tools/anim/methodes/*.json`, `-Methode=<id ou auto>`) en quatre parties : 1) la pose du mannequin source, A ou T, est le calque d'origine ; 2) Bridget et Michael sont placés dans la même pose, le calque étant ajusté à leur morphologie ; 3) la méthode est choisie d'après le profil identifié ; 4) la transformation est appliquée. Méthodes : `ue4` (pose T, mannequin UE4 / Quaternius), `ue5` (pose A : calques = repos du mannequin UE5 et de Genesis, ajusté à la morphologie), `genesis_direct` (copie Daz, déclarée factice). Règle d'égalité : à score égal entre profils, le plus complet l'emporte (le mannequin UE5 contient tous les noms UE4 et était pris pour UE4 à 100 %). Pour UE5 la conversion se fait A → A : aucun angle A → T n'est inventé, les deux calques sont dans la même pose de repos. Borne des réglages : 30° pour le corps, 45° pour les doigts (`-SeuilReglageDoigts`), la main du mannequin UE5 étant au repos légèrement fermée (ajustement des doigts ≈ 31°). Voir CHG-221.
Un pilote unique, `Tools/AnimPipeline.ps1`, sur le modèle de `HairPipeline.ps1`, appelé par
`BuildMod.ps1 -Anim <pack>`. Chaque étape écrit un rapport JSON dans `Saved/AnimPipeline/<pack>/` et **s'arrête au
premier contrôle en échec**, avec la liste des causes. Une étape réussie n'est pas refaite (reprise par `-From <étape>`).
Rien n'est supprimé : un résultat précédent est déplacé dans `precedent_<date>`.
| # | Étape | Contenu | Contrôle bloquant |
|---|---|---|---|
| 0 | **Inventaire** | Pack déclaré dans `AnimSources/<pack>/pack.json` : origine, licence, fichiers (`.fbx`, `.glb`, ou dossier `.uasset`). | Licence présente et compatible (règle existante de `BuildMod` pour les mods payants) ; fichiers trouvés ; empreintes SHA-256 consignées. |
| 1 | **Import** | Import automatisé (généralisation d'`ImportQuaternius.py`) dans `/AnimSources/<pack>/`. | Nombre de clips importés = nombre attendu ; durée et cadence lues ; aucun clip vide ; un squelette unique. |
| 2 | **Identification** | Empreinte du squelette (noms d'os, hiérarchie) comparée aux profils `Tools/anim/profiles/*.json` : mannequin UE4, mannequin UE5, Mixamo, Genesis 8, Genesis 9, CC4… | Un seul profil reconnu, avec au moins 95 % de ses os obligatoires présents ; sinon arrêt « squelette inconnu » et liste des os manquants. |
| 3 | **Référence et pose T** | Pose T source = repos du squelette (ou conversion déclarée par le profil) ; ajustement de la pose T de chaque corps cible (phase A, sans la sandbox : commandlet). | Repos source vérifié en T (bras à l'horizontale, jambes droites, tolérance fixée) ; après ajustement : tous les segments ≤ 1 %, réglages symétriques gauche / droite (écart ≤ 0,5°), aucun réglage hors bornes ; fichier `retarget_pose_<Corps>.txt` écrit. |
| 4 | **Conversion** | Rigs et retargeters générés depuis le profil (chaînes, *Interpolated* pour colonne et cou, doigts, orteils, bassin) ; cuisson des copies dans `/<Mod>/Anims/<Corps>/`. Puis **répartition de la torsion***Bend* / *Twist* sur les clips produits (post-traitement). | Autant de clips produits que de clips source, par corps ; durées et nombres d'images identiques ; aucune valeur non numérique. |
| 5 | **Évaluation** | Tournée sans intervention (fenêtre de jeu automatisée) sur chaque corps cible : écart à la formule, plus des contrôles indépendants (voir § 4). Planches de captures face / profil / dos et rapport HTML. | Chaque clip est classé « accepté », « à revoir » ou « rejeté » selon des seuils fixés ; un clip rejeté bloque la livraison sauf exclusion explicite dans `pack.json`. |
| 6 | **Catalogue** | Une fiche par animation (§ 5), calculée sur les clips produits et complétée par `pack.json`. | Une fiche par clé et par clip ; clés uniques dans le mod **et** sans collision avec les clés du jeu ni des mods connus ; champs obligatoires remplis ; vignettes présentes. |
| 7 | **Livraison** | Mod d'assets (H3) : clips renommés à la convention du jeu, `<clé>_F` / `<clé>_M`, sous `/<Mod>/Anims/…` ; module texte `.lpmod` ; catalogue joint ; cuisson DLC, contrôle du conteneur et archive `.zip` (étapes existantes de `BuildMod`). La clé peut contenir `_` (coupure au dernier `_`, CHG-209) ; outil : `Tools\anim\DeliverAnims.ps1`. | Conteneur sans asset du jeu ; archive testée ; **essai de chargement** : jeu de test lancé avec le mod, chaque clé du catalogue appelée par `animate()` sur un personnage féminin et un masculin (journal). |
Réalisé par `Tools/anim/AnimPipeline.ps1` (CHG-223) ; ordre réel des étapes 6 et 7 : la livraison dans le mod précède les fiches et le catalogue, puis vient la cuisson DLC.
`LPAnimRedirect` ne sert plus qu'à la sandbox et au personnage piloté. Depuis le lot DC1, `LPAnimRedirect` et `LPCoupleSim` sont dans le plugin `LPAnimCore` du jeu (`Plugins/LPAnimCore`) ; la mesure reste dans le module `LPAnimMeasure` de l'addon. Depuis le lot DC2, les commandlets `LPAnimEval`, `LPAnimTwist`, `LPAnimCatalog` et `LPAnimIdentify` sont dans le module éditeur `LPModTools` de l'addon ; profils et méthodes sont lus via `LPAnimMeasurePaths` (addon, repli sur le jeu jusqu'à DC3).
| `version`, `empreinte` | version du mod, SHA-256 des clips | calculé |
**Réalisation (AP6, CHG-210)** : les champs calculés viennent de `LPAnimCatalogCommandlet` (`-run=LPAnimCatalog`, mesure image par image du clip féminin source, contrôle que F et M ont même durée et même nombre d'images) ; les champs déclarés viennent de `AnimSources/quaternius/pack.json` ; la qualité vient du rapport AP4 ; la taille, l'empreinte et les chemins viennent des fichiers livrés. `Tools/anim/Catalogue.py` assemble le tout. Définitions : *boucle* si la dernière image diffère de la première de 1 % de la taille ou moins (extrémités relatives à la hanche) ; *déplacement* : trajet horizontal de la hanche, « sur place » sous 5 cm ; *posture* par image (allongé : tronc > 60° ; à genoux : genou <12cmethanche<72%;assis:hanche<72%etplusde20cmenarrièredeschevilles;accroupi:hanche<72%;deboutsinon),postureduclipsiellecouvreaumoins60%desimages,sinon«variable».Lesvignettesfont512px,deprofil.`taille_ko`estlatailledes`.uasset`noncuits(`taille_cuite:false`)enattendantAP7.Laposturepeutêtredéclaréedans`pack.json`(champ`posture`),cequiremplacelaposturecalculée.Planches(CHG-211):chaqueficheporteaussiuneplancheJPEGde256px(12imagesparsecondedeclip,de4à24,côteàcôte)quelapagefaitdéfilerausurvoldelavignette(autoucher:lance/arrête;laplanchen'estchargéequ'aupremiersurvol);unGIFaétéécarté(256couleurs,lourd,aucunencodeurdisponible).
Le catalogue est **régénéré à chaque passage** de la chaîne : il ne peut pas décrire un état antérieur des clips.
Le pilote refuse de livrer un mod dont le catalogue n'a pas été produit dans le même passage.
---
## 6. Découpage en lots proposé
| Lot | Objet | Contrôle de sortie |
|---|---|---|
| AP1 ✔ | Profils de squelettes en JSON, lus par le C++ et le Python ; suppression des tables en dur | sandbox et retarget identiques à CHG-204 (tournée : 0,00 %) — **fait, CHG-205** |
| AP2 ✔ | Commandlet de pose T et d'évaluation sans fenêtre (étapes 3 et 5, mesures) | mêmes fichiers de pose et même `tour.csv` que la sandbox — **fait, CHG-206** (`-run=LPAnimEval`) |
| AP3 ✔ | Répartition de la torsion *Bend* / *Twist* sur les clips produits | contrôle de torsion vert, comparaison visuelle — **fait, CHG-207** (`-run=LPAnimTwist`) ; comparaison visuelle à faire |
| AP4 ✔ | Contrôles indépendants (§ 4) et rapport HTML | seuils étalonnés sur les 43 clips — **fait, CHG-208** (`-run=LPAnimEval`, `Tools/anim/RapportControles.py`) ; 0 rejeté, 8 clips à revoir (4 par corps : `Death01`, `Fixing_Kneeling`, `Roll` au sol, `Jump_Start` au saut) |
| AP5 ✔ | Livraison en mod : clips renommés `<clé>_F` / `<clé>_M` sous `/<Mod>/Anims`, ajout à `Room.Dailies` et correctif de `Person::PlayDaily`, essai de chargement par `animate()` | jeu de test avec le mod Quaternius, clés appelées depuis un scénario d'essai — **fait, CHG-209** ; essai empaqueté reporté (nouvelle version de référence nécessaire) |
| AP6 ✔ | Catalogue (§ 5) : fiches calculées, `pack.json`, vignettes, `catalogue.json` et `catalogue.html` | une fiche par clip, champs calculés relus sur 5 clips témoins (marche, assis, pistolet, danse, chute) — **fait, CHG-210** ; taille cuite en AP7 |
| AP7 ✔ | Pilote `AnimPipeline.ps1` + `BuildMod -Anim`, identification, méthodes `ue4` / `ue5`, deuxième pack (FreeAnimationsPack, mannequin UE5) | pack UE5 converti, évalué, catalogué et livré de bout en bout sans intervention — **fait, CHG-221 à 224** ; cuisson DLC contre la version d'essai `2026.10.1.6` et essai en jeu empaqueté validés (CHG-224) |