434 lines
35 KiB
Markdown
434 lines
35 KiB
Markdown
Fiche ERR145-SPEC — Niveau N3 — Modèle prévu non précisé par la fiche | utilisé claude-opus-5-5 — Agent lp-impl — Skills sk-protocol, ue-cpp-build, lp-build, lp-ue-compat
|
||
# ERR145-SPEC : C1 assis, os masqués figés — option A, témoin C1 et réglages de vue (2026-10-21)
|
||
|
||
Ordonnateur : Big Boss (local_104ff22d). Étude seule : aucun code modifié, aucune compilation, ni copie UE, ni MCP, ni commit.
|
||
Seul fichier écrit : ce livrable. Amont : `ERR145-DIAG.md` (H1), confirmation de l'utilisateur du 2026-10-21 07:11, puis
|
||
fait nouveau relayé par Big Boss (capture 34 : le cycle C assis ne corrige pas la vue).
|
||
|
||
## 1. Objectif mesurable
|
||
|
||
### 1.1 Ce qui est établi avant d'écrire
|
||
- **H1 (mécanisme)** : Room masque `neckLower` sur `PlayerCharacter.Mesh` (`etat/CAM/CAM-B1-IMPL.md:87`) ; un os masqué sort des os
|
||
requis sauf si `VisibilityBasedAnimTickOption == AlwaysTickPoseAndRefreshBones` (`SkeletalMeshComponent.cpp:2321-2332`) ;
|
||
`lEye` / `rEye` (sous `head`, sous `neckLower`) gardent leur pose en espace composant ; `UpdateRoomCam` pose la caméra au milieu
|
||
de ces os à chaque passage (`CAM-B2k-IMPL.md:75`, nœud `SWL_512`). Confirmé en jeu par l'utilisateur à 07:11 (debout → C1 → assis).
|
||
- **Journal de l'essai du 2026-10-21** (`D:\Game\LifePlay_Build_5.8_Development_COM23\Windows\LifePlay\Saved\Logs\LifePlay.log`,
|
||
heure UTC, locale = UTC + 2) :
|
||
- l. 1725 (05:08:56) : entrée en C1 debout ; l. 1736 : action « Sit » (`have_a_meal_in`) ; l. 1755-1757 (05:09:13) : assis au
|
||
meuble `_196`, « racine Genesis8Female à 77 cm du bassin, à 9 cm du composant » ; l. 1758-1768 : C1 assis, tangage -11 à -57,
|
||
lacet de contrôle de -175 à +187 (vue décapitée, cas ERR-145) ;
|
||
- l. 1781 (05:11:21) C1 → C2 (`C1Hidden=0`, donc `RestoreC1` passé) ; l. 1784 (05:12:10) → C3 ; l. 1787-1790 (05:12:14.978) → C1,
|
||
toujours assis (aucun `unOccupyFnt` entre l. 1757 et l. 1809) ;
|
||
- l. 1791-1792 (05:12:15 à 05:12:16) : **tangage -0,1 puis -2,1**, lacet de contrôle +133 puis +86 ; plus aucune ligne « C1 souris »
|
||
jusqu'à la sortie de C1 en l. 1800 (05:14:01). `TickC1Pitch` n'écrit qu'au mouvement vertical de la souris
|
||
(`LPCameraModeSubsystem.cpp:782-786`) et c'est le seul code qui change le tangage hors sortie (`:790`, `:811-828`).
|
||
- **Capture 34** (description de Big Boss) : C1 assis après le cycle, surface de peau cylindrique collée à gauche, haut violet et collier
|
||
vus de très près à droite. Elle tombe dans la fenêtre 05:12:15 → 05:14:01 (07:12 → 07:14 locales), seule fenêtre « C1 assis après
|
||
un cycle » du journal (estimé : l'heure exacte de la capture n'est pas dans le journal).
|
||
|
||
### 1.2 Conséquences du fait nouveau (établi / estimé)
|
||
- **Établi** : après le cycle, les yeux ont été relus dans la pose assise du moment (os démasqués en C2 et C3, puis remasqués à
|
||
l'entrée en C1 ; la voie C1 lit les yeux avant le masquage, `CAM-B1-IMPL.md:104-107`). C'est l'état que l'option A maintient en
|
||
permanence pour une pose immobile. La vue reste mauvaise : **l'option A seule n'est pas prouvée suffisante**.
|
||
- **Établi** : le tangage valait environ -2° pendant la capture. « Borner le tangage » ne traite pas ce cas, et l'hypothèse H2
|
||
du DIAG (regard vers le bas sur la coupe du cou) n'explique pas la capture 34.
|
||
- **Estimé** (géométrie) : la largeur horizontale vue à 90° (FOV de `FirstPersonSM`, `CAM-B1e-CART.md:79`) ne couvre verticalement
|
||
qu'environ ±29° en 16:9. Pour qu'une épaule ou un cou remplisse la gauche de l'image à un tangage de -2°, il faut que le point de vue
|
||
soit **à hauteur d'épaule ou de cou**, pas à hauteur des yeux d'une tête droite. Deux lectures restent ouvertes :
|
||
- **L1, gel à une phase basse** : la pose « repas » plonge peut-être la tête vers l'assiette en boucle. Le remasquage a figé les
|
||
yeux à un instant où la tête était baissée. A corrige le gel, mais la caméra suivra alors la tête qui plonge et remonte.
|
||
- **L2, point de vue trop près du corps visible**, yeux vivants : tête très penchée, regard de côté (lacet de contrôle +86, orientation
|
||
du buste inconnue), ou sommets du cou à poids mixtes étirés (voir R8). Le tout avec un plan proche à **1 cm**
|
||
(`Config/DefaultEngine.ini:50`, contre 10 cm par défaut dans `BaseEngine.ini:361`) : toute peau à plus de 1 cm est dessinée.
|
||
- **Inconnu** : le contenu de l'animation `fnt_new017-f-0-use` (l. 1748) ; l'orientation du buste pendant la capture ; la distance
|
||
réelle entre la caméra et la peau.
|
||
|
||
### 1.3 Objectifs (critères chiffrés au § 7)
|
||
1. **A** : en C1, les os masqués du joueur restent requis et animés. Le témoin montre « yeux-buste » dans la plage debout,
|
||
assis comme debout, et la recette du DIAG § 1 ne montre plus le corps décapité vu de dehors.
|
||
2. **Témoin** : une ligne par seconde en C1. Elle mesure la vue contre un os **visible** (parent de `neckLower`) et contre l'avant du
|
||
buste. Elle donne aussi les yeux, `assis=`, l'écart racine ↔ composant, l'option d'animation et l'état du masquage.
|
||
3. **Réglages de vue C1** (partie C, sans effet par défaut) : plan proche propre à la vue C1 et décalage du point de vue vers l'avant
|
||
du buste, réglables à la console. Ils servent à trancher L1 / L2 dans la même build, sans second aller-retour.
|
||
4. PNJ, POV de sexe, C2, C3, Overview, studio : comportement inchangé.
|
||
|
||
## 2. Fichiers touchés, ordre
|
||
Ordre : (1) `LPCameraModeSubsystem.h`, (2) `LPCameraModeSubsystem.cpp`, (3) `LPRoomBoundsCameraModifier.h`, (4)
|
||
`LPRoomBoundsCameraModifier.cpp` (tous sous `D:\Game\LifePlay-master 5.8\Source\LifePlay\`). Les parties 3 et 4 portent seulement la
|
||
partie C. Elles débordent du périmètre de la fiche, qui ne nommait que le sous-système (écart, voir Constats). Si l'orchestrateur ne
|
||
retient pas C, on retire § 2.4, les appels à `SetC1View` et `CVarLPC1NearClip` / `CVarLPC1Forward` ; le témoin écrit alors `plan proche 0.0`.
|
||
|
||
Volume estimé : partie A ≈ 45 lignes ; témoin ≈ 60 lignes, dont 36 remplacées ; partie C ≈ 35 lignes. Total ≈ 140 lignes touchées
|
||
(≈ +115 / −30), aucune écriture d'asset, une compilation.
|
||
|
||
### 2.1 `LPCameraModeSubsystem.h`
|
||
- Ajouter `class USkeletalMeshComponent;` aux déclarations avancées (l. 17-25).
|
||
- Remplacer le commentaire du témoin C1 (l. 71) par : « Témoin C1 (ERR145) : une ligne par seconde en C1, vue mesurée contre le
|
||
parent de neckLower (os visible) et l'avant du buste ».
|
||
- Après `float C1Cooldown = 0.f;` (l. 74) :
|
||
```cpp
|
||
/** ERR145 (option A) : en C1, les os masques par Room restent requis et animes sur le maillage du joueur
|
||
* (AlwaysTickPoseAndRefreshBones) ; option d'origine rendue des que le libelle quitte C1 ou que le joueur change.
|
||
* Transmet aussi les reglages de vue C1 au modificateur (partie C). Appele a chaque image depuis TickSwitchKey. */
|
||
void TickC1BoneRefresh(bool bC1Now);
|
||
/** Maillage dont l'option a ete changee, et sa valeur d'origine (EVisibilityBasedAnimTickOption). */
|
||
TWeakObjectPtr<USkeletalMeshComponent> C1RefreshMesh;
|
||
uint8 C1SavedTickOption = 0;
|
||
```
|
||
|
||
### 2.2 `LPCameraModeSubsystem.cpp` : partie A
|
||
- Include : `#include "HAL/IConsoleManager.h"` (`TAutoConsoleVariable`, `IConsoleManager.h:2028`).
|
||
- Dans `namespace LPCameraModeLocal`, après la l. 108 :
|
||
```cpp
|
||
// ERR145 : os masque par Room en C1 (CAM-B1c) ; son parent (chestUpper attendu sur Genesis 8, lu a l'execution) est le
|
||
// repere visible du temoin ; les clavicules donnent l'avant du buste.
|
||
const FName NeckLowerBone(TEXT("neckLower"));
|
||
const FName LeftCollarBone(TEXT("lCollar"));
|
||
const FName RightCollarBone(TEXT("rCollar"));
|
||
|
||
// Avant horizontal du buste : perpendiculaire a la ligne des clavicules (UE : X avant, Y droite, Z haut, donc
|
||
// (droite - gauche) x haut = avant) ; a defaut, avant de l'acteur. OutFrom nomme la source pour le temoin.
|
||
FVector BodyForward(const ACharacter* Player, const USkeletalMeshComponent* Mesh, const TCHAR*& OutFrom)
|
||
{
|
||
OutFrom = TEXT("acteur");
|
||
FVector Forward = Player->GetActorForwardVector();
|
||
if (Mesh->GetBoneIndex(LeftCollarBone) != INDEX_NONE && Mesh->GetBoneIndex(RightCollarBone) != INDEX_NONE)
|
||
{
|
||
const FVector Across = Mesh->GetSocketLocation(RightCollarBone) - Mesh->GetSocketLocation(LeftCollarBone);
|
||
const FVector Candidate = FVector::CrossProduct(Across, FVector::UpVector).GetSafeNormal2D();
|
||
if (!Candidate.IsNearlyZero())
|
||
{
|
||
Forward = Candidate;
|
||
OutFrom = TEXT("clavicules");
|
||
}
|
||
}
|
||
return Forward.GetSafeNormal2D();
|
||
}
|
||
```
|
||
`BodyForward` doit être défini après les includes de `GameFramework/Character.h` et `Components/SkeletalMeshComponent.h`
|
||
(déjà en l. 20 et 26). Il reste dans l'espace nommé, avec appel qualifié (règle C2374, `lp-build` § 8).
|
||
- Après la fermeture de l'espace de noms (l. 728) :
|
||
```cpp
|
||
// ERR145 : bascules de l'essai en jeu (console, touche ² ou ~, DefaultInput.ini:166-168), sans recompiler.
|
||
static TAutoConsoleVariable<int32> CVarLPC1RefreshBones(TEXT("lp.C1RefreshBones"), 1,
|
||
TEXT("ERR145 : 1 = en C1, les os masques du joueur restent animes (option A) ; 0 = comportement d'avant (os figes)."));
|
||
static TAutoConsoleVariable<float> CVarLPC1NearClip(TEXT("lp.C1NearClip"), 0.f,
|
||
TEXT("ERR145 : plan proche de la vue C1, en cm (0 = plan global du projet, 1 cm)."));
|
||
static TAutoConsoleVariable<float> CVarLPC1Forward(TEXT("lp.C1Forward"), 0.f,
|
||
TEXT("ERR145 : avance du point de vue C1 vers l'avant du buste, en cm (0 = aucune)."));
|
||
```
|
||
- Nouvelle méthode, placée après `ResetC1PitchOnExit` (l. 828) :
|
||
```cpp
|
||
// ERR145 (option A, ERR145-SPEC § 2.2) : Room masque neckLower en C1 ; un os masque sort des os requis et sa pose reste
|
||
// figee (SkeletalMeshComponent.cpp:2321-2332), or UpdateRoomCam pose la camera sur lEye / rEye, sous neckLower. En C1,
|
||
// le maillage du joueur passe en AlwaysTickPoseAndRefreshBones : les os masques restent requis et animes, le rendu les
|
||
// garde masques (SkeletalRender.cpp:516-520). Le drapeau bRequiredBonesUpToDate = faux fait recalculer les os requis au
|
||
// prochain passage d'animation (SkeletalMeshComponent.cpp:1707-1712, 2879-2884), comme HideBone (:3950).
|
||
void ULPCameraModeSubsystem::TickC1BoneRefresh(bool bC1Now)
|
||
{
|
||
UGameInstance* GI = GetGameInstance();
|
||
ACharacter* Player = bC1Now ? Cast<ACharacter>(LPCameraModeLocal::ReadObject(GI, LPCameraModeLocal::GIPlayerCharacter)) : nullptr;
|
||
if (!IsValid(Player))
|
||
{
|
||
Player = nullptr;
|
||
}
|
||
USkeletalMeshComponent* Mesh = (Player && CVarLPC1RefreshBones.GetValueOnGameThread() != 0) ? Player->GetMesh() : nullptr;
|
||
USkeletalMeshComponent* Applied = C1RefreshMesh.Get();
|
||
if (Applied && Applied != Mesh)
|
||
{
|
||
Applied->VisibilityBasedAnimTickOption = static_cast<EVisibilityBasedAnimTickOption>(C1SavedTickOption);
|
||
Applied->bRequiredBonesUpToDate = false;
|
||
UE_LOG(LogLifePlay, Log, TEXT("C1 : os du joueur rendus a l'option %d (sortie de C1, scene, joueur change ou lp.C1RefreshBones 0)"), C1SavedTickOption);
|
||
Applied = nullptr;
|
||
}
|
||
if (!Applied)
|
||
{
|
||
C1RefreshMesh.Reset();
|
||
}
|
||
if (Mesh && !Applied)
|
||
{
|
||
C1SavedTickOption = static_cast<uint8>(Mesh->VisibilityBasedAnimTickOption);
|
||
Mesh->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones;
|
||
Mesh->bRequiredBonesUpToDate = false;
|
||
C1RefreshMesh = Mesh;
|
||
UE_LOG(LogLifePlay, Log, TEXT("C1 : os masques du joueur gardes animes (option %d -> %d ; neckLower masque=%d)"),
|
||
C1SavedTickOption, static_cast<int32>(EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones),
|
||
Mesh->IsBoneHiddenByName(LPCameraModeLocal::NeckLowerBone) ? 1 : 0);
|
||
}
|
||
// Partie C (§ 2.4) : reglages de vue C1 transmis au modificateur a chaque image ; nul hors C1.
|
||
UWorld* World = GI ? GI->GetWorld() : nullptr;
|
||
APlayerController* PC = World ? World->GetFirstPlayerController() : nullptr;
|
||
APlayerCameraManager* Manager = PC ? PC->PlayerCameraManager.Get() : nullptr;
|
||
ULPRoomBoundsCameraModifier* Modifier = Manager ? Cast<ULPRoomBoundsCameraModifier>(Manager->FindCameraModifierByClass(ULPRoomBoundsCameraModifier::StaticClass())) : nullptr;
|
||
if (Modifier)
|
||
{
|
||
FVector Offset = FVector::ZeroVector;
|
||
const float Forward = CVarLPC1Forward.GetValueOnGameThread();
|
||
if (Player && Player->GetMesh() && Forward != 0.f)
|
||
{
|
||
const TCHAR* From = nullptr;
|
||
Offset = LPCameraModeLocal::BodyForward(Player, Player->GetMesh(), From) * Forward;
|
||
}
|
||
Modifier->SetC1View(Player, CVarLPC1NearClip.GetValueOnGameThread(), Offset);
|
||
}
|
||
}
|
||
```
|
||
- `TickSwitchKey`, après la l. 1264 (`ResetC1PitchOnExit(...)`) :
|
||
`TickC1BoneRefresh(FrameLabel == LPCameraModeLocal::LabelC1);`. Le libellé est calculé à chaque image (l. 1261). Il vaut C1 seulement
|
||
si la vue est `PlayerCharacter` et `CamC1On` (l. 2632). Pendant une scène, il vaut « Scene » ou « POV » (l. 2620-2627). L'entrée en
|
||
scène rend donc l'option dans la même image ; aucun autre point d'appel n'est nécessaire. Un menu ouvert en C1 garde le libellé C1
|
||
et l'option : voulu, car la vue reste C1 derrière le menu.
|
||
- `Deinitialize`, avant `RemoveViewBounds` (l. 744) : `TickC1BoneRefresh(false);` (rend l'option si le jeu se ferme en C1).
|
||
|
||
### 2.3 `LPCameraModeSubsystem.cpp` : témoin C1 (remplace `WitnessC1`, l. 1196-1231)
|
||
```cpp
|
||
// ERR145 : une ligne par seconde en C1, sans condition. L'ancien ecart aux yeux etait aveugle : Room pose la camera sur ces
|
||
// memes yeux (ERR145-DIAG § 3.3). Reperes : parent de neckLower (os visible) et avant du buste (clavicules).
|
||
void ULPCameraModeSubsystem::WitnessC1(float DeltaTime)
|
||
{
|
||
C1Cooldown = FMath::Max(0.f, C1Cooldown - DeltaTime);
|
||
if (C1Cooldown > 0.f)
|
||
{
|
||
return;
|
||
}
|
||
C1Cooldown = LPCameraModeLocal::C1LogInterval;
|
||
const UGameInstance* GI = GetGameInstance();
|
||
const UWorld* World = GI ? GI->GetWorld() : nullptr;
|
||
const APlayerController* PC = World ? World->GetFirstPlayerController() : nullptr;
|
||
const ACharacter* Player = Cast<ACharacter>(LPCameraModeLocal::ReadObject(GI, LPCameraModeLocal::GIPlayerCharacter));
|
||
USkeletalMeshComponent* Mesh = IsValid(Player) ? Player->GetMesh() : nullptr;
|
||
const USceneComponent* Camera = IsValid(Player) ? Cast<USceneComponent>(LPCameraModeLocal::ReadObject(Player, LPCameraModeLocal::PlayerFirstPersonSM)) : nullptr;
|
||
const FName Chest = Mesh ? Mesh->GetParentBone(LPCameraModeLocal::NeckLowerBone) : NAME_None;
|
||
if (!PC || !Mesh || !IsValid(Camera) || Chest.IsNone())
|
||
{
|
||
return;
|
||
}
|
||
const FVector ChestAt = Mesh->GetSocketLocation(Chest);
|
||
const FVector Eyes = (Mesh->GetSocketLocation(LPCameraModeLocal::LeftEyeSocket) + Mesh->GetSocketLocation(LPCameraModeLocal::RightEyeSocket)) * 0.5f;
|
||
const FVector CameraAt = Camera->GetComponentLocation();
|
||
const FVector ViewAt = PC->PlayerCameraManager ? PC->PlayerCameraManager->GetCameraLocation() : CameraAt;
|
||
const TCHAR* From = nullptr;
|
||
const FVector Forward = LPCameraModeLocal::BodyForward(Player, Mesh, From);
|
||
const FVector Right = FVector::CrossProduct(FVector::UpVector, Forward);
|
||
const FVector Rel = ViewAt - ChestAt;
|
||
const FRotator CameraRotation = Camera->GetComponentRotation();
|
||
const FVector Root = Mesh->GetBoneLocation(Mesh->GetBoneName(0));
|
||
const UObject* RoomObject = LPCameraModeLocal::ReadObject(GI, LPCameraModeLocal::GIRoom);
|
||
UE_LOG(LogLifePlay, Log, TEXT("Temoin C1 : vue-%s %.0f cm (avant %+.0f, droite %+.0f, haut %+.0f) ; yeux-buste %.0f cm ; camera-yeux %.0f cm ; vue-camera %.0f cm ; lacet vue-buste %+.0f (%s) ; tangage %+.0f ; assis=%d ; racine-composant %.0f cm ; option os %d ; cou masque=%d ; plan proche %.1f ; minuterie=%d"),
|
||
*Chest.ToString(), Rel.Size(), FVector::DotProduct(Rel, Forward), FVector::DotProduct(Rel, Right), Rel.Z,
|
||
FVector::Dist(Eyes, ChestAt), FVector::Dist(CameraAt, Eyes), FVector::Dist(ViewAt, CameraAt),
|
||
FRotator::NormalizeAxis(CameraRotation.Yaw - Forward.Rotation().Yaw), From, FRotator::NormalizeAxis(CameraRotation.Pitch),
|
||
IsValid(LPCameraModeLocal::ReadObject(Player, LPCameraModeLocal::PlayerOccupyingFnt)) ? 1 : 0,
|
||
FVector::Dist(Root, Mesh->GetComponentLocation()), static_cast<int32>(Mesh->VisibilityBasedAnimTickOption),
|
||
Mesh->IsBoneHiddenByName(LPCameraModeLocal::NeckLowerBone) ? 1 : 0, CVarLPC1NearClip.GetValueOnGameThread(),
|
||
LPCameraModeLocal::TimerActive(IsValid(RoomObject) ? RoomObject : nullptr, LPCameraModeLocal::RoomCamTimer, World) ? 1 : 0);
|
||
}
|
||
```
|
||
- Constantes devenues orphelines (`C1Interval`, `C1MaxEyeDistance`, `C1MaxPitch`, `C1MaxYawGap`, l. 104-108) : l'IMPL les cherche par
|
||
Grep et retire celles qui ne servent plus. Le préfixe « Temoin C1 : » est gardé pour les recherches de journal.
|
||
- **Plages attendues** (estimées sur les proportions Genesis 8, non mesurées ; la VAL les cale sur la première ligne debout) :
|
||
|
||
| Champ | Debout, A actif | Assis, A actif | A coupé (`lp.C1RefreshBones 0`) après debout → assis |
|
||
|---|---|---|---|
|
||
| `yeux-buste` | 20 à 35 cm (référence R0) | R0 ± 5 cm (la chaîne du cou est rigide) | s'écarte de R0 de plus de 10 cm (COM17 : racine à 104 cm du composant) |
|
||
| `camera-yeux` | 0 à 3 cm (minuterie de 0,016 s, une image de retard) | 0 à 3 cm | 0 à 3 cm (la caméra suit les yeux figés : aveugle, comme l'ancien témoin) |
|
||
| `vue-buste` haut / avant | +15 à +30 / 0 à +15 cm | idem si la tête est droite ; haut < +10 = tête baissée (L1 / L2) | quelconque |
|
||
| `vue-camera` | 0 (= `lp.C1Forward` si réglé) | idem | idem |
|
||
| `lacet vue-buste` | libre (souris X) ; au-delà de ±90 = regard par-dessus l'épaule | idem | idem |
|
||
| `option os` / `cou masque` | 0 / 1 | 0 / 1 | 1 / 1 (valeur d'`ACharacter`, `Character.cpp:125` cité par le DIAG) |
|
||
|
||
### 2.4 Partie C : réglages de vue C1 (`LPRoomBoundsCameraModifier.h/.cpp`)
|
||
- `.h`, section publique, à côté de `SetBounds` (l. 28) :
|
||
```cpp
|
||
/** ERR145-SPEC § 2.4 : vue C1 (cible de vue = joueur) : plan proche propre en cm (0 = plan global) et decalage du point
|
||
* de vue en coordonnees monde. Joueur nul = rien. Rafraichi a chaque image par LPCameraModeSubsystem. */
|
||
void SetC1View(AActor* InPlayer, float InNearClip, const FVector& InOffset);
|
||
```
|
||
Section privée : `TWeakObjectPtr<AActor> C1View; float C1NearClip = 0.f; FVector C1Offset = FVector::ZeroVector;`
|
||
- `.cpp` : `SetC1View` recopie les trois valeurs. Dans `ModifyCamera`, après le bloc Studio (l. 42-47) et **avant** le filtre des
|
||
l. 50-54 (qui rend `false` pour toute vue non bornée, C1 comprise) :
|
||
```cpp
|
||
// ERR145 : vue C1 seulement (le joueur est aussi cible de vue en POV : SetC1View recoit nul hors C1).
|
||
const AActor* C1 = C1View.Get();
|
||
if (C1 && CameraOwner && CameraOwner->GetViewTarget() == C1)
|
||
{
|
||
if (C1NearClip > 0.f)
|
||
{
|
||
InOutPOV.PerspectiveNearClipPlane = C1NearClip;
|
||
}
|
||
InOutPOV.Location += C1Offset;
|
||
}
|
||
```
|
||
- Pourquoi un modificateur et pas `FirstPersonSM` : `UCameraComponent` n'a pas de plan proche en perspective en 5.8, seulement
|
||
`OrthoNearClipPlane` (`CameraComponent.h:84-86`). Son décalage additif (`:221`, `:311`, `:330-334`) resterait posé sur un composant
|
||
partagé avec la POV de sexe (`CAM-B1e-CART.md:89`). Le champ `FMinimalViewInfo::PerspectiveNearClipPlane` (`CameraTypes.h:92-94`) est
|
||
pris par le calcul de projection (`CameraStackTypes.cpp:342`, appelé par `LocalPlayer.cpp:1285`). Il ne touche que la vue, pas le
|
||
plan global. Le modificateur est toujours installé, quel que soit le libellé (`LPCameraModeSubsystem.cpp:873-881`).
|
||
|
||
## 3. Repli
|
||
- **Sans build** : `lp.C1RefreshBones 0` à la console rend l'option d'origine dès l'image suivante (§ 2.2) ; `lp.C1NearClip 0` et
|
||
`lp.C1Forward 0` (valeurs par défaut) annulent la partie C. En Shipping, sans console, ces variables gardent leur défaut.
|
||
- **Code** : instantané pris par la session principale avant l'IMPL (`Tools\Snapshot.ps1 -Lot ERR145`). Retour arrière seulement
|
||
proposé (`Tools\Restore.ps1 -Lot ERR145`). Les quatre fichiers ne touchent ni Room, ni Person, ni aucun asset.
|
||
|
||
## 4. Risques R#, questions Q#
|
||
|
||
### 4.1 L'option A suffit-elle ? (réponse à Big Boss)
|
||
**Non prouvé, et la capture 34 dit plutôt non pour la pose « repas ».** A est **nécessaire** : le mécanisme est établi et confirmé en
|
||
jeu, et c'est lui qui donne la vue décapitée de dehors (COM17 ; COM23, l. 1757-1768). Mais le cycle C a produit, pour cette pose, l'état
|
||
que A maintient, et la vue est restée mauvaise à un tangage de -2°. Mesures proposées par Big Boss, jugées sur le code :
|
||
|
||
| Mesure | Effet sur la capture 34 | Verdict |
|
||
|---|---|---|
|
||
| Borner le tangage | aucun : tangage -0,1 / -2,1 (l. 1791-1792), borne actuelle -60 (`LPCameraModeSubsystem.cpp:132`) jamais atteinte dans la fenêtre | écartée (et contraire à la décision A1-R « tous les axes », `:766`) |
|
||
| Masquer plus haut (`neckUpper`) | laisse `neckLower` visible, donc de la peau **plus près** des yeux | écartée (aggrave, estimé) |
|
||
| Masquer plus bas (`chestUpper`) | masque aussi les clavicules et les bras, s'ils descendent de `chestUpper` (estimé, hiérarchie Genesis 8 non relue) ; c'est un changement de Room (Blueprint), pas du C++ | écartée |
|
||
| Plan proche propre à C1 (C, `lp.C1NearClip`) | retire toute peau à moins de N cm du point de vue (projet : 1 cm, `DefaultEngine.ini:50`) ; ne déplace rien | **1er complément**, réglage en jeu |
|
||
| Décalage vers l'avant (C, `lp.C1Forward`) | sort le point de vue du volume du buste quel que soit le lacet, s'il suit l'avant **du buste** ; s'il suivait le regard, il pousserait la vue dans l'épaule en regardant de côté | **2e complément**, réglage en jeu |
|
||
|
||
Recommandation : livrer A, le témoin et C (C sans effet par défaut) dans la **même build**. On choisit les valeurs par défaut de C
|
||
d'après les essais T0 (sans build) et T6 (§ 7.2). Décision par les chiffres du témoin, A actif, assis, vue mauvaise :
|
||
- `yeux-buste` hors de R0 ± 5 → A n'agit pas (option non posée, voir `option os`) : retour à l'IMPL ;
|
||
- `haut` < +10 (tête baissée, L1 ou L2) → `lp.C1Forward` 10 à 20, ou plan proche 10 à 15 ;
|
||
- `haut` ≥ +15 et |lacet| > 90 → regard par-dessus l'épaule : plan proche ; une borne de lacet relative au buste serait une mesure
|
||
nouvelle, hors de cette fiche ;
|
||
- `haut` ≥ +15, |lacet| ≤ 60, vue mauvaise → peau étirée du cou (R8) : plan proche ;
|
||
- vue bonne → A suffit ; C reste à 0.
|
||
|
||
### 4.2 Risques
|
||
- **R1** : A seul ne corrige pas la capture 34 (§ 4.1) ; couvert par C et le témoin.
|
||
- **R2** : avec A, la caméra suit la tête animée : plongée vers l'assiette si la pose « repas » en a une (L1), petits mouvements de
|
||
tête des poses de repos. Gêne possible ; réponse par `lp.C1Forward` ou plus tard un lissage (Q2).
|
||
- **R3** : 1 à 3 images de gel à l'entrée en C1. Room masque, attend une image, puis change la vue (`CAM-B1-IMPL.md:104-105`) ; le
|
||
libellé C1 n'est vu qu'après ce changement (l. 2632). Les yeux viennent d'être lus : écart négligeable (estimé).
|
||
- **R4** : A demande deux recalculs des os requis par passage en C1, un à l'entrée et un à la sortie, le même chemin que
|
||
`HideBone` / `UnHideBone` (`SkeletalMeshComponent.cpp:3950`, `:3983`). `RecalcRequiredBones` remet la pose locale à la référence
|
||
(`:2369`) avant l'évaluation de la même image (`:1707-1712` puis `:2879-2884`). Pas d'image en pose de référence attendue (estimé).
|
||
C'est la raison pour laquelle on n'appelle **pas** `RecalcRequiredBones` hors du passage d'animation.
|
||
- **R5** : coût : les os de la tête (visage compris) redeviennent évalués en C1, comme hors C1. `AlwaysTickPoseAndRefreshBones`
|
||
rafraîchit même sans rendu (`SkinnedMeshComponent.cpp:1805-1808`), mais le corps est rendu en C1 : surcoût nul attendu (estimé).
|
||
- **R6** : avec un plan proche au-dessus de 1 cm, un mur à moins de N cm du point de vue est traversé du regard. Les passes qui liraient
|
||
`GNearClippingPlane` directement plutôt que la vue ne sont pas relues (incertain). Le risque n'existe que si `lp.C1NearClip` > 0.
|
||
- **R7** : la POV de sexe n'est pas couverte. PC masque aussi `neckLower` sur `CurrentActor` (`CAM-B1e-CART.md:62-63`) et y attache
|
||
`FirstPersonSM` à `head`, un os sous l'os masqué : même gel possible (estimé, non observé). Voir Q3.
|
||
- **R8** : les sommets à poids mixtes du cou (`chestUpper` et `neckLower`) sont dessinés entre leur place et l'**origine** de la
|
||
matrice du parent. En effet `ApplyScale(0)` = `ScaleMatrix * M` annule les trois axes et garde la ligne de translation
|
||
(`Matrix.inl:897-905` ; `SkeletalRender.cpp:486`, `:519`). Ce point est solidaire de `chestUpper`, à l'endroit où se trouvait
|
||
l'origine du modèle en pose de liaison, donc loin sous le buste. D'où un « tube » de peau étiré (les « lambeaux » de
|
||
`CAM-B1e-CART.md:72`). A ne le change pas, C ne le cache que près de la vue. Candidat (estimé) pour la « surface de peau
|
||
cylindrique » de la capture 34.
|
||
- **R9** : sans os `lCollar` / `rCollar`, `BodyForward` prend l'avant de l'acteur ; le témoin l'écrit (`acteur`). Une pose de meuble
|
||
qui tourne le corps par rapport à l'acteur fausserait alors `avant` et `lacet` : sans effet sur A.
|
||
- **R10** : compilation unitaire. Les noms neufs sont uniques (`CVarLPC1*`) ou dans l'espace nommé `LPCameraModeLocal` (`lp-build` § 8).
|
||
|
||
### 4.3 Questions pour l'utilisateur
|
||
- **Q1** : quelles valeurs par défaut pour `lp.C1NearClip` et `lp.C1Forward` dans la build d'essai ? Proposition : 0 (aucun effet).
|
||
Réglage à la console pendant T6, puis valeur retenue écrite dans le code, ou dans `[SystemSettings]` de `DefaultEngine.ini`, pour la
|
||
Shipping.
|
||
- **Q2** : la vue C1 doit-elle suivre les mouvements de tête des animations, au risque de plonger avec la tête dans la pose « repas » ?
|
||
Si non, la position doit venir d'un os du buste plus un décalage fixe (option B du DIAG, voir `vue-buste` du témoin).
|
||
- **Q3** : étendre A à la POV de sexe (personne filmée = joueur ou PNJ) dans une fiche à part ?
|
||
|
||
## 5. Interdits
|
||
- Aucun changement de Room, PC, GI, Person ni d'asset ; aucun MCP ; aucun changement du plan proche global (`DefaultEngine.ini:50`).
|
||
- Ne toucher à aucun maillage autre que `GI.PlayerCharacter->GetMesh()`. Les PNJ gardent leur option : la propriété est par instance
|
||
de composant (`SkinnedMeshComponent.h:793-794`).
|
||
- Pas d'appel direct à `RecalcRequiredBones` ni à `RecalcRequiredCurves` (R4) ; pas de décalage additif sur `FirstPersonSM` (§ 2.4).
|
||
- Ne pas retirer ni modifier `LPStringCompat`, `LPMeshCompat`, `LPNavigationHelpers` et voisins (`lp-ue-compat` § 4).
|
||
- Pas de compilation par l'IMPL (fiche BUILD à `lp-builder`), pas de commande git qui écrit, modification par Edit uniquement.
|
||
|
||
## 6. Valeurs moteur lues dans le moteur installé
|
||
Moteur `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\` (préfixe omis : `Engine/` sauf mention).
|
||
- `Classes/Components/SkinnedMeshComponent.h:94-113` : `EVisibilityBasedAnimTickOption` ; `AlwaysTickPoseAndRefreshBones` = « Always
|
||
Tick and Refresh BoneTransforms whether rendered or not ». Lignes `:787-794` : `VisibilityBasedAnimTickOption` est publique,
|
||
`UPROPERTY(… BlueprintReadWrite, Config …)`, sans setter (Grep sur `Classes/Components` : seulement `:794`, `:797-798`, et des
|
||
commentaires). Lignes `:1156`, `:1165` : `GetBoneIndex`, `GetBoneName` ; `:1263` : `GetParentBone` ; `:2014` : `GetBoneLocation` ;
|
||
`:2153`, `:2161`, `:2171` : `HideBoneByName`, `UnHideBoneByName`, `IsBoneHiddenByName` (non const) ; `:1759-1772` : états de
|
||
visibilité.
|
||
- `Classes/Components/SkeletalMeshComponent.h:894-896` : `bRequiredBonesUpToDate` est public et transitoire ; à faux, les os requis
|
||
sont recalculés au prochain passage. Lignes `:1862-1868` : `RecalcRequiredBones` (public) ; `:1885` : `ExcludeHiddenBones` ;
|
||
`:1898` : `RecalcRequiredCurves`. Aucune API `MarkRequiredCurvesDirty` à appeler : `RecalcRequiredBones` marque les courbes à jour
|
||
(`SkeletalMeshComponent.cpp:2400`) après le recalcul des instances d'animation (`:2383-2396`).
|
||
- `Private/Components/SkeletalMeshComponent.cpp:1707-1718` (passage d'animation) et `:2879-2890` (`RefreshBoneTransforms`) :
|
||
recalcul si le drapeau est faux. Lignes `:2246-2258` : les os demandés par les suiveurs sont ajoutés, puis les os masqués purgés.
|
||
Lignes `:2296-2336` : `ExcludeHiddenBones` garde un os s'il est `BVS_Visible` **ou** si l'option vaut
|
||
`AlwaysTickPoseAndRefreshBones` (`:2321-2322`). Lignes `:2358-2408` : `RecalcRequiredBones`. Lignes `:3933-3965` et `:3967-3983` :
|
||
`HideBone` / `UnHideBone` mettent le drapeau à faux, sauf sur un suiveur (`:3942-3945`, `:3976-3979`).
|
||
- `Private/Components/SkinnedMeshComponent.cpp:2119-2123` : un suiveur ne met pas à jour sa visibilité. Lignes `:2124-2163` :
|
||
propagation `BVS_HiddenByParent`. Lignes `:4424-4448`, `:4482-4500` : masquage par nom et par index. Lignes `:2413-2434` et
|
||
`:2468-2474` : position d'un os = transformée en espace composant (celle du leader pour un suiveur) × `LocalToWorld`.
|
||
Lignes `:1805-1808`, `:1881-1897` : rafraîchissement selon le rendu ou l'option.
|
||
- `Private/SkeletalRender.cpp:453`, `:458-466` : le rendu parcourt les os actifs de la LOD (`ActiveBoneIndices`), **pas** les os
|
||
requis. Lignes `:483-487` (suiveur, états du leader) et `:516-520` (sans leader) : un os non visible reçoit la matrice du parent à
|
||
l'échelle 0. **Un os masqué mais requis reste invisible au rendu.** Établi.
|
||
- `Core/Public/Math/Matrix.inl:897-905` : `ApplyScale` = `ScaleMatrix * M` ; la ligne de translation de M est gardée (R8).
|
||
- `Classes/GameFramework/Character.h:487` : `GetMesh() const` rend un pointeur non const.
|
||
`Classes/Camera/PlayerCameraManager.h:705` : `GetCameraLocation() const`. `Classes/Camera/CameraModifier.h:95` :
|
||
`ModifyCamera(float, FMinimalViewInfo&)`.
|
||
- `Classes/Camera/CameraTypes.h:92-94`, `:178`, `:216-219` : `PerspectiveNearClipPlane` (négatif = plan global, -1 par défaut).
|
||
`Private/Camera/CameraStackTypes.cpp:342` (et `:210`) : pris par la projection. `Private/LocalPlayer.cpp:1285` : appel de la vue du
|
||
joueur. `Classes/Camera/CameraComponent.h:84-86` : seul un plan proche orthographique existe sur la caméra ;
|
||
`Private/Camera/CameraComponent.cpp:448-461` : décalage additif.
|
||
- `Private/UnrealEngine.cpp:19090-19102` : commande console `r.SetNearClipPlane <cm>`, minimum 1, 20 sans argument, réglage
|
||
**global**. Aucune garde de compilation lue entre les l. 19050 et 19102 ; présence en Development estimée.
|
||
- `Core/Public/HAL/IConsoleManager.h:2028` : `TAutoConsoleVariable(const TCHAR* Name, const T& DefaultValue, const TCHAR* Help, uint32 Flags = ECVF_Default)`.
|
||
- Configuration : `Engine/Config/BaseEngine.ini:361` `NearClipPlane=10.0` ; projet `Config/DefaultEngine.ini:50`
|
||
`NearClipPlane=1.000000` ; console `Config/DefaultInput.ini:166-168` (`Tilde`, `²`).
|
||
- Leader et suiveurs dans le jeu : Room masque sur `PlayerCharacter.Mesh` = `CharacterMesh0` (`CAM-B1-IMPL.md:87` ;
|
||
`CAM-B1e-CART.md:48`, `:61`). Ce maillage est le leader de `HairSM`, `PenisSM` et des tenues (`SetLeaderPoseComponent(Mesh)`,
|
||
`CAM-B1e-CART.md:52-57`). Les suiveurs lisent les transformées et la visibilité du leader : A ne demande rien sur eux.
|
||
|
||
## 7. Critères de VAL (commande → attendu)
|
||
|
||
### 7.1 Contrôles de code et de build (`lp-validator`, après BUILD par `lp-builder`)
|
||
1. `git -C "D:\Game\LifePlay-master 5.8" diff --stat` → les 4 fichiers du § 2 seulement, ou 2 si C n'est pas retenue.
|
||
2. Grep `VisibilityBasedAnimTickOption` sur `Source/` → occurrences dans `LPCameraModeSubsystem.cpp` seulement. Grep
|
||
`RecalcRequiredBones|ApplyScale|AddAdditiveOffset` sur `Source/LifePlay` → 0.
|
||
3. `.\Tools\Build.ps1 -Lot ERR145` (LifePlayEditor) → code 0, module `LifePlay` compilé, 0 C4996 nouveau, « autres lignes : 20 ».
|
||
Puis build Development empaquetée neuve (`BuildDebug.ps1`, `-ArchiveDir` neuf) pour l'utilisateur.
|
||
4. Journal de l'essai : `.\Tools\LogStats.ps1 -Log <LifePlay.log> -Pattern 'Temoin C1', 'C1 : os'` → une ligne « gardes animes » par
|
||
entrée en C1, une ligne « rendus » par sortie, et des lignes « Temoin C1 » à 1 s d'écart en C1.
|
||
|
||
### 7.2 Cahier de tests pour l'utilisateur
|
||
Essai dans une build donnée ; console = touche ² ; chaque ligne rapporte « vue correcte / non », plus une capture si non.
|
||
- **T0, sans build, sur COM23, maintenant** (oriente le défaut de C, Q1) :
|
||
- T0a : s'asseoir à la même chaise « repas », en C1. Faire trois cycles C (C2 → C3 → C1) espacés de 5 s, en regardant à l'horizontale.
|
||
La vue change d'un cycle à l'autre (plus ou moins basse) → L1 (gel à une phase de la boucle) ; toujours la même → L2.
|
||
- T0b : dans la vue mauvaise, `r.SetNearClipPlane 10`, puis `r.SetNearClipPlane 20` ; noter si la peau proche disparaît.
|
||
Remettre ensuite `r.SetNearClipPlane 1` (valeur du projet), ou relancer le jeu.
|
||
- T0c : même essai sur une chaise sans repas (autre action « Sit »).
|
||
- **T1, recette du DIAG § 1** (build ERR145) : debout → C1 → s'asseoir sans quitter C1 → regarder autour, en bas, à l'horizontale.
|
||
Attendu : vue depuis les yeux, plus de corps décapité vu de dehors. Témoin : `yeux-buste` = R0 ± 5, `option os 0`, `cou masque=1`.
|
||
- **T2, preuve par bascule** : même recette avec `lp.C1RefreshBones 0` → le défaut d'origine revient, et `yeux-buste` s'écarte de R0 de
|
||
plus de 10 cm. Puis `lp.C1RefreshBones 1` → la vue revient aux yeux dans la seconde.
|
||
- **T3, lever en C1** (ERR-111, ERR-106) : assis en C1, se lever (Z) sans quitter C1. Attendu : caméra aux yeux, pas à côté de la tête ;
|
||
témoin : `yeux-buste` = R0 ± 5 et `assis=0`.
|
||
- **T4, cycle C** debout et assis : aucun écart ; une ligne « C1 : os … rendus » à chaque sortie de C1.
|
||
- **T5, PNJ et scènes inchangés** : en C2, PNJ assis et en marche (tête visible, aucun changement) ; lancer une scène depuis C1 (ligne
|
||
« rendus » à l'entrée), POV sur le partenaire (tête normale), retour en C1 après la scène.
|
||
- **T6, compléments** (si T1 assis reste mauvais, pose « repas ») : `lp.C1NearClip 10`, puis 15 ; remettre à 0 ; `lp.C1Forward 10`,
|
||
puis 20 ; remettre à 0. Noter le réglage qui donne une vue correcte et recopier trois lignes « Temoin C1 » de chaque réglage.
|
||
|
||
### 7.3 Effet attendu sur ERR-106 et ERR-111
|
||
- **ERR-111** (`Registre_Erreurs.md:118`). Volet « C1 à côté de la tête après un lever … un cycle C corrige » : même mécanisme dans le
|
||
sens inverse (yeux figés assis, corps levé). **Corrigé par A** (estimé fort), preuve par T3. Volets « vue au plafond au retour d'une
|
||
scène » (orientation, B2s) et « à-coup à la sortie de CharGen » : sans effet attendu de A.
|
||
- **ERR-106** (`Registre_Erreurs.md:113`). Résidu « décalage après un lever » : **corrigé par A** (estimé fort). Le recalage de B2k à
|
||
chaque passage de minuterie lit enfin des yeux vivants. La partie principale (caméra posée une seule fois) était déjà corrigée par B2k.
|
||
- **ERR-145** (`Registre_Erreurs.md:152`) : volet « entré debout puis assis » corrigé par A (T1, T2) ; volet « capture 34 » (assis,
|
||
après cycle) : A plus un complément C, selon T0 et T6.
|
||
|
||
Fichiers à commiter :
|
||
- `A Docs/Migration/etat/REL/ERR145/ERR145-SPEC.md` (dépôt du jeu, branche `ue5.8`)
|
||
|
||
Tours : 30 / 60
|
||
|
||
## Constats
|
||
- Écart au périmètre de la fiche : la partie C touche `LPRoomBoundsCameraModifier.h/.cpp`, non nommés par la fiche. Raison : aucun
|
||
plan proche par vue n'existe sur `UCameraComponent` en 5.8 (§ 2.4). Partie séparable, décision de l'orchestrateur.
|
||
- Écart au DIAG : la plage du témoin « 15 à 40 cm » (`ERR145-DIAG.md:103`) devient « R0 ± 5 », calée sur la première ligne debout de la
|
||
build. Les 20 à 35 cm sont une estimation Genesis 8 non mesurée.
|
||
- Le journal COM23 contient aussi, à l'action « Sit », trois `Divide by zero: Divide_DoubleDouble` de GI (l. 1740-1745) : sans lien
|
||
établi avec ERR-145.
|
||
- Il reste à vérifier, par MCP en lecture (hors fiche) : la hiérarchie Genesis 8 sous `chestUpper` (`get_bone_children`), et
|
||
l'existence de `lCollar` / `rCollar` sur Michael et Bridget. Le témoin écrit aussi `clavicules` ou `acteur`.
|