// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. #include "GeometryCollection/GeometryCollectionComponent.h" #include "Async/ParallelFor.h" #include "Components/BoxComponent.h" #include "GeometryCollection/GeometryCollectionObject.h" #include "GeometryCollection/GeometryCollectionAlgo.h" #include "GeometryCollection/GeometryCollectionComponentPluginPrivate.h" #include "GeometryCollection/GeometryCollectionSceneProxy.h" #include "GeometryCollection/GeometryCollectionSQAccelerator.h" #include "GeometryCollection/GeometryCollectionUtility.h" #include "GeometryCollection/GeometryCollectionClusteringUtility.h" #include "GeometryCollection/GeometryCollectionPhysicsProxy.h" #include "GeometryCollection/GeometryCollectionCache.h" #include "Physics/Experimental/PhysScene_Chaos.h" #include "Chaos/DebugDrawQueue.h" #include "DrawDebugHelpers.h" #include "Modules/ModuleManager.h" #include "ChaosSolversModule.h" #include "ChaosStats.h" #if WITH_EDITOR #include "AssetToolsModule.h" #include "Editor.h" #endif DEFINE_LOG_CATEGORY_STATIC(UGCC_LOG, NoLogging, All); #if INCLUDE_CHAOS && WITH_PHYSX FGeometryCollectionSQAccelerator GlobalGeomCollectionAccelerator; //todo(ocohen): proper lifetime management needed void HackRegisterGeomAccelerator(UGeometryCollectionComponent& Component) { if (UWorld* World = Component.GetWorld()) { if (FPhysScene* PhysScene = World->GetPhysicsScene()) { PhysScene->GetSQAcceleratorUnion()->AddSQAccelerator(&GlobalGeomCollectionAccelerator); } } } #endif FGeomComponentCacheParameters::FGeomComponentCacheParameters() : CacheMode(EGeometryCollectionCacheType::None) , TargetCache(nullptr) , ReverseCacheBeginTime(0.0f) , SaveCollisionData(false) , CollisionDataMaxSize(1024) , DoCollisionDataSpatialHash(true) , SpatialHashRadius(15.f) , MaxCollisionPerCell(1) , SaveTrailingData(false) , TrailingDataSizeMax(1024) , TrailingMinSpeedThreshold(100.f) , TrailingMinVolumeThreshold(10000.f) { } UGeometryCollectionComponent::UGeometryCollectionComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , ChaosSolverActor(nullptr) , Simulating(true) , ObjectType(EObjectTypeEnum::Chaos_Object_Dynamic) , EnableClustering(true) , MaxClusterLevel(100) , DamageThreshold({250.0}) , CollisionType(ECollisionTypeEnum::Chaos_Surface_Volumetric) , ImplicitType(EImplicitTypeEnum::Chaos_Implicit_Cube) , MinLevelSetResolution(5) , MaxLevelSetResolution(10) , MassAsDensity(false) , Mass(1.0) , MinimumMassClamp(0.1) , CollisionParticlesFraction(1.0) , Friction(0.8) , Bouncyness(0.0) , LinearSleepingThreshold(1.f) , AngularSleepingThreshold(1.f) , InitialVelocityType(EInitialVelocityTypeEnum::Chaos_Initial_Velocity_User_Defined) , InitialLinearVelocity(0.f, 0.f, 0.f) , InitialAngularVelocity(0.f, 0.f, 0.f) , bRenderStateDirty(true) , ShowBoneColors(false) , ShowSelectedBones(false) , ViewLevel(-1) , PhysicsProxy(nullptr) #if WITH_EDITOR && WITH_EDITORONLY_DATA , EditorActor(nullptr) #endif { PrimaryComponentTick.bCanEverTick = true; bTickInEditor = true; bAutoActivate = true; DummyBoxComponent = CreateDefaultSubobject(TEXT("DummyBoxComponent")); //TODO(ocohen):this is just a placeholder for now so we can keep using physx API for SQ DummyBoxComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly); DummyBoxComponent->SetCollisionResponseToAllChannels(ECR_Block); DummyBoxComponent->SetWorldLocation(FVector(0.f, 0.f, -9999999.f)); //we rely on the shape's collision filter so we need to hide this thing. All of this needs to go away when refactor is finished //DummyBoxComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform); } #if INCLUDE_CHAOS Chaos::PBDRigidsSolver* GetSolver(const UGeometryCollectionComponent& GeometryCollectionComponent) { return GeometryCollectionComponent.ChaosSolverActor != nullptr ? GeometryCollectionComponent.ChaosSolverActor->GetSolver() : FPhysScene_Chaos::GetInstance()->GetSolver(); } #endif void UGeometryCollectionComponent::BeginPlay() { Super::BeginPlay(); #if INCLUDE_CHAOS #if WITH_PHYSX GlobalGeomCollectionAccelerator.AddComponent(this); HackRegisterGeomAccelerator(*this); #endif if (DynamicCollection) { ////////////////////////////////////////////////////////////////////////// // Commenting out these callbacks for now due to the threading model. The callbacks here // expect the rest collection to be mutable which is not the case when running in multiple // threads. Ideally we have some separate animation collection or track that we cache to // without affecting the data we've dispatched to the physics thread ////////////////////////////////////////////////////////////////////////// // ---------- SolverCallbacks->SetResetAnimationCacheFunction([&]() // ---------- { // ---------- FGeometryCollectionEdit Edit = EditRestCollection(); // ---------- Edit.GetRestCollection()->RecordedData.SetNum(0); // ---------- }); // ---------- SolverCallbacks->SetUpdateTransformsFunction([&](const TArrayView&) // ---------- { // ---------- // todo : Move the update to the array passed here... // ---------- }); // ---------- // ---------- SolverCallbacks->SetUpdateRestStateFunction([&](const int32 & CurrentFrame, const TManagedArray & RigidBodyID, const TManagedArray& Hierarchy, const FSolverCallbacks::FParticlesType& Particles) // ---------- { // ---------- FGeometryCollectionEdit Edit = EditRestCollection(); // ---------- UGeometryCollection * RestCollection = Edit.GetRestCollection(); // ---------- check(RestCollection); // ---------- // ---------- if (CurrentFrame >= RestCollection->RecordedData.Num()) // ---------- { // ---------- RestCollection->RecordedData.SetNum(CurrentFrame + 1); // ---------- RestCollection->RecordedData[CurrentFrame].SetNum(RigidBodyID.Num()); // ---------- ParallelFor(RigidBodyID.Num(), [&](int32 i) // ---------- { // ---------- if (!Hierarchy[i].Children.Num()) // ---------- { // ---------- RestCollection->RecordedData[CurrentFrame][i].SetTranslation(Particles.X(RigidBodyID[i])); // ---------- RestCollection->RecordedData[CurrentFrame][i].SetRotation(Particles.R(RigidBodyID[i])); // ---------- } // ---------- else // ---------- { // ---------- RestCollection->RecordedData[CurrentFrame][i].SetTranslation(FVector::ZeroVector); // ---------- RestCollection->RecordedData[CurrentFrame][i].SetRotation(FQuat::Identity); // ---------- } // ---------- }); // ---------- } // ---------- }); ////////////////////////////////////////////////////////////////////////// // #BG TODO - Find a better place for this - something that gets hit once on scene begin. FChaosSolversModule* ChaosModule = FModuleManager::Get().GetModulePtr("ChaosSolvers"); Chaos::PBDRigidsSolver* Solver = GetSolver(*this); ensure(Solver); ChaosModule->GetDispatcher()->EnqueueCommand(Solver, [ InFriction = Friction , InRestitution = Bouncyness , InLinearSleepThreshold = LinearSleepingThreshold , InAngularSleepThreshold = AngularSleepingThreshold , InDtMultiplier = ChaosSolverActor != nullptr ? ChaosSolverActor->TimeStepMultiplier : 1.f , 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.1f , InDoCollisionDataSpatialHash = ChaosSolverActor != nullptr ? ChaosSolverActor->DoCollisionDataSpatialHash : true , InCollisionDataSpatialHashRadius = ChaosSolverActor != nullptr ? ChaosSolverActor->CollisionDataSpatialHashRadius : 15.f , InMaxCollisionPerCell = ChaosSolverActor != nullptr ? ChaosSolverActor->MaxCollisionPerCell : 1 , InTrailingDataSizeMax = ChaosSolverActor != nullptr ? ChaosSolverActor->TrailingDataSizeMax : 1024 , InTrailingDataTimeWindow = ChaosSolverActor != nullptr ? ChaosSolverActor->TrailingDataTimeWindow : 0.1f , InTrailingMinSpeedThreshold = ChaosSolverActor != nullptr ? ChaosSolverActor->TrailingMinSpeedThreshold : 100.f , InTrailingMinVolumeThreshold = ChaosSolverActor != nullptr ? ChaosSolverActor->TrailingMinVolumeThreshold : 10000.f , InHasFloor = ChaosSolverActor != nullptr ? ChaosSolverActor->HasFloor : true , InFloorHeight = ChaosSolverActor != nullptr ? ChaosSolverActor->FloorHeight : 0.f] (Chaos::PBDRigidsSolver* InSolver) { InSolver->SetFriction(InFriction); InSolver->SetRestitution(InRestitution); InSolver->SetSleepThresholds(InLinearSleepThreshold, InAngularSleepThreshold); InSolver->SetTimeStepMultiplier(InDtMultiplier); InSolver->SetIterations(InCollisionIters); InSolver->SetPushOutIterations(InPushOutIters); InSolver->SetPushOutPairIterations(InPushOutPairIters); InSolver->SetMaxCollisionDataSize(InCollisionDataSizeMax); InSolver->SetCollisionDataTimeWindow(InCollisionDataTimeWindow); InSolver->SetDoCollisionDataSpatialHash(InDoCollisionDataSpatialHash); InSolver->SetCollisionDataSpatialHashRadius(InCollisionDataSpatialHashRadius); InSolver->SetMaxCollisionPerCell(InMaxCollisionPerCell); InSolver->SetMaxTrailingDataSize(InTrailingDataSizeMax); InSolver->SetTrailingDataTimeWindow(InTrailingDataTimeWindow); InSolver->SetTrailingMinSpeedThreshold(InTrailingMinSpeedThreshold); InSolver->SetTrailingMinVolumeThreshold(InTrailingMinVolumeThreshold); InSolver->SetHasFloor(InHasFloor); InSolver->SetFloorHeight(InFloorHeight); InSolver->SetEnabled(true); }); } #endif } void UGeometryCollectionComponent::EndPlay(EEndPlayReason::Type ReasonEnd) { #if INCLUDE_CHAOS #if WITH_PHYSX GlobalGeomCollectionAccelerator.RemoveComponent(this); #endif Chaos::PBDRigidsSolver* Solver = GetSolver(*this); ensure(Solver); FChaosSolversModule* ChaosModule = FModuleManager::Get().GetModulePtr("ChaosSolvers"); ChaosModule->GetDispatcher()->EnqueueCommand(Solver, [](Chaos::PBDRigidsSolver* InSolver) { InSolver->Reset(); }); #endif #if WITH_EDITOR && WITH_EDITORONLY_DATA // Track our editor component if needed for syncing simulations back from PIE on shutdown EditorActor = EditorUtilities::GetEditorWorldCounterpartActor(GetTypedOuter()); #endif Super::EndPlay(ReasonEnd); } FBoxSphereBounds UGeometryCollectionComponent::CalcBounds(const FTransform& LocalToWorldIn) const { SCOPE_CYCLE_COUNTER(STAT_GCUpdateBounds); if (DynamicCollection && DynamicCollection->HasVisibleGeometry()) { FGeometryCollection* Collection = DynamicCollection->GetGeometryCollection().Get(); check(Collection); FBox BoundingBox(ForceInit); const TManagedArray& BoundingBoxes = *Collection->BoundingBox; const TManagedArray& TransformIndices = *Collection->TransformIndex; const TManagedArray& BoneHierarchyArray = *Collection->BoneHierarchy; TArray Transforms; GeometryCollectionAlgo::GlobalMatrices(Collection, Transforms); const int32 NumBoxes = BoundingBoxes.Num(); for(int32 BoxIdx = 0; BoxIdx < NumBoxes; ++BoxIdx) { int32 TransformIndex = TransformIndices[BoxIdx]; if(BoneHierarchyArray[TransformIndex].IsGeometry()) { BoundingBox += BoundingBoxes[BoxIdx].TransformBy(Transforms[TransformIndices[BoxIdx]] * LocalToWorldIn); } } return FBoxSphereBounds(BoundingBox); } return FBoxSphereBounds(ForceInitToZero); } void UGeometryCollectionComponent::CreateRenderState_Concurrent() { Super::CreateRenderState_Concurrent(); if (SceneProxy && DynamicCollection && DynamicCollection->HasVisibleGeometry()) { FGeometryCollectionConstantData * ConstantData = ::new FGeometryCollectionConstantData; InitConstantData(ConstantData); FGeometryCollectionDynamicData * DynamicData = ::new FGeometryCollectionDynamicData; InitDynamicData(DynamicData); // Enqueue command to send to render thread ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(FSendGeometryCollectionData, FGeometryCollectionSceneProxy*, GeometryCollectionSceneProxy, (FGeometryCollectionSceneProxy*)SceneProxy, FGeometryCollectionConstantData*, ConstantData, ConstantData, FGeometryCollectionDynamicData*, DynamicData, DynamicData, { GeometryCollectionSceneProxy->SetConstantData_RenderThread(ConstantData); GeometryCollectionSceneProxy->SetDynamicData_RenderThread(DynamicData); }); } } FPrimitiveSceneProxy* UGeometryCollectionComponent::CreateSceneProxy() { if (DynamicCollection) { return new FGeometryCollectionSceneProxy(this); } return nullptr; } bool UGeometryCollectionComponent::ShouldCreatePhysicsState() const { // Geometry collections always create physics state, not relying on the // underlying implementation that requires the body instance to decide return true; } bool UGeometryCollectionComponent::HasValidPhysicsState() const { return PhysicsProxy != nullptr; } void UGeometryCollectionComponent::InitConstantData(FGeometryCollectionConstantData * ConstantData) { check(ConstantData); check(DynamicCollection); FGeometryCollection* Collection = DynamicCollection->GetGeometryCollection().Get(); check(Collection); int32 NumPoints = Collection->NumElements(FGeometryCollection::VerticesGroup); TManagedArray& Vertex = *Collection->Vertex; TManagedArray& BoneMap = *Collection->BoneMap; TManagedArray& TangentU = *Collection->TangentU; TManagedArray& TangentV = *Collection->TangentV; TManagedArray& Normal = *Collection->Normal; TManagedArray& UV = *Collection->UV; TManagedArray& Color = *Collection->Color; TManagedArray& BoneHierarchy = *Collection->BoneHierarchy; TManagedArray& BoneColors = *Collection->BoneColor; ConstantData->Vertices.AddUninitialized(NumPoints); ConstantData->BoneMap.AddUninitialized(NumPoints); ConstantData->TangentU.AddUninitialized(NumPoints); ConstantData->TangentV.AddUninitialized(NumPoints); ConstantData->Normals.AddUninitialized(NumPoints); ConstantData->UVs.AddUninitialized(NumPoints); ConstantData->Colors.AddUninitialized(NumPoints); ConstantData->BoneColors.AddUninitialized(NumPoints); ParallelFor(NumPoints, [&](int32 PointIdx) { ConstantData->Vertices[PointIdx] = Vertex[PointIdx]; ConstantData->BoneMap[PointIdx] = BoneMap[PointIdx]; ConstantData->TangentU[PointIdx] = TangentU[PointIdx]; ConstantData->TangentV[PointIdx] = TangentV[PointIdx]; ConstantData->Normals[PointIdx] = Normal[PointIdx]; ConstantData->UVs[PointIdx] = UV[PointIdx]; ConstantData->Colors[PointIdx] = Color[PointIdx]; int32 BoneIndex = ConstantData->BoneMap[PointIdx]; ConstantData->BoneColors[PointIdx] = BoneColors[BoneIndex]; }); int32 NumIndices = 0; TManagedArray& Indices = *Collection->Indices; TManagedArray& Visible = *Collection->Visible; TManagedArray& MaterialIndex = *Collection->MaterialIndex; for (int vdx = 0; vdx < Collection->NumElements(FGeometryCollection::FacesGroup); vdx++) { NumIndices += static_cast(Visible[vdx]); } ConstantData->Indices.AddUninitialized(NumIndices); for (int IndexIdx = 0, cdx = 0; IndexIdx < Collection->NumElements(FGeometryCollection::FacesGroup); IndexIdx++) { if (Visible[ MaterialIndex[IndexIdx] ]) { ConstantData->Indices[cdx++] = Indices[ MaterialIndex[IndexIdx] ]; } } // We need to correct the section index start point & number of triangles since only the visible ones have been copied across in the code above int32 NumMaterialSections = Collection->NumElements(FGeometryCollection::MaterialGroup); ConstantData->Sections.AddUninitialized(NumMaterialSections); TManagedArray& Sections = *Collection->Sections; for (int sdx = 0; sdx < NumMaterialSections; sdx++) { FGeometryCollectionSection Section = Sections[sdx]; // deliberate copy for (int32 TriangleIndex = 0; TriangleIndex < Sections[sdx].FirstIndex / 3; TriangleIndex++) { if (!Visible[MaterialIndex[TriangleIndex]]) Section.FirstIndex-=3; } for (int32 TriangleIndex = 0; TriangleIndex < Sections[sdx].NumTriangles; TriangleIndex++) { if (!Visible[MaterialIndex[Sections[sdx].FirstIndex / 3 + TriangleIndex]]) Section.NumTriangles--; } ConstantData->Sections[sdx] = Section; } } void UGeometryCollectionComponent::InitDynamicData(FGeometryCollectionDynamicData * DynamicData) { check(DynamicData); check(DynamicCollection); FGeometryCollection* Collection = DynamicCollection->GetGeometryCollection().Get(); check(Collection); TArray GlobalMatrices; GeometryCollectionAlgo::GlobalMatrices(Collection, GlobalMatrices); int32 NumTransforms = Collection->NumElements(FGeometryCollection::TransformGroup); DynamicData->Transforms.AddUninitialized(NumTransforms); check(GlobalMatrices.Num() == NumTransforms); //ParallelFor(NumTransforms, [&](int32 MatrixIdx) for(int MatrixIdx=0;MatrixIdxTransforms[MatrixIdx] = GlobalMatrices[MatrixIdx].ToMatrixWithScale(); UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent::InitDynamicData::[%p][%d](%s)(%s)"), Collection, MatrixIdx, *GlobalMatrices[MatrixIdx].GetTranslation().ToString(), *GlobalMatrices[MatrixIdx].GetRotation().ToString()); }//); } void UGeometryCollectionComponent::ForceInitRenderData() { // Reset SceneProxy state to reflect the change in visible geometry FGeometryCollectionConstantData * ConstantData = ::new FGeometryCollectionConstantData; InitConstantData(ConstantData); FGeometryCollectionDynamicData * DynamicData = ::new FGeometryCollectionDynamicData; InitDynamicData(DynamicData); // Enqueue command to send to render thread ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(FSendGeometryCollectionData, FGeometryCollectionSceneProxy*, GeometryCollectionSceneProxy, (FGeometryCollectionSceneProxy*)SceneProxy, FGeometryCollectionConstantData*, ConstantData, ConstantData, FGeometryCollectionDynamicData*, DynamicData, DynamicData, { GeometryCollectionSceneProxy->SetConstantData_RenderThread(ConstantData, true); GeometryCollectionSceneProxy->SetDynamicData_RenderThread(DynamicData); }); } #if CHAOS_DEBUG_DRAW void DebugDrawChaos(UWorld* World) { if (!World->IsGameWorld()) { return; } using namespace Chaos; TArray LatenetDrawCommands; FDebugDrawQueue::GetInstance().ExtractAllElements(LatenetDrawCommands); for (const FLatentDrawCommand& Command : LatenetDrawCommands) { switch (Command.Type) { case FLatentDrawCommand::EDrawType::Point: { DrawDebugPoint(World, Command.LineStart, Command.Thickness, Command.Color, Command.bPersistentLines, Command.LifeTime, Command.DepthPriority); break; } case FLatentDrawCommand::EDrawType::Line: { DrawDebugLine(World, Command.LineStart, Command.LineEnd, Command.Color, Command.bPersistentLines, Command.LifeTime, Command.DepthPriority, Command.Thickness); break; } case FLatentDrawCommand::EDrawType::DirectionalArrow: { DrawDebugDirectionalArrow(World, Command.LineStart, Command.LineEnd, Command.ArrowSize, Command.Color, Command.bPersistentLines, Command.LifeTime, Command.DepthPriority, Command.Thickness); break; } case FLatentDrawCommand::EDrawType::Sphere: { DrawDebugSphere(World, Command.LineStart, Command.Radius, Command.Segments, Command.Color, Command.bPersistentLines, Command.LifeTime, Command.DepthPriority, Command.Thickness); break; } case FLatentDrawCommand::EDrawType::Box: { DrawDebugBox(World, Command.Center, Command.Extent, Command.Rotation, Command.Color, Command.bPersistentLines, Command.LifeTime, Command.DepthPriority, Command.Thickness); break; } default: break; } } } #endif void UGeometryCollectionComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { //UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent[%p]::TickComponent()"), this); Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (bRenderStateDirty && DynamicCollection) { check(DynamicCollection->GetGeometryCollection()); if (DynamicCollection->HasVisibleGeometry() || DynamicCollection->GetGeometryCollection()->IsDirty()) { // #BGallagher below is required for dynamic bounds update but // it's too slow on large components currently, re-enable when faster //MarkRenderTransformDirty(); MarkRenderDynamicDataDirty(); bRenderStateDirty = false; DynamicCollection->GetGeometryCollection()->MakeClean(); } } #if CHAOS_DEBUG_DRAW using namespace Chaos; // General debug drawing if (FDebugDrawQueue::EnableDebugDrawing) { if (UWorld* World = GetWorld()) { DebugDrawChaos(World); } } #endif } void UGeometryCollectionComponent::OnRegister() { //UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent[%p]::OnRegister()[%p]"), this,RestCollection ); Super::OnRegister(); ResetDynamicCollection(); } void UGeometryCollectionComponent::ResetDynamicCollection() { //UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent[%p]::ResetDynamicCollection()"), static_cast(this)); if (RestCollection) { DynamicCollection = NewObject(this); DynamicCollection->Initialize(*RestCollection->GetGeometryCollection()); DynamicCollection->GetGeometryCollection()->LocalizeAttribute("Transform", FGeometryCollection::TransformGroup); DynamicCollection->GetGeometryCollection()->LocalizeAttribute("BoneHierarchy", FGeometryCollection::TransformGroup); SetRenderStateDirty(); } } void UGeometryCollectionComponent::OnCreatePhysicsState() { // Skip the chain - don't care about body instance setup UActorComponent::OnCreatePhysicsState(); #if WITH_EDITOR && WITH_EDITORONLY_DATA EditorActor = nullptr; #endif #if INCLUDE_CHAOS const bool bValidWorld = GetWorld() && GetWorld()->IsGameWorld(); const bool bValidCollection = DynamicCollection && DynamicCollection->GetGeometryCollection().Get()->Transform->Num() > 0; if(bValidWorld && bValidCollection) { auto InitFunc = [this](FSimulationParameters& InParams, FFieldSystem& InFieldSystem) { InParams.RestCollection = GetRestCollection()->GetGeometryCollection().Get(); InParams.Simulating = Simulating; InParams.WorldTransform = GetComponentToWorld(); InParams.ObjectType = ObjectType; InParams.CollisionType = CollisionType; InParams.ImplicitType = ImplicitType; InParams.MinLevelSetResolution = MinLevelSetResolution; InParams.MaxLevelSetResolution = MaxLevelSetResolution; InParams.EnableClustering = EnableClustering; InParams.MaxClusterLevel = MaxClusterLevel; InParams.DamageThreshold = DamageThreshold; InParams.MassAsDensity = MassAsDensity; InParams.Mass = Mass; InParams.MinimumMassClamp = MinimumMassClamp; InParams.CollisionParticlesFraction = CollisionParticlesFraction; InParams.Friction = Friction; InParams.Bouncyness = Bouncyness; InParams.InitialVelocityType = InitialVelocityType; InParams.InitialLinearVelocity = InitialLinearVelocity; InParams.InitialAngularVelocity = InitialAngularVelocity; InParams.bClearCache = true; InParams.CacheType = CacheParameters.CacheMode; InParams.ReverseCacheBeginTime = CacheParameters.ReverseCacheBeginTime; InParams.SaveCollisionData = CacheParameters.SaveCollisionData; InParams.CollisionDataMaxSize = CacheParameters.CollisionDataMaxSize; InParams.DoCollisionDataSpatialHash = CacheParameters.DoCollisionDataSpatialHash; InParams.SpatialHashRadius = CacheParameters.SpatialHashRadius; InParams.MaxCollisionPerCell = CacheParameters.MaxCollisionPerCell; InParams.SaveTrailingData = CacheParameters.SaveTrailingData; InParams.TrailingDataSizeMax = CacheParameters.TrailingDataSizeMax; InParams.TrailingMinSpeedThreshold = CacheParameters.TrailingMinSpeedThreshold; InParams.TrailingMinVolumeThreshold = CacheParameters.TrailingMinVolumeThreshold; InParams.RecordedTrack = (InParams.IsCachePlaying() && CacheParameters.TargetCache) ? CacheParameters.TargetCache->GetData() : nullptr; if(FieldSystem && FieldSystem->GetFieldSystemComponent() && FieldSystem->GetFieldSystemComponent()->GetFieldSystem() ) { InFieldSystem.BuildFrom(FieldSystem->GetFieldSystemComponent()->GetFieldSystem()->GetFieldData()); } }; auto CacheSyncFunc = [this](const TManagedArray& BodyIds) { RigidBodyIds.Init(BodyIds); }; auto FinalSyncFunc = [this](const FRecordedTransformTrack& InTrack) { #if WITH_EDITOR && WITH_EDITORONLY_DATA if(CacheParameters.CacheMode == EGeometryCollectionCacheType::Record && InTrack.Records.Num() > 0) { Modify(); if(!CacheParameters.TargetCache) { CacheParameters.TargetCache = UGeometryCollectionCache::CreateCacheForCollection(RestCollection); } if(CacheParameters.TargetCache) { // Queue this up to be dirtied after PIE ends TSharedPtr Scene = GetPhysicsScene(); CacheParameters.TargetCache->PreEditChange(nullptr); CacheParameters.TargetCache->Modify(); CacheParameters.TargetCache->SetFromRawTrack(InTrack); CacheParameters.TargetCache->PostEditChange(); Scene->AddPieModifiedObject(CacheParameters.TargetCache); if(EditorActor) { UGeometryCollectionComponent* EditorComponent = Cast(EditorUtilities::FindMatchingComponentInstance(this, EditorActor)); if(EditorComponent) { EditorComponent->PreEditChange(FindField(EditorComponent->GetClass(), GET_MEMBER_NAME_CHECKED(UGeometryCollectionComponent, CacheParameters))); EditorComponent->Modify(); EditorComponent->CacheParameters.TargetCache = CacheParameters.TargetCache; EditorComponent->PostEditChange(); Scene->AddPieModifiedObject(EditorComponent); Scene->AddPieModifiedObject(EditorActor); } EditorActor = nullptr; } } } #endif }; PhysicsProxy = new FGeometryCollectionPhysicsProxy(DynamicCollection->GetGeometryCollection().Get(), InitFunc, CacheSyncFunc, FinalSyncFunc); TSharedPtr Scene = GetPhysicsScene(); Scene->AddProxy(PhysicsProxy); } #endif } void UGeometryCollectionComponent::OnDestroyPhysicsState() { UActorComponent::OnDestroyPhysicsState(); #if INCLUDE_CHAOS if(PhysicsProxy) { // Handle scene remove, right now we rely on the reset of EndPlay to clean up TSharedPtr Scene = GetPhysicsScene(); Scene->RemoveProxy(PhysicsProxy); // Discard the pointer PhysicsProxy = nullptr; } #endif } void UGeometryCollectionComponent::SendRenderDynamicData_Concurrent() { //UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent[%p]::SendRenderDynamicData_Concurrent()"), this); Super::SendRenderDynamicData_Concurrent(); if (SceneProxy) { if (DynamicCollection) { FGeometryCollectionDynamicData * DynamicData = ::new FGeometryCollectionDynamicData; InitDynamicData(DynamicData); // Enqueue command to send to render thread ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(FSendGeometryCollectionData, FGeometryCollectionSceneProxy*, GeometryCollectionSceneProxy, (FGeometryCollectionSceneProxy*)SceneProxy, FGeometryCollectionDynamicData*, DynamicData, DynamicData, { GeometryCollectionSceneProxy->SetDynamicData_RenderThread(DynamicData); }); } } } void UGeometryCollectionComponent::SetRestCollection(UGeometryCollection * RestCollectionIn) { //UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent[%p]::SetRestCollection()"), this); if (RestCollectionIn) { RestCollection = RestCollectionIn; // All rest states are shared across components and will have a AssetScope. RestCollection->GetGeometryCollection()->SetArrayScopes(FManagedArrayCollection::EArrayScope::FScopeShared); ResetDynamicCollection(); RestCollection->Modify(); } } FGeometryCollectionEdit::FGeometryCollectionEdit(UGeometryCollectionComponent * InComponent, bool InUpdate /*= true*/) : Component(InComponent) , bUpdate(InUpdate) { bHadPhysicsState = Component->HasValidPhysicsState(); if(bUpdate && bHadPhysicsState) { Component->DestroyPhysicsState(); } } FGeometryCollectionEdit::~FGeometryCollectionEdit() { if (bUpdate) { Component->ResetDynamicCollection(); if (GetRestCollection()) { GetRestCollection()->Modify(); } if(bHadPhysicsState) { Component->RecreatePhysicsState(); } } } UGeometryCollection* FGeometryCollectionEdit::GetRestCollection() { if (Component) { return Component->RestCollection; } return nullptr; } TArray FScopedColorEdit::RandomColors; FScopedColorEdit::FScopedColorEdit(UGeometryCollectionComponent* InComponent) : Component(InComponent) { if (RandomColors.Num() == 0) { for (int i = 0; i < 100; i++) { const FColor Color(FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, 255); RandomColors.Push(FLinearColor(Color)); } } } FScopedColorEdit::~FScopedColorEdit() { UpdateBoneColors(); } void FScopedColorEdit::SetShowBoneColors(bool ShowBoneColorsIn) { Component->ShowBoneColors = ShowBoneColorsIn; } bool FScopedColorEdit::GetShowBoneColors() const { return Component->ShowBoneColors; } void FScopedColorEdit::SetShowSelectedBones(bool ShowSelectedBonesIn) { Component->ShowSelectedBones = ShowSelectedBonesIn; } bool FScopedColorEdit::GetShowSelectedBones() const { return Component->ShowSelectedBones; } bool FScopedColorEdit::IsBoneSelected(int BoneIndex) const { return Component->SelectedBones.Contains(BoneIndex); } void FScopedColorEdit::SetSelectedBones(const TArray& SelectedBonesIn) { Component->SelectedBones = SelectedBonesIn; } void FScopedColorEdit::AppendSelectedBones(const TArray& SelectedBonesIn) { Component->SelectedBones.Append(SelectedBonesIn); } void FScopedColorEdit::AddSelectedBone(int32 BoneIndex) { Component->SelectedBones.Push(BoneIndex); } void FScopedColorEdit::ClearSelectedBone(int32 BoneIndex) { Component->SelectedBones.Remove(BoneIndex); } const TArray& FScopedColorEdit::GetSelectedBones() const { return Component->GetSelectedBones(); } void FScopedColorEdit::ResetBoneSelection() { Component->SelectedBones.Empty(); } void FScopedColorEdit::SelectBones(GeometryCollection::ESelectionMode SelectionMode) { check(Component); const UGeometryCollection* GeometryCollection = Component->GetRestCollection(); if (GeometryCollection) { TSharedPtr GeometryCollectionPtr = GeometryCollection->GetGeometryCollection(); switch (SelectionMode) { case GeometryCollection::ESelectionMode::None: ResetBoneSelection(); break; case GeometryCollection::ESelectionMode::AllGeometry: { TArray Roots; FGeometryCollectionClusteringUtility::GetRootBones(GeometryCollectionPtr.Get(), Roots); ResetBoneSelection(); for (int32 RootElement : Roots) { TArray LeafBones; FGeometryCollectionClusteringUtility::GetLeafBones(GeometryCollectionPtr.Get(), RootElement, LeafBones); AppendSelectedBones(LeafBones); } } break; case GeometryCollection::ESelectionMode::InverseGeometry: { TArray Roots; FGeometryCollectionClusteringUtility::GetRootBones(GeometryCollectionPtr.Get(), Roots); TArray NewSelection; for (int32 RootElement : Roots) { TArray LeafBones; FGeometryCollectionClusteringUtility::GetLeafBones(GeometryCollectionPtr.Get(), RootElement, LeafBones); for (int32 Element : LeafBones) { if (!IsBoneSelected(Element)) { NewSelection.Push(Element); } } } ResetBoneSelection(); AppendSelectedBones(NewSelection); } break; default: check(false); // unexpected selection mode break; } const TArray SelectedBones = GetSelectedBones(); SetHighlightedBones(SelectedBones); } } bool FScopedColorEdit::IsBoneHighlighted(int BoneIndex) const { return Component->HighlightedBones.Contains(BoneIndex); } void FScopedColorEdit::SetHighlightedBones(const TArray& HighlightedBonesIn) { Component->HighlightedBones = HighlightedBonesIn; } void FScopedColorEdit::AddHighlightedBone(int32 BoneIndex) { Component->HighlightedBones.Push(BoneIndex); } const TArray& FScopedColorEdit::GetHighlightedBones() const { return Component->GetHighlightedBones(); } void FScopedColorEdit::ResetHighlightedBones() { Component->HighlightedBones.Empty(); } void FScopedColorEdit::SetLevelViewMode(int ViewLevelIn) { Component->ViewLevel = ViewLevelIn; } int FScopedColorEdit::GetViewLevel() { return Component->ViewLevel; } void FScopedColorEdit::UpdateBoneColors() { FGeometryCollectionEdit GeometryCollectionEdit = Component->EditRestCollection(); UGeometryCollection* GeometryCollection = GeometryCollectionEdit.GetRestCollection(); FGeometryCollection* Collection = GeometryCollection->GetGeometryCollection().Get(); FLinearColor BlankColor(FColor(80, 80, 80, 50)); const TManagedArray& BoneHierarchy = *Collection->BoneHierarchy; TManagedArray& BoneColors = *Collection->BoneColor; for (int BoneIndex = 0; BoneIndex < BoneHierarchy.Num(); BoneIndex++) { FLinearColor BoneColor = FLinearColor(FColor::Black); if (IsBoneHighlighted(BoneIndex)) { BoneColor = FLinearColor(FColor::White); } else { if (Component->ViewLevel == -1) { BoneColor = RandomColors[BoneIndex % RandomColors.Num()]; } else { if (BoneHierarchy[BoneIndex].Level >= Component->ViewLevel) { // go up until we find parent at the required ViewLevel int32 Bone = BoneIndex; while (Bone != -1 && BoneHierarchy[Bone].Level > Component->ViewLevel) { Bone = BoneHierarchy[Bone].Parent; } int32 ColorIndex = Bone + 1; // parent can be -1 for root, range [-1..n] BoneColor = RandomColors[ColorIndex % RandomColors.Num()]; } else { BoneColor = BlankColor; } } } BoneColors[BoneIndex] = BoneColor; } Component->MarkRenderStateDirty(); Component->MarkRenderDynamicDataDirty(); } void UGeometryCollectionComponent::InitializeMaterials(const TArray &Materials, int32 InteriorMaterialIndex, int32 BoneSelectedMaterialIndex) { // We assume that we are resetting all material slots on this component to belong to this array int CurrIndex = 0; for (UMaterialInterface *CurrMaterial : Materials) { SetMaterial(CurrIndex++, CurrMaterial); } InteriorMaterialID = InteriorMaterialIndex; BoneSelectedMaterialID = BoneSelectedMaterialIndex; } #if INCLUDE_CHAOS const TSharedPtr UGeometryCollectionComponent::GetPhysicsScene() const { if (ChaosSolverActor) { return ChaosSolverActor->GetPhysicsScene(); } else { return FPhysScene_Chaos::GetInstance(); } } #endif