LifePlay-Revival/Docs/Migration/20_Chaine_Animations.md

166 lines
20 KiB
Markdown
Raw Permalink Normal View History

# 20. Chaîne des animations : de l'achat du pack au mod livré (étude, 2026-10-06)
> 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. | |
### Écarts à combler
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.
4. Torsion des membres non répartie entre *Bend* et *Twist* (peau de l'avant-bras).
5. Michael converti mais jamais mesuré.
6. Livraison hors mod : impossible de distribuer un pack sans recuire le jeu.
7. Aucun contrôle bloquant entre les étapes.
8. Clips nommés hors de la convention `<clé>_F` / `<clé>_M` : inutilisables depuis un scénario.
9. Aucun catalogue : un moddeur doit ouvrir chaque clip pour savoir ce qu'il fait.
---
## 2. Réponses de fond
**Le modèle de référence du pack doit-il servir aux calques ?** Oui, et c'est même nécessaire. Le calque est le
squelette cible posé par la transformation ; la transformation part de la **pose T du squelette source**. Chaque pack
apporte donc sa référence : son squelette au repos suffit (un `USkeleton` a toujours une pose de référence, même sans
maillage) ; son maillage ne sert qu'à l'affichage (touche M). Notre mannequin `UAL1_Standard` ne convient que pour les
packs au squelette mannequin UE4.
**Condition** : le repos du squelette source doit être une pose T connue. Un pack livré en pose A (comme Genesis) doit
déclarer sa conversion A → T dans son profil (comme les réglages Daz). Un contrôle le vérifie (étape 3).
**Conversion d'un modèle vers un autre** : la méthode validée est générale :
- pose T commune, ajustée segment par segment (phase A) ;
- rotation de chaque os depuis la pose T, passée dans le repère du corps cible (phase B) ;
- chaînes de longueurs différentes réparties par interpolation (colonne, cou) ;
- torsion séparée entre os *Bend* et *Twist* ;
- report dans le retargeter Unreal (phase C), qui applique la même formule quand sa pose de retarget est la pose T
ajustée (0,00 % d'écart sur 129 instants, CHG-204).
Seule la **table de correspondance** change d'un squelette à l'autre : elle devient une donnée (profil), plus du code.
**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.
---
## 3. Chaîne cible
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.
Le jeu résout les clés par `Room.Dailies` ; les clips des mods y sont ajoutés par `ULPModDailiesSubsystem` (CHG-209).
`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).
---
## 4. Évaluation : ce qui est mesuré
L'écart à la formule (0,00 % aujourd'hui) prouve que la conversion reproduit la transformation ; il ne prouve pas que
le résultat est beau. Des contrôles indépendants s'ajoutent, tous calculés sur la pose et sans rendu :
| Contrôle | Défaut visé | Seuil « à revoir » (rejet : 3 × le seuil) |
|---|---|---|
| Écart à la formule (direction des segments, rotation du tronc) | conversion fausse | ≤ 1 % |
| Torsion locale des os *Bend* / *Twist* | peau de l'avant-bras ou de la cuisse vrillée | relevée ; bloquante sur option `-SeuilTorsion` |
| Main ou coude dans le volume du tronc (capsules) | « le bras passe à travers le corps » | pénétration ≤ 1 cm |
| Pied sous le sol, glissement au contact | jambes plus longues ou plus courtes que la source | sol ≤ 3 cm, glissement ≤ 3 cm/image |
| Limites articulaires (coude, genou) | hyperextension | genou ≤ 1 cm, flexion ≤ 5° |
| Continuité d'une image à l'autre | saut, tremblement | ≤ 10°, au-delà de 1,5 × la source + 2° |
| Symétrie des réglages de pose T | ajustement aberrant d'un côté | ≤ 0,5° |
Faute d'actif physique sur les corps, les capsules du tronc sont construites depuis la peau du maillage.
Les planches de captures restent la validation finale à l'œil, mais seulement pour les clips classés « à revoir ».
---
## 5. Catalogue des animations (livré avec le mod)
But : qu'un moddeur choisisse une animation et l'écrive dans un scénario **sans ouvrir l'éditeur ni le jeu**.
**Formats** :
- `Content/Modules/<Mod>/Animations/catalogue.json` : pour les outils (lecture par script, éditeur de scénarios) ;
- `Content/Modules/<Mod>/Animations/catalogue.html` : pour le moddeur, une ligne par animation avec sa vignette,
filtrable par posture, usage et durée ; lisible hors ligne ;
- `Content/Modules/<Mod>/Animations/vignettes/<clé>.png` : profil droit à 1/3 de la durée, mode vignettes de la tournée.
**Fiche d'une animation** : *calculé* = mesuré sur le clip produit, sans saisie ; *déclaré* = repris de `pack.json`
(ou déduit du nom, puis corrigeable).
| Champ | Exemple | Origine |
|---|---|---|
| `cle` (identifiant) | `qua_walk_loop` | calculé : préfixe du pack + nom source normalisé ; unique et stable d'une version à l'autre |
| `appel` | `Actor.animate(qua_walk_loop)` / `ANIMATION: qua_walk_loop` | calculé |
| `nom` | « Marche » / « Walk » | déclaré (nom source par défaut) |
| `assets` | `/<Mod>/Anims/Female/qua_walk_loop_F`, `…/Male/qua_walk_loop_M` | calculé |
| `duree_s`, `images`, `cadence` | 1,07 s, 32 images, 30 i/s | calculé |
| `taille_ko` | 186 Ko (`_F`) + 190 Ko (`_M`), cuits | calculé (après cuisson) |
| `boucle` | oui (première et dernière pose identiques à 1 % près) | calculé |
| `deplacement` | avance 1,2 m par cycle (1,1 m/s), sans root motion : le personnage marche sur place | calculé (trajet du bassin) |
| `posture` | debout / assis / accroupi / à genoux / allongé | calculé (hauteur du bassin et inclinaison du tronc) |
| `hauteur_assise_cm` | 46 cm (pour placer un siège) | calculé, postures assises |
| `contacts` | pieds au sol ; mains libres / main droite occupée | calculé (hauteur des pieds, vitesse et position des mains) |
| `accessoire` | pistolet, épée, torche, volant | déclaré, proposé d'après le nom (`Pistol`, `Sword`, `Torch`, `Driving`) |
| `categorie` | locomotion, attente, conversation, combat, assis, interaction, sport, danse | déclaré, proposé d'après le nom |
| `style` | neutre, formel, détendu, agressif | déclaré |
| `usage` | « attente en conversation, debout » | déclaré |
| `corps` | féminin, masculin | calculé (clips produits) |
| `qualite` | accepté ; pire écart 0,00 % ; contrôles verts | calculé (étape 5) |
| `source`, `licence`, `credit` | Quaternius UAL, CC0 | déclaré (`pack.json`) |
| `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 < 12 cm et hanche < 72 % ; assis : hanche < 72 % et plus de 20 cm en arrière des chevilles ; accroupi : hanche < 72 % ; debout sinon), posture du clip si elle couvre au moins 60 % des images, sinon « variable ». Les vignettes font 512 px, de profil. `taille_ko` est la taille des `.uasset` non cuits (`taille_cuite: false`) en attendant AP7. La posture peut être déclarée dans `pack.json` (champ `posture`), ce qui remplace la posture calculée. Planches (CHG-211) : chaque fiche porte aussi une planche JPEG de 256 px (12 images par seconde de clip, de 4 à 24, côte à côte) que la page fait défiler au survol de la vignette (au toucher : lance / arrête ; la planche n'est chargée qu'au premier survol) ; un GIF a été écarté (256 couleurs, lourd, aucun encodeur disponible).
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) |
Chaque lot suit `10_Protocole_Operations.md` : instantané, journal `CHG`, retour arrière par `Restore.ps1`.