520 lines
19 KiB
C++
520 lines
19 KiB
C++
|
|
// Portage UE 5.5 - voir LPPairCollisionTestCommandlet.h
|
||
|
|
|
||
|
|
#include "LPPairCollisionTestCommandlet.h"
|
||
|
|
|
||
|
|
#include "AssetRegistry/AssetData.h"
|
||
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
||
|
|
#include "Components/CapsuleComponent.h"
|
||
|
|
#include "Components/StaticMeshComponent.h"
|
||
|
|
#include "Engine/StaticMesh.h"
|
||
|
|
#include "Engine/World.h"
|
||
|
|
#include "GameFramework/Character.h"
|
||
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
||
|
|
#include "Misc/FileHelper.h"
|
||
|
|
#include "Misc/Paths.h"
|
||
|
|
#include "PhysicsEngine/BodySetup.h"
|
||
|
|
#include "PreviewScene.h"
|
||
|
|
|
||
|
|
DEFINE_LOG_CATEGORY_STATIC(LogLPPairCollision, Log, All);
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
struct FProp
|
||
|
|
{
|
||
|
|
FString Name;
|
||
|
|
FVector Location = FVector::ZeroVector;
|
||
|
|
FRotator Rotation = FRotator::ZeroRotator;
|
||
|
|
FVector Scale = FVector::OneVector;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct FProbe
|
||
|
|
{
|
||
|
|
bool bBlocked = false;
|
||
|
|
bool bStartInside = false;
|
||
|
|
FString Hit;
|
||
|
|
float Moved = 0.f;
|
||
|
|
};
|
||
|
|
|
||
|
|
float Token(const FString& Part, const TCHAR* Key)
|
||
|
|
{
|
||
|
|
const FString Needle = FString::Printf(TEXT("%s="), Key);
|
||
|
|
const int32 At = Part.Find(Needle);
|
||
|
|
return At == INDEX_NONE ? 0.f : FCString::Atof(*Part + At + Needle.Len());
|
||
|
|
}
|
||
|
|
|
||
|
|
bool LoadWorldFile(const FString& Path, FVector& RoomScale, TArray<FProp>& Props)
|
||
|
|
{
|
||
|
|
TArray<FString> Lines;
|
||
|
|
if (!FFileHelper::LoadFileToStringArray(Lines, *Path))
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool bWantName = false;
|
||
|
|
int32 Slash = 0;
|
||
|
|
FString Name;
|
||
|
|
for (FString Line : Lines)
|
||
|
|
{
|
||
|
|
Line.TrimStartAndEndInline();
|
||
|
|
if (Line.IsEmpty())
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (Line == TEXT("*****"))
|
||
|
|
{
|
||
|
|
bWantName = true;
|
||
|
|
Slash = 0;
|
||
|
|
Name.Reset();
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (bWantName)
|
||
|
|
{
|
||
|
|
Name = Line;
|
||
|
|
bWantName = false;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (Line == TEXT("///"))
|
||
|
|
{
|
||
|
|
++Slash;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (Name.IsEmpty() && Line.StartsWith(TEXT("X=")))
|
||
|
|
{
|
||
|
|
RoomScale = FVector(Token(Line, TEXT("X")), Token(Line, TEXT("Y")), Token(Line, TEXT("Z")));
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (Slash < 2 || !Line.StartsWith(TEXT("X=")))
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
TArray<FString> Parts;
|
||
|
|
Line.ParseIntoArray(Parts, TEXT(";"), true);
|
||
|
|
if (Parts.Num() < 3)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
FProp Prop;
|
||
|
|
Prop.Name = Name;
|
||
|
|
Prop.Location = FVector(Token(Parts[0], TEXT("X")), Token(Parts[0], TEXT("Y")), Token(Parts[0], TEXT("Z")));
|
||
|
|
Prop.Rotation = FRotator(Token(Parts[1], TEXT("P")), Token(Parts[1], TEXT("Y")), Token(Parts[1], TEXT("R")));
|
||
|
|
Prop.Scale = FVector(Token(Parts[2], TEXT("X")), Token(Parts[2], TEXT("Y")), Token(Parts[2], TEXT("Z")));
|
||
|
|
Props.Add(Prop);
|
||
|
|
Slash = 0;
|
||
|
|
}
|
||
|
|
return Props.Num() > 0 && !RoomScale.IsNearlyZero();
|
||
|
|
}
|
||
|
|
|
||
|
|
int32 BodyElems(UStaticMeshComponent* Comp)
|
||
|
|
{
|
||
|
|
const UBodySetup* Body = Comp ? Comp->GetBodySetup() : nullptr;
|
||
|
|
return Body ? Body->AggGeom.GetElementCount() : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void SetPawnResponse(const TArray<UStaticMeshComponent*>& Meshes, const ECollisionResponse Response)
|
||
|
|
{
|
||
|
|
for (UStaticMeshComponent* Mesh : Meshes)
|
||
|
|
{
|
||
|
|
Mesh->SetCollisionResponseToChannel(ECC_Pawn, Response);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void ApplyBodies(ACharacter* A, ACharacter* B, const bool bOn, const bool bIgnoreEachOther)
|
||
|
|
{
|
||
|
|
auto Apply = [bOn](ACharacter* Who)
|
||
|
|
{
|
||
|
|
UCapsuleComponent* Cap = Who->GetCapsuleComponent();
|
||
|
|
Cap->SetCollisionEnabled(bOn ? ECollisionEnabled::QueryAndPhysics : ECollisionEnabled::NoCollision);
|
||
|
|
if (UCharacterMovementComponent* Move = Who->GetCharacterMovement())
|
||
|
|
{
|
||
|
|
Move->GravityScale = bOn ? 1.f : 0.f;
|
||
|
|
}
|
||
|
|
Cap->ClearMoveIgnoreActors();
|
||
|
|
};
|
||
|
|
Apply(A);
|
||
|
|
Apply(B);
|
||
|
|
if (bIgnoreEachOther)
|
||
|
|
{
|
||
|
|
A->GetCapsuleComponent()->IgnoreActorWhenMoving(B, true);
|
||
|
|
B->GetCapsuleComponent()->IgnoreActorWhenMoving(A, true);
|
||
|
|
}
|
||
|
|
A->GetCapsuleComponent()->RecreatePhysicsState();
|
||
|
|
B->GetCapsuleComponent()->RecreatePhysicsState();
|
||
|
|
}
|
||
|
|
|
||
|
|
FProbe Probe(ACharacter* Who, const FVector& From, const FVector& Delta)
|
||
|
|
{
|
||
|
|
Who->SetActorLocation(From, false, nullptr, ETeleportType::TeleportPhysics);
|
||
|
|
UCapsuleComponent* Cap = Who->GetCapsuleComponent();
|
||
|
|
const FVector Start = Cap->GetComponentLocation();
|
||
|
|
FHitResult Hit;
|
||
|
|
Cap->MoveComponent(Delta, Cap->GetComponentQuat(), true, &Hit);
|
||
|
|
FProbe Result;
|
||
|
|
Result.Moved = FVector::Dist(Start, Cap->GetComponentLocation());
|
||
|
|
Result.bStartInside = Hit.bStartPenetrating;
|
||
|
|
Result.bBlocked = Result.Moved < Delta.Size() - 2.f;
|
||
|
|
if (Hit.bBlockingHit || Hit.bStartPenetrating)
|
||
|
|
{
|
||
|
|
Result.Hit = GetNameSafe(Hit.GetActor());
|
||
|
|
if (const UPrimitiveComponent* Comp = Hit.GetComponent())
|
||
|
|
{
|
||
|
|
const UStaticMeshComponent* Mesh = Cast<UStaticMeshComponent>(Comp);
|
||
|
|
const FString MeshName = (Mesh && Mesh->GetStaticMesh())
|
||
|
|
? Mesh->GetStaticMesh()->GetName()
|
||
|
|
: Comp->GetName();
|
||
|
|
Result.Hit += TEXT("/") + MeshName;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Who->SetActorLocation(From, false, nullptr, ETeleportType::TeleportPhysics);
|
||
|
|
return Result;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool FindPairSpot(UWorld* World, float Radius, float HalfHeight, FVector& OutA, FVector& OutB)
|
||
|
|
{
|
||
|
|
const FCollisionShape Shape = FCollisionShape::MakeCapsule(Radius, HalfHeight);
|
||
|
|
FCollisionQueryParams Query(SCENE_QUERY_STAT(LPPairSpot), false);
|
||
|
|
const float Gap = Radius * 2.f + 40.f;
|
||
|
|
const float Z = HalfHeight + 20.f;
|
||
|
|
for (float Y = -700.f; Y <= 700.f; Y += 80.f)
|
||
|
|
{
|
||
|
|
for (float X = -700.f; X <= 700.f; X += 80.f)
|
||
|
|
{
|
||
|
|
const FVector A(X, Y, Z);
|
||
|
|
const FVector B(X + Gap, Y, Z);
|
||
|
|
const bool bA = World->OverlapBlockingTestByChannel(A, FQuat::Identity, ECC_WorldStatic, Shape, Query);
|
||
|
|
const bool bB = World->OverlapBlockingTestByChannel(B, FQuat::Identity, ECC_WorldStatic, Shape, Query);
|
||
|
|
if (!bA && !bB)
|
||
|
|
{
|
||
|
|
OutA = A;
|
||
|
|
OutB = B;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ULPPairCollisionTestCommandlet::ULPPairCollisionTestCommandlet()
|
||
|
|
{
|
||
|
|
IsClient = false;
|
||
|
|
IsServer = false;
|
||
|
|
IsEditor = true;
|
||
|
|
LogToConsole = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32 ULPPairCollisionTestCommandlet::Main(const FString& Params)
|
||
|
|
{
|
||
|
|
TArray<FString> Tokens, Switches;
|
||
|
|
TMap<FString, FString> ParamsMap;
|
||
|
|
ParseCommandLine(*Params, Tokens, Switches, ParamsMap);
|
||
|
|
|
||
|
|
const FString WorldFile = ParamsMap.FindRef(TEXT("World")).IsEmpty()
|
||
|
|
? FPaths::Combine(FPaths::ProjectDir(), TEXT("Content/Modules/vin_Base/World/work___vin01.lpworld"))
|
||
|
|
: ParamsMap.FindRef(TEXT("World"));
|
||
|
|
const FString OutPath = ParamsMap.FindRef(TEXT("Out")).IsEmpty()
|
||
|
|
? FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Logs/portage/LPPairCollisionTest.txt"))
|
||
|
|
: ParamsMap.FindRef(TEXT("Out"));
|
||
|
|
|
||
|
|
FVector RoomScale = FVector::OneVector;
|
||
|
|
TArray<FProp> Props;
|
||
|
|
if (!LoadWorldFile(WorldFile, RoomScale, Props))
|
||
|
|
{
|
||
|
|
UE_LOG(LogLPPairCollision, Error, TEXT("Chambre illisible : %s"), *WorldFile);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
FAssetRegistryModule& Assets = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
||
|
|
Assets.Get().SearchAllAssets(true);
|
||
|
|
TArray<FAssetData> MeshAssets;
|
||
|
|
Assets.Get().GetAssetsByClass(UStaticMesh::StaticClass()->GetClassPathName(), MeshAssets, true);
|
||
|
|
TMap<FString, FString> MeshPath;
|
||
|
|
for (const FAssetData& Data : MeshAssets)
|
||
|
|
{
|
||
|
|
const FString Name = Data.AssetName.ToString();
|
||
|
|
const FString Path = FString::Printf(TEXT("%s.%s"), *Data.PackageName.ToString(), *Name);
|
||
|
|
const bool bFurniture = Path.Contains(TEXT("/Furniture/")) || Path.Contains(TEXT("/Heavy/"));
|
||
|
|
if (!MeshPath.Contains(Name) || bFurniture)
|
||
|
|
{
|
||
|
|
MeshPath.Add(Name, Path);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!GEngine)
|
||
|
|
{
|
||
|
|
UE_LOG(LogLPPairCollision, Error, TEXT("Pas de moteur, scene d'essai impossible."));
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
FPreviewScene::ConstructionValues SceneValues;
|
||
|
|
SceneValues.SetCreateDefaultLighting(false).SetEditor(false).SetTransactional(false).SetForceMipsResident(false);
|
||
|
|
FPreviewScene Scene(SceneValues);
|
||
|
|
UWorld* World = Scene.GetWorld();
|
||
|
|
|
||
|
|
FActorSpawnParameters Spawn;
|
||
|
|
Spawn.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||
|
|
Spawn.ObjectFlags |= RF_Transient;
|
||
|
|
|
||
|
|
AActor* Room = World->SpawnActor<AActor>(AActor::StaticClass(), FTransform::Identity, Spawn);
|
||
|
|
USceneComponent* Root = NewObject<USceneComponent>(Room, TEXT("RoomRoot"));
|
||
|
|
Room->SetRootComponent(Root);
|
||
|
|
Root->RegisterComponent();
|
||
|
|
Root->SetWorldScale3D(RoomScale);
|
||
|
|
|
||
|
|
TArray<UStaticMeshComponent*> Furniture;
|
||
|
|
UStaticMeshComponent* Wall = nullptr;
|
||
|
|
double WallArea = 0.0;
|
||
|
|
int32 Missing = 0;
|
||
|
|
int32 WithoutBody = 0;
|
||
|
|
for (const FProp& Prop : Props)
|
||
|
|
{
|
||
|
|
const FString* Path = MeshPath.Find(Prop.Name);
|
||
|
|
UStaticMesh* Mesh = Path ? LoadObject<UStaticMesh>(nullptr, **Path) : nullptr;
|
||
|
|
if (!Mesh)
|
||
|
|
{
|
||
|
|
++Missing;
|
||
|
|
UE_LOG(LogLPPairCollision, Warning, TEXT("Maillage absent : %s"), *Prop.Name);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
UStaticMeshComponent* Comp = NewObject<UStaticMeshComponent>(Room);
|
||
|
|
Comp->SetStaticMesh(Mesh);
|
||
|
|
Comp->SetupAttachment(Root);
|
||
|
|
Comp->SetRelativeLocation(Prop.Location);
|
||
|
|
Comp->SetRelativeRotation(Prop.Rotation);
|
||
|
|
Comp->SetRelativeScale3D(Prop.Scale);
|
||
|
|
Comp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
||
|
|
Comp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore);
|
||
|
|
Comp->RegisterComponent();
|
||
|
|
Furniture.Add(Comp);
|
||
|
|
if (BodyElems(Comp) == 0)
|
||
|
|
{
|
||
|
|
++WithoutBody;
|
||
|
|
}
|
||
|
|
const FVector Extent = Comp->Bounds.GetBox().GetExtent();
|
||
|
|
const double Area = double(Extent.X) * double(Extent.Y);
|
||
|
|
if ((Prop.Name.StartsWith(TEXT("fnt_new14")) || Prop.Name.StartsWith(TEXT("fnt_new18"))) && Area > WallArea)
|
||
|
|
{
|
||
|
|
Wall = Comp;
|
||
|
|
WallArea = Area;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!Wall && Furniture.Num() > 0)
|
||
|
|
{
|
||
|
|
Wall = Furniture[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
UStaticMesh* CubeMesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Engine/BasicShapes/Cube.Cube"));
|
||
|
|
UStaticMeshComponent* Cube = nullptr;
|
||
|
|
if (CubeMesh)
|
||
|
|
{
|
||
|
|
AActor* CubeActor = World->SpawnActor<AActor>(AActor::StaticClass(), FTransform::Identity, Spawn);
|
||
|
|
Cube = NewObject<UStaticMeshComponent>(CubeActor, TEXT("Cube"));
|
||
|
|
CubeActor->SetRootComponent(Cube);
|
||
|
|
Cube->SetStaticMesh(CubeMesh);
|
||
|
|
Cube->SetRelativeLocation(FVector(3000.f, 0.f, 100.f));
|
||
|
|
Cube->SetRelativeScale3D(FVector(1.5f));
|
||
|
|
Cube->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
||
|
|
Cube->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore);
|
||
|
|
Cube->RegisterComponent();
|
||
|
|
Furniture.Add(Cube);
|
||
|
|
}
|
||
|
|
|
||
|
|
UClass* PersonClass = LoadObject<UClass>(nullptr, TEXT("/Game/BPs/Person.Person_C"));
|
||
|
|
const ACharacter* PersonDefaults = PersonClass ? Cast<ACharacter>(PersonClass->GetDefaultObject()) : nullptr;
|
||
|
|
const UCapsuleComponent* PersonCap = PersonDefaults ? PersonDefaults->GetCapsuleComponent() : nullptr;
|
||
|
|
|
||
|
|
auto MakeBody = [&](const FVector& At) -> ACharacter*
|
||
|
|
{
|
||
|
|
ACharacter* Who = World->SpawnActor<ACharacter>(ACharacter::StaticClass(), At, FRotator::ZeroRotator, Spawn);
|
||
|
|
if (!Who)
|
||
|
|
{
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
UCapsuleComponent* Cap = Who->GetCapsuleComponent();
|
||
|
|
if (PersonCap)
|
||
|
|
{
|
||
|
|
Cap->SetCapsuleSize(PersonCap->GetUnscaledCapsuleRadius(), PersonCap->GetUnscaledCapsuleHalfHeight());
|
||
|
|
Cap->SetCollisionProfileName(PersonCap->GetCollisionProfileName());
|
||
|
|
Cap->SetCollisionObjectType(PersonCap->GetCollisionObjectType());
|
||
|
|
Cap->SetCollisionResponseToChannels(PersonCap->GetCollisionResponseToChannels());
|
||
|
|
}
|
||
|
|
Cap->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
||
|
|
Cap->RecreatePhysicsState();
|
||
|
|
return Who;
|
||
|
|
};
|
||
|
|
|
||
|
|
auto ResponseText = [](const UCapsuleComponent* Cap, const ECollisionChannel Channel) -> const TCHAR*
|
||
|
|
{
|
||
|
|
switch (Cap->GetCollisionResponseToChannel(Channel))
|
||
|
|
{
|
||
|
|
case ECR_Ignore: return TEXT("Ignore");
|
||
|
|
case ECR_Overlap: return TEXT("Overlap");
|
||
|
|
case ECR_Block: return TEXT("Block");
|
||
|
|
default: return TEXT("?");
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const FVector Park(0.f, 0.f, 8000.f);
|
||
|
|
ACharacter* A = MakeBody(Park);
|
||
|
|
ACharacter* B = MakeBody(Park + FVector(200.f, 0.f, 0.f));
|
||
|
|
if (!A || !B || !Cube || !Wall)
|
||
|
|
{
|
||
|
|
UE_LOG(LogLPPairCollision, Error, TEXT("Scene incomplete (personnes %d, cube %d, mur %d, meubles %d)."),
|
||
|
|
(A ? 1 : 0) + (B ? 1 : 0), Cube ? 1 : 0, Wall ? 1 : 0, Furniture.Num());
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
const float Radius = A->GetCapsuleComponent()->GetScaledCapsuleRadius();
|
||
|
|
const float Half = A->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
|
||
|
|
FVector SpotA, SpotB;
|
||
|
|
const bool bInside = FindPairSpot(World, Radius, Half, SpotA, SpotB);
|
||
|
|
if (!bInside)
|
||
|
|
{
|
||
|
|
SpotA = FVector(0.f, 0.f, 4000.f);
|
||
|
|
SpotB = SpotA + FVector(Radius * 2.f + 40.f, 0.f, 0.f);
|
||
|
|
}
|
||
|
|
|
||
|
|
const FBox WallBox = Wall->Bounds.GetBox();
|
||
|
|
const FVector WallCenter = WallBox.GetCenter();
|
||
|
|
FVector WallStart(WallBox.Max.X + Radius + 30.f, WallCenter.Y, FMath::Clamp(Half + 20.f, WallBox.Min.Z, WallBox.Max.Z));
|
||
|
|
const FVector WallDelta = FVector(WallCenter.X, WallCenter.Y, WallStart.Z) - WallStart;
|
||
|
|
|
||
|
|
const FBox CubeBox = Cube->Bounds.GetBox();
|
||
|
|
const FVector CubeStart(CubeBox.Max.X + Radius + 30.f, CubeBox.GetCenter().Y, CubeBox.GetCenter().Z);
|
||
|
|
const FVector CubeDelta = FVector(CubeBox.GetCenter().X, CubeStart.Y, CubeStart.Z) - CubeStart;
|
||
|
|
|
||
|
|
const FVector TowardB = (SpotB - SpotA).GetSafeNormal() * 80.f;
|
||
|
|
|
||
|
|
struct FCase
|
||
|
|
{
|
||
|
|
const TCHAR* Name;
|
||
|
|
bool bBlockPawn;
|
||
|
|
bool bCapsulesOn;
|
||
|
|
bool bIgnore;
|
||
|
|
bool bExpectOther;
|
||
|
|
bool bExpectSolid;
|
||
|
|
};
|
||
|
|
const FCase Cases[] = {
|
||
|
|
{ TEXT("Bureau actuel, capsules allumees"), false, true, false, true, false },
|
||
|
|
{ TEXT("Scene actuelle, capsules coupees"), false, false, false, false, false },
|
||
|
|
{ TEXT("Meubles bloquent, capsules encore coupees"), true, false, false, false, false },
|
||
|
|
{ TEXT("Principes : capsules allumees, ignore mutuel, meubles bloquent"), true, true, true, false, true },
|
||
|
|
{ TEXT("Meubles bloquent, sans ignorer l'autre"), true, true, false, true, true },
|
||
|
|
};
|
||
|
|
|
||
|
|
TArray<FString> Report;
|
||
|
|
Report.Add(FString::Printf(TEXT("Chambre : %s"), *WorldFile));
|
||
|
|
Report.Add(FString::Printf(TEXT("Echelle racine : %s"), *RoomScale.ToString()));
|
||
|
|
Report.Add(FString::Printf(TEXT("Meubles poses : %d, absents : %d, sans volume simple : %d"), Furniture.Num() - (Cube ? 1 : 0), Missing, WithoutBody));
|
||
|
|
if (Furniture.Num() > (Cube ? 1 : 0))
|
||
|
|
{
|
||
|
|
UStaticMeshComponent* Sample = Furniture[0];
|
||
|
|
Report.Add(FString::Printf(TEXT("Exemple %s : position monde %s, echelle monde %s, volumes %d"),
|
||
|
|
*Sample->GetStaticMesh()->GetName(), *Sample->GetComponentLocation().ToString(),
|
||
|
|
*Sample->GetComponentScale().ToString(), BodyElems(Sample)));
|
||
|
|
}
|
||
|
|
Report.Add(FString::Printf(TEXT("Mur d'essai : %s, boite %s"),
|
||
|
|
Wall->GetStaticMesh() ? *Wall->GetStaticMesh()->GetName() : TEXT("?"), *WallBox.ToString()));
|
||
|
|
Report.Add(FString::Printf(TEXT("Personnes : capsules moteur, rayon %.1f, demi-hauteur %.1f, profil %s, type %d, corps physique %s. CDO Person : %s"),
|
||
|
|
Radius, Half, *A->GetCapsuleComponent()->GetCollisionProfileName().ToString(),
|
||
|
|
int32(A->GetCapsuleComponent()->GetCollisionObjectType()),
|
||
|
|
A->GetCapsuleComponent()->IsPhysicsStateCreated() ? TEXT("oui") : TEXT("non"),
|
||
|
|
PersonCap ? TEXT("lu") : TEXT("introuvable, valeurs moteur")));
|
||
|
|
Report.Add(FString::Printf(TEXT("Reponses de la capsule Person : Pawn=%s WorldStatic=%s WorldDynamic=%s"),
|
||
|
|
ResponseText(A->GetCapsuleComponent(), ECC_Pawn),
|
||
|
|
ResponseText(A->GetCapsuleComponent(), ECC_WorldStatic),
|
||
|
|
ResponseText(A->GetCapsuleComponent(), ECC_WorldDynamic)));
|
||
|
|
const bool bPersonBlocksPawn = A->GetCapsuleComponent()->GetCollisionResponseToChannel(ECC_Pawn) == ECR_Block;
|
||
|
|
Report.Add(bPersonBlocksPawn
|
||
|
|
? TEXT("La capsule Person bloque le canal Pawn.")
|
||
|
|
: TEXT("La capsule Person ne bloque pas le canal Pawn : deux Person se traversent deja."));
|
||
|
|
Report.Add(FString::Printf(TEXT("Emplacement du couple : %s -> %s (%s)"),
|
||
|
|
*SpotA.ToString(), *SpotB.ToString(), bInside ? TEXT("dans la chambre") : TEXT("au-dessus, chambre trop occupee")));
|
||
|
|
Report.Add(TEXT("Deplacement = balayage de la capsule (le meme que la marche). L'animation n'est pas jouee."));
|
||
|
|
Report.Add(TEXT(""));
|
||
|
|
|
||
|
|
auto ForcePawnProfile = [](ACharacter* Who)
|
||
|
|
{
|
||
|
|
UCapsuleComponent* Cap = Who->GetCapsuleComponent();
|
||
|
|
Cap->SetCollisionProfileName(TEXT("Pawn"));
|
||
|
|
Cap->SetCollisionObjectType(ECC_Pawn);
|
||
|
|
Cap->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
||
|
|
Cap->RecreatePhysicsState();
|
||
|
|
};
|
||
|
|
|
||
|
|
bool bPrinciples = true;
|
||
|
|
bool bRoom = true;
|
||
|
|
bool bForced = true;
|
||
|
|
auto RunCases = [&](const TCHAR* Series, const bool bForcePawn)
|
||
|
|
{
|
||
|
|
if (bForcePawn)
|
||
|
|
{
|
||
|
|
ForcePawnProfile(A);
|
||
|
|
ForcePawnProfile(B);
|
||
|
|
Report.Add(TEXT(""));
|
||
|
|
Report.Add(TEXT("--- Profil Pawn du moteur (les deux capsules se bloquent, pour isoler IgnoreActorWhenMoving) ---"));
|
||
|
|
}
|
||
|
|
for (const FCase& Case : Cases)
|
||
|
|
{
|
||
|
|
SetPawnResponse(Furniture, Case.bBlockPawn ? ECR_Block : ECR_Ignore);
|
||
|
|
ApplyBodies(A, B, Case.bCapsulesOn, Case.bIgnore);
|
||
|
|
if (bForcePawn && Case.bCapsulesOn)
|
||
|
|
{
|
||
|
|
ForcePawnProfile(A);
|
||
|
|
ForcePawnProfile(B);
|
||
|
|
if (Case.bIgnore)
|
||
|
|
{
|
||
|
|
A->GetCapsuleComponent()->IgnoreActorWhenMoving(B, true);
|
||
|
|
B->GetCapsuleComponent()->IgnoreActorWhenMoving(A, true);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
B->SetActorLocation(SpotB, false, nullptr, ETeleportType::TeleportPhysics);
|
||
|
|
const FProbe Other = Probe(A, SpotA, TowardB);
|
||
|
|
B->SetActorLocation(Park, false, nullptr, ETeleportType::TeleportPhysics);
|
||
|
|
const FProbe Solid = Probe(A, CubeStart, CubeDelta);
|
||
|
|
const FProbe RoomHit = Probe(A, WallStart, WallDelta);
|
||
|
|
|
||
|
|
const bool bExpectOther = (bForcePawn || bPersonBlocksPawn) ? Case.bExpectOther : false;
|
||
|
|
const bool bOtherOk = Other.bBlocked == bExpectOther && !Other.bStartInside;
|
||
|
|
const bool bWallOk = RoomHit.bBlocked == Case.bExpectSolid && !RoomHit.bStartInside;
|
||
|
|
if (!bForcePawn)
|
||
|
|
{
|
||
|
|
bPrinciples &= bOtherOk;
|
||
|
|
bRoom &= bWallOk;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
bForced &= bOtherOk && bWallOk;
|
||
|
|
}
|
||
|
|
|
||
|
|
const FString Line = FString::Printf(
|
||
|
|
TEXT("%s | %s | autre:%s (attendu %s, %.0f cm, %s) | cube:%s (attendu %s, %.0f cm, %s) | meuble:%s (attendu %s, %.0f cm, %s) | %s"),
|
||
|
|
Series, Case.Name,
|
||
|
|
Other.bBlocked ? TEXT("bloque") : TEXT("passe"), bExpectOther ? TEXT("bloque") : TEXT("passe"), Other.Moved, *Other.Hit,
|
||
|
|
Solid.bBlocked ? TEXT("bloque") : TEXT("passe"), Case.bExpectSolid ? TEXT("bloque") : TEXT("passe"), Solid.Moved, *Solid.Hit,
|
||
|
|
RoomHit.bBlocked ? TEXT("bloque") : TEXT("passe"), Case.bExpectSolid ? TEXT("bloque") : TEXT("passe"), RoomHit.Moved, *RoomHit.Hit,
|
||
|
|
(bOtherOk && bWallOk) ? TEXT("OK") : TEXT("ECART"));
|
||
|
|
Report.Add(Line);
|
||
|
|
UE_LOG(LogLPPairCollision, Warning, TEXT("%s"), *Line);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
RunCases(TEXT("Person"), false);
|
||
|
|
RunCases(TEXT("Pawn"), true);
|
||
|
|
|
||
|
|
Report.Add(TEXT(""));
|
||
|
|
Report.Add(FString::Printf(TEXT("Profil Person : %s"), bPrinciples ? TEXT("TIENT") : TEXT("NE TIENT PAS")));
|
||
|
|
Report.Add(FString::Printf(TEXT("Profil Pawn force, ignore mutuel : %s"), bForced ? TEXT("TIENT") : TEXT("NE TIENT PAS")));
|
||
|
|
Report.Add(FString::Printf(TEXT("Meubles du bureau : %s"), bRoom ? TEXT("TIENT") : TEXT("NE TIENT PAS")));
|
||
|
|
Report.Add(TEXT("Le cube du moteur est mesure a part. Le verdict porte sur les meubles de la chambre et sur les deux capsules."));
|
||
|
|
UE_LOG(LogLPPairCollision, Warning, TEXT("Person : %s. Pawn force : %s. Meubles : %s."),
|
||
|
|
bPrinciples ? TEXT("TIENT") : TEXT("NE TIENT PAS"),
|
||
|
|
bForced ? TEXT("TIENT") : TEXT("NE TIENT PAS"),
|
||
|
|
bRoom ? TEXT("TIENT") : TEXT("NE TIENT PAS"));
|
||
|
|
|
||
|
|
FFileHelper::SaveStringArrayToFile(Report, *OutPath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM);
|
||
|
|
UE_LOG(LogLPPairCollision, Warning, TEXT("Rapport : %s"), *OutPath);
|
||
|
|
return (bPrinciples && bRoom && bForced) ? 0 : 2;
|
||
|
|
}
|