From af1861b9899201570cb34867ecf3068460cd572c Mon Sep 17 00:00:00 2001 From: edujiro5972 <336072401+edujiro5972@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:08:14 +0200 Subject: [PATCH] L7j : camera SecondScreen dotee d'une racine et placee (ERR-031) ; L7l : diagnostic du sol de neige (ERR-036) Co-Authored-By: Claude Opus 5.5 --- Content/Scenes/CentralLondon.umap | Bin 430569 -> 430735 bytes Docs/Migration/04_Cahier_Des_Charges.md | 4 +- Docs/Migration/Journal_Modifications.md | 31 ++++ .../LPCheckMeshesCommandlet.cpp | 9 +- .../LPFixSceneCaptureCommandlet.cpp | 159 ++++++++++++++++++ .../LPFixSceneCaptureCommandlet.h | 31 ++++ 6 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet.cpp create mode 100644 Source/LifePlayEditorTools/LPFixSceneCaptureCommandlet.h diff --git a/Content/Scenes/CentralLondon.umap b/Content/Scenes/CentralLondon.umap index dfba3db20766d4ca69033bff6ee10f2515664a8e..4bd8e88562e5e15b221666d73fec23a14a4e4275 100644 GIT binary patch delta 7426 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 EA2o-0YybcN delta 7337 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 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; +};