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