LifePlay-Revival/Docs/Migration/20_Chaine_Animations.md
edujiro5972 e46093c81c DC3 : atelier de mods hors du jeu, dans l'addon LifePlay-Sandbox (CHG-236)
Retrait de BuildMod, NewMod, EditMod, ModSource, HairPipeline et leurs .py, Tools/anim,
Tools/hairs, Tools/mods, ModSources, AnimSources, Config/LPSandbox.ini, lanceurs sandbox et
guide des moddeurs (57 fichiers suivis ; contenu disque deplace vers la sauvegarde DC3).
References et docs mises a jour. Pin addon 79f0205. Validation : DC3-VAL PASS (cuisson
lpr_anim_freeanims depuis l'addon identique au mod publie, tournee identique a SB2-REF).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 19:53:13 +02:00

165 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`.