diff --git a/Content/Scenes/CentralLondon.umap b/Content/Scenes/CentralLondon.umap index dfba3db..4bd8e88 100644 Binary files a/Content/Scenes/CentralLondon.umap and b/Content/Scenes/CentralLondon.umap differ diff --git a/Docs/Migration/04_Cahier_Des_Charges.md b/Docs/Migration/04_Cahier_Des_Charges.md index 8e2eefd..aec6507 100644 --- a/Docs/Migration/04_Cahier_Des_Charges.md +++ b/Docs/Migration/04_Cahier_Des_Charges.md @@ -150,8 +150,8 @@ Ordre indicatif, à réviser selon le registre : | L7g | Packaging | build Shipping `LifePlay_Build_5.5_Shipping` construite (CHG-046), même structure que la version de l'auteur ; `McpAutomationBridge` déjà limité à l'éditeur ; ✅ validée en jeu le 2026-10-01 | | L7h | Vérification StreetMap | ✅ 280 villes et 280 maillages identiques 4.27/5.5 (ERR-018 clos, CHG-044) | | L7i | Variables DSL `Person` vides | ✅ ERR-030, ERR-037, ERR-003 préexistants en 4.27 ; ERR-005 corrigé par CX-B12 (385 nœuds `Contains`/`Split`/`FindSubstring`), validé en jeu le 2026-10-01 | -| L7j | Écran secondaire (amélioration) | ERR-031, préexistant en 4.27 : caméra `SecondScreen` sans racine ; seulement si souhaité | -| L7l | Personnages enfoncés dans le sol en extérieur (amélioration) | ERR-036, préexistant en 4.27 ; seulement si souhaité | +| L7j | Écran secondaire (amélioration) | caméra `SecondScreen` dotée d'une racine et placée sur `CameraScene` − 10 000 000 Y (CHG-052) ; test en jeu en attente | +| L7l | Personnages enfoncés dans le sol en extérieur (amélioration) | diagnostic (CHG-053) : sol de neige `fnt_new208` étiré ×100 en hauteur dans `forest___vin01.lpworld` (donnée de module) ; correction à caler par une mesure en jeu | | L7k | Erreurs d'exécution fréquentes (ex-L7b) | ✅ ERR-006 et ERR-007 revenus au niveau 4.27 avec CX-B12 ; signatures restantes au niveau 4.27 (CHG-045) | ## 4. Décisions diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index cc37b04..5418307 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -823,3 +823,34 @@ le rendu passait donc déjà par SkyAtmosphere. Éclairage précalculé : `Centr sont déjà actifs ; ils exécutent les 34 actions et axes hérités de `DefaultInput.ini`. Une migration complète (Input Actions, Mapping Contexts, réécriture des événements d'entrée des Blueprints) n'apporterait rien au joueur et demanderait un test manuel des 34 commandes : reportée. + +### L7j — image du contact pendant un appel (ERR-031, amélioration), 2026-10-01 + +| CHG | Date | Lot | Fichiers | Raison | Retour arrière | +|---|---|---|---|---|---| +| CHG-052 | 2026-10-01 | L7j | `Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet` (nouveau) ; `Content/Scenes/CentralLondon.umap` | ERR-031 (présent en 4.27) : la caméra `SecondScreen` filme le ciel | `Tools\Restore.ps1 -Lot L7j` | + +Chaîne (L6f et L7j) : `GI::secondScreen` marque le contact (`Person.secondScreen`) ; `GI::Show` le place sur la position +de son emplacement de scène (`ActorTransforms[i]`) **décalée de −10 000 000 en Y**, sur la copie de studio +`SecondScreenFloor` (−3 131 543 ; −24 967 212, soit le studio des scènes − 10 000 000 Y) ; l'encadré affiche +`Render/SecondScreen`, rendu par la caméra `SecondScreen`. Aucun Blueprint ne déplace cette caméra ; dans la carte +(4.27 comme 5.5) elle n'a pas de racine (`RootComponent = None`) et ses composants sont à l'origine. + +Correctif : racine = son `SceneComponent` existant, capture rattachée, position = `CameraScene` (cadrage des scènes du +studio : −3 131 132 ; −14 967 241 ; 50, lacet 180°) − 10 000 000 Y. Contrôle intégré (racine, rattachement, position +monde = cible) puis diff `LPDumpAssets` de la carte : **3 lignes modifiées**, toutes sur `SecondScreen` +(`Saved/Logs/portage/L7d_map_apres.txt` → `L7j_map_apres.txt`). Le cadrage (scène entière plutôt que portrait) est à +ajuster d'après le test en jeu si nécessaire (`-From=CameraPortrait`). + +### L7l — personnages enfoncés dans le sol en extérieur (ERR-036, amélioration), 2026-10-01 : diagnostic + +| CHG | Date | Lot | Fichiers | Raison | Retour arrière | +|---|---|---|---|---|---| +| CHG-053 | 2026-10-01 | L7l | `Source/LifePlayEditorTools/LPCheckMeshesCommandlet` (colonnes `MinZ`, `MaxZ`) | diagnostic | — | + +Le lieu `Modules/vin_Base/World/forest___vin01.lpworld` (donnée de module, identique en 4.27) pose comme sol le meuble +`fnt_new208` (dalle 230 × 99 × 0,6) en Z = −31,2 avec une échelle (0,713 ; 1,481 ; **100**) ; ailleurs (`bathroom`, +`shower`, `sports_centre`) le même meuble est un petit objet (Z = 60, échelle 5). Les meubles sont rattachés à un point +d'ancrage en Z = 60 (`Room::addObject`). La hauteur exacte de la surface de neige par rapport au sol de marche dépend +de la chaîne de composants du meuble : une mesure en jeu est nécessaire avant de corriger le fichier (ou d'ajuster la +dalle avec l'éditeur de lieu intégré au jeu, « Edit building »). diff --git a/Source/LifePlayEditorTools/LPCheckMeshesCommandlet.cpp b/Source/LifePlayEditorTools/LPCheckMeshesCommandlet.cpp index d6d2b86..58430a3 100644 --- a/Source/LifePlayEditorTools/LPCheckMeshesCommandlet.cpp +++ b/Source/LifePlayEditorTools/LPCheckMeshesCommandlet.cpp @@ -42,7 +42,7 @@ int32 ULPCheckMeshesCommandlet::Main(const FString& Params) Assets.Sort([](const FAssetData& A, const FAssetData& B) { return A.AssetName.LexicalLess(B.AssetName); }); TArray Lines; - Lines.Add(TEXT("Mesh;LODs;SommetsSource;TrianglesSource;SommetsRendu;TrianglesRendu;Sections;Materiaux;TailleX;TailleY;TailleZ")); + Lines.Add(TEXT("Mesh;LODs;SommetsSource;TrianglesSource;SommetsRendu;TrianglesRendu;Sections;Materiaux;TailleX;TailleY;TailleZ;MinZ;MaxZ")); int32 Failures = 0; for (const FAssetData& AssetData : Assets) @@ -66,11 +66,12 @@ int32 ULPCheckMeshesCommandlet::Main(const FString& Params) const int32 RenderVerts = Mesh->GetNumVertices(0); const int32 RenderTris = Mesh->GetNumTriangles(0); const int32 Sections = Mesh->GetNumSections(0); - const FVector Size = Mesh->GetBoundingBox().GetSize(); + const FBox Box = Mesh->GetBoundingBox(); + const FVector Size = Box.GetSize(); - Lines.Add(FString::Printf(TEXT("%s;%d;%d;%d;%d;%d;%d;%d;%.0f;%.0f;%.0f"), + Lines.Add(FString::Printf(TEXT("%s;%d;%d;%d;%d;%d;%d;%d;%.0f;%.0f;%.0f;%.2f;%.2f"), *AssetData.AssetName.ToString(), Mesh->GetNumLODs(), SrcVerts, SrcTris, RenderVerts, RenderTris, - Sections, Mesh->GetStaticMaterials().Num(), Size.X, Size.Y, Size.Z)); + Sections, Mesh->GetStaticMaterials().Num(), Size.X, Size.Y, Size.Z, Box.Min.Z, Box.Max.Z)); } if (!FFileHelper::SaveStringArrayToFile(Lines, *CsvPath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) diff --git a/Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet.cpp b/Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet.cpp new file mode 100644 index 0000000..0a6783e --- /dev/null +++ b/Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet.cpp @@ -0,0 +1,159 @@ +// Portage UE 5.5 - voir LPFixSceneCaptureCommandlet.h + +#include "LPFixSceneCaptureCommandlet.h" + +#include "Components/SceneCaptureComponent2D.h" +#include "Engine/Level.h" +#include "Engine/SceneCapture2D.h" +#include "Engine/World.h" +#include "Misc/PackageName.h" +#include "UObject/Package.h" +#include "UObject/SavePackage.h" + +DEFINE_LOG_CATEGORY_STATIC(LogLPFixSceneCapture, Log, All); + +namespace +{ + AActor* FindActorByLabelOrName(ULevel* Level, const FString& Name) + { + for (AActor* Actor : Level->Actors) + { + if (Actor && (Actor->GetActorLabel() == Name || Actor->GetName() == Name)) + { + return Actor; + } + } + return nullptr; + } +} + +ULPFixSceneCaptureCommandlet::ULPFixSceneCaptureCommandlet() +{ + IsClient = false; + IsServer = false; + IsEditor = true; + LogToConsole = true; +} + +int32 ULPFixSceneCaptureCommandlet::Main(const FString& Params) +{ + TArray Tokens, Switches; + TMap ParamsMap; + ParseCommandLine(*Params, Tokens, Switches, ParamsMap); + const bool bDryRun = Switches.Contains(TEXT("DryRun")); + const FString MapPath = ParamsMap.FindRef(TEXT("Map")); + const FString ActorName = ParamsMap.FindRef(TEXT("Actor")); + const FString FromName = ParamsMap.FindRef(TEXT("From")); + FVector Offset = FVector::ZeroVector; + { + TArray Parts; + ParamsMap.FindRef(TEXT("Offset")).ParseIntoArray(Parts, TEXT(","), true); + if (Parts.Num() == 3) + { + Offset = FVector(FCString::Atod(*Parts[0]), FCString::Atod(*Parts[1]), FCString::Atod(*Parts[2])); + } + } + if (MapPath.IsEmpty() || ActorName.IsEmpty() || FromName.IsEmpty()) + { + UE_LOG(LogLPFixSceneCapture, Error, TEXT("Usage : -Map=/Game/Scenes/X -Actor= -From= [-Offset=x,y,z] [-DryRun]")); + return 1; + } + + const FString WorldPath = MapPath + TEXT(".") + FPackageName::GetShortName(MapPath); + UWorld* World = LoadObject(nullptr, *WorldPath); + if (!World || !World->PersistentLevel) + { + UE_LOG(LogLPFixSceneCapture, Error, TEXT("Carte introuvable : %s"), *WorldPath); + return 1; + } + World->AddToRoot(); + + ASceneCapture2D* Capture = Cast(FindActorByLabelOrName(World->PersistentLevel, ActorName)); + AActor* From = FindActorByLabelOrName(World->PersistentLevel, FromName); + if (!Capture || !From || !From->GetRootComponent()) + { + UE_LOG(LogLPFixSceneCapture, Error, TEXT("Acteur introuvable : %s (capture) ou %s (reference, avec racine)"), *ActorName, *FromName); + World->RemoveFromRoot(); + return 1; + } + + USceneCaptureComponent2D* CaptureComp = Capture->GetCaptureComponent2D(); + USceneComponent* NewRoot = nullptr; + for (UActorComponent* Comp : Capture->GetComponents()) + { + USceneComponent* SceneComp = Cast(Comp); + if (SceneComp && SceneComp != CaptureComp && SceneComp->GetClass() == USceneComponent::StaticClass()) + { + NewRoot = SceneComp; + break; + } + } + if (!NewRoot) + { + NewRoot = CaptureComp; + } + + const FTransform FromTransform = From->GetRootComponent()->GetRelativeTransform(); + const FTransform Target(FromTransform.GetRotation(), FromTransform.GetLocation() + Offset, FVector::OneVector); + + UE_LOG(LogLPFixSceneCapture, Warning, TEXT("=== %s : racine %s -> %s, cible %s (= %s + %s)%s ==="), + *Capture->GetActorLabel(), *GetNameSafe(Capture->GetRootComponent()), *GetNameSafe(NewRoot), + *Target.ToString(), *FromName, *Offset.ToString(), bDryRun ? TEXT(" [DRY RUN]") : TEXT("")); + if (bDryRun) + { + World->RemoveFromRoot(); + return 0; + } + + Capture->Modify(); + Capture->SetRootComponent(NewRoot); + if (CaptureComp && CaptureComp != NewRoot) + { + CaptureComp->AttachToComponent(NewRoot, FAttachmentTransformRules::KeepRelativeTransform); + CaptureComp->SetRelativeTransform(FTransform::Identity); + } + NewRoot->SetRelativeTransform(Target); + NewRoot->UpdateComponentToWorld(); + if (CaptureComp && CaptureComp != NewRoot) + { + CaptureComp->UpdateComponentToWorld(); + } + + // Verification : racine, rattachement, position monde de la capture. + int32 Errors = 0; + if (Capture->GetRootComponent() != NewRoot) { ++Errors; UE_LOG(LogLPFixSceneCapture, Error, TEXT(" racine non affectee")); } + if (CaptureComp && CaptureComp != NewRoot && CaptureComp->GetAttachParent() != NewRoot) { ++Errors; UE_LOG(LogLPFixSceneCapture, Error, TEXT(" capture non rattachee")); } + const FTransform Final = CaptureComp ? CaptureComp->GetComponentTransform() : NewRoot->GetComponentTransform(); + if (!Final.Equals(Target, 0.01)) + { + ++Errors; + UE_LOG(LogLPFixSceneCapture, Error, TEXT(" position finale %s differente de la cible %s"), *Final.ToString(), *Target.ToString()); + } + UE_LOG(LogLPFixSceneCapture, Warning, TEXT(" capture : position monde %s"), *Final.ToString()); + + int32 Result = 0; + if (Errors == 0) + { + UPackage* Package = World->GetOutermost(); + const FString Filename = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetMapPackageExtension()); + FSavePackageArgs SaveArgs; + SaveArgs.TopLevelFlags = RF_Standalone; + SaveArgs.Error = GWarn; + if (UPackage::SavePackage(Package, World, *Filename, SaveArgs)) + { + UE_LOG(LogLPFixSceneCapture, Warning, TEXT(" -> carte sauvegardee")); + } + else + { + UE_LOG(LogLPFixSceneCapture, Error, TEXT(" ECHEC de sauvegarde")); + Result = 1; + } + } + else + { + UE_LOG(LogLPFixSceneCapture, Error, TEXT(" %d ecart(s) : carte NON sauvegardee"), Errors); + Result = 1; + } + World->RemoveFromRoot(); + return Result; +} diff --git a/Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet.h b/Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet.h new file mode 100644 index 0000000..b011d98 --- /dev/null +++ b/Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet.h @@ -0,0 +1,31 @@ +// Portage UE 5.5 - lot L7j (ERR-031, amelioration : defaut deja present en 4.27). +// +// La camera SceneCapture2D "SecondScreen" de CentralLondon (image du contact dans l'encadre d'appel) n'a +// pas de composant racine (RootComponent = None) et ses composants sont a l'origine : elle filme le ciel. +// Les Blueprints ne la deplacent pas. Le contact affiche est place a la position de son emplacement de +// scene decalee de -10 000 000 en Y (GI::Show), sur la copie de scene SecondScreenFloor. +// +// Cette commandlet donne a l'acteur vise un composant racine (son SceneComponent existant, ou le +// composant de capture a defaut), y rattache le composant de capture, et le place sur la transformation +// d'un acteur de reference (camera du portrait) augmentee d'un decalage. Tout est verifie apres coup ; +// en cas d'ecart, la carte n'est pas sauvegardee. +// +// Usage : +// UnrealEditor-Cmd.exe -run=LPFixSceneCapture -Map=/Game/Scenes/CentralLondon -Actor=SecondScreen +// -From=CameraPortrait -Offset=0,-10000000,0 [-DryRun] + +#pragma once + +#include "CoreMinimal.h" +#include "Commandlets/Commandlet.h" +#include "LPFixSceneCaptureCommandlet.generated.h" + +UCLASS() +class ULPFixSceneCaptureCommandlet : public UCommandlet +{ + GENERATED_BODY() + +public: + ULPFixSceneCaptureCommandlet(); + virtual int32 Main(const FString& Params) override; +};