31 KiB
Fiche GIREV-D8-RECH — Niveau N2 — Modèle prévu non précisé par la fiche | utilisé claude-sonnet-5-5 — Agent lp-cart — Skills sk-protocol, ue-cpp-build, lp-commandlets, lp-paths
GIREV-D8-RECH : conflits entre morphs opposés, comportement actuel de GI.ResolveConflictingMorphs (D8) et bonnes pratiques (2026-10-21)
Hors gabarit : la rubrique 3 est subdivisée (3.1 à 3.6) pour porter le comportement actuel, les sources web, la comparaison et les options demandées par la fiche. Lecture seule : ni MCP, ni éditeur, ni commandlet, aucun téléchargement (les pages web sont lues par l'outil de lecture, rien n'est enregistré sur le disque). Le code source du moteur 5.8 installé est lu en place. Seul ce fichier est écrit. Descriptions neutres (doc 27) : aucun nom de morph, de préréglage ni de section réservée n'est cité ; « morph » désigne une cible de forme. Les questions sont numérotées DQ1 à DQ6 pour ne pas les confondre avec les GQ1 à GQ9 de GIREV-PRE.md. La recherche répond à la consigne de l'utilisateur rapportée par la fiche : « recherche ce qui se fait en best practice sur D8 ». Aucune décision n'est prise ici.
Résumé (à lire en premier)
- Comportement actuel (établi, § 3.1) : à la fin de
LoadPreset, pour chaque ligne de la tableMorphTabledont le côtéMinest renseigné, si les deux morphs opposés (MinetMax) sont non nuls sur le composant corps, le plus faible est remis à 0 ; à égalité stricte, c'est le morphMaxqui est annulé. Aucun journal, aucun paramètre, un seul appelant connu. - Pourquoi seulement le corps : non documenté (établi comme absence). Aucune source interne ne donne la raison ; R04 range D8 en « information » et CG2-CART-01 en « conception » (
CG2-CART-01.md:51). Conséquence structurelle (établi) :Person.setMorphTargetécrit un nom de morph sur le corps et sur les cheveux, la résolution ne corrige que le corps ; un conflit résolu côté corps reste donc entier côté cheveux (effet visuel : estimé). - Pratique observée dans les sources lues (§ 3.2) : on évite le conflit par construction (un seul paramètre signé pilote les deux côtés : MakeHuman, Character Creator), ou on le borne (limites Daz, option de bornage Unity, saturation VRM), ou on fixe une priorité explicite et documentée (recouvrements VRM
block/blend). Unreal ne normalise pas et le dit dans son code. Aucune source lue ne fixe de règle d'égalité universelle : le choix de D8 est une décision de conception, pas l'application d'une norme. - Mesure (§ 3.5) : dans les 145 fichiers
.lpcharacterdu dossierContent, un seul contient les deux côtés d'une même paire sous l'hypothèse « mêmes radicaux_min/_max» (estimé : les paires réelles deMorphTablene sont pas lues). Le conflit vient donc surtout de l'empilement de préréglages (Blend) ou de tirages, pas des fichiers pris isolément (estimé). - Options (§ 3.6) : A garder (0 écriture) ; B règle déterministe documentée (égalité écrite, une trace au journal, cheveux en variante) ; C groupes exclusifs ou normalisation (net signé, ou exclusivité à l'écriture). Recommandation non contraignante : B1 (règle actuelle conservée et écrite, une ligne de journal par conflit, aucun changement d'aspect) ; la décision revient à l'utilisateur (DQ1 à DQ6).
1. Objet et périmètre
Un domaine : le défaut D8 de R04 (04-defauts.md:20), repris au n° 8 du tableau de CG2-CART-01.md (fichier ligne 51 ; « ligne 8 » de la fiche = n° 8 du tableau, pas la ligne 8 du fichier). Décrire précisément GI.ResolveConflictingMorphs, rechercher les bonnes pratiques de résolution de conflits entre morph targets / blend shapes / curseurs, comparer, proposer 2 ou 3 options avec coût, risque et recommandation. Contexte de calendrier : GI.uasset n'est révisé qu'une fois, en build 2 (GIREV-PRE.md:6) ; D8 est un candidat non retenu par BOSS-ETAT.md:143, posé en question GQ5 (GIREV-PRE.md:149, :246). Hors périmètre : toute écriture, tout essai, la décision, la mesure des paires réelles de MorphTable.
2. Commandes et lectures (exactes) avec compte brut
| # | Commande ou lecture | Résultat brut |
|---|---|---|
| 1 | Read lp-doc/gabarits.md (CART) ; Read REL/GIREV/GIREV-PRE.md:1-80 (gabarit d'en-tête) ; Read CG/CG2/CG2-CART-01.md (entier) ; Glob **/*GIREV* sous etat |
gabarit lu ; D8 = CG2-CART-01.md:51 ; Glob : REL\GIREV\GIREV-PRE.md seul (le livrable n'existait pas) |
| 2 | Grep ResolveConflictingMorphs dans D:\Game\LifePlay-Sandbox-5.8\Docs\CG |
12 lignes dans 4 fichiers : 04-defauts.md:20, :60, :75 ; 03-parcours.md:116, :134 ; 06-rapport-audit.md:33, :37 ; archive/CG-REFERENTIEL.md:280, :297, :305, :318, :336 (copie ancienne) |
| 3 | rg --no-ignore -l "ResolveConflictingMorphs" "D:/Game/LifePlay-master 5.8/Docs" "D:/Game/LifePlay-Sandbox-5.8/Docs" |
16 fichiers : 7 dumps sous etat (portage55/L6f/L6f_dump_GI.txt, portage55/L6f/L6f_LPDumpFunction_20260930-102202.txt, ST/STA/dumps/GI__graphes.txt, ST/STA/STA-T2a_LPDumpFunction_20261009-192922.txt, ST/STE/STE-D_LPDumpFunction_20261010-110623.txt, ST/STE/dumps/GI__LoadPreset.txt, sandbox/DOC-MOD/DOC-MOD_LPDumpFunction_20261007-083441.txt) ; Docs/01_Architecture_Initiale.md ; CG2-CART-01.md ; 4 fichiers du référentiel de l'addon (04-defauts, 03-parcours, 06-rapport-audit, archive/CG-REFERENTIEL) ; 3 fichiers CGREF1-* de l'addon (T3-MCP, T3b-FICHE, T3b-MCP). Aucun dump d'EventGraph (CAM-B1-PRE, STE-ORG4cV) dans la liste |
| 4 | Read CGREF1-T3b-MCP.md:1-70 (addon Docs/etat/CG/CGREF1/) |
lecture MCP 5.8 du 2026-10-16 : A11 :24, règle :44-49, D8 :92 |
| 5 | Read L6f_dump_GI.txt:18210-18300 et :3505-3530 ; Grep ResolveConflictingMorphs|MorphTable dans ce fichier |
fonction à :18214 (24 nœuds) ; appel :3518 ; MorphTable aussi à :50274-50316 (construction des curseurs) |
| 6 | Grep ResolveConflictingMorphs + -C 8 dans ST/STE/dumps/GI__LoadPreset.txt ; Grep K2Node_IfThenElse_5 (-A 8) ; rg --no-ignore -m3 "^Commandlet|^Date|^Moteur|Engine" sur STE-D_… |
appel :410-412 ; condition LoadPresetLite :496 ; en-tête STE-D : LPDumpFunction -BP=GI -Fn=LoadPreset -Pins -Exec, 2026-10-10T11:07:09 … code de sortie : 0 |
| 7 | Read 04-defauts.md:1-83, 03-parcours.md:100-197, 02-format.md:1-40, 06-rapport-audit.md:29-35, :123-178 |
cités en § 3 |
| 8 | Read Engine/Source/Runtime/Engine/Private/Components/SkeletalMeshComponent.cpp:4005-4079 et SkeletalRender.cpp:20-59 (moteur D:\Game\Launcher\Engine\UE_5.8) ; Grep MaxMorphTargetBlendWeight|MinMorphTargetBlendWeight|MorphTarget\.MaxBlendWeight|MorphTarget\.MinBlendWeight|define ZERO_ANIMWEIGHT_THRESH sous Engine/Source/Runtime (*.h, *.cpp) |
SetMorphTarget :4011-4035, GetMorphTarget :4042-4054 ; MaxMorphTargetBlendWeight = 5.0f SkeletalRender.cpp:26 ; #define ZERO_ANIMWEIGHT_THRESH (0.00001f) VectorRegister.h:133 |
| 9 | rg --no-ignore --files -g "*.lpcharacter" . (depuis Content) puis | wc -l ; idem avec les racines Modules et Saves |
145 ; 141 ; 4 |
| 10 | rg --no-ignore --pcre2 -U -l -g "*.lpcharacter" "(?ms)^MORPH ([^:\r\n]+)_min:.*?^MORPH \1_max:" . | wc -l ; formulation inverse (_max puis _min) |
0 ; 1 |
| 11 | Contrôle du motif : rg --no-ignore -l -g "*.lpcharacter" "^MORPH [^:]*_min:" . | wc -l ; idem _max ; rg --no-ignore -c … sommé par awk ; fichiers ayant au moins une ligne ^MORPH |
58 ; 59 ; lignes 472 (_min) et 523 (_max) ; 126 |
| 12 | Recherche web et lecture de pages (13 lignes au § 3.2) | statut de lecture par ligne du tableau § 3.2 |
| 13 | git log -1 -- Content/BPs/GI.uasset ; md5sum ; date |
c4cf6f3 2026-10-14 … ; 32fc0a970ab194626e8dc07df3a9633d (égal à GIREV-PRE.md:57) ; 2026-10-21 07:41 |
Écart de méthode : une première forme de la commande 9 (rg --files suivi de cut, sort, uniq -c, sans wc -l) a affiché les noms des fichiers dans la sortie de l'outil, dont certains à caractère réservé ; ils ne sont pas repris ici. Compter, ne pas lister (à verser).
3. Constats fichier:ligne
3.1 Comportement actuel de GI.ResolveConflictingMorphs (établi sauf mention)
Notation : L6f = Docs/Migration/etat/portage55/L6f/L6f_dump_GI.txt (dump 5.5 du 2026-09-30, 24 nœuds) ; T3b = CGREF1-T3b-MCP.md (lecture MCP 5.8 du 2026-10-16, 24 nœuds : même nombre, T3b:24).
- Signature :
FunctionEntry_192sans broche d'entrée ni de sortie (L6f:18215). La fonction n'a donc aucun paramètre. - Entrées implicites :
- la variable membre
MorphTable, tableau de la structureMorphSlider(champsDisplayName,Max,Min,Female,Male:L6f:18219-18232,T3b:44). La même table construit les curseurs de l'écran du générateur (L6f:50274-50316) : une ligne = un curseur à deux côtés,MinetMaxétant les noms de deux morphs opposés ; - le personnage édité
CharacterBeingEdited(L6f:18216,:18285) et, via lui, le composantMesh(L6f:18240-18242,:18288-18290) ; - les valeurs courantes des deux morphs, lues par
SkeletalMeshComponent::GetMorphTarget(L6f:18243-18256). Dans le moteur 5.8, cette lecture rend la valeur de la listeMorphTargetCurvesdu composant, ou0.0si le morph n'y figure pas (SkeletalMeshComponent.cpp:4042-4054).
- la variable membre
- Règle, ligne par ligne de
MorphTable, indépendamment des autres lignes (T3b:44-49, câblage relu dansL6f:18233-18300) :- si
Minvaut le nom « None », la ligne est ignorée (L6f:18233-18238) ; - lecture de
Maxet deMinsur le corps (L6f:18243-18256) ; - si
Max != 0etMin != 0(comparaison exacte à0.0,L6f:18257-18273), sinon rien ; - si
Max > Min(comparaison stricte :L6f:18278-18284),SetMorphTarget(nom Min, 0.0, bRemoveZeroWeight = true); sinonSetMorphTarget(nom Max, 0.0, true)(L6f:18291-18300; branche vraie →Min, branche fausse →Max:T3b:48).
- si
- Cas d'égalité :
Max > Minest faux, donc le morphMaxest annulé et leMinest gardé (T3b:49,04-defauts.md:20). Le départage est donc asymétrique (toujours le même côté), mais déterministe. - Effet moteur de l'annulation : avec
bRemoveZeroWeight = true, le moteur retire l'entrée du composant quand|valeur| ≤ 0,00001(SkeletalMeshComponent.cpp:4014,:4028-4034;VectorRegister.h:133). Le perdant est donc supprimé de la liste active, pas seulement mis à zéro. - Propriétés déduites de la structure (établi pour la structure, estimé pour l'usage) :
- idempotente : après un passage, l'un des deux côtés vaut 0 et la condition 3 est fausse ;
- silencieuse : aucun nœud de journal ni de message parmi les 24 (
L6f:18214-18300) ; l'audit R06 range « le silence » parmi ses faiblesses (06-rapport-audit.md:69, titre § 3.1) ; - destructive : la valeur perdante n'est pas conservée ;
- « le plus fort gagne », pas « le dernier écrit gagne » : la fonction ne voit que des valeurs finales, pas l'ordre des écritures (estimé : déduit du fait qu'elle ne lit que
GetMorphTarget).
- Pourquoi seulement le corps : aucune source interne ne le dit. Faits établis : la fonction lit et écrit uniquement sur
Mesh(L6f:18240-18242,:18288-18290) ; le défaut est consigné ainsi : « ne traite queGetMesh(ignoreHairSM) » (04-defauts.md:20) ;Person.setMorphTargetapplique, lui, le même nom au composant cheveux puis au corps, chacun deux fois (T3b:35-41, D704-defauts.md:19). Hypothèses (estimé, non départagées) : (a) une fonction écrite autour d'un composant de référence unique ; (b) le maillage de cheveux n'a pas tous ces morphs ; (c) oubli. Essai qui trancherait (b) : lire la liste des morph targets de l'asset de cheveux (lecture MCP ou dump), voir § 5. - Appelant : un seul site connu,
K2Node_CallFunction_6074dansLoadPreset, branche « faux » deIfThenElse_5dont la condition estLoadPresetLite(GI__LoadPreset.txt:410-412,:496;L6f:3518), suivi de l'affichage approprié du personnage (GI__LoadPreset.txt:414). La fonction s'exécute donc une fois, après le rejeu de toutes les lignes du préréglage, et pas à chaque écriture de morph. Elle n'est pas appelée par le curseur de l'écran (le curseur écrit un seul côté :Minsous 0,5,Maxau-dessus,03-parcours.md:164). Les corps desRandomize*et deGeneratePersonne sont pas lus (04-defauts.md:55) : appelants inconnus possibles (estimé : aucun, car aucun dump d'EventGraphne contient l'appel, commande 3).01_Architecture_Initiale.md:151place l'appel aprèsGenerateAndApplyBools: résumé de l'ancienne architecture, contredit par les dumps (l'appel est dansLoadPreset). - Lien avec la superposition :
Blend = truerejoue les lignes du fichier par-dessus l'état courant, sans moyenne (03-parcours.md:114-116,06-rapport-audit.md:31) ;BlendPresetne fusionne rien, c'est un empilement de fichiers partiels (06-rapport-audit.md:33). Le texte de R06 dit que le seul arbitrage automatique de ce dispositif estResolveConflictingMorphs(06-rapport-audit.md:33). Conséquence (estimé) : si un préréglage posé après un autre fixe le côté opposé à une valeur plus faible que celle déjà en place, c'est la contribution la plus récente qui est annulée. - Ce qui n'est pas lu : le contenu de
MorphTable(nombre de lignes, lesquelles ontMinrenseigné, drapeauxFemale/Male: la fonction les ignore,L6f:18231-18232). La valeur par défaut d'une variable de Blueprint n'est lisible ni dans ces dumps ni dansOUT1_uasset_rejeu_log_sans_emulation.txt:305(qui ne donne que le typeArray<struct:MorphSlider>).
3.2 Sources web : ce qui se fait ailleurs
Statut : lu = page récupérée et citée ; résumé = seulement le résumé du moteur de recherche, page non relue (indicatif) ; non lu = page tronquée ou inaccessible.
| # | Source (URL) | Ce qu'elle établit | Statut | Lien avec D8 |
|---|---|---|---|---|
| W1 | https://raw.githubusercontent.com/makehumancommunity/makehuman/master/makehuman/apps/humanmodifier.py | Modificateur à deux cibles : valeur bornée à [-1, 1] (ou [0, 1] sans cible gauche) ; poids gauche -min(value, 0.0), poids droit max(0.0, value) ; cible centrale 1.0 - abs(value) |
lu | Exclusion par construction : un seul paramètre signé, l'un des deux côtés vaut toujours 0 ; aucun conflit à résoudre |
| W2 | https://manual.reallusion.com/Character-Creator-4/Content/ENU/4.0/06_Customizing_Morphing_Sliders/User_Interface.htm | « a morphing value can be within a range from -100 to 100 » ; Min et Max règlent les bornes du curseur | lu | Même principe : curseur signé unique ; la page ne traite pas des morphs opposés combinés |
| W3 | https://docs.daz3d.com/public/software/dazstudio/4/userguide/creating_content/assembling/tutorials/creating_joint_controlled_morphs/start | « Change the Min Limit to 0. The JCM only works from 0 to 100 percent » ; « leaving limits on in negative values will cause problems when the shoulder joint is bent in the opposite direction » | lu | Bornage comme garde contre l'activation du côté opposé |
| W4 | https://docs.daz3d.com/public/software/dazstudio/4/referenceguide/terms/property/start | Certaines propriétés ont les attributs « Clamped, Mappable, Must Map » ; la page n'explique pas le bornage ni l'ERC | lu | Confirme l'existence d'un attribut de bornage ; rien sur les conflits |
| W5 | https://www.daz3d.com/forums/discussion/comment/381884 ; https://bitbucket.org/Diffeomorphic/import_daz/issues/480/plussing-morphs | Un « contrôleur » créé par ERC Freeze pilote plusieurs propriétés à la fois ; retirer les limites d'un morph peut casser la géométrie (résumé de recherche) | résumé | Pattern « un contrôle, plusieurs morphs » ; prudence sur le retrait des bornes. Pas de règle d'arbitrage trouvée |
| W6 | https://dev.epicgames.com/documentation/unreal-engine/BlueprintAPI/Components/SkeletalMesh/SetMorphTarget | Nœud « Set Morph Target with Name and Value(0-1) » ; « Remove Zero Weight : Used by editor code when it should stay in the active list with zero weight » | lu | Le nœud que D8 appelle ; plage documentée 0-1 |
| W7 | Moteur 5.8 installé : Engine/Source/Runtime/Engine/Private/Components/SkeletalMeshComponent.cpp:4019-4021 ; .../Private/SkeletalRender.cpp:26-35 |
Commentaire du moteur : « sum up, in the future we might normalize, but for now this just sums up / this won't work well if all of them have full weight - i.e. additive » (le code affecte =, :4021) ; poids absolu accepté jusqu'à r.MorphTarget.MaxBlendWeight, 5,0 par défaut |
lu | Unreal ne normalise ni n'arbitre : la politique est laissée au contenu. Les valeurs hors [0, 1] sont tolérées jusqu'à 5,0 |
| W8 | https://dev.epicgames.com/documentation/metahuman/metahuman-creator-blend-tool-in-unreal-engine | Mélange de un à trois préréglages par points de contrôle ; « As a control point moves closer to a preset, that preset's features blend with your character » ; la page ne donne ni formule de poids, ni normalisation, ni règle de conflit | lu | Fusion pondérée par proximité, sans conflit exposé à l'utilisateur ; la page est muette sur la normalisation |
| W9 | https://docs.unity3d.com/ScriptReference/PlayerSettings-legacyClampBlendShapeWeights.html ; https://discussions.unity.com/t/removing-clamping-of-blendshapes-to-the-range-0-100/683047 | Option : « If set to true, the range of BlendShape weights in SkinnedMeshRenderers will be clamped, otherwise BlendShape weights can take any value » ; défaut non précisé par la page. Le fil indique que Unity a retiré le bornage 0-100 et que le code doit borner lui-même (résumé) | lu (page) / résumé (fil) | Bornage configurable ; la responsabilité de borner revient au contenu |
| W10 | https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_vrm-1.0/expressions.md | Recouvrements overrideBlink / overrideLookAt / overrideMouth : none (rien), block (« Set the target weight to 0 » tant que l'expression dominante a un poids > 0), blend (« Attenuates the target weight ») ; la somme des atténuations est saturée (factor = 1.0 - saturate(value)) ; valeurs d'expression à borner à [0, 1] ; « settings for the same kind are treated as invalid » |
lu | Priorité explicite et documentée, avec deux modes (coupure ou atténuation) ; aucune règle d'égalité |
| W11 | https://simscommunity.info/2018/07/10/learning-your-tools-the-sims-4-morphmaker/ | Les directions « haut » et « bas » d'un curseur sont deux modificateurs distincts rattachés au même curseur ; l'article ne traite ni des poids, ni des conflits | lu | Non concluant : structure à deux côtés, aucune règle de conflit publiée trouvée |
| W12 | https://www.tech-artists.org/t/blendshape-condition-node/10797 | Un répondant déconseille de « couper des formes quand d'autres s'allument » au-delà des combinaisons de deux ; la voie courante est la forme corrective pour les combinaisons problématiques (résumé) | résumé | Mise en garde contre la logique d'extinction en chaîne |
| W13 | https://docs.blender.org/manual/en/latest/animation/shape_keys/shape_keys_panel.html | Page tronquée à la lecture (215 969 caractères, 100 000 lus, partie utile absente) | non lu | Aucune conclusion tirée (additif par défaut : non vérifié ici) |
Recherches sans résultat utile : « Sims 4 modificateur bidirectionnel » (aucune documentation officielle sur un arbitrage), « Character Creator morphs opposés combinés » (aucune page officielle), « MetaHuman plausibilité » (la page ne donne ni borne ni règle). L'absence de résultat n'est pas une preuve d'absence de règle.
3.3 Les patrons de pratique qui se dégagent
| Patron | Sources | Idée |
|---|---|---|
| P1 Exclusion par construction | W1, W2 | un seul paramètre signé pilote les deux côtés ; l'état « les deux côtés actifs » n'existe pas |
| P2 Bornage | W3, W9, W10, W7 | on limite l'amplitude (Daz, Unity, VRM, plafond 5,0 d'Unreal) ; borner n'arbitre pas entre deux côtés |
| P3 Priorité explicite et documentée | W10 | une expression dominante coupe (block) ou atténue (blend) une autre, règle écrite dans la spécification |
| P4 Fusion pondérée | W8, W10 | des poids continus plutôt qu'une coupure ; la normalisation reste souvent non documentée (W8) |
| P5 Pas d'arbitrage dans le moteur | W7 | le moteur additionne et laisse la politique au contenu ou à l'outil |
| P6 Forme corrective plutôt que chaîne d'extinctions | W12 (indicatif) | traiter la combinaison problématique par une forme dédiée |
3.4 Comparaison au comportement actuel
| Critère | ResolveConflictingMorphs actuel |
Pratique observée |
|---|---|---|
| Où le conflit est-il évité ? | après coup, à la fin du chargement (GI__LoadPreset.txt:410-412) ; le curseur de l'écran, lui, est déjà signé (03-parcours.md:164, P1) |
P1 évite l'état, la fonction le répare |
| Règle de priorité | le plus fort gagne, perdant mis à 0 (T3b:48-49) |
proche de P3 block, sans atténuation (P3 blend, P4 non utilisés) |
| Égalité | Max annulé, asymétrique, non écrit hors R04 |
aucune source lue ne fixe de règle d'égalité (W10 : même cas « invalide ») |
| Périmètre | corps seul (L6f:18240-18242) |
non documenté ; incohérence corps / cheveux possible (estimé) |
| Visibilité | aucun journal | R06 demande des branches d'échec journalisées (06-rapport-audit.md:166) |
| Dépendance à l'ordre | aucune (valeurs finales) | P3 documenté par priorité fixe ; ici l'ordre d'empilement est ignoré (estimé) |
| Points solides | déterministe, idempotente, simple, indépendante par ligne, coût d'exécution nul | alignée avec le choix de R06 : superposition plutôt que moteur de fusion (06-rapport-audit.md:144) |
3.5 Mesure sur le corpus (hypothèse de paire par radical _min / _max)
- 145 fichiers
.lpcharactersousContent: 141 sousModules, 4 sousSaves(commande 9). - Fichiers contenant au moins une clé
MORPHse terminant par_min: 58 ; par_max: 59 ; lignes : 472 et 523 (commande 11). - Fichiers contenant les deux côtés du même radical : 0 (ordre
_minpuis_max) et 1 (ordre inverse), soit 1 sur 145 (commande 10, deux formulations). - Limites (incertain) : les noms de
MorphTablene sont pas lus ; seule une partie des morphs suit la convention de suffixe (CGREF1-T2-KEYS.csv, addonDocs/etat/CG/CGREF1/: 1 151 lignes dont 101 contiennent_minet 108_maxd'aprèsrg -c, colonnes d'exemples comprises, donc compte indicatif) ; le fichier trouvé peut avoir un des deux côtés à 0 ; les sauvegardes des joueurs hors dépôt ne sont pas mesurées. - Lecture (estimé) : le conflit survient peu dans les fichiers isolés ; il vient de l'empilement (
Blend,generatePersonTemporaryà plusieurs noms :02-format.md:23) et des tirages d'intervalles.
3.6 Options
Échelle de coût de CG2-CART-01.md:35-40 (S ≤ 30 min de MCP, M 0,5 à 2 h, L > 2 h). Le nombre d'écritures est une estimation par analogie avec GIREV-PRE.md (M1 : 8 à 12 écritures ; charge totale de la révision : 50 à 90). Toute écriture dans GI entre dans la révision unique de la build 2.
| A. Garder tel quel | B. Règle déterministe documentée | C. Groupes exclusifs / normalisation | |
|---|---|---|---|
| Principe | aucune modification de Blueprint ; la règle actuelle est seulement décrite (guide de moddage, fiche) | la règle « le plus fort gagne » est conservée et écrite, avec l'égalité nommée ; trace de journal ; variantes B2 (cheveux) et B3 (égalité neutre) | C1 : valeur nette signée Max - Min appliquée à un seul côté (P1 appliqué après coup) ; C2 : exclusivité imposée à chaque écriture de morph de MorphTable (le dernier écrit gagne) |
| Changement Blueprint | aucun | B1 : 1 nœud de journal + 2 concaténations de noms sur les deux branches de IfThenElse_643 ; B2 : remplacer les 2 SetMorphTarget du corps par Person.setMorphTarget ; B3 : un test Max == Min annulant les deux |
C1 : calcul de la différence, choix du côté, 2 SetMorphTarget ; C2 : toucher Person.setMorphTarget (chemin chaud, D7 CG2-CART-01.md:50), la voie directe de GenerateAndApplyFloats (T3b:42), et lire des graphes non lus (Randomize*, GeneratePerson) |
| Coût (échelle CG2) | 0 | B1 S, 8 à 15 écritures ; B2 S-M, + 6 à 10 ; B3 S, + 4 à 6 | C1 M, 15 à 25 ; C2 L, 40 à 80 + lectures préalables |
| Effet sur les préréglages existants | aucun | B1 aucun (comportement identique) ; B2 : cheveux modifiés quand un conflit existe ; B3 : seules les égalités changent | C1 : toute paire en conflit change de valeur (le gagnant n'est plus gardé à sa valeur pleine) ; C2 : tout empilement Blend change (le plus récent gagne au lieu du plus fort) |
| Effet sur les sauvegardes | aucun | B1 aucun ; B2 / B3 : un personnage rechargé avec un conflit peut changer d'aspect à l'ouverture (fréquence inconnue) | idem, sur un plus grand nombre de cas ; les fichiers eux-mêmes ne sont pas réécrits (la résolution est faite au chargement), l'aspect rechargé peut différer |
| Risque | nul sur l'aspect ; la dette de silence reste (06-rapport-audit.md:69) |
B1 très faible (un message journalisé) ; B2 / B3 faible à moyen selon la fréquence des conflits | moyen à élevé ; C2 s'éloigne du choix « superposition sans moteur de fusion » (06-rapport-audit.md:144) |
| Bon point | rien à tester ; aucune charge sur la révision de GI | règle lisible pour un moddeur ; la trace mesure enfin la fréquence réelle des conflits ; aucun changement d'aspect en B1 | supprime l'asymétrie et l'état « deux côtés actifs » ; conforme à P1 |
| Mauvais point | égalité asymétrique non écrite, cheveux incohérents, silence | ajoute des écritures à la révision de GI ; B2 / B3 changent l'aspect | redéfinit la sémantique de tous les préréglages et mods ; coût disproportionné pour un défaut de gravité « information » |
Recommandation (non contraignante) : B1, avec B2 seulement si un décalage corps / cheveux est constaté en jeu. Raisons : (1) aucune source lue n'impose une règle d'égalité, l'essentiel de la pratique est de fixer et documenter une priorité (W10) ; (2) B1 ne change aucun aspect de personnage, donc aucun risque sur les préréglages ni sur les sauvegardes ; (3) la trace donne la mesure qui manque (fréquence des conflits, § 3.5) avant d'envisager C ; (4) coût S, compatible avec la contrainte de révision unique de GI. A reste raisonnable si la révision de GI doit rester minimale (D8 est « information », 04-defauts.md:20). C n'est pas recommandé avant la mesure de B1 : il change la sémantique de tout le corpus pour un gain non démontré. Interaction à noter : la décision D9 (morph remis à 0 ou laissé, CG2-CART-01.md:52) change les valeurs effectives avant la résolution et donc la fréquence des conflits.
4. Établi / estimé / incertain
- Établi :
- la règle, l'égalité, le périmètre « corps seul » et l'absence de journal (
L6f:18214-18300,T3b:44-49) ; - l'appelant unique connu et sa condition (
GI__LoadPreset.txt:410-412,:496) ; - l'usage de
MorphTablepour les curseurs (L6f:50274-50316) ; - le comportement moteur de
SetMorphTarget/GetMorphTargetet le plafond 5,0 (SkeletalMeshComponent.cpp:4011-4054,SkeletalRender.cpp:26-35) ; - le contenu des pages W1, W2, W3, W4, W6, W8, W9 (page), W10, W11 ;
- les comptes des commandes 9 à 11.
- la règle, l'égalité, le périmètre « corps seul » et l'absence de journal (
- Estimé :
- l'incohérence corps / cheveux (déduite de
T3b:35-41etL6f:18240) ; - « le plus fort gagne, pas le dernier » (déduit de la lecture des seules valeurs finales) ;
- l'absence d'autre appelant (aucun dump d'
EventGraphne l'appelle ;GeneratePerson,Randomize*non lus) ; - la rareté des conflits dans les fichiers isolés (hypothèse de paire par radical) ;
- les coûts et nombres d'écritures du § 3.6 ;
- l'identité des dumps 5.5 et 5.8 de la fonction (même nombre de nœuds, règle identique dans les deux lectures, branches non comparées nœud par nœud).
- l'incohérence corps / cheveux (déduite de
- Incertain :
- le contenu et la taille de
MorphTable; - la raison historique du « corps seul » ;
- si les maillages de cheveux possèdent ces morphs ;
- si
SavePersonécrit les valeurs après résolution ; - W5, W12 (résumés seuls), W13 (non lu) ;
- la fréquence des conflits en jeu.
- le contenu et la taille de
5. Points à confirmer (repris par la fiche suivante)
- Contenu de
MorphTable: nombre de lignes, nombre de lignes àMinrenseigné, noms des paires (à garder hors livrable public). Lecture seule par MCP ou par un dump de valeurs par défaut deGI; fonde la mesure de § 3.5. - Cheveux : les assets de cheveux portent-ils les morphs de
MorphTable? (liste des morph targets). Tranche l'hypothèse (b) du § 3.1 et l'intérêt de B2. - Appelants : confirmer que
LoadPresetest l'unique appelant (Room.LoadPresetdu mode constructeur,GeneratePerson,Randomize*,BodyChange), parfind_nodesou dump. - Sauvegardes :
SavePerson/SavePresetécrivent-ils les valeurs lues sur le composant après résolution ? Détermine si un personnage déjà sauvé porte l'état résolu. - Fréquence en jeu : n'est mesurable qu'avec la trace de B1 (aucune trace aujourd'hui). Mesure alternative hors jeu : balayer
Content/Saveset les dossiers de joueurs avec la liste de paires du point 1. - Pages non relues : W5, W12 (résumés) et W13 (Blender, tronquée) à relire si l'utilisateur veut s'en servir.
6. Questions pour la SPEC ou l'utilisateur
- DQ1 : option A, B (B1 seul, ou avec B2 / B3) ou C ? Recommandation : B1.
- DQ2 : à égalité, garder «
Maxannulé » (aucun changement d'aspect, à écrire) ou annuler les deux côtés (neutre, change les seuls cas d'égalité) ? - DQ3 : étendre la résolution aux cheveux (B2) maintenant, ou seulement après un décalage constaté en jeu ?
- DQ4 : journaliser chaque conflit ? Niveau : journal seul (recommandé), jamais à l'écran (les textes de débogage à l'écran sont déjà un défaut : D18, D19
04-defauts.md:35-36). - DQ5 : écrire la règle (le plus fort gagne, égalité) dans le guide de moddage livré avec le jeu, pour les auteurs de préréglages ? C'est une publication : règle doc 27.
- DQ6 : D8 entre-t-il dans la révision unique de GI (question
GQ5deGIREV-PRE.md:246) ? B1 ajoute environ 8 à 15 écritures à une charge estimée de 50 à 90.
Tours : 31 / 40 (estimé ; Tools/FicheStats.py fait foi)
Constats
- Fichier écrit :
D:\Game\LifePlay-master 5.8\Docs\Migration\etat\REL\GIREV\GIREV-D8-RECH.md. Aucun autre fichier écrit ; aucune lecture n'a modifié un fichier du projet. - Fichiers à commiter :
A Docs/Migration/etat/REL/GIREV/GIREV-D8-RECH.md(dépôt du jeu, brancheue5.8, par le gardien sur ticket). - À verser :
lp-doc(règle doc 27) —rg --files -g "*.lpcharacter"sans| wc -laffiche des noms de préréglages à caractère réservé dans la sortie de l'outil : compter, ne pas lister.lp-commandlets—rg --no-ignore --pcre2 -U -l "(?ms)^MORPH ([^:\r\n]+)_min:.*?^MORPH \1_max:"apparie deux clés d'un même fichier.lpcharacter(rétroréférence PCRE2, ripgrep 14.1.1).lp-cart— la page Blender du manuel dépasse 200 000 caractères et la partie utile est hors des 100 000 premiers : lire paroffset.