LifePlay-Revival/Docs/Migration/13_Protocole_Coiffure.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

242 lines
16 KiB
Markdown
Raw 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.

# 13. Protocole de conversion d'une coiffure
> 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`).
Ce protocole décrit comment ajouter au jeu une coiffure livrée en **maillage statique** (FBX de Fab ou d'ailleurs),
sans modifier aucun Blueprint. Il a été établi sur *Pocolov Hair 17* (lot O7e, `11_Audit_Optimisation.md` §16.8) puis
rejoué de bout en bout avec les outils ci-dessous : résultat identique au millimètre.
Durée : environ 30 minutes pour une coiffure, contrôles en jeu compris.
## 1. Ce que le jeu attend d'une coiffure
| Exigence | Raison |
|---|---|
| `SkeletalMesh` dont les os portent les noms Genesis 8 (`hip`, `pelvis`… `neckUpper`, `head`) | `Person` fait suivre la coiffure au corps par `SetLeaderPoseComponent`, qui associe les os **par leur nom** |
| Placée autour de la tête de Bridget, dans la pose de référence | une coiffure mal placée flotte ou traverse le crâne |
| Poids de peau répartis sur tête, cou, clavicules et poitrine | une coiffure longue liée à la seule tête tourne d'un bloc avec elle et ses mèches traversent épaules et poitrine |
| Matériaux enfants de `/Game/Heavy/Daz/Bridget/Hairs/DazHair` | le jeu règle la couleur et l'épaisseur par les paramètres `HairColor` et `HairThickness` |
| Déclarée dans un fichier `Modules/<module>/**/*.lphair` | la liste des coiffures du jeu est construite depuis ces fichiers (lots H1, H2) ; un maillage non déclaré n'est jamais utilisé |
| Nom d'objet unique | les personnages désignent leur coiffure par ce nom (`HAIR Hairstyle: <nom>` dans les `.lpcharacter`) |
Coiffures **exclues** : les « grooms » MetaHuman (liés à la tête MetaHuman ; plugin `HairStrands` limité à l'éditeur
depuis O1). Une coiffure déjà riggée Genesis 8 (export Daz) n'a pas besoin des étapes 2 et 3 : l'importer en
`SkeletalMesh` et passer à l'étape 4.
## 2. Outils
| Outil | Rôle |
|---|---|
| `Tools/HairImportFbx.py` | import du FBX en maillages statiques séparés, dans un dossier de travail ; affiche tailles et emplacements de matériaux |
| commandlet `LPHairFromStaticMesh` (`Source/LifePlayEditorTools`) | ajuste la coiffure sur la tête de Bridget et la convertit en `SkeletalMesh` Genesis 8 |
| `Tools/HairMaterials.py` | importe les textures, crée les matériaux `<Nom>_Cap` et `<Nom>_Strands`, les affecte, contrôle les dépendances |
| `Tools/HairPipeline.ps1`, `Tools/FbxMaterialMap.py` | chaîne industrialisée d'un lot de coiffures (§7) |
| `Tools/BuildCollections.py` | range la coiffure dans la collection `LifePlay_Cheveux` de l'éditeur |
| option de jeu `-LPVerifyHairstyles` | journalise la construction de la liste des coiffures |
Variables utilisées ci-dessous (PowerShell) :
```powershell
$UE = 'D:\Game\Launcher\Engine\UE_5.5\Engine\Binaries\Win64\UnrealEditor-Cmd.exe'
$P = 'D:\Game\LifePlay-master 5.5\LifePlay.uproject'
$T = 'D:/Game/LifePlay-master 5.5/Tools'
$W = 'D:/Game/LifePlay_Backup_5.5/<lot>/src' # sources de travail, chemins SANS espaces
```
## 3. Étapes
### Étape 0 : préparation
1. Vérifier la **licence** du produit (page Fab : « License terms »). CC BY impose une attribution ; noter auteur, titre,
lien.
2. Télécharger l'archive (accord de l'utilisateur), la copier dans `D:\Game\LifePlay_Backup_5.5\<lot>\`.
3. Extraire ; repérer le FBX et les textures. Copier dans `$W` sous des noms **sans espaces** (les arguments des
scripts Python sont séparés par des espaces) : `hair.fbx`, `diffuse.png`, `strands_opacity.jpg`…
4. Identifier les textures en les ouvrant :
- **couleur des mèches** : de préférence grise (elle est teintée par `HairColor`) ;
- **masque des mèches** : blanc = visible, noir = transparent, en bandes verticales ;
- **masque de la calotte** (lisière) : facultatif, voir étape 4.
5. Tag git `<lot>-avant`. Choisir le **nom** de la coiffure (unique, sans espace, ex. `Pocolov17`) et son dossier :
`/Game/Heavy/New5/Hairs/` (hors des dossiers à chemin fixe, `08_Guide_Developpeur.md` §2.1).
### Étape 1 : import
```powershell
& $UE $P -run=pythonscript "-script=$T/HairImportFbx.py fbx=$W/hair.fbx dest=/Game/Heavy/New5/Hairs/_Work" -unattended -nopause -nosplash
```
Lire les lignes `HAIRIMPORT` : repérer le **maillage de la coiffure** et la **tête de référence** souvent livrée avec
(ex. Pocolov : `Hair_17`, 8 250 triangles, emplacements `Hair_Base`, `Hair_01` ; `Head`, 21 262 triangles). Noter
quel emplacement est la **calotte** (ici `Hair_Base`). Sans tête de référence, voir §5.
### Étape 2 : ajustement et conversion
Essai à blanc d'abord :
```powershell
& $UE $P -run=LPHairFromStaticMesh '-Static=/Game/Heavy/New5/Hairs/_Work/Hair_17' '-FitHead=/Game/Heavy/New5/Hairs/_Work/Head' '-Out=/Game/Heavy/New5/Hairs/<Nom>' -DryRun -unattended -nopause -nosplash
```
Contrôler les lignes `LogLPHairFromStatic` :
| Ligne | Attendu |
|---|---|
| `Tete du corps` | Bridget : haut du crâne ≈ 15 × 20 cm |
| `Tete de reference` | haut du crâne du même ordre |
| `Ajustement : echelle` | entre 0,8 et 1,5 sur chaque axe ; un écart fort entre X et Y signale une tête de référence atypique |
| `Os 'head'` | `correction aucune` (squelette cible identique au corps) |
| `Coiffure placee` | largeur ≈ 25 à 32 cm (Flora : 31 cm), sommet ≈ 181-183 cm |
Puis sans `-DryRun`. La commandlet refuse d'écraser un asset existant : pour refaire, déplacer d'abord `<Nom>` et
`<Nom>_Skeleton` vers la sauvegarde.
**Liaison au squelette** (`-Skin`, défaut `transfer`) : chaque sommet de la coiffure reçoit les poids des 6 sommets du
corps les plus proches (`-Neighbors`), moyennés selon la distance, 4 influences au plus ; les os des bras et des jambes
sont ramenés à leur ancêtre (une mèche ne suit pas un bras). Tout sommet à moins de 3 mm de la peau, ou dessous, est
repoussé le long de la normale du corps (`-Gap`, en cm). Résultat type (Pocolov 06) : tête 61 %, haut de la poitrine
10 %, clavicules 14 %, cou 4 %, pectoraux 3 % ; 660 sommets sur 5 618 sortis de la peau. `-Skin=rigid` : tout sur l'os
`head` (première version, à réserver aux coiffures courtes).
Paramètres de retouche (après un premier contrôle en jeu) : `-OffsetZ=` (cm, monter/descendre), `-OffsetY=` (cm,
avancer/reculer), `-Scale=` (facteur global), `-Margin=` (défaut 1,05 : écart calotte/cuir chevelu), `-Cranium=` (défaut
0,3 : hauteur mesurée sous le sommet, en fraction de la profondeur de la tête), `-Uniform` (même échelle sur X et Y).
### Étape 3 : textures et matériaux
```powershell
& $UE $P -run=pythonscript "-script=$T/HairMaterials.py mesh=/Game/Heavy/New5/Hairs/<Nom> diffuse=$W/diffuse.png strands_opacity=$W/strands_opacity.jpg cap_opacity=white cap=Hair_Base thicken=1" -unattended -nopause -nosplash
```
- `cap=` : emplacements de la calotte (séparés par des virgules) ; tous les autres reçoivent le matériau des mèches.
- `cap_opacity=white` : calotte **opaque** (recommandé : une lisière découpée laisse voir le crâne entre les mèches) ;
sinon le fichier du masque de la calotte.
- `thicken=` : épaississement du masque des mèches. **1** pour un masque large (cartes de mèches, Pocolov) ; 10 pour un
masque de mèches fines (coiffures Daz). Une valeur trop forte donne des cartes opaques.
Contrôle : la dernière ligne `HAIRMAT dependances … -> OK` (la coiffure ne dépend que de son squelette et de ses deux
matériaux).
### Étape 4 : nettoyage
Déplacer `Content/Heavy/New5/Hairs/_Work` vers `D:\Game\LifePlay_Backup_5.5\<lot>\` (il ne doit pas être cuit dans le
jeu). Ne rien supprimer sans copie.
### Étape 5 : déclaration
Fichier `Content/Modules/<module>/Hairstyles/<nom>.lphair` (module existant, ex. `lp_Revival`, ou module du mod) :
```
// <Titre> - <auteur> - licence <licence>
// Source : <lien>
// Modifications : rattachee au squelette Genesis 8 (os head), ajustee a la tete de Bridget, materiaux DazHair.
HAIRSTYLE: /Game/Heavy/New5/Hairs/<Nom>.<Nom>
SEX: F
DAZ: true
```
Licence à attribution : ajouter la même mention dans `Licenses/Free_3D_Models_Credits.txt`.
### Étape 6 : collections, build, contrôle du journal
```powershell
& $UE $P -run=pythonscript "-script=$T/BuildCollections.py" -unattended -nopause -nosplash
```
Build Development (`09_Commandes_Utiles.md` §2) dans un **nouveau** dossier d'archive (la mise en place avec `-iterate`
ne retire pas les fichiers supprimés). Lancer le jeu ; le journal doit contenir :
```
LogLifePlay: Coiffures : <Nom> ajoutee (index N, F, module <module>)
```
### Étape 7 : contrôle en jeu (utilisateur)
| # | Contrôle | Défaut typique → correction |
|---|---|---|
| 1 | Création de personnage du bon sexe : la coiffure est en fin de liste | absente : chemin du `.lphair`, module désactivé, journal |
| 2 | **De face** : le crâne est couvert | zones chauves, coiffure étroite : vérifier l'ajustement (§4), `-Scale`, `-Margin` |
| 3 | **De profil** : rien ne traverse le crâne, l'oreille, les épaules ; pas de trou au-dessus de l'oreille | trous : `cap_opacity=white` ; trop haute/basse : `-OffsetZ` ; trop en avant/arrière : `-OffsetY` |
| 4 | Couleur et épaisseur réglables dans le menu | sans effet : matériau non `DazHair` |
| 5 | Mouvement (marche, animation) : la coiffure suit la tête | décalée : os `head` absent ou correction de pose non nulle |
| 6 | Nouvelle partie (liste des villes) et Continue | — |
Puis : entrée CHG dans `Journal_Modifications.md`, commit, tags `<lot>-apres` / `<lot>-valide`.
## 4. Pièges rencontrés (O7e)
| Piège | Effet | Règle |
|---|---|---|
| Ajuster sur la tête **entière** | la tête Pocolov a des oreilles très larges (24,4 cm) : coiffure rétrécie à 74 %, enfoncée dans le crâne | ajuster sur le **haut du crâne** (option `-Cranium`, par défaut) |
| `Thicken` des coiffures Daz (10) sur un masque large | mèches en cartes noires opaques | `thicken=1` pour les cartes de mèches |
| Lisière découpée de la calotte | crâne visible au-dessus de l'oreille | `cap_opacity=white` |
| Affectation des matériaux en Python | `save_loaded_asset` ne sauve que les assets « modifiés » ; une affectation par propriété ne les marque pas | sauvegarder avec `only_if_is_dirty=False` (fait par `HairMaterials.py`) |
| Matériaux importés du FBX | souvent sans texture reliée | ne pas importer les matériaux (`HairImportFbx.py`) ; recréer avec `HairMaterials.py` |
| Comparer des noms d'assets | `soldier` et `Soldier` désignent le même asset | comparaisons insensibles à la casse |
| Liaison rigide à la tête (première version) | cheveux longs : les mèches tournent avec la tête et traversent épaules et poitrine (vidéo utilisateur) | transfert de poids depuis le corps et sortie de la peau (`-Skin=transfer`, défaut) |
## 5. Cas particuliers
- **Pas de tête de référence** dans le FBX : utiliser la coiffure elle-même comme `-FitHead` (son haut est la calotte),
avec `-Margin=1.0`, puis corriger en jeu avec `-Scale` et `-OffsetZ`.
- **Coiffure masculine** : `SEX: M` dans le `.lphair` (le menu du personnage masculin la propose).
- **Morphs de forme** : non générés (la coiffure ne suit pas les morphs du visage). Un morph existant de la coiffure peut
être déclaré avec `FIT_MORPH: <morph> <valeur>`.
- **Mèches animées** : pas de simulation physique ; les mèches suivent tête, cou et poitrine par leurs poids.
- **Morphs du corps** : la sortie de la peau est calculée sur le corps de base ; une poitrine ou des épaules agrandies
par les morphs peuvent encore traverser les mèches les plus proches.
- **Coiffure apportée par un mod sans recompiler le jeu** : impossible aujourd'hui (le maillage doit être cuit avec le
jeu) ; étape H3 prévue.
## 7. Chaîne industrialisée (`Tools/HairPipeline.ps1`)
Les étapes 0 à 6 sont enchaînées pour un **lot** de coiffures décrit par un fichier JSON (`Tools/hairs/<lot>.json`) :
```json
{ "module": "lp_Revival", "folder": "/Game/Heavy/New5/Hairs", "backup": "D:/Game/LifePlay_Backup_5.5/<lot>",
"hairs": [ { "name": "Pocolov15", "archive": "D:/Game/pocolov-hair-15.zip", "title": "Pocolov Hair 15",
"author": "...", "license": "CC BY 4.0", "url": "https://www.fab.com/listings/...", "sex": "F",
"mesh": "auto", "head": "auto", "cap_slots": "auto", "diffuse": "auto", "strands_opacity": "auto",
"cap_opacity": "white", "thicken": 1 } ] }
```
Options par coiffure : `margin`, `scale`, `offset_z`, `offset_y`, `skin`, `gap` (transmises à `LPHairFromStaticMesh`).
`-Rebuild` refait une coiffure déjà convertie (la version précédente est déplacée dans `<backup>\<nom>\precedente_<date>`).
```powershell
.\Tools\HairPipeline.ps1 -Config Tools\hairs\<lot>.json -Step Inspect # puis relire <backup>\rapport_Inspect.txt
.\Tools\HairPipeline.ps1 -Config Tools\hairs\<lot>.json -Step Apply # [-Only <nom>] pour une seule coiffure
```
**Inspect** : extraction (zip, rar, 7z, archives imbriquées, avec 7-Zip), copie du FBX et des textures sous des noms
sans espaces, import (`HairImportFbx.py`), lecture des associations matériau → textures du FBX
(`Tools/FbxMaterialMap.py`, lecteur de FBX binaire sans SDK), puis détection des champs `auto` :
| Champ | Règle |
|---|---|
| `mesh` | plus gros maillage qui n'est pas la tête (nom quelconque : `Box001` pour Pocolov 03) |
| `head` | maillage dont le nom contient `head` ; sinon la coiffure elle-même (et `margin` 1,0) |
| `cap_slots` | emplacements nommés `base`, `cap` ou `scalp`, **seulement s'il reste un autre emplacement** pour les mèches |
| `diffuse` | parmi les textures nommées `hair` (si l'archive contient une tête complète : peau, yeux…) : non masque, grise de préférence, la plus grande |
| `strands_opacity` | masque (gris, noir/blanc) **vertical** (un masque carré est la lisière de la calotte) ; préférence au nom d'un emplacement des mèches, puis aux bords latéraux sombres (carte à bords transparents) ; emplacement unique : masque déclaré par le FBX |
Les champs détectés sont mémorisés dans `auto_fields` et recalculés à chaque inspection ; une valeur saisie à la main
n'est jamais écrasée. Le rapport liste chaque texture (taille, saturation, part noir/blanc, bas et bords sombres) et
les associations du FBX.
**Apply** : essai à blanc et contrôle des échelles (entre 0,6 et 1,8, sinon la coiffure est sautée), conversion,
matériaux (`HairMaterials.py` ; matériau de calotte créé seulement s'il sert), dossier de travail déplacé vers la
sauvegarde, `.lphair` avec attribution, ligne de crédits (sans doublon), collections, bilan. Une coiffure dont le
`SkeletalMesh` existe déjà est sautée : la chaîne peut être relancée après une interruption.
**Premier lot (HAIRS1, 2026-10-02)** : Pocolov Hair 03, 06, 11, 15, 16 (CC BY 4.0). Cas rencontrés et intégrés aux
règles : modèle dans un RAR (03) ; archive avec tête complète, textures de peau et d'yeux (03) ; maillage nommé
`Box001` (03) ; masque carré pris pour celui des mèches (16) ; emplacement unique (11) ; masques `A`/`A1` voisins (03,
15). Résultat : 5 coiffures conformes (dépendances limitées au squelette et aux matériaux), toutes ajoutées par le jeu.
**Licences** : seules les licences permettant la redistribution (CC BY, CC0) conviennent, l'archive des sources
publiée contenant `Content/Heavy`. La licence standard Fab (« personal/professional ») ne le permet pas.
## 6. Retour arrière
Retirer le fichier `.lphair` suffit à retirer la coiffure du jeu. Pour retirer aussi les assets : déplacer
`Content/Heavy/New5/Hairs/<Nom>*` vers la sauvegarde, régénérer les collections, reconstruire.