You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Merging conflict by hand.
/src/ROBOMERGE_ENGINE_Dev_Networking/Engine/Plugins/Experimental/GeometryCollectionPlugin/Source/GeometryCollectionEditor/GeometryCollectionEditor.Build.cs - merging //UE4/Main/Engine/Plugins/Experimental/GeometryCollectionPlugin/Source/GeometryCollectionEditor/GeometryCollectionEditor.Build.cs#1 /src/ROBOMERGE_ENGINE_Dev_Networking/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/PBDCollisionConstraint.cpp - merging //UE4/Main/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/PBDCollisionConstraint.cpp#1 /src/ROBOMERGE_ENGINE_Dev_Networking/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/PBDRigidsEvolution.cpp - merging //UE4/Main/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/PBDRigidsEvolution.cpp#1 -------------------------------------- Copying //UE4/Dev-Physics to //UE4/Dev-Main. #rb none #lockdown Nick.Penwarden #ROBOMERGE-OWNER: ryan.gerleve #ROBOMERGE-AUTHOR: michael.lentine #ROBOMERGE-SOURCE: CL 4653110 in //UE4/Main/... #ROBOMERGE-BOT: ENGINE (Main -> Dev-Networking) [CL 4653305 by ryan gerleve in Dev-Networking branch]
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
/*=============================================================================
|
||||
GeometryCollectionActor.cpp: AGeometryCollectionActor methods.
|
||||
=============================================================================*/
|
||||
|
||||
#if INCLUDE_CHAOS
|
||||
|
||||
#include "GeometryCollection/GeometryCollectionActor.h"
|
||||
|
||||
#include "Chaos/Utilities.h"
|
||||
#include "Chaos/Plane.h"
|
||||
#include "Chaos/Box.h"
|
||||
#include "Chaos/Sphere.h"
|
||||
#include "Chaos/PerParticleGravity.h"
|
||||
#include "Chaos/ImplicitObject.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "GeometryCollection/GeometryCollectionAlgo.h"
|
||||
#include "GeometryCollection/GeometryCollectionComponent.h"
|
||||
#include "GeometryCollection/GeometryCollectionUtility.h"
|
||||
#include "GeometryCollection/GeometryCollectionBoneNode.h"
|
||||
#include "Math/Box.h"
|
||||
#include "Physics/PhysicsInterfaceCore.h"
|
||||
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(AGeometryCollectionActorLogging, Log, All);
|
||||
|
||||
AGeometryCollectionActor::AGeometryCollectionActor(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
UE_LOG(AGeometryCollectionActorLogging, Verbose, TEXT("AGeometryCollectionActor::AGeometryCollectionActor()"));
|
||||
|
||||
GeometryCollectionComponent = CreateDefaultSubobject<UGeometryCollectionComponent>(TEXT("GeometryCollectionComponent0"));
|
||||
RootComponent = GeometryCollectionComponent;
|
||||
|
||||
#if GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
GeometryCollectionDebugDrawComponent = CreateDefaultSubobject<UGeometryCollectionDebugDrawComponent>(TEXT("GeometryCollectionDrawComponent0"));
|
||||
GeometryCollectionDebugDrawComponent->GeometryCollectionComponent = GeometryCollectionComponent;
|
||||
#else
|
||||
GeometryCollectionDebugDrawComponent = nullptr;
|
||||
#endif
|
||||
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
SetActorTickEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
void AGeometryCollectionActor::Tick(float DeltaTime)
|
||||
{
|
||||
UE_LOG(AGeometryCollectionActorLogging, Verbose, TEXT("AGeometryCollectionActor::Tick()"));
|
||||
if (GeometryCollectionComponent)
|
||||
{
|
||||
GeometryCollectionComponent->SetRenderStateDirty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Chaos::PBDRigidsSolver* GetSolver(const AGeometryCollectionActor& GeomCollectionActor)
|
||||
{
|
||||
return GeomCollectionActor.GetGeometryCollectionComponent()->ChaosSolverActor != nullptr ? GeomCollectionActor.GetGeometryCollectionComponent()->ChaosSolverActor->GetSolver() : FPhysScene_Chaos::GetInstance()->GetSolver();
|
||||
}
|
||||
|
||||
|
||||
bool LowLevelRaycastImp(const Chaos::TVector<float, 3>& Start, const Chaos::TVector<float, 3>& Dir, float DeltaMag, const AGeometryCollectionActor& GeomCollectionActor, FHitResult& OutHit)
|
||||
{
|
||||
using namespace Chaos;
|
||||
//todo(ocohen): need to add thread safety / lock semantics
|
||||
const TManagedArray<int32>& RigidBodyIdArray = GeomCollectionActor.GetGeometryCollectionComponent()->GetRigidBodyIdArray();
|
||||
const TSharedPtr<FPhysScene_Chaos> Scene = GeomCollectionActor.GetGeometryCollectionComponent()->GetPhysicsScene();
|
||||
ensure(Scene);
|
||||
|
||||
const Chaos::PBDRigidsSolver* Solver = GetSolver(GeomCollectionActor);
|
||||
ensure(Solver);
|
||||
|
||||
const TPBDRigidParticles<float, 3>& Particles = Solver->GetRigidParticles(); //todo(ocohen): should these just get passed in instead of hopping through scene?
|
||||
|
||||
for (int32 Idx = 0; Idx < RigidBodyIdArray.Num(); ++Idx)
|
||||
{
|
||||
const int32 RigidBodyIdx = RigidBodyIdArray[Idx];
|
||||
const TRigidTransform<float, 3> TM(Particles.X(RigidBodyIdx), Particles.R(RigidBodyIdx));
|
||||
const TVector<float, 3> StartLocal = TM.InverseTransformPositionNoScale(Start);
|
||||
const TVector<float, 3> DirLocal = TM.InverseTransformVectorNoScale(Dir);
|
||||
const TVector<float, 3> EndLocal = StartLocal + DirLocal * DeltaMag; //todo(ocohen): apeiron just undoes this later, we should fix the API
|
||||
|
||||
const TImplicitObject<float, 3>* Object = Particles.Geometry(RigidBodyIdx); //todo(ocohen): can this ever be null?
|
||||
Pair<TVector<float, 3>, bool> Result = Object->FindClosestIntersection(StartLocal, EndLocal, /*Thickness=*/0.f);
|
||||
if (Result.Second) //todo(ocohen): once we do more than just a bool we need to get the closest point
|
||||
{
|
||||
const float Distance = (Result.First - StartLocal).Size();
|
||||
OutHit.Actor = const_cast<AGeometryCollectionActor*>(&GeomCollectionActor);
|
||||
OutHit.Component = GeomCollectionActor.GetGeometryCollectionComponent();
|
||||
OutHit.bBlockingHit = true;
|
||||
OutHit.Distance = Distance;
|
||||
OutHit.Time = Distance / (EndLocal - StartLocal).Size();
|
||||
OutHit.Location = TM.TransformPositionNoScale(Result.First);
|
||||
OutHit.ImpactPoint = OutHit.Location;
|
||||
const TVector<float, 3> LocalNormal = Object->Normal(Result.First);
|
||||
OutHit.ImpactNormal = TM.TransformVectorNoScale(LocalNormal);
|
||||
OutHit.Normal = OutHit.ImpactNormal;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AGeometryCollectionActor::RaycastSingle(FVector Start, FVector End, FHitResult& OutHit) const
|
||||
{
|
||||
if (GeometryCollectionComponent)
|
||||
{
|
||||
OutHit = FHitResult();
|
||||
OutHit.TraceStart = Start;
|
||||
OutHit.TraceEnd = End;
|
||||
const FVector Delta = (End - Start);
|
||||
const float DeltaMag = Delta.Size();
|
||||
if (DeltaMag > KINDA_SMALL_NUMBER)
|
||||
{
|
||||
const FVector Dir = Delta / DeltaMag;
|
||||
return LowLevelRaycastImp(Start, Dir, DeltaMag, *this, OutHit);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,217 @@
|
||||
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
/*=============================================================================
|
||||
GeometryCollectionActor.cpp: AGeometryCollectionActor methods.
|
||||
=============================================================================*/
|
||||
|
||||
#include "GeometryCollection/GeometryCollectionActor.h"
|
||||
|
||||
#if !INCLUDE_CHAOS
|
||||
|
||||
#include "GeometryCollection/GeometryCollectionAlgo.h"
|
||||
#include "GeometryCollection/GeometryCollectionComponent.h"
|
||||
#include "GeometryCollection/GeometryCollectionUtility.h"
|
||||
#include "GeometryCollection/GeometryCollectionBoneNode.h"
|
||||
#include "GeometryCollection/GeometryCollectionObject.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "Math/Box.h"
|
||||
|
||||
#include "Physics/PhysicsInterfaceCore.h"
|
||||
#include "Physics/ImmediatePhysics/ImmediatePhysicsActorHandle.h"
|
||||
#include "Chaos/Utilities.h"
|
||||
#include "Chaos/Plane.h"
|
||||
#include "Chaos/Box.h"
|
||||
#include "Chaos/Sphere.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(AGeometryCollectionActorLogging, Log, All);
|
||||
|
||||
FTransform TransformMatrix(const FTransform& A, const FTransform& B) { return B * A; }
|
||||
|
||||
AGeometryCollectionActor::AGeometryCollectionActor(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, bInitializedState(false)
|
||||
, RigidBodyIdArray(new TManagedArray<int32>())
|
||||
, CenterOfMassArray(new TManagedArray<FVector>())
|
||||
{
|
||||
GeometryCollectionComponent = CreateDefaultSubobject<UGeometryCollectionComponent>(TEXT("GeometryCollectionComponent0"));
|
||||
RootComponent = GeometryCollectionComponent;
|
||||
AGeometryCollectionActor::bInitializedState = false;
|
||||
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
SetActorTickEnabled(true);
|
||||
}
|
||||
|
||||
void AGeometryCollectionActor::Tick(float DeltaTime)
|
||||
{
|
||||
UE_LOG(AGeometryCollectionActorLogging, Verbose, TEXT("AGeometryCollectionActor::Tick()"));
|
||||
|
||||
UGeometryCollection* Collection = GeometryCollectionComponent->GetDynamicCollection();
|
||||
if (Collection && !AGeometryCollectionActor::bInitializedState)
|
||||
{
|
||||
Collection->GetGeometryCollection()->AddAttribute<int32>("RigidBodyID", FGeometryCollection::TransformGroup, RigidBodyIdArray);
|
||||
Collection->GetGeometryCollection()->AddAttribute<FVector>("CenterOfMass", FGeometryCollection::TransformGroup, CenterOfMassArray);
|
||||
|
||||
Scene.SetKinematicUpdateFunction([this](FSolverCallbacks::FParticlesType& Particles, const float Dt, const float Time, const int32 Index) {
|
||||
|
||||
});
|
||||
|
||||
Scene.SetStartFrameFunction([this](const float StartFrame) {
|
||||
StartFrameCallback(StartFrame);
|
||||
});
|
||||
|
||||
Scene.SetEndFrameFunction([this](const float EndFrame) {
|
||||
EndFrameCallback(EndFrame);
|
||||
});
|
||||
|
||||
Scene.SetCreateBodiesFunction([this](FSolverCallbacks::FParticlesType& Particles) {
|
||||
CreateRigidBodyCallback(Particles);
|
||||
});
|
||||
|
||||
Scene.SetParameterUpdateFunction([this](FSolverCallbacks::FParticlesType& Particles, const float, const int32 Index) {
|
||||
|
||||
});
|
||||
|
||||
Scene.SetDisableCollisionsUpdateFunction([this](TSet<TTuple<int32, int32>>&) {
|
||||
});
|
||||
|
||||
Scene.AddPBDConstraintFunction([this](FSolverCallbacks::FParticlesType&, const float) {
|
||||
});
|
||||
|
||||
Scene.AddForceFunction([this](FSolverCallbacks::FParticlesType& Particles, const float, const int32 Index) {
|
||||
Particles[Index]->AddForce(FVector(0, 0, -980.f));
|
||||
});
|
||||
|
||||
bInitializedState = true;
|
||||
|
||||
Scene.Init();
|
||||
#if INCLUDE_CHAOS
|
||||
UGeometryCollection* RestCollection = const_cast<UGeometryCollection*>(GeometryCollectionComponent->GetRestCollection());
|
||||
if (GeometryCollectionComponent->bClearCache)
|
||||
{
|
||||
RestCollection->RecordedTracks.Records.Reset();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int32 NumTimeSteps = 1.f;
|
||||
float dt = GWorld->DeltaTimeSeconds / (float)NumTimeSteps;
|
||||
for (int i = 0; i < NumTimeSteps; i++)
|
||||
{
|
||||
Scene.Tick(dt);
|
||||
}
|
||||
}
|
||||
|
||||
void AGeometryCollectionActor::StartFrameCallback(float EndFrame)
|
||||
{
|
||||
UE_LOG(AGeometryCollectionActorLogging, Verbose, TEXT("AGeometryCollectionActor::StartFrameCallback()"));
|
||||
UGeometryCollection* Collection = GeometryCollectionComponent->GetDynamicCollection();
|
||||
if (!Scene.GetSimulation()->NumActors() && Collection->GetGeometryCollection()->HasAttribute("RigidBodyID", FGeometryCollection::TransformGroup))
|
||||
{
|
||||
TManagedArray<int32> & RigidBodyId = *RigidBodyIdArray;
|
||||
TManagedArray<FVector> & CenterOfMass = *CenterOfMassArray;
|
||||
|
||||
TManagedArray<FTransform> & Transform = *Collection->GetGeometryCollection()->Transform;
|
||||
TManagedArray<int32> & BoneMap = *Collection->GetGeometryCollection()->BoneMap;
|
||||
TManagedArray<FVector> & Vertex = *Collection->GetGeometryCollection()->Vertex;
|
||||
|
||||
PxMaterial* NewMaterial = GPhysXSDK->createMaterial(0, 0, 0);
|
||||
|
||||
// floor
|
||||
FTransform FloorTransform;
|
||||
PxRigidStatic* FloorActor = GPhysXSDK->createRigidStatic(U2PTransform(FTransform::Identity));
|
||||
PxShape* FloorShape = PxRigidActorExt::createExclusiveShape(*FloorActor, PxBoxGeometry(U2PVector(FVector(10000.f, 10000.f, 10.f))), *NewMaterial);
|
||||
// This breaks threading correctness in a general sense but is needed until we can call this in create rigid bodies
|
||||
const_cast<ImmediatePhysics::FSimulation*>(Scene.GetSimulation())->CreateStaticActor(FloorActor, FloorTransform);
|
||||
|
||||
FVector Scale = GeometryCollectionComponent->GetComponentTransform().GetScale3D();
|
||||
|
||||
TArray<FBox> Bounds;
|
||||
Bounds.AddZeroed(Collection->GetGeometryCollection()->NumElements(FGeometryCollection::TransformGroup));
|
||||
|
||||
TArray<int32> SurfaceParticlesCount;
|
||||
SurfaceParticlesCount.AddZeroed(Collection->GetGeometryCollection()->NumElements(FGeometryCollection::TransformGroup));
|
||||
|
||||
TArray<FVector> SumOfMass;
|
||||
SumOfMass.AddZeroed(Collection->GetGeometryCollection()->NumElements(FGeometryCollection::TransformGroup));
|
||||
|
||||
for (int i = 0; i < Vertex.Num(); i++)
|
||||
{
|
||||
FVector ScaledVertex = Scale * Vertex[i];
|
||||
int32 ParticleIndex = BoneMap[i];
|
||||
Bounds[ParticleIndex] += ScaledVertex;
|
||||
SurfaceParticlesCount[ParticleIndex]++;
|
||||
SumOfMass[ParticleIndex] += ScaledVertex;
|
||||
}
|
||||
|
||||
|
||||
for (int32 i = 0; i < Collection->GetGeometryCollection()->Transform->Num(); ++i)
|
||||
{
|
||||
if (SurfaceParticlesCount[i] && 0.f < Bounds[i].GetSize().SizeSquared())
|
||||
{
|
||||
CenterOfMass[i] = SumOfMass[i] / SurfaceParticlesCount[i];
|
||||
Bounds[i] = Bounds[i].InverseTransformBy(FTransform(CenterOfMass[i]));
|
||||
|
||||
RigidBodyId[i] = i;
|
||||
int32 RigidBodyIndex = RigidBodyId[i];
|
||||
|
||||
FTransform NewTransform = TransformMatrix(GeometryCollectionComponent->GetComponentTransform(), Transform[i]);
|
||||
float SideSquared = Bounds[i].GetSize()[0] * Bounds[i].GetSize()[0] / 6.f;
|
||||
|
||||
PxRigidDynamic* NewActor = GPhysXSDK->createRigidDynamic(U2PTransform(FTransform::Identity));
|
||||
NewActor->setLinearVelocity(U2PVector(FVector(0.f, 0.f, 0.f)));
|
||||
NewActor->setAngularVelocity(U2PVector(FVector(0.f, 0.f, 0.f)));
|
||||
NewActor->setMass(1.f);
|
||||
NewActor->setMassSpaceInertiaTensor(U2PVector(FVector(SideSquared, SideSquared, SideSquared)));
|
||||
PxShape* NewShape = PxRigidActorExt::createExclusiveShape(*NewActor, PxBoxGeometry(U2PVector((Bounds[i].Max - Bounds[i].Min) / 2.f)), *NewMaterial);
|
||||
const_cast<ImmediatePhysics::FSimulation*>(Scene.GetSimulation())->CreateDynamicActor(NewActor, NewTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AGeometryCollectionActor::CreateRigidBodyCallback(FSolverCallbacks::FParticlesType& Particles)
|
||||
{
|
||||
}
|
||||
|
||||
void AGeometryCollectionActor::EndFrameCallback(float EndFrame)
|
||||
{
|
||||
UE_LOG(AGeometryCollectionActorLogging, Log, TEXT("AGeometryCollectionActor::EndFrameFunction()"));
|
||||
UGeometryCollection* Collection = GeometryCollectionComponent->GetDynamicCollection();
|
||||
if (Collection->GetGeometryCollection()->HasAttribute("RigidBodyID", FGeometryCollection::TransformGroup))
|
||||
{
|
||||
TManagedArray<int32> & RigidBodyId = *RigidBodyIdArray;
|
||||
TManagedArray<FVector> & CenterOfMass = *CenterOfMassArray;
|
||||
TManagedArray<FTransform> & Transform = *Collection->GetGeometryCollection()->Transform;
|
||||
|
||||
const TArray<ImmediatePhysics::FActorHandle*>& Actors = Scene.GetSimulation()->GetActorHandles();
|
||||
|
||||
FTransform InverseComponentTransform = GeometryCollectionComponent->GetComponentTransform().Inverse();
|
||||
for (int i = 0; i < Collection->GetGeometryCollection()->NumElements(FGeometryCollection::TransformGroup); i++)
|
||||
{
|
||||
int32 RigidBodyIndex = RigidBodyId[i];
|
||||
Transform[i] = TransformMatrix(InverseComponentTransform, Actors[RigidBodyIndex]->GetWorldTransform());
|
||||
}
|
||||
|
||||
GeometryCollectionComponent->SetRenderStateDirty();
|
||||
}
|
||||
}
|
||||
|
||||
bool AGeometryCollectionActor::RaycastSingle(FVector Start, FVector End, FHitResult& OutHit) const
|
||||
{
|
||||
OutHit = FHitResult();
|
||||
OutHit.TraceStart = Start;
|
||||
OutHit.TraceEnd = End;
|
||||
return false;
|
||||
}
|
||||
|
||||
void AGeometryCollectionActor::UpdateKinematicBodiesCallback(FSolverCallbacks::FParticlesType& Particles, const float Dt, const float Time, const int32 Index) {}
|
||||
|
||||
void AGeometryCollectionActor::ParameterUpdateCallback(FSolverCallbacks::FParticlesType& Particles, const float Time) {}
|
||||
|
||||
void AGeometryCollectionActor::DisableCollisionsCallback(TSet<TTuple<int32, int32>>& CollisionPairs) {}
|
||||
|
||||
void AGeometryCollectionActor::AddConstraintCallback(FSolverCallbacks::FParticlesType& Particles, const float Time) {}
|
||||
|
||||
void AGeometryCollectionActor::AddForceCallback(FSolverCallbacks::FParticlesType& Particles, const float Dt, const int32 Index) {}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GeometryCollection/GeometryCollectionCache.h"
|
||||
#include "GeometryCollection/GeometryCollectionObject.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "Serialization/ArchiveCountMem.h"
|
||||
#include "Features/IModularFeatures.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogGeometryCollectionCache);
|
||||
|
||||
FName UGeometryCollectionCache::TagName_Name = FName("CollectionName");
|
||||
FName UGeometryCollectionCache::TagName_IdGuid = FName("CollectionIdGuid");
|
||||
FName UGeometryCollectionCache::TagName_StateGuid = FName("CollectionStateGuid");
|
||||
|
||||
void UGeometryCollectionCache::SetFromRawTrack(const FRecordedTransformTrack& InTrack)
|
||||
{
|
||||
ProcessRawRecordedDataInternal(InTrack);
|
||||
CompatibleCollectionState = SupportedCollection ? SupportedCollection->GetStateGuid() : FGuid();
|
||||
}
|
||||
|
||||
void UGeometryCollectionCache::SetFromTrack(const FRecordedTransformTrack& InTrack)
|
||||
{
|
||||
RecordedData = InTrack;
|
||||
CompatibleCollectionState = SupportedCollection ? SupportedCollection->GetStateGuid() : FGuid();
|
||||
}
|
||||
|
||||
void UGeometryCollectionCache::SetSupportedCollection(UGeometryCollection* InCollection)
|
||||
{
|
||||
if(InCollection != SupportedCollection)
|
||||
{
|
||||
// New collection. Set it and then clear out recorded data
|
||||
SupportedCollection = InCollection;
|
||||
RecordedData.Records.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void UGeometryCollectionCache::GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const
|
||||
{
|
||||
OutTags.Add(FAssetRegistryTag(TagName_Name, SupportedCollection ? SupportedCollection->GetName() : FString(TEXT("None")), FAssetRegistryTag::TT_Alphabetical));
|
||||
OutTags.Add(FAssetRegistryTag(TagName_IdGuid, SupportedCollection ? SupportedCollection->GetIdGuid().ToString() : FString(TEXT("INVALID")), FAssetRegistryTag::TT_Hidden));
|
||||
OutTags.Add(FAssetRegistryTag(TagName_StateGuid, SupportedCollection ? CompatibleCollectionState.ToString() : FString(TEXT("INVALID")), FAssetRegistryTag::TT_Hidden));
|
||||
}
|
||||
|
||||
UGeometryCollectionCache* UGeometryCollectionCache::CreateCacheForCollection(UGeometryCollection* InCollection)
|
||||
{
|
||||
UGeometryCollectionCache* ResultCache = nullptr;
|
||||
|
||||
if(InCollection)
|
||||
{
|
||||
IModularFeatures& ModularFeatures = IModularFeatures::Get();
|
||||
if(ModularFeatures.IsModularFeatureAvailable(ITargetCacheProvider::GetFeatureName()))
|
||||
{
|
||||
ITargetCacheProvider* Provider = &ModularFeatures.GetModularFeature<ITargetCacheProvider>(ITargetCacheProvider::GetFeatureName());
|
||||
check(Provider);
|
||||
|
||||
ResultCache = Provider->GetCacheForCollection(InCollection);
|
||||
|
||||
if(ResultCache)
|
||||
{
|
||||
ResultCache->SetSupportedCollection(InCollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ResultCache;
|
||||
}
|
||||
|
||||
void UGeometryCollectionCache::ProcessRawRecordedDataInternal(const FRecordedTransformTrack& InTrack)
|
||||
{
|
||||
RecordedData = FRecordedTransformTrack::ProcessRawRecordedData(InTrack);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GeometryCollection/GeometryCollectionComponentPlugin.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
|
||||
|
||||
|
||||
class FGeometryCollectionComponentPlugin : public IGeometryCollectionComponentPlugin
|
||||
{
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
|
||||
IMPLEMENT_MODULE( FGeometryCollectionComponentPlugin, GeometryCollectionComponent )
|
||||
|
||||
|
||||
|
||||
void FGeometryCollectionComponentPlugin::StartupModule()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void FGeometryCollectionComponentPlugin::ShutdownModule()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Stats/Stats.h"
|
||||
|
||||
// You should place include statements to your module's private header files here. You only need to
|
||||
// add includes for headers that are used in most of your module's source files though.
|
||||
|
||||
|
||||
DECLARE_STATS_GROUP(TEXT("GeometryCollection"), STATGROUP_GeometryCollection, STATCAT_Advanced);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,494 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GeometryCollection/GeometryCollectionDebugDrawComponent.h"
|
||||
|
||||
#if GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
#include "GeometryCollection/GeometryCollectionRenderLevelSetActor.h"
|
||||
#include "GeometryCollection/GeometryCollectionComponent.h"
|
||||
#include "GeometryCollection/GeometryCollectionObject.h"
|
||||
#include "GeometryCollection/GeometryCollectionAlgo.h"
|
||||
#include "GeometryCollection/GeometryCollectionDebugDrawActor.h"
|
||||
#include "GeometryCollection/GeometryCollectionPhysicsProxy.h"
|
||||
#if INCLUDE_CHAOS
|
||||
#include "PBDRigidsSolver.h"
|
||||
#endif // #if INCLUDE_CHAOS
|
||||
#endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
#include "EngineUtils.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(UGCCDD_LOG, All, All);
|
||||
|
||||
// Constants
|
||||
static const FLinearColor DarkerColorFactor(1.0f, 1.0f, 0.7f); // Darker HSV multiplier
|
||||
static const FLinearColor LighterColorFactor(1.0f, 1.0f, 3.0f); // Lighter HSV multiplier
|
||||
static const FLinearColor VertexColorDefault(0.2f, 0.4f, 0.6f, 1.0f); // Blue
|
||||
static const FLinearColor FaceColorDefault(0.4f, 0.2f, 0.6f, 1.0f); // Purple
|
||||
static const FLinearColor GeometryColorDefault(0.6, 0.4f, 0.2f, 1.0f); // Orange
|
||||
static const FLinearColor BreakingColorDefault(0.4f, 0.6f, 0.2f, 1.0f); // Green
|
||||
|
||||
UGeometryCollectionDebugDrawComponent::UGeometryCollectionDebugDrawComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, GeometryCollectionDebugDrawActor(nullptr)
|
||||
, GeometryCollectionRenderLevelSet(nullptr)
|
||||
, bDebugDrawLevelSet(false)
|
||||
, bRenderLevelSetAtOrigin(false)
|
||||
, LevelSetIndex(0)
|
||||
, bDebugDrawTransform(false)
|
||||
, bDebugDrawTransformIndex(false)
|
||||
, bDebugDrawBoundingBox(false)
|
||||
, GeometryColor(GeometryColorDefault)
|
||||
, bDebugDrawProximity(false)
|
||||
, bDebugDrawBreakingFace(false)
|
||||
, bDebugDrawBreakingRegionData(false)
|
||||
, BreakingColor(BreakingColorDefault)
|
||||
, bDebugDrawFace(false)
|
||||
, bDebugDrawFaceIndex(false)
|
||||
, bDebugDrawFaceNormal(false)
|
||||
, bDebugDrawSingleFace(false)
|
||||
, SingleFaceIdx(0)
|
||||
, FaceColor(FaceColorDefault)
|
||||
, bDebugDrawVertex(false)
|
||||
, bDebugDrawVertexIndex(false)
|
||||
, bDebugDrawVertexNormal(false)
|
||||
, VertexColor(VertexColorDefault)
|
||||
, GeometryCollectionComponent(nullptr)
|
||||
, bLevelSetTextureDirty(false)
|
||||
, LevelSetTextureTransformIndex(-1)
|
||||
, BaseVisibilityArray()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
bTickInEditor = false;
|
||||
}
|
||||
|
||||
void UGeometryCollectionDebugDrawComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
DebugDrawBeginPlay();
|
||||
DebugDrawLevelSetBeginPlay();
|
||||
}
|
||||
|
||||
void UGeometryCollectionDebugDrawComponent::EndPlay(EEndPlayReason::Type ReasonEnd)
|
||||
{
|
||||
Super::EndPlay(ReasonEnd);
|
||||
|
||||
DebugDrawLevelSetEndPlay();
|
||||
}
|
||||
|
||||
void UGeometryCollectionDebugDrawComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
DebugDrawTick();
|
||||
DebugDrawLevelSetTick();
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void UGeometryCollectionDebugDrawComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
#if GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
if (GeometryCollectionComponent)
|
||||
{
|
||||
const FName PropertyName = PropertyChangedEvent.Property ? PropertyChangedEvent.Property->GetFName(): NAME_None;
|
||||
if (PropertyName == GET_MEMBER_NAME_CHECKED(UGeometryCollectionDebugDrawComponent, SingleFaceIdx))
|
||||
{
|
||||
check(GeometryCollectionComponent->DynamicCollection != nullptr);
|
||||
check(GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().IsValid());
|
||||
FGeometryCollection* const Collection = GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().Get();
|
||||
if (Collection)
|
||||
{
|
||||
const int32 NumFaces = Collection->NumElements(FGeometryCollection::FacesGroup);
|
||||
SingleFaceIdx = FMath::Clamp(SingleFaceIdx, 0, NumFaces - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
}
|
||||
#endif // #if WITH_EDITOR
|
||||
|
||||
void UGeometryCollectionDebugDrawComponent::DebugDrawLevelSetBeginPlay()
|
||||
{
|
||||
#if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
// Look out for existing render level set actor, or create one when needed
|
||||
if (!GeometryCollectionRenderLevelSet)
|
||||
{
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
const TActorIterator<AGeometryCollectionRenderLevelSetActor> ActorIterator(world);
|
||||
if (ActorIterator)
|
||||
{
|
||||
GeometryCollectionRenderLevelSet = *ActorIterator;
|
||||
}
|
||||
else
|
||||
{
|
||||
FActorSpawnParameters SpawnInfo;
|
||||
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
GeometryCollectionRenderLevelSet = world->SpawnActor<AGeometryCollectionRenderLevelSetActor>(SpawnInfo);
|
||||
if (GeometryCollectionRenderLevelSet)
|
||||
{
|
||||
GeometryCollectionRenderLevelSet->SetActorEnableCollision(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GeometryCollectionComponent && GeometryCollectionComponent->DynamicCollection && GeometryCollectionRenderLevelSet)
|
||||
{
|
||||
// For now it makes sense to always initialize our visibility arrays since a user might start with vis debug off, then turn it on
|
||||
// This logic could change in the future
|
||||
TManagedArray<bool> &VisibleArray = *GeometryCollectionComponent->DynamicCollection->GetGeometryCollection()->Visible;
|
||||
BaseVisibilityArray = GeometryCollectionComponent->DynamicCollection->GetGeometryCollection()->AddAttribute<bool>("BaseVisibility", FGeometryCollection::FacesGroup);
|
||||
(*BaseVisibilityArray).Init(VisibleArray);
|
||||
|
||||
bLevelSetTextureDirty = true;
|
||||
LevelSetTextureTransformIndex = -1;
|
||||
}
|
||||
#endif // #if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
}
|
||||
|
||||
void UGeometryCollectionDebugDrawComponent::DebugDrawLevelSetEndPlay()
|
||||
{
|
||||
#if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
if (GeometryCollectionComponent && GeometryCollectionRenderLevelSet)
|
||||
{
|
||||
// @note: it might be the case that the user checks and unchecks render level set multiple times, and when they exit it is off
|
||||
// but we still want to reset the visibility. One solution is to always reset visibility at run time when the check box changes
|
||||
if (bDebugDrawLevelSet) {
|
||||
DebugDrawLevelSetResetVisiblity();
|
||||
}
|
||||
|
||||
// turn off rendering
|
||||
GeometryCollectionRenderLevelSet->SetEnabled(false);
|
||||
|
||||
bLevelSetTextureDirty = true;
|
||||
LevelSetTextureTransformIndex = -1;
|
||||
}
|
||||
#endif // #if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
}
|
||||
|
||||
void UGeometryCollectionDebugDrawComponent::DebugDrawLevelSetResetVisiblity()
|
||||
{
|
||||
#if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
check(GeometryCollectionComponent != nullptr);
|
||||
check(GeometryCollectionComponent->DynamicCollection != nullptr);
|
||||
check(GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().IsValid());
|
||||
check(BaseVisibilityArray.IsValid());
|
||||
// reset visibility array
|
||||
TManagedArray<bool> &VisibleArray = *GeometryCollectionComponent->DynamicCollection->GetGeometryCollection()->Visible;
|
||||
VisibleArray.Init(*BaseVisibilityArray);
|
||||
|
||||
// if we only have one piece, and all of the faces were hidden, then ensure we set visibility true
|
||||
if (!GeometryCollectionComponent->IsVisible()) {
|
||||
GeometryCollectionComponent->SetVisibility(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
GeometryCollectionComponent->ForceInitRenderData();
|
||||
}
|
||||
#endif // #if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
}
|
||||
|
||||
void UGeometryCollectionDebugDrawComponent::DebugDrawLevelSetTick()
|
||||
{
|
||||
#if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
if (!bDebugDrawLevelSet)
|
||||
{
|
||||
if (LevelSetTextureTransformIndex != -1 && GeometryCollectionRenderLevelSet && BaseVisibilityArray)
|
||||
{
|
||||
// in this case, the user switched from debug draw to not at run time, reset visibility
|
||||
DebugDrawLevelSetResetVisiblity();
|
||||
|
||||
// turn off rendering
|
||||
GeometryCollectionRenderLevelSet->SetEnabled(false);
|
||||
|
||||
bLevelSetTextureDirty = true;
|
||||
LevelSetTextureTransformIndex = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the level set index has changed at run time, then reload the volume
|
||||
// because someone wants to visualize another piece
|
||||
if (LevelSetTextureTransformIndex != -1 && LevelSetTextureTransformIndex != LevelSetIndex) {
|
||||
bLevelSetTextureDirty = true;
|
||||
}
|
||||
|
||||
// Error case if the level set renderer, physics proxy, or solver are null
|
||||
if (!GeometryCollectionRenderLevelSet) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("level set renderer: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GeometryCollectionComponent) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("no geometry component: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
FGeometryCollectionPhysicsProxy *PhysicsProxy = GeometryCollectionComponent->GetPhysicsProxy();
|
||||
if (!GeometryCollectionRenderLevelSet || !PhysicsProxy || !PhysicsProxy->GetSolver()) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("No solver context: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
// We must have at least one body to continue
|
||||
const Chaos::PBDRigidsSolver::FParticlesType &Particles = PhysicsProxy->GetSolver()->GetRigidParticles();
|
||||
if (Particles.Size() == 0) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("No rbds in solver context: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Map the piece index to the rbd index to extract the level set
|
||||
if (GeometryCollectionComponent->RigidBodyIds.Num() == 0) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("No rbd ids synced: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have a valid tranform index
|
||||
if (LevelSetIndex < 0 || LevelSetIndex > GeometryCollectionComponent->RigidBodyIds.Num() - 1) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("Invalid level set index: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have a valid rbd index
|
||||
int32 RbdId = GeometryCollectionComponent->RigidBodyIds[LevelSetIndex];
|
||||
if (RbdId < 0) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("No rbd ids synced: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
Chaos::TImplicitObject<float, 3>* CollisionBase = Particles.Geometry(RbdId);
|
||||
|
||||
// Make sure the actual implicit object isn't null
|
||||
if (CollisionBase == NULL) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("Collision is null for level set visualization: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Cast to level set, make sure the type is correct
|
||||
Chaos::TLevelSet<float, 3>* CollisionLevelSet = CollisionBase->GetObject< Chaos::TLevelSet<float, 3> >();
|
||||
|
||||
if (CollisionLevelSet == NULL) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("Incorrect collision type for level set rendering. It must be a level set: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the transform for the current piece
|
||||
FTransform CurrTransform = FTransform::Identity;
|
||||
|
||||
UGeometryCollection *DynamicCollection = GeometryCollectionComponent->DynamicCollection;
|
||||
|
||||
// Update the transform if we are rendering the level set aligned with the simulated geometry
|
||||
if (!bRenderLevelSetAtOrigin) {
|
||||
//TManagedArray<FTransform> &TransformArray = DynamicCollection->GetGeometryCollection()->GetAttribute<FTransform>("Transform", FGeometryCollection::TransformGroup).Get();
|
||||
//CurrTransform = TransformArray[LevelSetIndex];
|
||||
|
||||
// @todo: this is slow to recompute the global matrices here. Ideally we'd grab some cached ones from the geom collection component
|
||||
TArray<FTransform> GlobalMatrices;
|
||||
GeometryCollectionAlgo::GlobalMatrices(DynamicCollection->GetGeometryCollection().Get(), GlobalMatrices);
|
||||
CurrTransform = GlobalMatrices[LevelSetIndex];
|
||||
|
||||
CurrTransform *= this->GetOwner()->GetTransform();
|
||||
}
|
||||
|
||||
// If we are only updating the transform, or also loading the volume
|
||||
if (!bLevelSetTextureDirty) {
|
||||
GeometryCollectionRenderLevelSet->SyncLevelSetTransform(CurrTransform);
|
||||
}
|
||||
else {
|
||||
// Build the volume texture
|
||||
// @note: we only want to do this once, so we have a state variable on the component to ensure that
|
||||
bool success = GeometryCollectionRenderLevelSet->SetLevelSetToRender(*CollisionLevelSet, CurrTransform);
|
||||
|
||||
// Error case if volume fill didn't work
|
||||
if (!success) {
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("Levelset generation failed: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
// hide the original piece
|
||||
TManagedArray<bool> &VisibleArray = *DynamicCollection->GetGeometryCollection()->Visible;
|
||||
|
||||
// reset visibility to original state
|
||||
// @todo: there is some logic missing here, but also GeometryCollectionComponent doesn't like
|
||||
// debug rendering flags being set in simulate mode, so we don't switch piece often right now
|
||||
if (LevelSetTextureTransformIndex != -1) {
|
||||
VisibleArray.Init(*BaseVisibilityArray);
|
||||
}
|
||||
|
||||
const TManagedArray<int32> &TransformIndexArray = *DynamicCollection->GetGeometryCollection()->TransformIndex;
|
||||
|
||||
const TManagedArray<int32> &FaceStartArray = *DynamicCollection->GetGeometryCollection()->FaceStart;
|
||||
const TManagedArray<int32> &FaceCountArray = *DynamicCollection->GetGeometryCollection()->FaceCount;
|
||||
|
||||
const TManagedArray<FIntVector> &Indices = *DynamicCollection->GetGeometryCollection()->Indices;
|
||||
const TManagedArray<int32>& MaterialIndex = *DynamicCollection->GetGeometryCollection()->MaterialIndex;
|
||||
|
||||
// for each geom, check if it is the transform object
|
||||
// if it is, hide all the faces
|
||||
int NumHid = 0;
|
||||
for (int i = 0; i < TransformIndexArray.Num(); ++i) {
|
||||
const int32 currT = TransformIndexArray[i];
|
||||
|
||||
if (currT == LevelSetIndex) {
|
||||
|
||||
int32 FaceStart = FaceStartArray[i];
|
||||
int32 FaceCount = FaceCountArray[i];
|
||||
|
||||
// set visibility on faces to false
|
||||
for (int j = FaceStart; j < FaceStart + FaceCount; ++j) {
|
||||
VisibleArray[j] = false;
|
||||
}
|
||||
NumHid = FaceCount;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have no visible faces, hide the geometry without changing the collection
|
||||
// #todo: right now we can't send zero vertices to force the vertex buffer to be empty,
|
||||
// so we just hide the component.
|
||||
if (NumHid == VisibleArray.Num())
|
||||
{
|
||||
GeometryCollectionComponent->SetVisibility(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// init all render data
|
||||
GeometryCollectionComponent->ForceInitRenderData();
|
||||
}
|
||||
|
||||
// Make sure we know not to refill the texture on subsequent frames
|
||||
bLevelSetTextureDirty = false;
|
||||
LevelSetTextureTransformIndex = LevelSetIndex;
|
||||
|
||||
// Turn on the volume rendering
|
||||
GeometryCollectionRenderLevelSet->SetEnabled(true);
|
||||
}
|
||||
}
|
||||
#endif // #if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
}
|
||||
|
||||
void UGeometryCollectionDebugDrawComponent::DebugDrawBeginPlay()
|
||||
{
|
||||
#if GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
if (!GeometryCollectionDebugDrawActor)
|
||||
{
|
||||
// Look out for existing debug draw actor, or create one when needed
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
const TActorIterator<AGeometryCollectionDebugDrawActor> ActorIterator(world);
|
||||
if (ActorIterator)
|
||||
{
|
||||
GeometryCollectionDebugDrawActor = *ActorIterator;
|
||||
}
|
||||
else
|
||||
{
|
||||
FActorSpawnParameters SpawnInfo;
|
||||
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
GeometryCollectionDebugDrawActor = world->SpawnActor<AGeometryCollectionDebugDrawActor>(SpawnInfo);
|
||||
if (GeometryCollectionDebugDrawActor)
|
||||
{
|
||||
GeometryCollectionDebugDrawActor->SetActorEnableCollision(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure to tick the debug draw first, it is required to clear up the persistent lines before drawing a new frame
|
||||
if (GeometryCollectionDebugDrawActor)
|
||||
{
|
||||
AActor* const actor = GetOwner();
|
||||
check(actor);
|
||||
GeometryCollectionDebugDrawActor->AddTickPrerequisiteActor(actor);
|
||||
}
|
||||
#endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
}
|
||||
|
||||
void UGeometryCollectionDebugDrawComponent::DebugDrawTick()
|
||||
{
|
||||
#if GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
check(GeometryCollectionDebugDrawActor);
|
||||
// Only draw when a GeometryCollectionComponent is also attached to the actor (GeometryCollectionComponent is set by AGeometryCollectionActor::AGeometryCollectionActor())
|
||||
if (!GeometryCollectionComponent)
|
||||
{
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("Null geometry component pointer: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
if (!GeometryCollectionComponent->DynamicCollection)
|
||||
{
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("Null geometry dynamic collection pointer: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
if (!GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().IsValid())
|
||||
{
|
||||
UE_LOG(UGCCDD_LOG, Warning, TEXT("No valid geometry collection: %s"), *GetFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw collection
|
||||
FGeometryCollection* const Collection = GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().Get();
|
||||
check(Collection);
|
||||
AActor* const Actor = GetOwner();
|
||||
check(Actor);
|
||||
|
||||
if (bDebugDrawVertex)
|
||||
{
|
||||
const FColor Color = VertexColor.ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawVertices(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawVertexIndex)
|
||||
{
|
||||
const FColor Color = (VertexColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawVertexIndices(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawVertexNormal)
|
||||
{
|
||||
const FColor Color = (VertexColor.LinearRGBToHSV() * DarkerColorFactor).HSVToLinearRGB().ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawVertexNormals(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawFace)
|
||||
{
|
||||
const FColor Color = FaceColor.ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawFaces(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawFaceIndex)
|
||||
{
|
||||
const FColor Color = (FaceColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawFaceIndices(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawSingleFace)
|
||||
{
|
||||
const FColor Color = (FaceColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawSingleFace(Collection, Actor, SingleFaceIdx, Color);
|
||||
}
|
||||
if (bDebugDrawFaceNormal)
|
||||
{
|
||||
const FColor Color = (FaceColor.LinearRGBToHSV() * DarkerColorFactor).HSVToLinearRGB().ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawFaceNormals(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawTransform)
|
||||
{
|
||||
GeometryCollectionDebugDrawActor->DrawTransforms(Collection, Actor);
|
||||
}
|
||||
if (bDebugDrawTransformIndex)
|
||||
{
|
||||
const FColor Color = (GeometryColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawTransformIndices(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawBoundingBox)
|
||||
{
|
||||
const FColor Color = GeometryColor.ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawBoundingBoxes(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawProximity)
|
||||
{
|
||||
const FColor Color = BreakingColor.ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawProximity(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawBreakingFace)
|
||||
{
|
||||
const FColor Color = (BreakingColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawBreakingFaces(Collection, Actor, Color);
|
||||
}
|
||||
if (bDebugDrawBreakingRegionData)
|
||||
{
|
||||
const FColor Color = (BreakingColor.LinearRGBToHSV() * DarkerColorFactor).HSVToLinearRGB().ToFColor(false);
|
||||
GeometryCollectionDebugDrawActor->DrawBreakingRegionData(Collection, Actor, Color);
|
||||
}
|
||||
#endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GeometryCollection/GeometryCollectionEngineUtility.h"
|
||||
#include "GeometryCollection/GeometryCollection.h"
|
||||
#include "GeometryCollection/GeometryCollectionCache.h"
|
||||
#include "GeometryCollection/GeometryCollectionAlgo.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogGeoemtryCollectionClean, Verbose, All);
|
||||
|
||||
|
||||
void GeometryCollectionEngineUtility::PrintDetailedStatistics(const FGeometryCollection* GeometryCollection, const UGeometryCollectionCache* InCache)
|
||||
{
|
||||
check(GeometryCollection);
|
||||
|
||||
TSharedPtr< TManagedArray<FTransform> > Transform;
|
||||
TSharedPtr< TManagedArray<FGeometryCollectionBoneNode> > BoneHierarchy;
|
||||
|
||||
int32 NumTransforms = GeometryCollection->NumElements(FGeometryCollection::TransformGroup);
|
||||
int32 NumVertices = GeometryCollection->NumElements(FGeometryCollection::VerticesGroup);
|
||||
int32 NumFaces = GeometryCollection->NumElements(FGeometryCollection::FacesGroup);
|
||||
int32 NumGeometries = GeometryCollection->NumElements(FGeometryCollection::GeometryGroup);
|
||||
int32 NumBreakings = GeometryCollection->NumElements(FGeometryCollection::BreakingGroup);
|
||||
|
||||
const TManagedArray<FVector>& VertexArray = *GeometryCollection->Vertex;
|
||||
const TManagedArray<int32>& BoneMapArray = *GeometryCollection->BoneMap;
|
||||
|
||||
TArray<FTransform> GlobalTransformArray;
|
||||
GeometryCollectionAlgo::GlobalMatrices(GeometryCollection, GlobalTransformArray);
|
||||
|
||||
FBox BoundingBox(ForceInitToZero);
|
||||
for (int32 IdxVertex = 0; IdxVertex < NumVertices; ++IdxVertex)
|
||||
{
|
||||
FTransform GlobalTransform = GlobalTransformArray[BoneMapArray[IdxVertex]];
|
||||
FVector VertexInWorld = GlobalTransform.TransformPosition(VertexArray[IdxVertex]);
|
||||
|
||||
BoundingBox += VertexInWorld;
|
||||
}
|
||||
|
||||
FString Buffer;
|
||||
Buffer += FString::Printf(TEXT("\n\n"));
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("TRANSFORM GROUP\n"));
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("Number of transforms = %d\n"), NumTransforms);
|
||||
// Print Transform array
|
||||
// Print BoneHierarchy array
|
||||
GeometryCollectionAlgo::PrintParentHierarchy(GeometryCollection);
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("VERTICES GROUP\n"));
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("Number of vertices = %d\n"), NumVertices);
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("FACES GROUP\n"));
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("Number of faces = %d\n"), NumFaces);
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("GEOMETRY GROUP\n"));
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("Number of geometries = %d\n"), NumGeometries);
|
||||
// Print TransformIndex array
|
||||
// Print Proximity array
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("BREAKING GROUP\n"));
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("Number of breakings = %d\n"), NumBreakings);
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("BOUNDING BOX\n"));
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("Min = (%f, %f, %f)\n"), BoundingBox.Min.X, BoundingBox.Min.Y, BoundingBox.Min.Z);
|
||||
Buffer += FString::Printf(TEXT("Max = (%f, %f, %f)\n"), BoundingBox.Max.X, BoundingBox.Max.Y, BoundingBox.Max.Z);
|
||||
Buffer += FString::Printf(TEXT("Center = (%f, %f, %f)\n"), BoundingBox.GetCenter().X, BoundingBox.GetCenter().Y, BoundingBox.GetCenter().Z);
|
||||
Buffer += FString::Printf(TEXT("Size = (%f, %f, %f)\n"), 2.f * BoundingBox.GetExtent().X, 2.f * BoundingBox.GetExtent().Y, 2.f * BoundingBox.GetExtent().Z);
|
||||
|
||||
|
||||
if(InCache && InCache->GetData())
|
||||
{
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("CACHE INFO\n"));
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
const FRecordedTransformTrack* Track = InCache->GetData();
|
||||
int32 NumRecords = Track->Records.Num();
|
||||
if(NumRecords == 0)
|
||||
{
|
||||
Buffer += FString::Printf(TEXT("Cache is empty\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
float FirstRecordTimestamp = Track->Records[0].Timestamp;
|
||||
float LastRecordTimestamp = Track->Records[NumRecords - 1].Timestamp;
|
||||
|
||||
TMultiMap<int32, int32> TimestampRecordMap;
|
||||
for(int32 IdxRecord = 0; IdxRecord < NumRecords; ++IdxRecord)
|
||||
{
|
||||
TimestampRecordMap.Add(FMath::FloorToInt(Track->Records[IdxRecord].Timestamp), IdxRecord);
|
||||
}
|
||||
|
||||
TArray<int32> NumRecordsPerSecond;
|
||||
NumRecordsPerSecond.SetNum(FMath::CeilToInt(LastRecordTimestamp));
|
||||
for(int32 Idx = 0; Idx < FMath::CeilToInt(LastRecordTimestamp); ++Idx)
|
||||
{
|
||||
NumRecordsPerSecond[Idx] = TimestampRecordMap.Num(Idx);
|
||||
}
|
||||
|
||||
int32 NumRecordsMin = INT_MAX, NumRecordsMax = INT_MIN;
|
||||
float NumRecordsAverage = 0.f;
|
||||
for(int32 Idx = 0; Idx < NumRecordsPerSecond.Num(); ++Idx)
|
||||
{
|
||||
if(NumRecordsPerSecond[Idx] < NumRecordsMin)
|
||||
{
|
||||
NumRecordsMin = NumRecordsPerSecond[Idx];
|
||||
}
|
||||
if(NumRecordsPerSecond[Idx] > NumRecordsMax)
|
||||
{
|
||||
NumRecordsMax = NumRecordsPerSecond[Idx];
|
||||
}
|
||||
NumRecordsAverage += (float)NumRecordsPerSecond[Idx];
|
||||
}
|
||||
NumRecordsAverage /= (float)NumRecordsPerSecond.Num();
|
||||
|
||||
Buffer += FString::Printf(TEXT("Cache length [%f - %f]\n"), FirstRecordTimestamp, LastRecordTimestamp);
|
||||
Buffer += FString::Printf(TEXT("Number of recorded frames = %d\n"), NumRecords);
|
||||
for(int32 Idx = 0; Idx < NumRecordsPerSecond.Num(); ++Idx)
|
||||
{
|
||||
Buffer += FString::Printf(TEXT("Number of recorded frames at %ds = %d\n"), Idx, NumRecordsPerSecond[Idx]);
|
||||
}
|
||||
Buffer += FString::Printf(TEXT("Minimum number of recorded frames per second = %d\n"), NumRecordsMin);
|
||||
Buffer += FString::Printf(TEXT("Maximum number of recorded frames per second = %d\n"), NumRecordsMax);
|
||||
Buffer += FString::Printf(TEXT("Average number of recorded frames per second = %f\n"), NumRecordsAverage);
|
||||
|
||||
TArray<int32> NumCollisionMinPerSecond;
|
||||
NumCollisionMinPerSecond.Init(0, FMath::CeilToInt(LastRecordTimestamp));
|
||||
TArray<int32> NumCollisionMaxPerSecond;
|
||||
NumCollisionMaxPerSecond.Init(0, FMath::CeilToInt(LastRecordTimestamp));
|
||||
TArray<float> NumCollisionAveragePerSecond;
|
||||
NumCollisionAveragePerSecond.Init(0.f, FMath::CeilToInt(LastRecordTimestamp));
|
||||
int32 NumTotalCollisions = 0;
|
||||
TArray<int32> RecordIdxForOneSecond;
|
||||
for(int32 IdxSeconds = 0; IdxSeconds < NumRecordsPerSecond.Num(); ++IdxSeconds)
|
||||
{
|
||||
RecordIdxForOneSecond.Empty();
|
||||
TimestampRecordMap.MultiFind(IdxSeconds, RecordIdxForOneSecond);
|
||||
|
||||
for(int32 IdxRecord = 0; IdxRecord < RecordIdxForOneSecond.Num(); ++IdxRecord)
|
||||
{
|
||||
int32 NumCollisions = Track->Records[RecordIdxForOneSecond[IdxRecord]].Collisions.Num();
|
||||
if(NumCollisions > 0)
|
||||
{
|
||||
if(NumCollisions < NumCollisionMinPerSecond[IdxSeconds])
|
||||
{
|
||||
NumCollisionMinPerSecond[IdxSeconds] = NumCollisions;
|
||||
}
|
||||
if(NumCollisions > NumCollisionMaxPerSecond[IdxSeconds])
|
||||
{
|
||||
NumCollisionMaxPerSecond[IdxSeconds] = NumCollisions;
|
||||
}
|
||||
NumCollisionAveragePerSecond[IdxSeconds] += NumCollisions;
|
||||
NumTotalCollisions += NumCollisions;
|
||||
}
|
||||
}
|
||||
NumCollisionAveragePerSecond[IdxSeconds] /= (float)RecordIdxForOneSecond.Num();
|
||||
}
|
||||
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
for(int32 Idx = 0; Idx < NumRecordsPerSecond.Num(); ++Idx)
|
||||
{
|
||||
Buffer += FString::Printf(TEXT("Number of min collisions at %ds = %d\n"), Idx, NumCollisionMinPerSecond[Idx]);
|
||||
Buffer += FString::Printf(TEXT("Number of max collisions at %ds = %d\n"), Idx, NumCollisionMaxPerSecond[Idx]);
|
||||
Buffer += FString::Printf(TEXT("Number of average collisions at %ds = %f\n"), Idx, NumCollisionAveragePerSecond[Idx]);
|
||||
}
|
||||
Buffer += FString::Printf(TEXT("Number of total collisions = %d\n"), NumTotalCollisions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
Buffer += FString::Printf(TEXT("MESH QUALITY\n"));
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n"));
|
||||
|
||||
TSet<int32> VertexToDeleteSet;
|
||||
TMap<int32, int32> CoincidentVerticesMap;
|
||||
GeometryCollectionAlgo::ComputeCoincidentVertices(GeometryCollection, 1e-2, CoincidentVerticesMap, VertexToDeleteSet);
|
||||
int32 NumCoincidentVertices = VertexToDeleteSet.Num();
|
||||
|
||||
TSet<int32> FaceToDeleteSet;
|
||||
GeometryCollectionAlgo::ComputeZeroAreaFaces(GeometryCollection, 1e-4, FaceToDeleteSet);
|
||||
int32 NumZeroAreaFaces = FaceToDeleteSet.Num();
|
||||
|
||||
GeometryCollectionAlgo::ComputeHiddenFaces(GeometryCollection, FaceToDeleteSet);
|
||||
int32 NumHiddenFaces = FaceToDeleteSet.Num();
|
||||
|
||||
GeometryCollectionAlgo::ComputeStaleVertices(GeometryCollection, VertexToDeleteSet);
|
||||
int32 NumStaleVertices = VertexToDeleteSet.Num();
|
||||
|
||||
TMap<GeometryCollectionAlgo::FFaceEdge, int32> FaceEdgeMap;
|
||||
GeometryCollectionAlgo::ComputeEdgeInFaces(GeometryCollection, FaceEdgeMap);
|
||||
|
||||
int32 NumBoundaryEdges = 0;
|
||||
int32 NumDegenerateEdges = 0;
|
||||
for (auto& Edge : FaceEdgeMap)
|
||||
{
|
||||
if (FaceEdgeMap[Edge.Key] == 0)
|
||||
{
|
||||
NumBoundaryEdges++;
|
||||
}
|
||||
else if (FaceEdgeMap[Edge.Key] > 2)
|
||||
{
|
||||
NumDegenerateEdges++;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer += FString::Printf(TEXT("Number of coincident vertices = %d\n"), NumCoincidentVertices);
|
||||
Buffer += FString::Printf(TEXT("Number of zero area faces = %d\n"), NumZeroAreaFaces);
|
||||
Buffer += FString::Printf(TEXT("Number of hidden faces = %d\n"), NumHiddenFaces);
|
||||
Buffer += FString::Printf(TEXT("Number of stale vertices = %d\n"), NumStaleVertices);
|
||||
Buffer += FString::Printf(TEXT("Number of boundary edges = %d\n"), NumBoundaryEdges);
|
||||
Buffer += FString::Printf(TEXT("Number of degenerate edges (included in more than 2 faces) = %d\n"), NumDegenerateEdges);
|
||||
Buffer += FString::Printf(TEXT("------------------------------------------------------------\n\n"));
|
||||
UE_LOG(LogGeoemtryCollectionClean, Log, TEXT("%s"), *Buffer);
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
/*=============================================================================
|
||||
GeometryCollection.cpp: UGeometryCollection methods.
|
||||
=============================================================================*/
|
||||
#include "GeometryCollection/GeometryCollectionObject.h"
|
||||
|
||||
#include "GeometryCollection/GeometryCollection.h"
|
||||
#include "GeometryCollection/GeometryCollectionCache.h"
|
||||
#include "UObject/DestructionObjectVersion.h"
|
||||
#include "Serialization/ArchiveCountMem.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "UObject/Package.h"
|
||||
#include "Materials/MaterialInstance.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(UGeometryCollectionLogging, NoLogging, All);
|
||||
|
||||
UGeometryCollection::UGeometryCollection(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, GeometryCollection(new FGeometryCollection())
|
||||
{
|
||||
check(ObjectInitializer.GetClass() == GetClass());
|
||||
if (UGeometryCollection* InputComponent = static_cast<UGeometryCollection*>(ObjectInitializer.GetObj()))
|
||||
{
|
||||
if (InputComponent->GetGeometryCollection())
|
||||
{
|
||||
GeometryCollection = TSharedPtr<FGeometryCollection>(
|
||||
new FGeometryCollection(*static_cast<UGeometryCollection*>(ObjectInitializer.GetObj())->GetGeometryCollection()));
|
||||
}
|
||||
}
|
||||
|
||||
PersistentGuid = FGuid::NewGuid();
|
||||
InvalidateCollection();
|
||||
}
|
||||
|
||||
//** Initialize */
|
||||
void UGeometryCollection::Initialize(FManagedArrayCollection & CollectionIn)
|
||||
{
|
||||
Modify();
|
||||
GeometryCollection->Initialize(CollectionIn);
|
||||
InvalidateCollection();
|
||||
}
|
||||
|
||||
|
||||
/** AppendGeometry */
|
||||
int32 UGeometryCollection::AppendGeometry(const UGeometryCollection & Element)
|
||||
{
|
||||
Modify();
|
||||
InvalidateCollection();
|
||||
return GeometryCollection->AppendGeometry(*Element.GetGeometryCollection());
|
||||
}
|
||||
|
||||
/** NumElements */
|
||||
int32 UGeometryCollection::NumElements(const FName & Group)
|
||||
{
|
||||
return GeometryCollection->NumElements(Group);
|
||||
}
|
||||
|
||||
|
||||
/** RemoveElements */
|
||||
void UGeometryCollection::RemoveElements(const FName & Group, const TArray<int32>& SortedDeletionList)
|
||||
{
|
||||
Modify();
|
||||
GeometryCollection->RemoveElements(Group, SortedDeletionList);
|
||||
InvalidateCollection();
|
||||
}
|
||||
|
||||
|
||||
/** ReindexMaterialSections */
|
||||
void UGeometryCollection::ReindexMaterialSections()
|
||||
{
|
||||
Modify();
|
||||
GeometryCollection->ReindexMaterials();
|
||||
InvalidateCollection();
|
||||
}
|
||||
|
||||
void UGeometryCollection::AppendStandardMaterials()
|
||||
{
|
||||
// Second to last material is the interior material
|
||||
// #todo(dmp): This will be replaced when we support multiple internal materials
|
||||
UMaterialInterface* InteriorMaterial = LoadObject<UMaterialInterface>(NULL, TEXT("/GeometryCollectionPlugin/InMaterial.InMaterial"), NULL, LOAD_None, NULL);
|
||||
InteriorMaterialIndex = Materials.Add(InteriorMaterial);
|
||||
|
||||
// Last Material is the selection one
|
||||
UMaterialInterface* BoneSelectedMaterial = LoadObject<UMaterialInterface>(NULL, TEXT("/GeometryCollectionPlugin/SelectedGeometryMaterial.SelectedGeometryMaterial"), NULL, LOAD_None, NULL);
|
||||
BoneSelectedMaterialIndex = Materials.Add(BoneSelectedMaterial);
|
||||
}
|
||||
|
||||
/** Returns true if there is anything to render */
|
||||
bool UGeometryCollection::HasVisibleGeometry()
|
||||
{
|
||||
return GeometryCollection->HasVisibleGeometry();
|
||||
}
|
||||
|
||||
/** Serialize */
|
||||
void UGeometryCollection::Serialize(FArchive& Ar)
|
||||
{
|
||||
Ar.UsingCustomVersion(FDestructionObjectVersion::GUID);
|
||||
|
||||
GeometryCollection->Serialize(Ar);
|
||||
|
||||
if(Ar.CustomVer(FDestructionObjectVersion::GUID) < FDestructionObjectVersion::AddedTimestampedGeometryComponentCache)
|
||||
{
|
||||
if(Ar.IsLoading())
|
||||
{
|
||||
// Strip old recorded cache data
|
||||
int32 DummyNumFrames;
|
||||
TArray<TArray<FTransform>> DummyTransforms;
|
||||
|
||||
Ar << DummyNumFrames;
|
||||
DummyTransforms.SetNum(DummyNumFrames);
|
||||
for(int32 Index = 0; Index < DummyNumFrames; ++Index)
|
||||
{
|
||||
Ar << DummyTransforms[Index];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Push up the chain to hit tagged properties too
|
||||
// This should have always been in here but because we have saved assets
|
||||
// from before this line was here it has to be gated
|
||||
Super::Serialize(Ar);
|
||||
}
|
||||
}
|
||||
|
||||
void UGeometryCollection::InvalidateCollection()
|
||||
{
|
||||
StateGuid = FGuid::NewGuid();
|
||||
}
|
||||
|
||||
FGuid UGeometryCollection::GetIdGuid() const
|
||||
{
|
||||
return PersistentGuid;
|
||||
}
|
||||
|
||||
FGuid UGeometryCollection::GetStateGuid() const
|
||||
{
|
||||
return StateGuid;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void UGeometryCollection::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
InvalidateCollection();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool UGeometryCollection::Modify(bool bAlwaysMarkDirty /*= true*/)
|
||||
{
|
||||
bool bSuperResult = Super::Modify(bAlwaysMarkDirty);
|
||||
|
||||
UPackage* Package = GetOutermost();
|
||||
if(Package->IsDirty())
|
||||
{
|
||||
InvalidateCollection();
|
||||
}
|
||||
|
||||
return bSuperResult;
|
||||
}
|
||||
|
||||
void UGeometryCollection::PostLoad()
|
||||
{
|
||||
Super::PostLoad();
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "GeometryCollection/GeometryCollectionRenderLevelSetActor.h"
|
||||
|
||||
|
||||
#include "Chaos/ArrayND.h"
|
||||
#include "Chaos/Vector.h"
|
||||
|
||||
using namespace Chaos;
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LSR_LOG, Log, All);
|
||||
|
||||
AGeometryCollectionRenderLevelSetActor::AGeometryCollectionRenderLevelSetActor(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer), SurfaceTolerance(0.01f), Isovalue(0.f), Enabled(true), RenderVolumeBoundingBox(false), DynRayMarchMaterial(NULL), StepSizeMult(1.f)
|
||||
{
|
||||
PostProcessComponent = CreateDefaultSubobject<UPostProcessComponent>(TEXT("PostProcessComponent0"));
|
||||
RootComponent = PostProcessComponent;
|
||||
|
||||
// set initial values
|
||||
// @todo: Need to make this work based on if the module is loaded
|
||||
#if 0
|
||||
TargetVolumeTexture = LoadObject<UVolumeTexture>(NULL, TEXT("/GeometryCollectionPlugin/VolumeVisualization/Textures/VolumeToRender"), NULL, LOAD_None, NULL);
|
||||
UMaterialInterface* MaterialInterface = LoadObject<UMaterialInterface>(NULL, TEXT("/GeometryCollectionPlugin/VolumeVisualization/Materials/M_VolumeRenderSphereTracePP"), NULL, LOAD_None, NULL);
|
||||
RayMarchMaterial = MaterialInterface ? MaterialInterface->GetBaseMaterial() : nullptr;
|
||||
#else
|
||||
TargetVolumeTexture = nullptr;
|
||||
RayMarchMaterial = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AGeometryCollectionRenderLevelSetActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// make sure to set enabled on the post process
|
||||
PostProcessComponent->bEnabled = Enabled;
|
||||
PostProcessComponent->bUnbound = true;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void AGeometryCollectionRenderLevelSetActor::PostEditChangeProperty(struct FPropertyChangedEvent& e)
|
||||
{
|
||||
Super::PostEditChangeProperty(e);
|
||||
|
||||
// sync all rendering properties each time a param changes.
|
||||
// @todo: optimize to only update parameters when rendering-specific ones are edited
|
||||
SyncMaterialParameters();
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void AGeometryCollectionRenderLevelSetActor::SyncMaterialParameters()
|
||||
{
|
||||
if (!RayMarchMaterial)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// make dynamic material instance if it hasn't been created yet
|
||||
if (!DynRayMarchMaterial) {
|
||||
DynRayMarchMaterial = UMaterialInstanceDynamic::Create(RayMarchMaterial, this);
|
||||
|
||||
// add the blendable with our post process material
|
||||
PostProcessComponent->AddOrUpdateBlendable(DynRayMarchMaterial);
|
||||
}
|
||||
|
||||
// Sync all render parameters to our material
|
||||
DynRayMarchMaterial->SetScalarParameterValue("Surface Tolerance", SurfaceTolerance);
|
||||
DynRayMarchMaterial->SetScalarParameterValue("Isovalue", Isovalue);
|
||||
|
||||
|
||||
DynRayMarchMaterial->SetScalarParameterValue("Step Size Mult", StepSizeMult);
|
||||
DynRayMarchMaterial->SetScalarParameterValue("Voxel Size", VoxelSize);
|
||||
|
||||
DynRayMarchMaterial->SetVectorParameterValue("Min Bounds", MinBBoxCorner);
|
||||
DynRayMarchMaterial->SetVectorParameterValue("Max Bounds", MaxBBoxCorner);
|
||||
|
||||
DynRayMarchMaterial->SetVectorParameterValue("WorldToLocalc0", FLinearColor(WorldToLocal.GetColumn(0)));
|
||||
DynRayMarchMaterial->SetVectorParameterValue("WorldToLocalc1", FLinearColor(WorldToLocal.GetColumn(1)));
|
||||
DynRayMarchMaterial->SetVectorParameterValue("WorldToLocalc2", FLinearColor(WorldToLocal.GetColumn(2)));
|
||||
DynRayMarchMaterial->SetVectorParameterValue("WorldToLocalTranslation", FLinearColor(WorldToLocal.GetOrigin()));
|
||||
|
||||
DynRayMarchMaterial->SetTextureParameterValue("Volume To Render", TargetVolumeTexture);
|
||||
|
||||
DynRayMarchMaterial->SetScalarParameterValue("Debug BBox", (float) RenderVolumeBoundingBox);
|
||||
|
||||
PostProcessComponent->bEnabled = Enabled;
|
||||
}
|
||||
|
||||
void AGeometryCollectionRenderLevelSetActor::SyncLevelSetTransform(const FTransform &LocalToWorld)
|
||||
{
|
||||
if (!RayMarchMaterial)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WorldToLocal = LocalToWorld.Inverse().ToMatrixWithScale();
|
||||
DynRayMarchMaterial->SetVectorParameterValue("WorldToLocalc0", FLinearColor(WorldToLocal.GetColumn(0)));
|
||||
DynRayMarchMaterial->SetVectorParameterValue("WorldToLocalc1", FLinearColor(WorldToLocal.GetColumn(1)));
|
||||
DynRayMarchMaterial->SetVectorParameterValue("WorldToLocalc2", FLinearColor(WorldToLocal.GetColumn(2)));
|
||||
DynRayMarchMaterial->SetVectorParameterValue("WorldToLocalTranslation", FLinearColor(WorldToLocal.GetOrigin()));
|
||||
}
|
||||
|
||||
bool AGeometryCollectionRenderLevelSetActor::SetLevelSetToRender(const Chaos::TLevelSet<float, 3> &LevelSet, const FTransform &LocalToWorld)
|
||||
{
|
||||
// error case when the target volume texture isn't set
|
||||
if (TargetVolumeTexture == NULL) {
|
||||
UE_LOG(LSR_LOG, Warning, TEXT("Target UVolumeTexture is null on %s"), *GetFullName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// get refs to the grid structures
|
||||
const TArrayND<float, 3> &LevelSetPhiArray = LevelSet.GetPhiArray();
|
||||
const TArrayND<TVector<float, 3>, 3> &LevelSetNormalsArray = LevelSet.GetNormalsArray();
|
||||
const TUniformGrid<float, 3> &LevelSetGrid = LevelSet.GetGrid();
|
||||
|
||||
const TVector<int32, 3> &Counts = LevelSetGrid.Counts();
|
||||
|
||||
// set bounding box
|
||||
MinBBoxCorner = LevelSetGrid.MinCorner();
|
||||
MaxBBoxCorner = LevelSetGrid.MaxCorner();
|
||||
WorldToLocal = LocalToWorld.Inverse().ToMatrixWithScale();
|
||||
|
||||
// @todo: do we need to deal with non square voxels?
|
||||
VoxelSize = LevelSetGrid.Dx().X;
|
||||
|
||||
// Error case when the voxel size is sufficiently small
|
||||
if (VoxelSize < 1e-5) {
|
||||
UE_LOG(LSR_LOG, Warning, TEXT("Voxel size is zero on %s"), *GetFullName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// lambda for querying the level set information
|
||||
// @note: x and z swap for volume textures to match TlevelSet
|
||||
// @todo: we could encode voxel ordering more nicely in the UVolumeTexture
|
||||
auto QueryVoxel = [&](const int32 x, const int32 y, const int32 z, FFloat16 *ret)
|
||||
{
|
||||
float sd = LevelSetPhiArray(TVector<int32, 3>(z, y, x));
|
||||
TVector<float, 3> n = LevelSetNormalsArray(TVector<int32, 3>(z, y, x));
|
||||
n.Normalize();
|
||||
|
||||
// @note: x and z swap for volume textures to render correctly
|
||||
ret[0] = n.X;
|
||||
ret[1] = n.Y;
|
||||
ret[2] = n.Z;
|
||||
ret[3] = sd;
|
||||
};
|
||||
|
||||
// fill volume texture from level set
|
||||
// @note: we swap z and x to match level set in world space
|
||||
bool success = TargetVolumeTexture->UpdateSourceFromFunction(QueryVoxel, Counts.Z, Counts.Y, Counts.X);
|
||||
|
||||
if (!success) {
|
||||
UE_LOG(LSR_LOG, Warning, TEXT("Couldn't create target volume texture from TLevelSet with %s"), *GetFullName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// set all parameters on our dynamic material instance to sync state
|
||||
SyncMaterialParameters();
|
||||
|
||||
UE_LOG(LSR_LOG, Log, TEXT("Volume Bounds: %s - %s -- Volume Dims: %d %d %d -- Voxel Size: %f -- World To Local: %s"), *MinBBoxCorner.ToString(), *MaxBBoxCorner.ToString(), Counts.X, Counts.Y, Counts.Z, VoxelSize, *WorldToLocal.ToString());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GeometryCollection/GeometryCollectionSQAccelerator.h"
|
||||
#include "Physics/Experimental/PhysScene_Chaos.h"
|
||||
#include "Chaos/PBDRigidParticles.h"
|
||||
#include "GeometryCollection/ManagedArray.h"
|
||||
#include "GeometryCollection/GeometryCollectionComponent.h"
|
||||
#include "GeometryCollection/GeometryCollectionActor.h"
|
||||
#include "Components/BoxComponent.h"
|
||||
#include "ChaosSolversModule.h"
|
||||
#include "ChaosStats.h"
|
||||
|
||||
#if INCLUDE_CHAOS
|
||||
|
||||
bool LowLevelRaycast(const UGeometryCollectionComponent& GeomCollectionComponent, const FVector& Start, const FVector& Dir, float DeltaMag, EHitFlags OutputFlags, FHitRaycast& OutHit)
|
||||
{
|
||||
using namespace Chaos;
|
||||
|
||||
const TManagedArray<int32>& RigidBodyIdArray = GeomCollectionComponent.GetRigidBodyIdArray();
|
||||
bool bFound = false;
|
||||
|
||||
if (const Chaos::PBDRigidsSolver* Solver = GeomCollectionComponent.ChaosSolverActor != nullptr ? GeomCollectionComponent.ChaosSolverActor->GetSolver() : FPhysScene_Chaos::GetInstance()->GetSolver())
|
||||
{
|
||||
const TPBDRigidParticles<float, 3>& Particles = Solver->GetRigidParticles(); //todo(ocohen): should these just get passed in instead of hopping through scene?
|
||||
|
||||
for (int32 Idx = 0; Idx < RigidBodyIdArray.Num(); ++Idx)
|
||||
{
|
||||
const int32 RigidBodyIdx = RigidBodyIdArray[Idx];
|
||||
if (RigidBodyIdx == -1) { continue; } //todo(ocohen): managed to avoid this invalid index, but need to investigate a bit more into whether we can always assume it's valid
|
||||
|
||||
if (Particles.Disabled(RigidBodyIdx)) { continue; } //disabled particles can actually have stale geometry in them and are clearly not useful anyway
|
||||
|
||||
if (!(ensure(!FMath::IsNaN(Particles.X(RigidBodyIdx)[0])) && ensure(!FMath::IsNaN(Particles.X(RigidBodyIdx)[1])) && ensure(!FMath::IsNaN(Particles.X(RigidBodyIdx)[2]))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const TRigidTransform<float, 3> TM(Particles.X(RigidBodyIdx), Particles.R(RigidBodyIdx));
|
||||
const TVector<float, 3> StartLocal = TM.InverseTransformPositionNoScale(Start);
|
||||
const TVector<float, 3> DirLocal = TM.InverseTransformVectorNoScale(Dir);
|
||||
const TVector<float, 3> EndLocal = StartLocal + DirLocal * DeltaMag; //todo(ocohen): apeiron just undoes this later, we should fix the API
|
||||
|
||||
const TImplicitObject<float, 3>* Object = Particles.Geometry(RigidBodyIdx); //todo(ocohen): can this ever be null?
|
||||
Pair<TVector<float, 3>, bool> Result = Object->FindClosestIntersection(StartLocal, EndLocal, /*Thickness=*/0.f);
|
||||
if (Result.Second) //todo(ocohen): once we do more than just a bool we need to get the closest point
|
||||
{
|
||||
#if WITH_PHYSX
|
||||
//todo(ocohen): check output flags?
|
||||
const float Distance = (Result.First - StartLocal).Size();
|
||||
if (!bFound || Distance < OutHit.distance)
|
||||
{
|
||||
OutHit.distance = Distance; //todo(ocohen): assuming physx structs for now
|
||||
OutHit.position = U2PVector(TM.TransformPositionNoScale(Result.First));
|
||||
const TVector<float, 3> LocalNormal = Object->Normal(Result.First);
|
||||
OutHit.normal = U2PVector(TM.TransformVectorNoScale(LocalNormal));
|
||||
SetFlags(OutHit, EHitFlags::Distance | EHitFlags::Normal | EHitFlags::Position);
|
||||
}
|
||||
bFound = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bFound;
|
||||
}
|
||||
|
||||
void FGeometryCollectionSQAccelerator::Raycast(const FVector& Start, const FVector& Dir, FPhysicsHitCallback<FHitRaycast>& HitBuffer, EHitFlags OutputFlags, FQueryFlags QueryFlags, const FCollisionFilterData& QueryFilter, FCollisionQueryFilterCallback& QueryCallback) const
|
||||
{
|
||||
// #BGallagher Temp lock semantics. Essentially this guarantees t or t+1 depending on which is closer by stalling
|
||||
// the physics thread at the next update step so we can perform the query. Long term we want multiple methods for
|
||||
// queries as t-1 can be made much cheaper for applications where immediate results don't matter.
|
||||
FChaosScopedPhysicsThreadLock ThreadLock;
|
||||
|
||||
SCOPE_CYCLE_COUNTER(STAT_GCRaycast);
|
||||
|
||||
#if WITH_PHYSX
|
||||
for (const UGeometryCollectionComponent* GeomCollectionComponent : Components)
|
||||
{
|
||||
FHitRaycast Hit;
|
||||
if (LowLevelRaycast(*GeomCollectionComponent, Start, Dir, GetCurrentBlockTraceDistance(HitBuffer), OutputFlags, Hit)) //assume all blocking hits for now
|
||||
{
|
||||
#if !WITH_IMMEDIATE_PHYSX && PHYSICS_INTERFACE_PHYSX
|
||||
//todo(ocohen):hack placeholder while we convert over to non physx API
|
||||
const FPhysicsActorHandle& ActorHandle = GeomCollectionComponent->DummyBoxComponent->BodyInstance.GetPhysicsActorHandle();
|
||||
PxRigidActor* PRigidActor = ActorHandle.SyncActor;
|
||||
uint32 PNumShapes = PRigidActor->getNbShapes();
|
||||
TArray<PxShape*> PShapes;
|
||||
PShapes.AddZeroed(PNumShapes);
|
||||
PRigidActor->getShapes(PShapes.GetData(), sizeof(PShapes[0]) * PNumShapes);
|
||||
SetActor(Hit, ActorHandle.SyncActor);
|
||||
SetShape(Hit, PShapes[0]);
|
||||
#else
|
||||
check(false); //this can't actually return nullptr since higher up API assumes both shape and actor exists in the low level
|
||||
SetActor(Hit, nullptr);
|
||||
SetShape(Hit, nullptr); //todo(ocohen): what do we return for apeiron?
|
||||
#endif
|
||||
Insert(HitBuffer, Hit, true); //for now assume all blocking hits
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FGeometryCollectionSQAccelerator::Sweep(const FPhysicsGeometry& QueryGeom, const FTransform& StartTM, const FVector& Dir, FPhysicsHitCallback<FHitSweep>& HitBuffer, EHitFlags OutputFlags, FQueryFlags QueryFlags, const FCollisionFilterData& QueryFilter, FCollisionQueryFilterCallback& QueryCallback) const
|
||||
{
|
||||
}
|
||||
|
||||
void FGeometryCollectionSQAccelerator::Overlap(const FPhysicsGeometry& QueryGeom, const FTransform& GeomPose, FPhysicsHitCallback<FHitOverlap>& HitBuffer, FQueryFlags QueryFlags, const FCollisionFilterData& QueryFilter, FCollisionQueryFilterCallback& QueryCallback) const
|
||||
{
|
||||
}
|
||||
|
||||
void FGeometryCollectionSQAccelerator::AddComponent(UGeometryCollectionComponent* Component)
|
||||
{
|
||||
Components.Add(Component);
|
||||
}
|
||||
|
||||
void FGeometryCollectionSQAccelerator::RemoveComponent(UGeometryCollectionComponent* Component)
|
||||
{
|
||||
Components.Remove(Component);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
#pragma once
|
||||
|
||||
#include "Physics/SQAccelerator.h"
|
||||
|
||||
#if INCLUDE_CHAOS
|
||||
|
||||
class UGeometryCollectionComponent;
|
||||
|
||||
class FGeometryCollectionSQAccelerator : public ISQAccelerator
|
||||
{
|
||||
public:
|
||||
virtual void Raycast(const FVector& Start, const FVector& Dir, FPhysicsHitCallback<FHitRaycast>& HitBuffer, EHitFlags OutputFlags, FQueryFlags QueryFlags, const FCollisionFilterData& QueryFilter, FCollisionQueryFilterCallback& QueryCallback) const override;
|
||||
virtual void Sweep(const FPhysicsGeometry& QueryGeom, const FTransform& StartTM, const FVector& Dir, FPhysicsHitCallback<FHitSweep>& HitBuffer, EHitFlags OutputFlags, FQueryFlags QueryFlags, const FCollisionFilterData& QueryFilter, FCollisionQueryFilterCallback& QueryCallback) const override;
|
||||
virtual void Overlap(const FPhysicsGeometry& QueryGeom, const FTransform& GeomPose, FPhysicsHitCallback<FHitOverlap>& HitBuffer, FQueryFlags QueryFlags, const FCollisionFilterData& QueryFilter, FCollisionQueryFilterCallback& QueryCallback) const override;
|
||||
virtual ~FGeometryCollectionSQAccelerator() {}
|
||||
|
||||
void AddComponent(UGeometryCollectionComponent* Component);
|
||||
void RemoveComponent(UGeometryCollectionComponent* Component);
|
||||
|
||||
private:
|
||||
TSet<UGeometryCollectionComponent*> Components;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,345 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GeometryCollection/GeometryCollectionSceneProxy.h"
|
||||
|
||||
#include "Async/ParallelFor.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "GeometryCollection/GeometryCollectionComponent.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(FGeometryCollectionSceneProxyLogging, Log, All);
|
||||
|
||||
FGeometryCollectionSceneProxy::FGeometryCollectionSceneProxy(UGeometryCollectionComponent* Component)
|
||||
: FPrimitiveSceneProxy(Component)
|
||||
, MaterialRelevance(Component->GetMaterialRelevance(GetScene().GetFeatureLevel()))
|
||||
, NumVertices(0)
|
||||
, NumIndices(0)
|
||||
, VertexFactory(GetScene().GetFeatureLevel(), "FGeometryCollectionSceneProxy")
|
||||
, DynamicData(nullptr)
|
||||
, ConstantData(nullptr)
|
||||
, ShowBoneColors(Component->GetShowBoneColors())
|
||||
, ShowSelectedBones(Component->GetShowSelectedBones())
|
||||
, BoneSelectionMaterialID(Component->GetBoneSelectedMaterialID())
|
||||
{
|
||||
Materials.Empty();
|
||||
for (int MaterialIndex = 0; MaterialIndex < Component->GetNumMaterials(); MaterialIndex++)
|
||||
{
|
||||
Materials.Push(Component->GetMaterial(MaterialIndex));
|
||||
if (Materials[MaterialIndex] == nullptr)
|
||||
{
|
||||
Materials[MaterialIndex] = UMaterial::GetDefaultMaterial(MD_Surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FGeometryCollectionSceneProxy::~FGeometryCollectionSceneProxy()
|
||||
{
|
||||
ReleaseResources();
|
||||
|
||||
if (DynamicData != nullptr)
|
||||
{
|
||||
delete DynamicData;
|
||||
}
|
||||
|
||||
if (ConstantData != nullptr)
|
||||
{
|
||||
delete ConstantData;
|
||||
}
|
||||
}
|
||||
|
||||
void FGeometryCollectionSceneProxy::InitResources()
|
||||
{
|
||||
check(ConstantData);
|
||||
|
||||
NumVertices = ConstantData->Vertices.Num();
|
||||
NumIndices = ConstantData->Indices.Num()*3;
|
||||
|
||||
VertexBuffers.InitWithDummyData(&VertexFactory, GetRequiredVertexCount());
|
||||
|
||||
IndexBuffer.NumIndices = GetRequiredIndexCount();
|
||||
|
||||
BeginInitResource(&IndexBuffer);
|
||||
}
|
||||
|
||||
void FGeometryCollectionSceneProxy::ReleaseResources()
|
||||
{
|
||||
VertexBuffers.PositionVertexBuffer.ReleaseResource();
|
||||
VertexBuffers.StaticMeshVertexBuffer.ReleaseResource();
|
||||
VertexBuffers.ColorVertexBuffer.ReleaseResource();
|
||||
IndexBuffer.ReleaseResource();
|
||||
VertexFactory.ReleaseResource();
|
||||
}
|
||||
|
||||
void FGeometryCollectionSceneProxy::BuildGeometry( const FGeometryCollectionConstantData* ConstantDataIn, TArray<FDynamicMeshVertex>& OutVertices, TArray<int32>& OutIndices)
|
||||
{
|
||||
OutVertices.SetNumUninitialized(ConstantDataIn->Vertices.Num());
|
||||
ParallelFor(ConstantData->Vertices.Num(), [&](int32 PointIdx)
|
||||
{
|
||||
OutVertices[PointIdx] =
|
||||
FDynamicMeshVertex(
|
||||
ConstantDataIn->Vertices[PointIdx],
|
||||
ConstantDataIn->UVs[PointIdx],
|
||||
ShowBoneColors||ShowSelectedBones ?
|
||||
ConstantDataIn->BoneColors[PointIdx].ToFColor(true) :
|
||||
ConstantDataIn->Colors[PointIdx].ToFColor(true)
|
||||
);
|
||||
OutVertices[PointIdx].SetTangents(ConstantDataIn->TangentU[PointIdx], ConstantDataIn->TangentV[PointIdx], ConstantDataIn->Normals[PointIdx]);
|
||||
});
|
||||
|
||||
check(ConstantDataIn->Indices.Num() * 3 == NumIndices);
|
||||
|
||||
OutIndices.SetNumUninitialized(NumIndices);
|
||||
ParallelFor (ConstantDataIn->Indices.Num(), [&](int32 IndexIdx)
|
||||
{
|
||||
OutIndices[IndexIdx * 3 ] = ConstantDataIn->Indices[IndexIdx].X;
|
||||
OutIndices[IndexIdx * 3 + 1] = ConstantDataIn->Indices[IndexIdx].Y;
|
||||
OutIndices[IndexIdx * 3 + 2] = ConstantDataIn->Indices[IndexIdx].Z;
|
||||
});
|
||||
}
|
||||
|
||||
void FGeometryCollectionSceneProxy::SetConstantData_RenderThread(FGeometryCollectionConstantData* NewConstantData, bool ForceInit)
|
||||
{
|
||||
check(IsInRenderingThread());
|
||||
check(NewConstantData);
|
||||
|
||||
if (ConstantData)
|
||||
{
|
||||
delete ConstantData;
|
||||
ConstantData = nullptr;
|
||||
}
|
||||
ConstantData = NewConstantData;
|
||||
|
||||
if (ConstantData->Vertices.Num() != VertexBuffers.PositionVertexBuffer.GetNumVertices() || ForceInit)
|
||||
{
|
||||
ReleaseResources();
|
||||
InitResources();
|
||||
}
|
||||
|
||||
TArray<int32> Indices;
|
||||
TArray<FDynamicMeshVertex> Vertices;
|
||||
BuildGeometry(ConstantData, Vertices, Indices);
|
||||
check(Vertices.Num() == GetRequiredVertexCount());
|
||||
check(Indices.Num() == GetRequiredIndexCount());
|
||||
|
||||
if (GetRequiredVertexCount())
|
||||
{
|
||||
ParallelFor(Vertices.Num(), [&](int32 i)
|
||||
{
|
||||
const FDynamicMeshVertex& Vertex = Vertices[i];
|
||||
|
||||
VertexBuffers.PositionVertexBuffer.VertexPosition(i) = Vertex.Position;
|
||||
VertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(i, Vertex.TangentX.ToFVector(), Vertex.GetTangentY(), Vertex.TangentZ.ToFVector());
|
||||
VertexBuffers.StaticMeshVertexBuffer.SetVertexUV(i, 0, Vertex.TextureCoordinate[0]);
|
||||
VertexBuffers.ColorVertexBuffer.VertexColor(i) = Vertex.Color;
|
||||
});
|
||||
|
||||
{
|
||||
auto& VertexBuffer = VertexBuffers.PositionVertexBuffer;
|
||||
void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetNumVertices() * VertexBuffer.GetStride(), RLM_WriteOnly);
|
||||
FMemory::Memcpy(VertexBufferData, VertexBuffer.GetVertexData(), VertexBuffer.GetNumVertices() * VertexBuffer.GetStride());
|
||||
RHIUnlockVertexBuffer(VertexBuffer.VertexBufferRHI);
|
||||
}
|
||||
|
||||
{
|
||||
auto& VertexBuffer = VertexBuffers.ColorVertexBuffer;
|
||||
void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetNumVertices() * VertexBuffer.GetStride(), RLM_WriteOnly);
|
||||
FMemory::Memcpy(VertexBufferData, VertexBuffer.GetVertexData(), VertexBuffer.GetNumVertices() * VertexBuffer.GetStride());
|
||||
RHIUnlockVertexBuffer(VertexBuffer.VertexBufferRHI);
|
||||
}
|
||||
|
||||
{
|
||||
auto& VertexBuffer = VertexBuffers.StaticMeshVertexBuffer;
|
||||
void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.TangentsVertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetTangentSize(), RLM_WriteOnly);
|
||||
FMemory::Memcpy(VertexBufferData, VertexBuffer.GetTangentData(), VertexBuffer.GetTangentSize());
|
||||
RHIUnlockVertexBuffer(VertexBuffer.TangentsVertexBuffer.VertexBufferRHI);
|
||||
}
|
||||
|
||||
{
|
||||
auto& VertexBuffer = VertexBuffers.StaticMeshVertexBuffer;
|
||||
void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.TexCoordVertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetTexCoordSize(), RLM_WriteOnly);
|
||||
FMemory::Memcpy(VertexBufferData, VertexBuffer.GetTexCoordData(), VertexBuffer.GetTexCoordSize());
|
||||
RHIUnlockVertexBuffer(VertexBuffer.TexCoordVertexBuffer.VertexBufferRHI);
|
||||
}
|
||||
|
||||
{
|
||||
void* IndexBufferData = RHILockIndexBuffer(IndexBuffer.IndexBufferRHI, 0, Indices.Num() * sizeof(int32), RLM_WriteOnly);
|
||||
FMemory::Memcpy(IndexBufferData, &Indices[0], Indices.Num() * sizeof(int32));
|
||||
RHIUnlockIndexBuffer(IndexBuffer.IndexBufferRHI);
|
||||
}
|
||||
|
||||
Sections.Empty();
|
||||
for (FGeometryCollectionSection Section : ConstantData->Sections)
|
||||
{
|
||||
if (Section.NumTriangles > 0)
|
||||
{
|
||||
FGeometryCollectionSection NewSection;
|
||||
NewSection.MaterialID = Section.MaterialID;
|
||||
NewSection.FirstIndex = Section.FirstIndex;
|
||||
NewSection.NumTriangles = Section.NumTriangles;
|
||||
NewSection.MinVertexIndex = Section.MinVertexIndex;
|
||||
NewSection.MaxVertexIndex = Section.MaxVertexIndex;
|
||||
Sections.Add(NewSection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void FGeometryCollectionSceneProxy::SetDynamicData_RenderThread(FGeometryCollectionDynamicData* NewDynamicData)
|
||||
{
|
||||
check(IsInRenderingThread());
|
||||
if (GetRequiredVertexCount())
|
||||
{
|
||||
if (DynamicData)
|
||||
{
|
||||
delete DynamicData;
|
||||
DynamicData = nullptr;
|
||||
}
|
||||
DynamicData = NewDynamicData;
|
||||
|
||||
check(VertexBuffers.PositionVertexBuffer.GetNumVertices() == (uint32)ConstantData->Vertices.Num());
|
||||
|
||||
ParallelFor(ConstantData->Vertices.Num(), [&](int32 i)
|
||||
{
|
||||
VertexBuffers.PositionVertexBuffer.VertexPosition(i) = DynamicData->Transforms[ConstantData->BoneMap[i]].TransformPosition(ConstantData->Vertices[i]);
|
||||
});
|
||||
|
||||
{
|
||||
auto& VertexBuffer = VertexBuffers.PositionVertexBuffer;
|
||||
void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetNumVertices() * VertexBuffer.GetStride(), RLM_WriteOnly);
|
||||
FMemory::Memcpy(VertexBufferData, VertexBuffer.GetVertexData(), VertexBuffer.GetNumVertices() * VertexBuffer.GetStride());
|
||||
RHIUnlockVertexBuffer(VertexBuffer.VertexBufferRHI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FMaterialRenderProxy* FGeometryCollectionSceneProxy::GetMaterial(FMeshElementCollector& Collector, int32 MaterialIndex) const
|
||||
{
|
||||
// material for wireframe
|
||||
auto WireframeMaterialInstance = new FColoredMaterialRenderProxy(
|
||||
GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy(IsSelected()) : nullptr,
|
||||
FLinearColor(0, 0.5f, 1.f)
|
||||
);
|
||||
Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance);
|
||||
|
||||
// material for colored bones
|
||||
UMaterial* VertexColorVisualizationMaterial = GEngine->VertexColorMaterial;
|
||||
auto VertexColorVisualizationMaterialInstance = new FColoredMaterialRenderProxy(
|
||||
VertexColorVisualizationMaterial->GetRenderProxy(false, false),
|
||||
GetSelectionColor(FLinearColor::White, false, false)
|
||||
);
|
||||
Collector.RegisterOneFrameMaterialProxy(VertexColorVisualizationMaterialInstance);
|
||||
|
||||
FMaterialRenderProxy* MaterialProxy = nullptr;
|
||||
|
||||
if (ShowBoneColors)
|
||||
{
|
||||
MaterialProxy = VertexColorVisualizationMaterialInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
MaterialProxy = Materials[MaterialIndex]->GetRenderProxy(IsSelected());
|
||||
}
|
||||
|
||||
if (MaterialProxy == nullptr)
|
||||
{
|
||||
MaterialProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy(IsSelected(), IsHovered());
|
||||
}
|
||||
|
||||
return MaterialProxy;
|
||||
}
|
||||
|
||||
void FGeometryCollectionSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
|
||||
{
|
||||
QUICK_SCOPE_CYCLE_COUNTER(STAT_GeometryCollectionSceneProxy_GetDynamicMeshElements);
|
||||
if (GetRequiredVertexCount())
|
||||
{
|
||||
const bool bWireframe = AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe;
|
||||
|
||||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||||
{
|
||||
//Grab the material proxies we'll be using for each section
|
||||
TArray<FMaterialRenderProxy*, TInlineAllocator<32>> MaterialProxies;
|
||||
for (int SectionIndex = 0; SectionIndex < Sections.Num(); SectionIndex++)
|
||||
{
|
||||
const FGeometryCollectionSection& Section = Sections[SectionIndex];
|
||||
FMaterialRenderProxy* MaterialProxy = GetMaterial(Collector, Section.MaterialID);
|
||||
MaterialProxies.Add(MaterialProxy);
|
||||
}
|
||||
|
||||
// Render Batches
|
||||
for (int SectionIndex = 0; SectionIndex < Sections.Num(); SectionIndex++)
|
||||
{
|
||||
const FGeometryCollectionSection& Section = Sections[SectionIndex];
|
||||
|
||||
if (VisibilityMap & (1 << ViewIndex))
|
||||
{
|
||||
const FSceneView* View = Views[ViewIndex];
|
||||
// Draw the mesh.
|
||||
FMeshBatch& Mesh = Collector.AllocateMesh();
|
||||
FMeshBatchElement& BatchElement = Mesh.Elements[0];
|
||||
BatchElement.IndexBuffer = &IndexBuffer;
|
||||
Mesh.bWireframe = bWireframe;
|
||||
Mesh.VertexFactory = &VertexFactory;
|
||||
Mesh.MaterialRenderProxy = MaterialProxies[SectionIndex];
|
||||
BatchElement.PrimitiveUniformBuffer = CreatePrimitiveUniformBufferImmediate(GetLocalToWorld(), GetBounds(), GetLocalBounds(), true, UseEditorDepthTest());
|
||||
BatchElement.FirstIndex = Section.FirstIndex;
|
||||
BatchElement.NumPrimitives = Section.NumTriangles;
|
||||
BatchElement.MinVertexIndex = Section.MinVertexIndex;
|
||||
BatchElement.MaxVertexIndex = Section.MaxVertexIndex;
|
||||
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
|
||||
Mesh.Type = PT_TriangleList;
|
||||
Mesh.DepthPriorityGroup = SDPG_World;
|
||||
Mesh.bCanApplyViewModeOverrides = false;
|
||||
Collector.AddMesh(ViewIndex, Mesh);
|
||||
}
|
||||
}
|
||||
|
||||
// Highlight selected bone using specialized material - when rendering bones as colors we don't need to run this code as the
|
||||
// bone selection is already contained in the rendered colors
|
||||
if (ShowBoneColors||ShowSelectedBones)
|
||||
{
|
||||
FMaterialRenderProxy* MaterialRenderProxy = Materials[BoneSelectionMaterialID]->GetRenderProxy(IsSelected());
|
||||
|
||||
if (VisibilityMap & (1 << ViewIndex))
|
||||
{
|
||||
const FSceneView* View = Views[ViewIndex];
|
||||
FMeshBatch& Mesh = Collector.AllocateMesh();
|
||||
FMeshBatchElement& BatchElement = Mesh.Elements[0];
|
||||
BatchElement.IndexBuffer = &IndexBuffer;
|
||||
Mesh.bWireframe = bWireframe;
|
||||
Mesh.VertexFactory = &VertexFactory;
|
||||
Mesh.MaterialRenderProxy = MaterialRenderProxy;
|
||||
BatchElement.PrimitiveUniformBuffer = CreatePrimitiveUniformBufferImmediate(GetLocalToWorld(), GetBounds(), GetLocalBounds(), true, UseEditorDepthTest());
|
||||
BatchElement.FirstIndex = 0;
|
||||
BatchElement.NumPrimitives = GetRequiredIndexCount() / 3;
|
||||
BatchElement.MinVertexIndex = 0;
|
||||
BatchElement.MaxVertexIndex = GetRequiredVertexCount();
|
||||
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
|
||||
Mesh.Type = PT_TriangleList;
|
||||
Mesh.DepthPriorityGroup = SDPG_World;
|
||||
Mesh.bCanApplyViewModeOverrides = false;
|
||||
Collector.AddMesh(ViewIndex, Mesh);
|
||||
}
|
||||
}
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
if(VisibilityMap & (1 << ViewIndex))
|
||||
{
|
||||
RenderBounds(Collector.GetPDI(ViewIndex), ViewFamily.EngineShowFlags, GetBounds(), IsSelected());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FPrimitiveViewRelevance FGeometryCollectionSceneProxy::GetViewRelevance(const FSceneView* View) const
|
||||
{
|
||||
FPrimitiveViewRelevance Result;
|
||||
Result.bDrawRelevance = IsShown(View);
|
||||
Result.bShadowRelevance = IsShadowCast(View);
|
||||
Result.bDynamicRelevance = true;
|
||||
MaterialRelevance.SetPrimitiveViewRelevance(Result);
|
||||
return Result;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
#pragma once
|
||||
|
||||
#include "DynamicMeshBuilder.h"
|
||||
#include "EngineGlobals.h"
|
||||
#include "PrimitiveViewRelevance.h"
|
||||
#include "PrimitiveSceneProxy.h"
|
||||
#include "StaticMeshResources.h"
|
||||
#include "Rendering/SkinWeightVertexBuffer.h"
|
||||
|
||||
class UGeometryCollectionComponent;
|
||||
struct FGeometryCollectionSection;
|
||||
|
||||
/** Index Buffer */
|
||||
class FGeometryCollectionIndexBuffer : public FIndexBuffer
|
||||
{
|
||||
public:
|
||||
virtual void InitRHI() override
|
||||
{
|
||||
FRHIResourceCreateInfo CreateInfo;
|
||||
IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), NumIndices * sizeof(int32), BUF_Dynamic, CreateInfo);
|
||||
}
|
||||
|
||||
int32 NumIndices;
|
||||
};
|
||||
|
||||
|
||||
/** Immutable rendering data (kind of) */
|
||||
struct FGeometryCollectionConstantData
|
||||
{
|
||||
TArray<FVector> Vertices;
|
||||
TArray<FIntVector> Indices;
|
||||
TArray<FVector> Normals;
|
||||
TArray<FVector> TangentU;
|
||||
TArray<FVector> TangentV;
|
||||
TArray<FVector2D> UVs;
|
||||
TArray<FLinearColor> Colors;
|
||||
TArray<uint16> BoneMap;
|
||||
TArray<FLinearColor> BoneColors;
|
||||
TArray<FGeometryCollectionSection> Sections;
|
||||
};
|
||||
|
||||
/** Mutable rendering data */
|
||||
struct FGeometryCollectionDynamicData
|
||||
{
|
||||
TArray<FMatrix> Transforms;
|
||||
};
|
||||
|
||||
/***
|
||||
* FGeometryCollectionSceneProxy
|
||||
*
|
||||
* The FGeometryCollectionSceneProxy manages the interaction between the GeometryCollectionComponent
|
||||
* on the game thread and the vertex buffers on the render thread.
|
||||
*
|
||||
* NOTE : This class is still in flux, and has a few pending todos. Your comments and
|
||||
* thoughts are appreciated though. The remaining items to address involve:
|
||||
* - @todo double buffer - The double buffering of the FGeometryCollectionDynamicData.
|
||||
* - @todo previous state - Saving the previous FGeometryCollectionDynamicData for rendering motion blur.
|
||||
* - @todo shared memory model - The Asset(or Actor?) should hold the Vertex buffer, and pass the reference to the SceneProxy
|
||||
* - @todo GPU skin : Make the skinning use the GpuVertexShader
|
||||
*/
|
||||
class FGeometryCollectionSceneProxy final : public FPrimitiveSceneProxy
|
||||
{
|
||||
TArray<UMaterialInterface*> Materials;
|
||||
|
||||
FMaterialRelevance MaterialRelevance;
|
||||
|
||||
int32 NumVertices;
|
||||
int32 NumIndices;
|
||||
|
||||
FLocalVertexFactory VertexFactory;
|
||||
FStaticMeshVertexBuffers VertexBuffers;
|
||||
FGeometryCollectionIndexBuffer IndexBuffer;
|
||||
|
||||
TArray<FGeometryCollectionSection> Sections;
|
||||
|
||||
FGeometryCollectionDynamicData* DynamicData;
|
||||
FGeometryCollectionConstantData* ConstantData;
|
||||
|
||||
bool ShowBoneColors;
|
||||
bool ShowSelectedBones;
|
||||
int BoneSelectionMaterialID;
|
||||
|
||||
public:
|
||||
SIZE_T GetTypeHash() const override
|
||||
{
|
||||
static size_t UniquePointer;
|
||||
return reinterpret_cast<size_t>(&UniquePointer);
|
||||
}
|
||||
|
||||
FGeometryCollectionSceneProxy(UGeometryCollectionComponent* Component);
|
||||
|
||||
/** virtual destructor */
|
||||
virtual ~FGeometryCollectionSceneProxy();
|
||||
|
||||
/** Current number of vertices to render */
|
||||
int32 GetRequiredVertexCount() const { return NumVertices;}
|
||||
|
||||
/** Current number of indices to connect */
|
||||
int32 GetRequiredIndexCount() const { return NumIndices; }
|
||||
|
||||
/** Called on render thread to setup static geometry for rendering */
|
||||
void SetConstantData_RenderThread(FGeometryCollectionConstantData* NewConstantData, bool ForceInit = false);
|
||||
|
||||
/** Called on render thread to setup dynamic geometry for rendering */
|
||||
void SetDynamicData_RenderThread(FGeometryCollectionDynamicData* NewDynamicData);
|
||||
|
||||
/** Called on render thread to construct the vertex definitions */
|
||||
void BuildGeometry(const FGeometryCollectionConstantData* ConstantDataIn, TArray<FDynamicMeshVertex>& OutVertices, TArray<int32>& OutIndices);
|
||||
|
||||
/** Called on render thread to setup dynamic geometry for rendering */
|
||||
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override;
|
||||
|
||||
/** Manage the view assignment */
|
||||
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override;
|
||||
|
||||
// @todo allocated size : make this reflect internally allocated memory.
|
||||
virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); }
|
||||
|
||||
/** Size of the base class */
|
||||
uint32 GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); }
|
||||
|
||||
protected:
|
||||
|
||||
/** Create the rendering buffer resources */
|
||||
void InitResources();
|
||||
|
||||
/** Return the rendering buffer resources */
|
||||
void ReleaseResources();
|
||||
|
||||
/** Get material proxy from material ID */
|
||||
FMaterialRenderProxy* GetMaterial(FMeshElementCollector& Collector, int32 MaterialIndex) const;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GeometryCollection/StaticMeshSimulationComponent.h"
|
||||
|
||||
#include "Async/ParallelFor.h"
|
||||
#include "Components/BoxComponent.h"
|
||||
#include "GeometryCollection/GeometryCollectionSimulationTypes.h"
|
||||
#include "GeometryCollection/StaticMeshSolverCallbacks.h"
|
||||
#include "Physics/Experimental/PhysScene_Chaos.h"
|
||||
#include "Chaos/DebugDrawQueue.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "GeometryCollection/StaticMeshSimulationComponentPhysicsProxy.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "ChaosSolversModule.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(UStaticMeshSimulationComponentLogging, NoLogging, All);
|
||||
|
||||
UStaticMeshSimulationComponent::UStaticMeshSimulationComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, Simulating(false)
|
||||
, ObjectType(EObjectTypeEnum::Chaos_Object_Dynamic)
|
||||
, Mass(1.0)
|
||||
, CollisionType(ECollisionTypeEnum::Chaos_Surface_Volumetric)
|
||||
, InitialVelocityType(EInitialVelocityTypeEnum::Chaos_Initial_Velocity_User_Defined)
|
||||
, Friction(0.8)
|
||||
, Bouncyness(0.0)
|
||||
, ChaosSolverActor(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void UStaticMeshSimulationComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
#if INCLUDE_CHAOS
|
||||
// #BG TODO - Find a better place for this - something that gets hit once on scene begin.
|
||||
FChaosSolversModule* ChaosModule = FModuleManager::Get().GetModulePtr<FChaosSolversModule>("ChaosSolvers");
|
||||
Chaos::PBDRigidsSolver* Solver = ChaosSolverActor != nullptr ? ChaosSolverActor->GetSolver() : FPhysScene_Chaos::GetInstance()->GetSolver();
|
||||
ChaosModule->GetDispatcher()->EnqueueCommand(Solver,
|
||||
[InFriction = Friction
|
||||
, InRestitution = Bouncyness
|
||||
, InCollisionIters = ChaosSolverActor != nullptr ? ChaosSolverActor->CollisionIterations : 5
|
||||
, InPushOutIters = ChaosSolverActor != nullptr ? ChaosSolverActor->PushOutIterations : 1
|
||||
, InPushOutPairIters = ChaosSolverActor != nullptr ? ChaosSolverActor->PushOutPairIterations : 1
|
||||
, InCollisionDataSizeMax = ChaosSolverActor != nullptr ? ChaosSolverActor->CollisionDataSizeMax : 1024
|
||||
, InCollisionDataTimeWindow = ChaosSolverActor != nullptr ? ChaosSolverActor->CollisionDataTimeWindow : 0.1
|
||||
, InHasFloor = ChaosSolverActor != nullptr ? ChaosSolverActor->HasFloor : true
|
||||
, InFloorHeight = ChaosSolverActor != nullptr ? ChaosSolverActor->FloorHeight : 0.f]
|
||||
(Chaos::PBDRigidsSolver* InSolver)
|
||||
{
|
||||
InSolver->SetFriction(InFriction);
|
||||
InSolver->SetRestitution(InRestitution);
|
||||
InSolver->SetIterations(InCollisionIters);
|
||||
InSolver->SetPushOutIterations(InPushOutIters);
|
||||
InSolver->SetPushOutPairIterations(InPushOutPairIters);
|
||||
InSolver->SetMaxCollisionDataSize(InCollisionDataSizeMax);
|
||||
InSolver->SetCollisionDataTimeWindow(InCollisionDataTimeWindow);
|
||||
InSolver->SetHasFloor(InHasFloor);
|
||||
InSolver->SetFloorHeight(InFloorHeight);
|
||||
InSolver->SetEnabled(true);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
#if INCLUDE_CHAOS
|
||||
Chaos::PBDRigidsSolver* GetSolver(const UStaticMeshSimulationComponent& StaticMeshSimulationComponent)
|
||||
{
|
||||
return StaticMeshSimulationComponent.ChaosSolverActor != nullptr ? StaticMeshSimulationComponent.ChaosSolverActor->GetSolver() : FPhysScene_Chaos::GetInstance()->GetSolver();
|
||||
}
|
||||
#endif
|
||||
|
||||
void UStaticMeshSimulationComponent::EndPlay(EEndPlayReason::Type ReasonEnd)
|
||||
{
|
||||
#if INCLUDE_CHAOS
|
||||
// @todo : This needs to be removed from the component.
|
||||
Chaos::PBDRigidsSolver* Solver = GetSolver(*this);
|
||||
ensure(Solver);
|
||||
|
||||
FChaosSolversModule* ChaosModule = FModuleManager::Get().GetModulePtr<FChaosSolversModule>("ChaosSolvers");
|
||||
ChaosModule->GetDispatcher()->EnqueueCommand(Solver, [](Chaos::PBDRigidsSolver* InSolver)
|
||||
{
|
||||
InSolver->Reset();
|
||||
});
|
||||
#endif
|
||||
|
||||
Super::EndPlay(ReasonEnd);
|
||||
}
|
||||
|
||||
void UStaticMeshSimulationComponent::OnCreatePhysicsState()
|
||||
{
|
||||
// Skip the chain - don't care about body instance setup
|
||||
UActorComponent::OnCreatePhysicsState();
|
||||
|
||||
#if INCLUDE_CHAOS
|
||||
const bool bValidWorld = GetWorld() && GetWorld()->IsGameWorld();
|
||||
|
||||
// Need to see if we actually have a target for the component
|
||||
AActor* OwningActor = GetOwner();
|
||||
UStaticMeshComponent* TargetComponent = OwningActor->FindComponentByClass<UStaticMeshComponent>();
|
||||
|
||||
if(bValidWorld && TargetComponent)
|
||||
{
|
||||
auto InitFunc = [this, TargetComponent](FStaticMeshSolverCallbacks::Params& InParams)
|
||||
{
|
||||
GetPathName(this, InParams.Name);
|
||||
InParams.InitialTransform = GetOwner()->GetTransform();
|
||||
if(InitialVelocityType == EInitialVelocityTypeEnum::Chaos_Initial_Velocity_User_Defined)
|
||||
{
|
||||
InParams.InitialLinearVelocity = InitialLinearVelocity;
|
||||
InParams.InitialAngularVelocity = InitialAngularVelocity;
|
||||
}
|
||||
|
||||
InParams.Mass = Mass;
|
||||
InParams.ObjectType = ObjectType;
|
||||
|
||||
UStaticMesh* StaticMesh = TargetComponent->GetStaticMesh();
|
||||
|
||||
if(StaticMesh)
|
||||
{
|
||||
FStaticMeshVertexBuffers& VB = StaticMesh->RenderData->LODResources[0].VertexBuffers;
|
||||
const int32 NumVerts = VB.PositionVertexBuffer.GetNumVertices();
|
||||
InParams.MeshVertexPositions.Reset(NumVerts);
|
||||
|
||||
for(int32 VertexIndex = 0; VertexIndex < NumVerts; ++VertexIndex)
|
||||
{
|
||||
InParams.MeshVertexPositions.Add(VB.PositionVertexBuffer.VertexPosition(VertexIndex));
|
||||
}
|
||||
|
||||
if(NumVerts > 0)
|
||||
{
|
||||
TargetComponent->SetMobility(EComponentMobility::Movable);
|
||||
InParams.bSimulating = Simulating;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto SyncFunc = [TargetComponent](const FTransform& InTransform)
|
||||
{
|
||||
TargetComponent->SetWorldTransform(InTransform);
|
||||
};
|
||||
|
||||
PhysicsProxy = new FStaticMeshSimulationComponentPhysicsProxy(InitFunc, SyncFunc);
|
||||
|
||||
TSharedPtr<FPhysScene_Chaos> Scene = GetPhysicsScene();
|
||||
Scene->AddProxy(PhysicsProxy);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UStaticMeshSimulationComponent::OnDestroyPhysicsState()
|
||||
{
|
||||
UActorComponent::OnDestroyPhysicsState();
|
||||
|
||||
#if INCLUDE_CHAOS
|
||||
if(PhysicsProxy)
|
||||
{
|
||||
// Handle scene remove, right now we rely on the reset of EndPlay to clean up
|
||||
TSharedPtr<FPhysScene_Chaos> Scene = GetPhysicsScene();
|
||||
Scene->RemoveProxy(PhysicsProxy);
|
||||
|
||||
// Discard the pointer
|
||||
PhysicsProxy = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UStaticMeshSimulationComponent::ShouldCreatePhysicsState() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UStaticMeshSimulationComponent::HasValidPhysicsState() const
|
||||
{
|
||||
return PhysicsProxy != nullptr;
|
||||
}
|
||||
|
||||
#if INCLUDE_CHAOS
|
||||
const TSharedPtr<FPhysScene_Chaos> UStaticMeshSimulationComponent::GetPhysicsScene() const
|
||||
{
|
||||
if (ChaosSolverActor)
|
||||
{
|
||||
return ChaosSolverActor->GetPhysicsScene();
|
||||
}
|
||||
else
|
||||
{
|
||||
return FPhysScene_Chaos::GetInstance();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user