# 13. Protocole de conversion d'une coiffure 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//**/*.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: ` 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 `_Cap` et `_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//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\\`. 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 `-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/' -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 `` et `_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/ 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\\` (il ne doit pas être cuit dans le jeu). Ne rien supprimer sans copie. ### Étape 5 : déclaration Fichier `Content/Modules//Hairstyles/.lphair` (module existant, ex. `lp_Revival`, ou module du mod) : ``` // - - licence // Source : // Modifications : rattachee au squelette Genesis 8 (os head), ajustee a la tete de Bridget, materiaux DazHair. HAIRSTYLE: /Game/Heavy/New5/Hairs/. 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 : ajoutee (index N, F, 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 `-apres` / `-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: `. - **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/.json`) : ```json { "module": "lp_Revival", "folder": "/Game/Heavy/New5/Hairs", "backup": "D:/Game/LifePlay_Backup_5.5/", "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 `\\precedente_`). ```powershell .\Tools\HairPipeline.ps1 -Config Tools\hairs\.json -Step Inspect # puis relire \rapport_Inspect.txt .\Tools\HairPipeline.ps1 -Config Tools\hairs\.json -Step Apply # [-Only ] 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/*` vers la sauvegarde, régénérer les collections, reconstruire.