2019-12-26 14:45:42 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2018-12-12 11:25:29 -05:00
# include "GeometryCollection/GeometryCollectionComponent.h"
# include "Async/ParallelFor.h"
# include "Components/BoxComponent.h"
2020-07-15 03:39:13 -04:00
# include "ComponentRecreateRenderStateContext.h"
2018-12-12 11:25:29 -05:00
# 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/GeometryCollectionCache.h"
2019-06-08 17:15:34 -04:00
# include "GeometryCollection/GeometryCollectionActor.h"
# include "GeometryCollection/GeometryCollectionDebugDrawComponent.h"
2018-12-12 11:25:29 -05:00
# include "Physics/Experimental/PhysScene_Chaos.h"
# include "Modules/ModuleManager.h"
# include "ChaosSolversModule.h"
# include "ChaosStats.h"
2019-08-02 09:01:58 -04:00
# include "PhysicsProxy/GeometryCollectionPhysicsProxy.h"
# include "PhysicsSolver.h"
2020-03-09 13:22:54 -04:00
# include "Physics/PhysicsFiltering.h"
2020-08-11 01:36:57 -04:00
# include "Chaos/ChaosPhysicalMaterial.h"
2020-04-30 07:34:59 -04:00
# include "AI/NavigationSystemHelpers.h"
2020-09-01 14:07:48 -04:00
# include "Net/UnrealNetwork.h"
# include "Net/Core/PushModel/PushModel.h"
2020-09-24 00:43:27 -04:00
# include "PhysicalMaterials/PhysicalMaterial.h"
2018-12-12 11:25:29 -05:00
# if WITH_EDITOR
# include "AssetToolsModule.h"
# include "Editor.h"
# endif
2019-06-08 17:15:34 -04:00
# include "PhysicsEngine/BodySetup.h"
# include "PhysicsEngine/BodyInstance.h"
# include "Chaos/ChaosGameplayEventDispatcher.h"
2018-12-12 11:25:29 -05:00
2020-07-15 03:39:13 -04:00
# include "Rendering/NaniteResources.h"
2020-07-15 19:46:08 -04:00
# include "PrimitiveSceneInfo.h"
2020-07-15 03:39:13 -04:00
2019-06-08 17:15:34 -04:00
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
# include "Logging/MessageLog.h"
# include "Misc/UObjectToken.h"
# endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
# if INTEL_ISPC
2019-08-16 11:28:08 -04:00
# if USING_CODE_ANALYSIS
MSVC_PRAGMA ( warning ( push ) )
MSVC_PRAGMA ( warning ( disable : ALL_CODE_ANALYSIS_WARNINGS ) )
# endif // USING_CODE_ANALYSIS
2019-06-08 17:15:34 -04:00
# include "GeometryCollectionComponent.ispc.generated.h"
2019-08-16 11:28:08 -04:00
# if USING_CODE_ANALYSIS
MSVC_PRAGMA ( warning ( pop ) )
# endif // USING_CODE_ANALYSIS
2019-06-08 17:15:34 -04:00
# endif
DEFINE_LOG_CATEGORY_STATIC ( UGCC_LOG , Error , All ) ;
2020-09-01 14:07:48 -04:00
FString NetModeToString ( ENetMode InMode )
{
switch ( InMode )
{
case ENetMode : : NM_Client :
return FString ( " Client " ) ;
case ENetMode : : NM_DedicatedServer :
return FString ( " DedicatedServer " ) ;
case ENetMode : : NM_ListenServer :
return FString ( " ListenServer " ) ;
case ENetMode : : NM_Standalone :
return FString ( " Standalone " ) ;
default :
break ;
}
return FString ( " INVALID NETMODE " ) ;
}
FString RoleToString ( ENetRole InRole )
{
switch ( InRole )
{
case ROLE_None :
return FString ( TEXT ( " None " ) ) ;
case ROLE_SimulatedProxy :
return FString ( TEXT ( " SimProxy " ) ) ;
case ROLE_AutonomousProxy :
return FString ( TEXT ( " AutoProxy " ) ) ;
case ROLE_Authority :
return FString ( TEXT ( " Auth " ) ) ;
default :
break ;
}
return FString ( TEXT ( " Invalid Role " ) ) ;
}
int32 GetClusterLevel ( const FTransformCollection * Collection , int32 TransformGroupIndex )
{
int32 Level = 0 ;
while ( Collection & & Collection - > Parent [ TransformGroupIndex ] ! = - 1 )
{
TransformGroupIndex = Collection - > Parent [ TransformGroupIndex ] ;
Level + + ;
}
return Level ;
}
2019-10-02 17:27:26 -04:00
# if WITH_PHYSX && !WITH_CHAOS_NEEDS_TO_BE_FIXED
2018-12-12 11:25:29 -05:00
FGeometryCollectionSQAccelerator GlobalGeomCollectionAccelerator ; //todo(ocohen): proper lifetime management needed
void HackRegisterGeomAccelerator ( UGeometryCollectionComponent & Component )
{
2019-08-02 09:01:58 -04:00
# if TODO_REIMPLEMENT_SCENEQUERY_CROSSENGINE
2018-12-12 11:25:29 -05:00
if ( UWorld * World = Component . GetWorld ( ) )
{
if ( FPhysScene * PhysScene = World - > GetPhysicsScene ( ) )
{
2019-06-08 17:15:34 -04:00
if ( FSQAcceleratorUnion * SQAccelerationUnion = PhysScene - > GetSQAcceleratorUnion ( ) )
{
SQAccelerationUnion - > AddSQAccelerator ( & GlobalGeomCollectionAccelerator ) ;
}
2018-12-12 11:25:29 -05:00
}
}
2019-08-02 09:01:58 -04:00
# endif
2018-12-12 11:25:29 -05:00
}
# endif
2020-09-01 14:07:48 -04:00
bool FGeometryCollectionRepData : : Identical ( const FGeometryCollectionRepData * Other , uint32 PortFlags ) const
{
return Other & & ( Version = = Other - > Version ) ;
}
bool FGeometryCollectionRepData : : NetSerialize ( FArchive & Ar , class UPackageMap * Map , bool & bOutSuccess )
{
bOutSuccess = true ;
Ar < < Version ;
int32 NumPoses = Poses . Num ( ) ;
Ar < < NumPoses ;
if ( Ar . IsLoading ( ) )
{
Poses . SetNum ( NumPoses ) ;
}
for ( FGeometryCollectionRepPose & Pose : Poses )
{
SerializePackedVector < 100 , 30 > ( Pose . Position , Ar ) ;
SerializePackedVector < 100 , 30 > ( Pose . LinearVelocity , Ar ) ;
SerializePackedVector < 100 , 30 > ( Pose . AngularVelocity , Ar ) ;
Pose . Rotation . NetSerialize ( Ar , Map , bOutSuccess ) ;
Ar < < Pose . ParticleIndex ;
}
return true ;
}
2020-07-15 03:39:13 -04:00
static void RecreateGlobalRenderState ( IConsoleVariable * Var )
{
FGlobalComponentRecreateRenderStateContext Context ;
}
2020-07-17 04:21:22 -04:00
int32 GGeometryCollectionNanite = 1 ;
2020-07-15 03:39:13 -04:00
FAutoConsoleVariableRef CVarGeometryCollectionNanite (
TEXT ( " r.GeometryCollection.Nanite " ) ,
GGeometryCollectionNanite ,
TEXT ( " Render geometry collections using Nanite. " ) ,
FConsoleVariableDelegate : : CreateStatic ( & RecreateGlobalRenderState ) ,
ECVF_RenderThreadSafe
) ;
2020-04-30 07:34:59 -04:00
// Size in CM used as a threshold for whether a geometry in the collection is collected and exported for
// navigation purposes. Measured as the diagonal of the leaf node bounds.
float GGeometryCollectionNavigationSizeThreshold = 20.0f ;
FAutoConsoleVariableRef CVarGeometryCollectionNavigationSizeThreshold ( TEXT ( " p.GeometryCollectionNavigationSizeThreshold " ) , GGeometryCollectionNavigationSizeThreshold , TEXT ( " Size in CM used as a threshold for whether a geometry in the collection is collected and exported for navigation purposes. Measured as the diagonal of the leaf node bounds. " ) ) ;
2018-12-12 11:25:29 -05:00
FGeomComponentCacheParameters : : FGeomComponentCacheParameters ( )
: CacheMode ( EGeometryCollectionCacheType : : None )
, TargetCache ( nullptr )
, ReverseCacheBeginTime ( 0.0f )
, SaveCollisionData ( false )
2019-06-08 17:15:34 -04:00
, DoGenerateCollisionData ( false )
, CollisionDataSizeMax ( 512 )
, DoCollisionDataSpatialHash ( false )
, CollisionDataSpatialHashRadius ( 50.f )
2018-12-12 11:25:29 -05:00
, MaxCollisionPerCell ( 1 )
2019-06-08 17:15:34 -04:00
, SaveBreakingData ( false )
, DoGenerateBreakingData ( false )
, BreakingDataSizeMax ( 512 )
, DoBreakingDataSpatialHash ( false )
, BreakingDataSpatialHashRadius ( 50.f )
, MaxBreakingPerCell ( 1 )
2018-12-12 11:25:29 -05:00
, SaveTrailingData ( false )
2019-06-08 17:15:34 -04:00
, DoGenerateTrailingData ( false )
, TrailingDataSizeMax ( 512 )
, TrailingMinSpeedThreshold ( 200.f )
2018-12-12 11:25:29 -05:00
, TrailingMinVolumeThreshold ( 10000.f )
{
}
UGeometryCollectionComponent : : UGeometryCollectionComponent ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
, ChaosSolverActor ( nullptr )
, Simulating ( true )
2019-06-08 17:15:34 -04:00
, InitializationState ( ESimulationInitializationState : : Unintialized )
, ObjectType ( EObjectStateTypeEnum : : Chaos_Object_Dynamic )
2018-12-12 11:25:29 -05:00
, EnableClustering ( true )
2019-06-08 17:15:34 -04:00
, ClusterGroupIndex ( 0 )
2018-12-12 11:25:29 -05:00
, MaxClusterLevel ( 100 )
, DamageThreshold ( { 250.0 } )
2021-01-29 01:12:30 -04:00
, bUseSizeSpecificDamageThreshold ( false )
2019-06-08 17:15:34 -04:00
, ClusterConnectionType ( EClusterConnectionTypeEnum : : Chaos_PointImplicit )
, CollisionGroup ( 0 )
, CollisionSampleFraction ( 1.0 )
2018-12-12 11:25:29 -05:00
, InitialVelocityType ( EInitialVelocityTypeEnum : : Chaos_Initial_Velocity_User_Defined )
, InitialLinearVelocity ( 0.f , 0.f , 0.f )
, InitialAngularVelocity ( 0.f , 0.f , 0.f )
2019-06-08 17:15:34 -04:00
, BaseRigidBodyIndex ( INDEX_NONE )
, NumParticlesAdded ( 0 )
, CachePlayback ( false )
, bNotifyBreaks ( false )
, bNotifyCollisions ( false )
2020-09-01 14:07:48 -04:00
, bEnableReplication ( false )
, bEnableAbandonAfterLevel ( false )
, ReplicationAbandonClusterLevel ( 0 )
2018-12-12 11:25:29 -05:00
, bRenderStateDirty ( true )
2019-06-08 17:15:34 -04:00
, bShowBoneColors ( false )
, bEnableBoneSelection ( false )
2018-12-12 11:25:29 -05:00
, ViewLevel ( - 1 )
2019-06-08 17:15:34 -04:00
, NavmeshInvalidationTimeSliceIndex ( 0 )
, IsObjectDynamic ( false )
, IsObjectLoading ( true )
2019-08-02 09:01:58 -04:00
, PhysicsProxy ( nullptr )
2018-12-12 11:25:29 -05:00
# if WITH_EDITOR && WITH_EDITORONLY_DATA
, EditorActor ( nullptr )
# endif
2019-06-08 17:15:34 -04:00
# if GEOMETRYCOLLECTION_EDITOR_SELECTION
, bIsTransformSelectionModeEnabled ( false )
# endif // #if GEOMETRYCOLLECTION_EDITOR_SELECTION
2018-12-12 11:25:29 -05:00
{
PrimaryComponentTick . bCanEverTick = true ;
bTickInEditor = true ;
bAutoActivate = true ;
2019-06-08 17:15:34 -04:00
static uint32 GlobalNavMeshInvalidationCounter = 0 ;
//space these out over several frames (3 is arbitrary)
GlobalNavMeshInvalidationCounter + = 3 ;
NavmeshInvalidationTimeSliceIndex = GlobalNavMeshInvalidationCounter ;
WorldBounds = FBoxSphereBounds ( FBox ( ForceInit ) ) ;
// default current cache time
CurrentCacheTime = MAX_flt ;
// Buffer for rolling cache of past N=3 transforms being equal.
TransformsAreEqual . AddDefaulted ( 3 ) ;
TransformsAreEqualIndex = 0 ;
SetGenerateOverlapEvents ( false ) ;
2020-04-02 11:57:01 -04:00
// By default use the destructible object channel unless the user specifies otherwise
BodyInstance . SetObjectType ( ECC_Destructible ) ;
2020-04-14 13:39:39 -04:00
EventDispatcher = ObjectInitializer . CreateDefaultSubobject < UChaosGameplayEventDispatcher > ( this , TEXT ( " GameplayEventDispatcher " ) ) ;
2020-04-30 07:34:59 -04:00
2020-06-23 18:40:00 -04:00
DynamicCollection = nullptr ;
2020-04-30 07:34:59 -04:00
bHasCustomNavigableGeometry = EHasCustomNavigableGeometry : : Yes ;
2020-09-01 14:07:48 -04:00
bWantsInitializeComponent = true ;
2018-12-12 11:25:29 -05:00
}
2019-08-02 09:01:58 -04:00
Chaos : : FPhysicsSolver * GetSolver ( const UGeometryCollectionComponent & GeometryCollectionComponent )
2018-12-12 11:25:29 -05:00
{
2019-10-02 17:27:26 -04:00
# if INCLUDE_CHAOS
2020-09-01 14:07:48 -04:00
if ( GeometryCollectionComponent . ChaosSolverActor )
{
return GeometryCollectionComponent . ChaosSolverActor - > GetSolver ( ) ;
}
else if ( UWorld * CurrentWorld = GeometryCollectionComponent . GetWorld ( ) )
{
if ( FPhysScene * Scene = CurrentWorld - > GetPhysicsScene ( ) )
{
return Scene - > GetSolver ( ) ;
}
}
2018-12-12 11:25:29 -05:00
# endif
2020-09-01 14:07:48 -04:00
return nullptr ;
2019-10-02 17:27:26 -04:00
}
2018-12-12 11:25:29 -05:00
void UGeometryCollectionComponent : : BeginPlay ( )
{
Super : : BeginPlay ( ) ;
2019-06-08 17:15:34 -04:00
# if WITH_PHYSX && !WITH_CHAOS_NEEDS_TO_BE_FIXED
2018-12-12 11:25:29 -05:00
HackRegisterGeomAccelerator ( * this ) ;
# endif
2019-06-08 17:15:34 -04:00
//////////////////////////////////////////////////////////////////////////
// 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<FTransform>&)
// ---------- {
// ---------- // todo : Move the update to the array passed here...
// ---------- });
// ----------
// ---------- SolverCallbacks->SetUpdateRestStateFunction([&](const int32 & CurrentFrame, const TManagedArray<int32> & RigidBodyID, const TManagedArray<FGeometryCollectionBoneNode>& 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);
// ---------- }
// ---------- });
// ---------- }
// ---------- });
//////////////////////////////////////////////////////////////////////////
// default current cache time
CurrentCacheTime = MAX_flt ;
2018-12-12 11:25:29 -05:00
}
2019-09-16 13:42:25 -04:00
void UGeometryCollectionComponent : : EndPlay ( const EEndPlayReason : : Type ReasonEnd )
2018-12-12 11:25:29 -05:00
{
# if WITH_EDITOR && WITH_EDITORONLY_DATA
// Track our editor component if needed for syncing simulations back from PIE on shutdown
EditorActor = EditorUtilities : : GetEditorWorldCounterpartActor ( GetTypedOuter < AActor > ( ) ) ;
# endif
Super : : EndPlay ( ReasonEnd ) ;
2019-06-08 17:15:34 -04:00
CurrentCacheTime = MAX_flt ;
2018-12-12 11:25:29 -05:00
}
2020-09-01 14:07:48 -04:00
void UGeometryCollectionComponent : : GetLifetimeReplicatedProps ( TArray < FLifetimeProperty > & OutLifetimeProps ) const
{
Super : : GetLifetimeReplicatedProps ( OutLifetimeProps ) ;
FDoRepLifetimeParams Params ;
Params . bIsPushBased = true ;
Params . RepNotifyCondition = REPNOTIFY_OnChanged ;
DOREPLIFETIME_WITH_PARAMS_FAST ( UGeometryCollectionComponent , RepData , Params ) ;
}
2018-12-12 11:25:29 -05:00
FBoxSphereBounds UGeometryCollectionComponent : : CalcBounds ( const FTransform & LocalToWorldIn ) const
2019-06-08 17:15:34 -04:00
{
SCOPE_CYCLE_COUNTER ( STAT_GCCUpdateBounds ) ;
2020-02-19 17:10:11 -05:00
// #todo(dmp): hack to make bounds calculation work when we don't have valid physics proxy data. This will
// force bounds calculation.
const FGeometryCollectionResults * Results = PhysicsProxy ? PhysicsProxy - > GetConsumerResultsGT ( ) : nullptr ;
const int32 NumTransforms = Results ? Results - > GlobalTransforms . Num ( ) : 0 ;
if ( ! CachePlayback & & WorldBounds . GetSphere ( ) . W > 1e-5 & & NumTransforms > 0 )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
return WorldBounds ;
}
else if ( RestCollection & & RestCollection - > HasVisibleGeometry ( ) )
{
const FMatrix LocalToWorldWithScale = LocalToWorldIn . ToMatrixWithScale ( ) ;
2018-12-12 11:25:29 -05:00
FBox BoundingBox ( ForceInit ) ;
2019-06-08 17:15:34 -04:00
//Hold on to reference so it doesn't get GC'ed
auto HackGeometryCollectionPtr = RestCollection - > GetGeometryCollection ( ) ;
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
const TManagedArray < FBox > & BoundingBoxes = GetBoundingBoxArray ( ) ;
const TManagedArray < int32 > & TransformIndices = GetTransformIndexArray ( ) ;
const TManagedArray < int32 > & ParentIndices = GetParentArray ( ) ;
const TManagedArray < int32 > & TransformToGeometryIndex = GetTransformToGeometryIndexArray ( ) ;
2018-12-12 11:25:29 -05:00
const int32 NumBoxes = BoundingBoxes . Num ( ) ;
2019-06-08 17:15:34 -04:00
// #todo(dmp): we could do the bbox transform in parallel with a bit of reformulating
// #todo(dmp): there are some cases where the calcbounds function is called before the component
// has set the global matrices cache while in the editor. This is a somewhat weak guard against this
// to default to just calculating tmp global matrices. This should be removed or modified somehow
// such that we always cache the global matrices and this method always does the correct behavior
if ( GlobalMatrices . Num ( ) ! = RestCollection - > NumElements ( FGeometryCollection : : TransformGroup ) )
{
TArray < FMatrix > TmpGlobalMatrices ;
GeometryCollectionAlgo : : GlobalMatrices ( GetTransformArray ( ) , ParentIndices , TmpGlobalMatrices ) ;
if ( TmpGlobalMatrices . Num ( ) = = 0 )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
return FBoxSphereBounds ( ForceInitToZero ) ;
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
for ( int32 BoxIdx = 0 ; BoxIdx < NumBoxes ; + + BoxIdx )
{
const int32 TransformIndex = TransformIndices [ BoxIdx ] ;
if ( RestCollection - > GetGeometryCollection ( ) - > IsGeometry ( TransformIndex ) )
{
BoundingBox + = BoundingBoxes [ BoxIdx ] . TransformBy ( TmpGlobalMatrices [ TransformIndex ] * LocalToWorldWithScale ) ;
}
}
}
else
{
# if INTEL_ISPC
ispc : : BoxCalcBounds (
( int32 * ) & TransformToGeometryIndex [ 0 ] ,
( int32 * ) & TransformIndices [ 0 ] ,
( ispc : : FMatrix * ) & GlobalMatrices [ 0 ] ,
( ispc : : FBox * ) & BoundingBoxes [ 0 ] ,
( ispc : : FMatrix & ) LocalToWorldWithScale ,
( ispc : : FBox & ) BoundingBox ,
NumBoxes ) ;
# else
for ( int32 BoxIdx = 0 ; BoxIdx < NumBoxes ; + + BoxIdx )
{
const int32 TransformIndex = TransformIndices [ BoxIdx ] ;
if ( RestCollection - > GetGeometryCollection ( ) - > IsGeometry ( TransformIndex ) )
{
BoundingBox + = BoundingBoxes [ BoxIdx ] . TransformBy ( GlobalMatrices [ TransformIndex ] * LocalToWorldWithScale ) ;
}
}
# endif
2018-12-12 11:25:29 -05:00
}
return FBoxSphereBounds ( BoundingBox ) ;
}
return FBoxSphereBounds ( ForceInitToZero ) ;
}
2020-02-06 13:13:41 -05:00
void UGeometryCollectionComponent : : CreateRenderState_Concurrent ( FRegisterComponentContext * Context )
2018-12-12 11:25:29 -05:00
{
2020-02-06 13:13:41 -05:00
Super : : CreateRenderState_Concurrent ( Context ) ;
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
if ( SceneProxy & & RestCollection & & RestCollection - > HasVisibleGeometry ( ) )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
FGeometryCollectionConstantData * const ConstantData = : : new FGeometryCollectionConstantData ;
2018-12-12 11:25:29 -05:00
InitConstantData ( ConstantData ) ;
2019-06-08 17:15:34 -04:00
FGeometryCollectionDynamicData * const DynamicData = : : new FGeometryCollectionDynamicData ;
2018-12-12 11:25:29 -05:00
InitDynamicData ( DynamicData ) ;
2020-07-15 03:39:13 -04:00
if ( SceneProxy - > IsNaniteMesh ( ) )
{
FNaniteGeometryCollectionSceneProxy * const GeometryCollectionSceneProxy = static_cast < FNaniteGeometryCollectionSceneProxy * > ( SceneProxy ) ;
// ...
# if GEOMETRYCOLLECTION_EDITOR_SELECTION
if ( bIsTransformSelectionModeEnabled )
2018-12-12 11:25:29 -05:00
{
2020-07-15 03:39:13 -04:00
// ...
2019-06-08 17:15:34 -04:00
}
2020-07-15 03:39:13 -04:00
# endif
ENQUEUE_RENDER_COMMAND ( CreateRenderState ) (
[ GeometryCollectionSceneProxy , ConstantData , DynamicData ] ( FRHICommandListImmediate & RHICmdList )
{
if ( GeometryCollectionSceneProxy )
{
2020-07-15 19:46:08 -04:00
GeometryCollectionSceneProxy - > SetConstantData_RenderThread ( ConstantData ) ;
GeometryCollectionSceneProxy - > SetDynamicData_RenderThread ( DynamicData ) ;
GeometryCollectionSceneProxy - > GetPrimitiveSceneInfo ( ) - > RequestGPUSceneUpdate ( ) ;
2020-07-15 03:39:13 -04:00
}
}
) ;
}
else
{
FGeometryCollectionSceneProxy * const GeometryCollectionSceneProxy = static_cast < FGeometryCollectionSceneProxy * > ( SceneProxy ) ;
2020-07-15 19:46:08 -04:00
2020-07-15 03:39:13 -04:00
# if GEOMETRYCOLLECTION_EDITOR_SELECTION
// Re-init subsections
if ( bIsTransformSelectionModeEnabled )
{
GeometryCollectionSceneProxy - > UseSubSections ( true , false ) ; // Do not force reinit now, it'll be done in SetConstantData_RenderThread
}
2020-07-15 19:46:08 -04:00
# endif
2020-07-15 03:39:13 -04:00
ENQUEUE_RENDER_COMMAND ( CreateRenderState ) (
[ GeometryCollectionSceneProxy , ConstantData , DynamicData ] ( FRHICommandListImmediate & RHICmdList )
{
if ( GeometryCollectionSceneProxy )
{
GeometryCollectionSceneProxy - > SetConstantData_RenderThread ( ConstantData ) ;
GeometryCollectionSceneProxy - > SetDynamicData_RenderThread ( DynamicData ) ;
}
}
) ;
}
2018-12-12 11:25:29 -05:00
}
}
FPrimitiveSceneProxy * UGeometryCollectionComponent : : CreateSceneProxy ( )
{
2020-07-15 03:39:13 -04:00
if ( ! RestCollection )
2018-12-12 11:25:29 -05:00
{
2020-07-15 03:39:13 -04:00
return nullptr ;
2018-12-12 11:25:29 -05:00
}
2020-07-15 03:39:13 -04:00
2020-08-14 16:09:16 -04:00
// TODO: Abstract with a common helper
2020-09-14 07:52:02 -04:00
if ( UseNanite ( GetScene ( ) - > GetShaderPlatform ( ) ) & &
2020-08-14 16:09:16 -04:00
RestCollection - > EnableNanite & &
2020-11-02 21:12:24 -04:00
RestCollection - > NaniteData ! = nullptr & &
2020-08-14 16:09:16 -04:00
GGeometryCollectionNanite ! = 0 )
2020-07-15 03:39:13 -04:00
{
return new FNaniteGeometryCollectionSceneProxy ( this ) ;
}
return new FGeometryCollectionSceneProxy ( this ) ;
2018-12-12 11:25:29 -05:00
}
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
{
2019-08-02 09:01:58 -04:00
return PhysicsProxy ! = nullptr ;
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
void UGeometryCollectionComponent : : SetNotifyBreaks ( bool bNewNotifyBreaks )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
if ( bNotifyBreaks ! = bNewNotifyBreaks )
{
bNotifyBreaks = bNewNotifyBreaks ;
UpdateBreakEventRegistration ( ) ;
}
}
FBodyInstance * UGeometryCollectionComponent : : GetBodyInstance ( FName BoneName /*= NAME_None*/ , bool bGetWelded /*= true*/ ) const
{
return nullptr ; // const_cast<FBodyInstance*>(&DummyBodyInstance);
}
void UGeometryCollectionComponent : : SetNotifyRigidBodyCollision ( bool bNewNotifyRigidBodyCollision )
{
Super : : SetNotifyRigidBodyCollision ( bNewNotifyRigidBodyCollision ) ;
UpdateRBCollisionEventRegistration ( ) ;
}
void UGeometryCollectionComponent : : DispatchBreakEvent ( const FChaosBreakEvent & Event )
{
// native
NotifyBreak ( Event ) ;
// bp
if ( OnChaosBreakEvent . IsBound ( ) )
{
OnChaosBreakEvent . Broadcast ( Event ) ;
}
}
2020-04-30 07:34:59 -04:00
bool UGeometryCollectionComponent : : DoCustomNavigableGeometryExport ( FNavigableGeometryExport & GeomExport ) const
{
2020-08-11 01:36:57 -04:00
if ( ! RestCollection )
{
// No geometry data so skip export - geometry collections don't have other geometry sources
// so return false here to skip non-custom export for this component as well.
return false ;
}
2020-04-30 07:34:59 -04:00
TArray < FVector > OutVertexBuffer ;
TArray < int32 > OutIndexBuffer ;
const FGeometryCollection * const Collection = RestCollection - > GetGeometryCollection ( ) . Get ( ) ;
check ( Collection ) ;
const float SizeThreshold = GGeometryCollectionNavigationSizeThreshold * GGeometryCollectionNavigationSizeThreshold ;
// for all geometry. inspect bounding box build int list of transform indices.
int32 VertexCount = 0 ;
int32 FaceCountEstimate = 0 ;
TArray < int32 > GeometryIndexBuffer ;
TArray < int32 > TransformIndexBuffer ;
int32 NumGeometry = Collection - > NumElements ( FGeometryCollection : : GeometryGroup ) ;
const TManagedArray < FBox > & BoundingBox = Collection - > BoundingBox ;
const TManagedArray < int32 > & TransformIndexArray = Collection - > TransformIndex ;
const TManagedArray < int32 > & VertexCountArray = Collection - > VertexCount ;
const TManagedArray < int32 > & FaceCountArray = Collection - > FaceCount ;
const TManagedArray < int32 > & VertexStartArray = Collection - > VertexStart ;
const TManagedArray < FVector > & Vertex = Collection - > Vertex ;
for ( int32 GeometryGroupIndex = 0 ; GeometryGroupIndex < NumGeometry ; GeometryGroupIndex + + )
{
if ( BoundingBox [ GeometryGroupIndex ] . GetSize ( ) . SizeSquared ( ) > SizeThreshold )
{
TransformIndexBuffer . Add ( TransformIndexArray [ GeometryGroupIndex ] ) ;
GeometryIndexBuffer . Add ( GeometryGroupIndex ) ;
VertexCount + = VertexCountArray [ GeometryGroupIndex ] ;
FaceCountEstimate + = FaceCountArray [ GeometryGroupIndex ] ;
}
}
// Get all the geometry transforms in component space (they are stored natively in parent-bone space)
TArray < FTransform > GeomToComponent ;
GeometryCollectionAlgo : : GlobalMatrices ( GetTransformArray ( ) , GetParentArray ( ) , TransformIndexBuffer , GeomToComponent ) ;
OutVertexBuffer . AddUninitialized ( VertexCount ) ;
int32 DestVertex = 0 ;
//for each "subset" we care about
for ( int32 SubsetIndex = 0 ; SubsetIndex < GeometryIndexBuffer . Num ( ) ; + + SubsetIndex )
{
//find indices into the collection data
int32 GeometryIndex = GeometryIndexBuffer [ SubsetIndex ] ;
int32 TransformIndex = TransformIndexBuffer [ SubsetIndex ] ;
int32 SourceGeometryVertexStart = VertexStartArray [ GeometryIndex ] ;
int32 SourceGeometryVertexCount = VertexCountArray [ GeometryIndex ] ;
ParallelFor ( SourceGeometryVertexCount , [ & ] ( int32 PointIdx )
{
//extract vertex from source
int32 SourceGeometryVertexIndex = SourceGeometryVertexStart + PointIdx ;
FVector const VertexInWorldSpace = GeomToComponent [ SubsetIndex ] . TransformPosition ( Vertex [ SourceGeometryVertexIndex ] ) ;
int32 DestVertexIndex = DestVertex + PointIdx ;
OutVertexBuffer [ DestVertexIndex ] . X = VertexInWorldSpace . X ;
OutVertexBuffer [ DestVertexIndex ] . Y = VertexInWorldSpace . Y ;
OutVertexBuffer [ DestVertexIndex ] . Z = VertexInWorldSpace . Z ;
} ) ;
DestVertex + = SourceGeometryVertexCount ;
}
//gather data needed for indices
const TManagedArray < int32 > & FaceStartArray = Collection - > FaceStart ;
const TManagedArray < FIntVector > & Indices = Collection - > Indices ;
const TManagedArray < bool > & Visible = GetVisibleArray ( ) ;
const TManagedArray < int32 > & MaterialIndex = Collection - > MaterialIndex ;
//pre-allocate enough room (assuming all faces are visible)
OutIndexBuffer . AddUninitialized ( 3 * FaceCountEstimate ) ;
//reset vertex counter so that we base the indices off the new location rather than the global vertex list
DestVertex = 0 ;
int32 DestinationIndex = 0 ;
//leaving index traversal in a different loop to help cache coherency of source data
for ( int32 SubsetIndex = 0 ; SubsetIndex < GeometryIndexBuffer . Num ( ) ; + + SubsetIndex )
{
int32 GeometryIndex = GeometryIndexBuffer [ SubsetIndex ] ;
//for each index, subtract the starting vertex for that geometry to make it 0-based. Then add the new starting vertex index for this geometry
int32 SourceGeometryVertexStart = VertexStartArray [ GeometryIndex ] ;
int32 SourceGeometryVertexCount = VertexCountArray [ GeometryIndex ] ;
int32 IndexDelta = DestVertex - SourceGeometryVertexStart ;
int32 FaceStart = FaceStartArray [ GeometryIndex ] ;
int32 FaceCount = FaceCountArray [ GeometryIndex ] ;
//Copy the faces
for ( int FaceIdx = FaceStart ; FaceIdx < FaceStart + FaceCount ; FaceIdx + + )
{
if ( Visible [ FaceIdx ] )
{
OutIndexBuffer [ DestinationIndex + + ] = Indices [ FaceIdx ] . X + IndexDelta ;
OutIndexBuffer [ DestinationIndex + + ] = Indices [ FaceIdx ] . Y + IndexDelta ;
OutIndexBuffer [ DestinationIndex + + ] = Indices [ FaceIdx ] . Z + IndexDelta ;
}
}
DestVertex + = SourceGeometryVertexCount ;
}
// Invisible faces make the index buffer smaller
OutIndexBuffer . SetNum ( DestinationIndex ) ;
// Push as a custom mesh to navigation system
// #CHAOSTODO This is pretty inefficient as it copies the whole buffer transforming each vert by the component to world
// transform. Investigate a move aware custom mesh for pre-transformed verts to speed this up.
GeomExport . ExportCustomMesh ( OutVertexBuffer . GetData ( ) , OutVertexBuffer . Num ( ) , OutIndexBuffer . GetData ( ) , OutIndexBuffer . Num ( ) , GetComponentToWorld ( ) ) ;
return true ;
}
2020-09-24 00:43:27 -04:00
UPhysicalMaterial * UGeometryCollectionComponent : : GetPhysicalMaterial ( ) const
{
// Pull material from first mesh element to grab physical material. Prefer an override if one exists
UPhysicalMaterial * PhysMatToUse = PhysicalMaterialOverride ;
if ( ! PhysMatToUse )
{
// No override, try render materials
const int32 NumMaterials = GetNumMaterials ( ) ;
if ( NumMaterials > 0 )
{
UMaterialInterface * FirstMatInterface = GetMaterial ( 0 ) ;
if ( FirstMatInterface & & FirstMatInterface - > GetPhysicalMaterial ( ) )
{
PhysMatToUse = FirstMatInterface - > GetPhysicalMaterial ( ) ;
}
}
}
if ( ! PhysMatToUse )
{
// Still no material, fallback on default
PhysMatToUse = GEngine - > DefaultPhysMaterial ;
}
// Should definitely have a material at this point.
check ( PhysMatToUse ) ;
return PhysMatToUse ;
}
2020-09-01 14:07:48 -04:00
void UGeometryCollectionComponent : : InitializeComponent ( )
{
Super : : InitializeComponent ( ) ;
AActor * Owner = GetOwner ( ) ;
if ( ! Owner )
{
return ;
}
const ENetRole LocalRole = Owner - > GetLocalRole ( ) ;
const ENetMode NetMode = Owner - > GetNetMode ( ) ;
// If we're replicating we need some extra setup - check netmode as we don't need this for
// standalone runtimes where we aren't going to network the component
if ( GetIsReplicated ( ) & & NetMode ! = NM_Standalone )
{
if ( LocalRole = = ENetRole : : ROLE_Authority )
{
// As we're the authority we need to track velocities in the dynamic collection so we
// can send them over to the other clients to correctly set their state. Attach this now.
// The physics proxy will pick them up and populate them as needed
DynamicCollection - > AddAttribute < FVector > ( " LinearVelocity " , FTransformCollection : : TransformGroup ) ;
DynamicCollection - > AddAttribute < FVector > ( " AngularVelocity " , FTransformCollection : : TransformGroup ) ;
// We also need to track our control of particles if that control can be shared between server and client
if ( bEnableAbandonAfterLevel )
{
TManagedArray < bool > & ControlFlags = DynamicCollection - > AddAttribute < bool > ( " AuthControl " , FTransformCollection : : TransformGroup ) ;
for ( bool & Flag : ControlFlags )
{
Flag = true ;
}
}
}
else
{
// We're a replicated component and we're not in control.
Chaos : : FPhysicsSolver * CurrSolver = GetSolver ( * this ) ;
if ( CurrSolver )
{
CurrSolver - > RegisterSimOneShotCallback ( [ Prox = PhysicsProxy ] ( )
{
// As we're not in control we make it so our simulated proxy cannot break clusters
// We have to set the strain to a high value but be below the max for the data type
// so releasing on authority demand works
const Chaos : : FReal MaxStrain = TNumericLimits < Chaos : : FReal > : : Max ( ) - TNumericLimits < Chaos : : FReal > : : Min ( ) ;
TArray < Chaos : : TPBDRigidClusteredParticleHandle < Chaos : : FReal , 3 > * > Particles = Prox - > GetParticles ( ) ;
for ( Chaos : : TPBDRigidClusteredParticleHandle < Chaos : : FReal , 3 > * P : Particles )
{
if ( ! P )
{
continue ;
}
P - > SetStrain ( MaxStrain ) ;
}
} ) ;
}
}
}
}
2019-06-08 17:15:34 -04:00
static void DispatchGeometryCollectionBreakEvent ( const FChaosBreakEvent & Event )
{
if ( UGeometryCollectionComponent * const GC = Cast < UGeometryCollectionComponent > ( Event . Component ) )
{
GC - > DispatchBreakEvent ( Event ) ;
}
}
void UGeometryCollectionComponent : : DispatchChaosPhysicsCollisionBlueprintEvents ( const FChaosPhysicsCollisionInfo & CollisionInfo )
{
ReceivePhysicsCollision ( CollisionInfo ) ;
OnChaosPhysicsCollision . Broadcast ( CollisionInfo ) ;
}
// call when first registering
void UGeometryCollectionComponent : : RegisterForEvents ( )
{
if ( BodyInstance . bNotifyRigidBodyCollision | | bNotifyBreaks | | bNotifyCollisions )
{
2020-04-14 13:39:39 -04:00
if ( bNotifyCollisions | | BodyInstance . bNotifyRigidBodyCollision )
2019-06-08 17:15:34 -04:00
{
2020-04-14 13:39:39 -04:00
EventDispatcher - > RegisterForCollisionEvents ( this , this ) ;
2020-04-14 13:39:41 -04:00
# if INCLUDE_CHAOS
2020-08-11 01:36:57 -04:00
GetWorld ( ) - > GetPhysicsScene ( ) - > GetSolver ( ) - > SetGenerateCollisionData ( true ) ;
2020-04-14 13:39:41 -04:00
# endif
2020-04-14 13:39:39 -04:00
}
2019-06-08 17:15:34 -04:00
2020-04-14 13:39:39 -04:00
if ( bNotifyBreaks )
{
EventDispatcher - > RegisterForBreakEvents ( this , & DispatchGeometryCollectionBreakEvent ) ;
2020-04-14 13:39:41 -04:00
# if INCLUDE_CHAOS
2020-08-11 01:36:57 -04:00
GetWorld ( ) - > GetPhysicsScene ( ) - > GetSolver ( ) - > SetGenerateBreakingData ( true ) ;
2020-04-14 13:39:41 -04:00
# endif
2019-06-08 17:15:34 -04:00
}
}
}
void UGeometryCollectionComponent : : UpdateRBCollisionEventRegistration ( )
{
2020-04-14 13:39:39 -04:00
if ( bNotifyCollisions | | BodyInstance . bNotifyRigidBodyCollision )
2019-06-08 17:15:34 -04:00
{
2020-04-14 13:39:39 -04:00
EventDispatcher - > RegisterForCollisionEvents ( this , this ) ;
}
else
{
EventDispatcher - > UnRegisterForCollisionEvents ( this , this ) ;
2019-06-08 17:15:34 -04:00
}
}
void UGeometryCollectionComponent : : UpdateBreakEventRegistration ( )
{
2020-04-14 13:39:39 -04:00
if ( bNotifyBreaks )
2019-06-08 17:15:34 -04:00
{
2020-04-14 13:39:39 -04:00
EventDispatcher - > RegisterForBreakEvents ( this , & DispatchGeometryCollectionBreakEvent ) ;
}
else
{
EventDispatcher - > UnRegisterForBreakEvents ( this ) ;
2019-06-08 17:15:34 -04:00
}
}
2020-09-01 14:07:48 -04:00
void ActivateClusters ( Chaos : : FPBDRigidsEvolution : : FRigidClustering & Clustering , Chaos : : TPBDRigidClusteredParticleHandle < float , 3 > * Cluster )
{
if ( ! Cluster )
{
return ;
}
if ( Cluster - > ClusterIds ( ) . Id )
{
ActivateClusters ( Clustering , Cluster - > ClusterIds ( ) . Id - > CastToClustered ( ) ) ;
}
Clustering . DeactivateClusterParticle ( Cluster ) ;
}
void UGeometryCollectionComponent : : OnRep_RepData ( const FGeometryCollectionRepData & OldData )
{
if ( ! DynamicCollection )
{
return ;
}
if ( AActor * Owner = GetOwner ( ) )
{
const int32 NumTransforms = DynamicCollection - > Transform . Num ( ) ;
const int32 NumNewPoses = RepData . Poses . Num ( ) ;
if ( NumTransforms < NumNewPoses )
{
return ;
}
Chaos : : FPhysicsSolver * Solver = GetSolver ( * this ) ;
for ( int32 Index = 0 ; Index < NumNewPoses ; + + Index )
{
const FGeometryCollectionRepPose & SourcePose = RepData . Poses [ Index ] ;
const int32 ParticleIndex = SourcePose . ParticleIndex ;
if ( ParticleIndex > = NumTransforms )
{
// Out of range
continue ;
}
Solver - > RegisterSimOneShotCallback ( [ SourcePose , Prox = PhysicsProxy ] ( )
{
Chaos : : TPBDRigidClusteredParticleHandle < float , 3 > * Particle = Prox - > GetParticles ( ) [ SourcePose . ParticleIndex ] ;
Chaos : : FPhysicsSolver * Solver = Prox - > GetSolver < Chaos : : FPhysicsSolver > ( ) ;
Chaos : : FPBDRigidsEvolution * Evo = Solver - > GetEvolution ( ) ;
check ( Evo ) ;
Chaos : : FPBDRigidsEvolution : : FRigidClustering & Clustering = Evo - > GetRigidClustering ( ) ;
// Set X/R/V/W for next sim step from the replicated state
Particle - > SetX ( SourcePose . Position ) ;
Particle - > SetR ( SourcePose . Rotation ) ;
Particle - > SetV ( SourcePose . LinearVelocity ) ;
Particle - > SetW ( SourcePose . AngularVelocity ) ;
if ( Particle - > ClusterIds ( ) . Id )
{
// This particle is clustered but the remote authority has it activated. Fracture the parent cluster
ActivateClusters ( Clustering , Particle - > ClusterIds ( ) . Id - > CastToClustered ( ) ) ;
}
else if ( Particle - > Disabled ( ) )
{
// We might have disabled the particle - need to reactivate if it's active on the remote.
Particle - > SetDisabled ( false ) ;
}
// Make sure to wake corrected particles
Particle - > SetSleeping ( false ) ;
} ) ;
}
}
}
void UGeometryCollectionComponent : : UpdateRepData ( )
{
if ( ! bEnableReplication )
{
return ;
}
AActor * Owner = GetOwner ( ) ;
// If we have no owner or our netmode means we never require replication then early out
if ( ! Owner | | Owner - > GetNetMode ( ) = = ENetMode : : NM_Standalone )
{
return ;
}
if ( Owner & & GetIsReplicated ( ) & & Owner - > GetLocalRole ( ) = = ROLE_Authority )
{
// We're inside a replicating actor and we're the authority - update the rep data
const int32 NumTransforms = DynamicCollection - > Transform . Num ( ) ;
RepData . Poses . Reset ( NumTransforms ) ;
TManagedArray < FVector > * LinearVelocity = DynamicCollection - > FindAttributeTyped < FVector > ( " LinearVelocity " , FTransformCollection : : TransformGroup ) ;
TManagedArray < FVector > * AngularVelocity = DynamicCollection - > FindAttributeTyped < FVector > ( " AngularVelocity " , FTransformCollection : : TransformGroup ) ;
for ( int32 Index = 0 ; Index < NumTransforms ; + + Index )
{
2021-02-03 14:57:28 -04:00
TManagedArray < TUniquePtr < Chaos : : FGeometryParticle > > & GTParticles = PhysicsProxy - > GetExternalParticles ( ) ;
Chaos : : FGeometryParticle * Particle = GTParticles [ Index ] . Get ( ) ;
2020-09-01 14:07:48 -04:00
if ( ! DynamicCollection - > Active [ Index ] | | DynamicCollection - > DynamicState [ Index ] ! = static_cast < uint8 > ( Chaos : : EObjectStateType : : Dynamic ) )
{
continue ;
}
const int32 ClusterLevel = GetClusterLevel ( RestCollection - > GetGeometryCollection ( ) . Get ( ) , Index ) ;
const bool bLevelValid = ! EnableClustering | | ! bEnableAbandonAfterLevel | | ClusterLevel < = ReplicationAbandonClusterLevel ;
if ( ! bLevelValid )
{
const int32 ParentTransformIndex = RestCollection - > GetGeometryCollection ( ) - > Parent [ Index ] ;
TManagedArray < bool > * ControlFlags = DynamicCollection - > FindAttributeTyped < bool > ( " AuthControl " , FTransformCollection : : TransformGroup ) ;
if ( ControlFlags & & ( * ControlFlags ) [ ParentTransformIndex ] )
{
( * ControlFlags ) [ ParentTransformIndex ] = false ;
NetAbandonCluster ( ParentTransformIndex ) ;
}
continue ;
}
RepData . Poses . AddDefaulted ( ) ;
FGeometryCollectionRepPose & Pose = RepData . Poses . Last ( ) ;
// No scale transfered - shouldn't be a simulated property
Pose . ParticleIndex = Index ;
Pose . Position = Particle - > X ( ) ;
Pose . Rotation = Particle - > R ( ) ;
if ( LinearVelocity )
{
check ( AngularVelocity ) ;
Pose . LinearVelocity = ( * LinearVelocity ) [ Index ] ;
Pose . AngularVelocity = ( * AngularVelocity ) [ Index ] ;
}
else
{
Pose . LinearVelocity = FVector : : ZeroVector ;
Pose . AngularVelocity = FVector : : ZeroVector ;
}
}
RepData . Version + + ;
MARK_PROPERTY_DIRTY_FROM_NAME ( UGeometryCollectionComponent , RepData , this ) ;
}
}
void SetHierarchyStrain ( Chaos : : TPBDRigidClusteredParticleHandle < Chaos : : FReal , 3 > * P , TMap < Chaos : : TPBDRigidParticleHandle < Chaos : : FReal , 3 > * , TArray < Chaos : : TPBDRigidParticleHandle < Chaos : : FReal , 3 > * > > & Map , float Strain )
{
TArray < Chaos : : TPBDRigidParticleHandle < Chaos : : FReal , 3 > * > * Children = Map . Find ( P ) ;
if ( Children )
{
for ( Chaos : : TPBDRigidParticleHandle < Chaos : : FReal , 3 > * ChildP : ( * Children ) )
{
SetHierarchyStrain ( ChildP - > CastToClustered ( ) , Map , Strain ) ;
}
}
if ( P )
{
P - > SetStrain ( Strain ) ;
}
}
void UGeometryCollectionComponent : : NetAbandonCluster_Implementation ( int32 TransformIndex )
{
// Called on clients when the server abandons a particle. TransformIndex is the index of the parent
// of that particle, should only get called once per cluster but survives multiple calls
if ( GetOwnerRole ( ) = = ENetRole : : ROLE_Authority )
{
// Owner called abandon - takes no action
return ;
}
if ( ! EnableClustering )
{
// No clustering information to update
return ;
}
if ( TransformIndex > = 0 & & TransformIndex < DynamicCollection - > NumElements ( FTransformCollection : : TransformGroup ) )
{
int32 ClusterLevel = GetClusterLevel ( RestCollection - > GetGeometryCollection ( ) . Get ( ) , TransformIndex ) ;
float Strain = DamageThreshold . IsValidIndex ( ClusterLevel ) ? DamageThreshold [ ClusterLevel ] : DamageThreshold . Num ( ) > 0 ? DamageThreshold [ 0 ] : 0.0f ;
if ( Strain > = 0 )
{
Chaos : : FPhysicsSolver * Solver = GetSolver ( * this ) ;
Solver - > RegisterSimOneShotCallback ( [ Prox = PhysicsProxy , Strain , TransformIndex , Solver ] ( )
{
Chaos : : TPBDRigidClustering < Chaos : : FPBDRigidsEvolution , Chaos : : FPBDCollisionConstraints , Chaos : : FReal , 3 > & Clustering = Solver - > GetEvolution ( ) - > GetRigidClustering ( ) ;
Chaos : : TPBDRigidClusteredParticleHandle < Chaos : : FReal , 3 > * Parent = Prox - > GetParticles ( ) [ TransformIndex ] ;
if ( ! Parent - > Disabled ( ) )
{
SetHierarchyStrain ( Parent , Clustering . GetChildrenMap ( ) , Strain ) ;
// We know the server must have fractured this cluster, so repeat here
Clustering . DeactivateClusterParticle ( Parent ) ;
}
} ) ;
}
}
}
2019-06-08 17:15:34 -04:00
void UGeometryCollectionComponent : : InitConstantData ( FGeometryCollectionConstantData * ConstantData ) const
{
// Constant data should all be moved to the DDC as time permits.
2018-12-12 11:25:29 -05:00
check ( ConstantData ) ;
2019-06-08 17:15:34 -04:00
check ( RestCollection ) ;
const FGeometryCollection * Collection = RestCollection - > GetGeometryCollection ( ) . Get ( ) ;
2018-12-12 11:25:29 -05:00
check ( Collection ) ;
2019-06-08 17:15:34 -04:00
const int32 NumPoints = Collection - > NumElements ( FGeometryCollection : : VerticesGroup ) ;
const TManagedArray < FVector > & Vertex = Collection - > Vertex ;
const TManagedArray < int32 > & BoneMap = Collection - > BoneMap ;
const TManagedArray < FVector > & TangentU = Collection - > TangentU ;
const TManagedArray < FVector > & TangentV = Collection - > TangentV ;
const TManagedArray < FVector > & Normal = Collection - > Normal ;
const TManagedArray < FVector2D > & UV = Collection - > UV ;
const TManagedArray < FLinearColor > & Color = Collection - > Color ;
const TManagedArray < FLinearColor > & BoneColors = Collection - > BoneColor ;
ConstantData - > Vertices = TArray < FVector > ( Vertex . GetData ( ) , Vertex . Num ( ) ) ;
ConstantData - > BoneMap = TArray < int32 > ( BoneMap . GetData ( ) , BoneMap . Num ( ) ) ;
ConstantData - > TangentU = TArray < FVector > ( TangentU . GetData ( ) , TangentU . Num ( ) ) ;
ConstantData - > TangentV = TArray < FVector > ( TangentV . GetData ( ) , TangentV . Num ( ) ) ;
ConstantData - > Normals = TArray < FVector > ( Normal . GetData ( ) , Normal . Num ( ) ) ;
ConstantData - > UVs = TArray < FVector2D > ( UV . GetData ( ) , UV . Num ( ) ) ;
ConstantData - > Colors = TArray < FLinearColor > ( Color . GetData ( ) , Color . Num ( ) ) ;
2018-12-12 11:25:29 -05:00
ConstantData - > BoneColors . AddUninitialized ( NumPoints ) ;
2019-06-08 17:15:34 -04:00
ParallelFor ( NumPoints , [ & ] ( const int32 InPointIndex )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
const int32 BoneIndex = ConstantData - > BoneMap [ InPointIndex ] ;
ConstantData - > BoneColors [ InPointIndex ] = BoneColors [ BoneIndex ] ;
2018-12-12 11:25:29 -05:00
} ) ;
int32 NumIndices = 0 ;
2019-06-08 17:15:34 -04:00
const TManagedArray < FIntVector > & Indices = Collection - > Indices ;
const TManagedArray < int32 > & MaterialID = Collection - > MaterialID ;
const TManagedArray < bool > & Visible = GetVisibleArray ( ) ; // Use copy on write attribute. The rest collection visible array can be overriden for the convenience of debug drawing the collision volumes
const TManagedArray < int32 > & MaterialIndex = Collection - > MaterialIndex ;
const int32 NumFaceGroupEntries = Collection - > NumElements ( FGeometryCollection : : FacesGroup ) ;
for ( int FaceIndex = 0 ; FaceIndex < NumFaceGroupEntries ; + + FaceIndex )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
NumIndices + = static_cast < int > ( Visible [ FaceIndex ] ) ;
2018-12-12 11:25:29 -05:00
}
ConstantData - > Indices . AddUninitialized ( NumIndices ) ;
2019-06-08 17:15:34 -04:00
for ( int IndexIdx = 0 , cdx = 0 ; IndexIdx < NumFaceGroupEntries ; + + IndexIdx )
2018-12-12 11:25:29 -05:00
{
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
2019-06-08 17:15:34 -04:00
const int32 NumMaterialSections = Collection - > NumElements ( FGeometryCollection : : MaterialGroup ) ;
2018-12-12 11:25:29 -05:00
ConstantData - > Sections . AddUninitialized ( NumMaterialSections ) ;
2019-06-08 17:15:34 -04:00
const TManagedArray < FGeometryCollectionSection > & Sections = Collection - > Sections ;
for ( int SectionIndex = 0 ; SectionIndex < NumMaterialSections ; + + SectionIndex )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
FGeometryCollectionSection Section = Sections [ SectionIndex ] ; // deliberate copy
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
for ( int32 TriangleIndex = 0 ; TriangleIndex < Sections [ SectionIndex ] . FirstIndex / 3 ; TriangleIndex + + )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
if ( ! Visible [ MaterialIndex [ TriangleIndex ] ] )
{
Section . FirstIndex - = 3 ;
}
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
for ( int32 TriangleIndex = 0 ; TriangleIndex < Sections [ SectionIndex ] . NumTriangles ; TriangleIndex + + )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
if ( ! Visible [ MaterialIndex [ Sections [ SectionIndex ] . FirstIndex / 3 + TriangleIndex ] ] )
{
2018-12-12 11:25:29 -05:00
Section . NumTriangles - - ;
2019-06-08 17:15:34 -04:00
}
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
ConstantData - > Sections [ SectionIndex ] = MoveTemp ( Section ) ;
}
ConstantData - > NumTransforms = Collection - > NumElements ( FGeometryCollection : : TransformGroup ) ;
ConstantData - > LocalBounds = LocalBounds ;
// store the index buffer and render sections for the base unfractured mesh
const TManagedArray < int32 > & TransformToGeometryIndex = Collection - > TransformToGeometryIndex ;
const TManagedArray < int32 > & FaceStart = Collection - > FaceStart ;
const TManagedArray < int32 > & FaceCount = Collection - > FaceCount ;
const int32 NumFaces = Collection - > NumElements ( FGeometryCollection : : FacesGroup ) ;
TArray < FIntVector > BaseMeshIndices ;
TArray < int32 > BaseMeshOriginalFaceIndices ;
BaseMeshIndices . Reserve ( NumFaces ) ;
BaseMeshOriginalFaceIndices . Reserve ( NumFaces ) ;
// add all visible external faces to the original geometry index array
// #note: This is a stopgap because the original geometry array is broken
for ( int FaceIndex = 0 ; FaceIndex < NumFaces ; + + FaceIndex )
{
// only add visible external faces. MaterialID that is even is an external material
if ( Visible [ FaceIndex ] & & MaterialID [ FaceIndex ] % 2 = = 0 )
{
BaseMeshIndices . Add ( Indices [ FaceIndex ] ) ;
BaseMeshOriginalFaceIndices . Add ( FaceIndex ) ;
}
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
// We should always have external faces of a geometry collection
ensure ( BaseMeshIndices . Num ( ) > 0 ) ;
// #todo(dmp): we should eventually get this working where we use geometry nodes
// that signify original unfractured geometry. For now, this system is broken.
/*
for ( int i = 0 ; i < Collection - > NumElements ( FGeometryCollection : : TransformGroup ) ; + + i )
{
const FGeometryCollectionBoneNode & CurrBone = BoneHierarchy [ i ] ;
// root node could be parent geo
if ( CurrBone . Parent = = INDEX_NONE )
{
int32 GeometryIndex = TransformToGeometryIndex [ i ] ;
// found geometry associated with base mesh root node
if ( GeometryIndex ! = INDEX_NONE )
{
int32 CurrFaceStart = FaceStart [ GeometryIndex ] ;
int32 CurrFaceCount = FaceCount [ GeometryIndex ] ;
// add all the faces to the original geometry face array
for ( int face = CurrFaceStart ; face < CurrFaceStart + CurrFaceCount ; + + face )
{
BaseMeshIndices . Add ( Indices [ face ] ) ;
BaseMeshOriginalFaceIndices . Add ( face ) ;
}
// build an array of mesh sections
ConstantData - > HasOriginalMesh = true ;
}
else
{
// all the direct decedents of the root node with no geometry are original geometry
for ( int32 CurrChild : CurrBone . Children )
{
int32 GeometryIndex = TransformToGeometryIndex [ CurrChild ] ;
if ( GeometryIndex ! = INDEX_NONE )
{
// original geo static mesh
int32 CurrFaceStart = FaceStart [ GeometryIndex ] ;
int32 CurrFaceCount = FaceCount [ GeometryIndex ] ;
// add all the faces to the original geometry face array
for ( int face = CurrFaceStart ; face < CurrFaceStart + CurrFaceCount ; + + face )
{
BaseMeshIndices . Add ( Indices [ face ] ) ;
BaseMeshOriginalFaceIndices . Add ( face ) ;
}
ConstantData - > HasOriginalMesh = true ;
}
}
}
}
}
*/
ConstantData - > OriginalMeshSections = Collection - > BuildMeshSections ( BaseMeshIndices , BaseMeshOriginalFaceIndices , ConstantData - > OriginalMeshIndices ) ;
TArray < FMatrix > RestMatrices ;
GeometryCollectionAlgo : : GlobalMatrices ( RestCollection - > GetGeometryCollection ( ) - > Transform , RestCollection - > GetGeometryCollection ( ) - > Parent , RestMatrices ) ;
ConstantData - > RestTransforms = MoveTemp ( RestMatrices ) ;
2018-12-12 11:25:29 -05:00
}
void UGeometryCollectionComponent : : InitDynamicData ( FGeometryCollectionDynamicData * DynamicData )
{
2019-06-08 17:15:34 -04:00
SCOPE_CYCLE_COUNTER ( STAT_GCInitDynamicData ) ;
2018-12-12 11:25:29 -05:00
check ( DynamicData ) ;
2019-06-08 17:15:34 -04:00
DynamicData - > IsDynamic = this - > GetIsObjectDynamic ( ) | | bShowBoneColors | | bEnableBoneSelection ;
DynamicData - > IsLoading = this - > GetIsObjectLoading ( ) ;
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
if ( CachePlayback & & CacheParameters . TargetCache & & CacheParameters . TargetCache - > GetData ( ) )
{
const TManagedArray < int32 > & Parents = GetParentArray ( ) ;
const TManagedArray < TSet < int32 > > & Children = GetChildrenArray ( ) ;
const TManagedArray < FTransform > & Transform = RestCollection - > GetGeometryCollection ( ) - > Transform ;
const TManagedArray < FTransform > & MassToLocal = RestCollection - > GetGeometryCollection ( ) - > GetAttribute < FTransform > ( " MassToLocal " , FGeometryCollection : : TransformGroup ) ;
2018-12-12 11:25:29 -05:00
2019-07-16 19:12:32 -04:00
// #todo(dmp): find a better place to calculate and store this
float CacheDt = CacheParameters . TargetCache - > GetData ( ) - > GetDt ( ) ;
// if we want the state before the first cached frame, then the collection doesn't need to be dynamic since it'll render the pre-fractured geometry
DynamicData - > IsDynamic = DesiredCacheTime > CacheDt ;
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
// if we are already on the current cached frame, return
if ( FMath : : IsNearlyEqual ( CurrentCacheTime , DesiredCacheTime ) & & GlobalMatrices . Num ( ) ! = 0 )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
DynamicData - > PrevTransforms = GlobalMatrices ;
DynamicData - > Transforms = GlobalMatrices ;
2019-07-16 19:12:32 -04:00
// maintaining the cache time means we should consider the transforms equal for dynamic data sending purposes
TransformsAreEqual [ ( TransformsAreEqualIndex + + ) % TransformsAreEqual . Num ( ) ] = true ;
2019-06-08 17:15:34 -04:00
return ;
}
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
// if the input simulation time to playback is the first frame, reset simulation time
if ( DesiredCacheTime < = CacheDt | | FMath : : IsNearlyEqual ( CurrentCacheTime , FLT_MAX ) )
{
CurrentCacheTime = DesiredCacheTime ;
GeometryCollectionAlgo : : GlobalMatrices ( GetTransformArray ( ) , GetParentArray ( ) , GlobalMatrices ) ;
DynamicData - > PrevTransforms = GlobalMatrices ;
DynamicData - > Transforms = GlobalMatrices ;
EventsPlayed . Empty ( ) ;
EventsPlayed . AddDefaulted ( CacheParameters . TargetCache - > GetData ( ) - > Records . Num ( ) ) ;
2019-07-16 19:12:32 -04:00
// reset should send new transforms to the RT
TransformsAreEqual [ ( TransformsAreEqualIndex + + ) % TransformsAreEqual . Num ( ) ] = false ;
2019-06-08 17:15:34 -04:00
}
else if ( GlobalMatrices . Num ( ) = = 0 )
{
// bad case here. Sequencer starts at non zero position We cannot correctly reconstruct the current frame, so give a warning
GeometryCollectionAlgo : : GlobalMatrices ( GetTransformArray ( ) , GetParentArray ( ) , GlobalMatrices ) ;
DynamicData - > PrevTransforms = GlobalMatrices ;
DynamicData - > Transforms = GlobalMatrices ;
EventsPlayed . Empty ( ) ;
EventsPlayed . AddDefaulted ( CacheParameters . TargetCache - > GetData ( ) - > Records . Num ( ) ) ;
2019-07-16 19:12:32 -04:00
// degenerate case causes and reset should send new transforms to the RT
TransformsAreEqual [ ( TransformsAreEqualIndex + + ) % TransformsAreEqual . Num ( ) ] = false ;
2019-06-08 17:15:34 -04:00
UE_LOG ( UGCC_LOG , Warning , TEXT ( " Cache out of sync - must rewind sequencer to start frame " ) ) ;
}
else if ( DesiredCacheTime > = CurrentCacheTime )
{
int NumSteps = floor ( ( DesiredCacheTime - CurrentCacheTime ) / CacheDt ) ;
float LastDt = FMath : : Fmod ( DesiredCacheTime - CurrentCacheTime , CacheDt ) ;
NumSteps + = LastDt > SMALL_NUMBER ? 1 : 0 ;
FTransform ActorToWorld = GetComponentTransform ( ) ;
2019-07-16 19:12:32 -04:00
bool HasAnyActiveTransforms = false ;
2019-06-08 17:15:34 -04:00
// Jump ahead in increments of CacheDt evaluating the cache until we reach our desired time
for ( int st = 0 ; st < NumSteps ; + + st )
{
float TimeIncrement = st = = NumSteps - 1 ? LastDt : CacheDt ;
CurrentCacheTime + = TimeIncrement ;
DynamicData - > PrevTransforms = GlobalMatrices ;
const FRecordedFrame * FirstFrame = nullptr ;
const FRecordedFrame * SecondFrame = nullptr ;
2019-07-16 19:12:32 -04:00
CacheParameters . TargetCache - > GetData ( ) - > GetFramesForTime ( CurrentCacheTime , FirstFrame , SecondFrame ) ;
2019-06-08 17:15:34 -04:00
if ( FirstFrame & & ! SecondFrame )
{
const TArray < FTransform > & xforms = FirstFrame - > Transforms ;
const TArray < int32 > & TransformIndices = FirstFrame - > TransformIndices ;
const int32 NumActives = FirstFrame - > TransformIndices . Num ( ) ;
2019-07-16 19:12:32 -04:00
if ( NumActives > 0 )
{
HasAnyActiveTransforms = true ;
}
2019-06-08 17:15:34 -04:00
for ( int i = 0 ; i < NumActives ; + + i )
{
const int32 InternalIndexTmp = TransformIndices [ i ] ;
if ( InternalIndexTmp > = GlobalMatrices . Num ( ) )
{
UE_LOG ( UGCC_LOG , Error ,
TEXT ( " %s: TargetCache (%s) is out of sync with GeometryCollection. Regenerate the cache. " ) ,
* RestCollection - > GetName ( ) , * CacheParameters . TargetCache - > GetName ( ) ) ;
DynamicData - > PrevTransforms = GlobalMatrices ;
DynamicData - > Transforms = GlobalMatrices ;
return ;
}
// calculate global matrix for current
FTransform ParticleToWorld = xforms [ i ] ;
FTransform CurrGlobalTransform = MassToLocal [ InternalIndexTmp ] . GetRelativeTransformReverse ( ParticleToWorld ) . GetRelativeTransform ( ActorToWorld ) ;
CurrGlobalTransform . NormalizeRotation ( ) ;
GlobalMatrices [ InternalIndexTmp ] = CurrGlobalTransform . ToMatrixWithScale ( ) ;
// Traverse from active parent node down to all children and set global transforms
GeometryCollectionAlgo : : GlobalMatricesFromRoot ( InternalIndexTmp , Transform , Children , GlobalMatrices ) ;
}
}
2019-07-16 19:12:32 -04:00
else if ( FirstFrame & & SecondFrame & & CurrentCacheTime > FirstFrame - > Timestamp )
2019-06-08 17:15:34 -04:00
{
2019-07-16 19:12:32 -04:00
const float Alpha = ( CurrentCacheTime - FirstFrame - > Timestamp ) / ( SecondFrame - > Timestamp - FirstFrame - > Timestamp ) ;
2019-06-08 17:15:34 -04:00
check ( 0 < = Alpha & & Alpha < = 1.0f ) ;
const int32 NumActives = SecondFrame - > TransformIndices . Num ( ) ;
2019-07-16 19:12:32 -04:00
if ( NumActives > 0 )
{
HasAnyActiveTransforms = true ;
}
2019-06-08 17:15:34 -04:00
for ( int Index = 0 ; Index < NumActives ; + + Index )
{
const int32 InternalIndexTmp = SecondFrame - > TransformIndices [ Index ] ;
// check if transform index is valid
if ( InternalIndexTmp > = GlobalMatrices . Num ( ) )
{
UE_LOG ( UGCC_LOG , Error ,
TEXT ( " %s: TargetCache (%s) is out of sync with GeometryCollection. Regenerate the cache. " ) ,
* RestCollection - > GetName ( ) , * CacheParameters . TargetCache - > GetName ( ) ) ;
DynamicData - > PrevTransforms = GlobalMatrices ;
DynamicData - > Transforms = GlobalMatrices ;
return ;
}
const int32 PreviousIndexSlot = Index < SecondFrame - > PreviousTransformIndices . Num ( ) ? SecondFrame - > PreviousTransformIndices [ Index ] : INDEX_NONE ;
if ( PreviousIndexSlot ! = INDEX_NONE )
{
FTransform ParticleToWorld ;
ParticleToWorld . Blend ( FirstFrame - > Transforms [ PreviousIndexSlot ] , SecondFrame - > Transforms [ Index ] , Alpha ) ;
FTransform CurrGlobalTransform = MassToLocal [ InternalIndexTmp ] . GetRelativeTransformReverse ( ParticleToWorld ) . GetRelativeTransform ( ActorToWorld ) ;
CurrGlobalTransform . NormalizeRotation ( ) ;
GlobalMatrices [ InternalIndexTmp ] = CurrGlobalTransform . ToMatrixWithScale ( ) ;
// Traverse from active parent node down to all children and set global transforms
GeometryCollectionAlgo : : GlobalMatricesFromRoot ( InternalIndexTmp , Transform , Children , GlobalMatrices ) ;
}
else
{
FTransform ParticleToWorld = SecondFrame - > Transforms [ Index ] ;
FTransform CurrGlobalTransform = MassToLocal [ InternalIndexTmp ] . GetRelativeTransformReverse ( ParticleToWorld ) . GetRelativeTransform ( ActorToWorld ) ;
CurrGlobalTransform . NormalizeRotation ( ) ;
GlobalMatrices [ InternalIndexTmp ] = CurrGlobalTransform . ToMatrixWithScale ( ) ;
// Traverse from active parent node down to all children and set global transforms
GeometryCollectionAlgo : : GlobalMatricesFromRoot ( InternalIndexTmp , Transform , Children , GlobalMatrices ) ;
}
}
}
DynamicData - > Transforms = GlobalMatrices ;
/**********************************************************************************************************************************************************************************/
// Capture all events for the given time
if ( false )
{
// clear events on the solver
2019-08-02 09:01:58 -04:00
Chaos : : FPhysicsSolver * Solver = GetSolver ( * this ) ;
2019-06-08 17:15:34 -04:00
if ( Solver )
{
2019-08-02 09:01:58 -04:00
# if TODO_REPLACE_SOLVER_LOCK
2019-06-08 17:15:34 -04:00
Chaos : : FSolverWriteLock ScopedWriteLock ( Solver ) ;
2019-08-02 09:01:58 -04:00
# endif
2019-06-08 17:15:34 -04:00
2019-08-02 09:01:58 -04:00
# if TODO_REIMPLEMENT_EVENTS_DATA_ARRAYS
2019-06-08 17:15:34 -04:00
//////////////////////////////////////////////////////////////////////////
// Temporary workaround for writing on multiple threads.
// The following is called wide from the render thread to populate
// Niagara data from a cache without invoking a solver directly
//
// The above write lock guarantees we can safely write to these buffers
//
// TO BE REFACTORED
// #TODO BG
//////////////////////////////////////////////////////////////////////////
2019-08-02 09:01:58 -04:00
Chaos : : FPhysicsSolver : : FAllCollisionData * CollisionDataToWriteTo = const_cast < Chaos : : FPhysicsSolver : : FAllCollisionData * > ( Solver - > GetAllCollisions_FromSequencerCache_NEEDSLOCK ( ) ) ;
Chaos : : FPhysicsSolver : : FAllBreakingData * BreakingDataToWriteTo = const_cast < Chaos : : FPhysicsSolver : : FAllBreakingData * > ( Solver - > GetAllBreakings_FromSequencerCache_NEEDSLOCK ( ) ) ;
Chaos : : FPhysicsSolver : : FAllTrailingData * TrailingDataToWriteTo = const_cast < Chaos : : FPhysicsSolver : : FAllTrailingData * > ( Solver - > GetAllTrailings_FromSequencerCache_NEEDSLOCK ( ) ) ;
2019-06-08 17:15:34 -04:00
if ( ! FMath : : IsNearlyEqual ( CollisionDataToWriteTo - > TimeCreated , DesiredCacheTime ) )
{
CollisionDataToWriteTo - > AllCollisionsArray . Empty ( ) ;
CollisionDataToWriteTo - > TimeCreated = DesiredCacheTime ;
}
int32 Index = CacheParameters . TargetCache - > GetData ( ) - > FindLastKeyBefore ( CurrentCacheTime ) ;
const FRecordedFrame * RecordedFrame = & CacheParameters . TargetCache - > GetData ( ) - > Records [ Index ] ;
2019-08-02 09:01:58 -04:00
if ( RecordedFrame & & PhysicsProxy & & ! EventsPlayed [ Index ] )
2019-06-08 17:15:34 -04:00
{
EventsPlayed [ Index ] = true ;
// Collisions
if ( RecordedFrame - > Collisions . Num ( ) > 0 )
{
for ( int32 Idx = 0 ; Idx < RecordedFrame - > Collisions . Num ( ) ; + + Idx )
{
// Check if the particle is still kinematic
int32 NewIdx = CollisionDataToWriteTo - > AllCollisionsArray . Add ( Chaos : : TCollisionData < float , 3 > ( ) ) ;
Chaos : : TCollisionData < float , 3 > & AllCollisionsDataArrayItem = CollisionDataToWriteTo - > AllCollisionsArray [ NewIdx ] ;
AllCollisionsDataArrayItem . Location = RecordedFrame - > Collisions [ Idx ] . Location ;
AllCollisionsDataArrayItem . AccumulatedImpulse = RecordedFrame - > Collisions [ Idx ] . AccumulatedImpulse ;
AllCollisionsDataArrayItem . Normal = RecordedFrame - > Collisions [ Idx ] . Normal ;
AllCollisionsDataArrayItem . Velocity1 = RecordedFrame - > Collisions [ Idx ] . Velocity1 ;
AllCollisionsDataArrayItem . Velocity2 = RecordedFrame - > Collisions [ Idx ] . Velocity2 ;
AllCollisionsDataArrayItem . AngularVelocity1 = RecordedFrame - > Collisions [ Idx ] . AngularVelocity1 ;
AllCollisionsDataArrayItem . AngularVelocity2 = RecordedFrame - > Collisions [ Idx ] . AngularVelocity2 ;
AllCollisionsDataArrayItem . Mass1 = RecordedFrame - > Collisions [ Idx ] . Mass1 ;
AllCollisionsDataArrayItem . Mass2 = RecordedFrame - > Collisions [ Idx ] . Mass2 ;
2019-08-02 09:01:58 -04:00
# if TODO_CONVERT_GEOMETRY_COLLECTION_PARTICLE_INDICES_TO_PARTICLE_POINTERS
2019-06-08 17:15:34 -04:00
AllCollisionsDataArrayItem . ParticleIndex = RecordedFrame - > Collisions [ Idx ] . ParticleIndex ;
2019-08-02 09:01:58 -04:00
# endif
2019-06-08 17:15:34 -04:00
AllCollisionsDataArrayItem . LevelsetIndex = RecordedFrame - > Collisions [ Idx ] . LevelsetIndex ;
AllCollisionsDataArrayItem . ParticleIndexMesh = RecordedFrame - > Collisions [ Idx ] . ParticleIndexMesh ;
AllCollisionsDataArrayItem . LevelsetIndexMesh = RecordedFrame - > Collisions [ Idx ] . LevelsetIndexMesh ;
}
}
// Breaking
if ( RecordedFrame - > Breakings . Num ( ) > 0 )
{
for ( int32 Idx = 0 ; Idx < RecordedFrame - > Breakings . Num ( ) ; + + Idx )
{
// Check if the particle is still kinematic
int32 NewIdx = BreakingDataToWriteTo - > AllBreakingsArray . Add ( Chaos : : TBreakingData < float , 3 > ( ) ) ;
Chaos : : TBreakingData < float , 3 > & AllBreakingsDataArrayItem = BreakingDataToWriteTo - > AllBreakingsArray [ NewIdx ] ;
AllBreakingsDataArrayItem . Location = RecordedFrame - > Breakings [ Idx ] . Location ;
AllBreakingsDataArrayItem . Velocity = RecordedFrame - > Breakings [ Idx ] . Velocity ;
AllBreakingsDataArrayItem . AngularVelocity = RecordedFrame - > Breakings [ Idx ] . AngularVelocity ;
AllBreakingsDataArrayItem . Mass = RecordedFrame - > Breakings [ Idx ] . Mass ;
2019-08-02 09:01:58 -04:00
# if TODO_CONVERT_GEOMETRY_COLLECTION_PARTICLE_INDICES_TO_PARTICLE_POINTERS
2019-06-08 17:15:34 -04:00
AllBreakingsDataArrayItem . ParticleIndex = RecordedFrame - > Breakings [ Idx ] . ParticleIndex ;
2019-08-02 09:01:58 -04:00
# endif
2019-06-08 17:15:34 -04:00
AllBreakingsDataArrayItem . ParticleIndexMesh = RecordedFrame - > Breakings [ Idx ] . ParticleIndexMesh ;
}
}
// Trailing
if ( RecordedFrame - > Trailings . Num ( ) > 0 )
{
for ( FSolverTrailingData Trailing : RecordedFrame - > Trailings )
{
// Check if the particle is still kinematic
int32 NewIdx = TrailingDataToWriteTo - > AllTrailingsArray . Add ( Chaos : : TTrailingData < float , 3 > ( ) ) ;
Chaos : : TTrailingData < float , 3 > & AllTrailingsDataArrayItem = TrailingDataToWriteTo - > AllTrailingsArray [ NewIdx ] ;
AllTrailingsDataArrayItem . Location = Trailing . Location ;
AllTrailingsDataArrayItem . Velocity = Trailing . Velocity ;
AllTrailingsDataArrayItem . AngularVelocity = Trailing . AngularVelocity ;
AllTrailingsDataArrayItem . Mass = Trailing . Mass ;
2019-08-02 09:01:58 -04:00
# if TODO_CONVERT_GEOMETRY_COLLECTION_PARTICLE_INDICES_TO_PARTICLE_POINTERS
2019-06-08 17:15:34 -04:00
AllTrailingsDataArrayItem . ParticleIndex = Trailing . ParticleIndex ;
2019-08-02 09:01:58 -04:00
# endif
2019-06-08 17:15:34 -04:00
AllTrailingsDataArrayItem . ParticleIndexMesh = Trailing . ParticleIndexMesh ;
}
}
}
2019-08-02 09:01:58 -04:00
# endif
2019-06-08 17:15:34 -04:00
}
}
}
2019-07-16 19:12:32 -04:00
// check if transforms at start of this tick are the same as what is calculated from the cache
TransformsAreEqual [ ( TransformsAreEqualIndex + + ) % TransformsAreEqual . Num ( ) ] = ! HasAnyActiveTransforms ;
2019-06-08 17:15:34 -04:00
}
else
{
// time is before current cache time so maintain the matrices we have since we can't rewind
DynamicData - > PrevTransforms = GlobalMatrices ;
DynamicData - > Transforms = GlobalMatrices ;
2019-07-16 19:12:32 -04:00
// reset event means we don't want to consider transforms as being equal between prev and current frame
TransformsAreEqual [ ( TransformsAreEqualIndex + + ) % TransformsAreEqual . Num ( ) ] = true ;
2019-06-08 17:15:34 -04:00
}
}
else if ( DynamicData - > IsDynamic )
{
// If we have no transforms stored in the dynamic data, then assign both prev and current to the same global matrices
if ( GlobalMatrices . Num ( ) = = 0 )
{
// Copy global matrices over to DynamicData
CalculateGlobalMatrices ( ) ;
DynamicData - > PrevTransforms = GlobalMatrices ;
DynamicData - > Transforms = GlobalMatrices ;
2019-07-16 19:12:32 -04:00
// reset event means we don't want to consider transforms as being equal between prev and current frame
TransformsAreEqual [ ( TransformsAreEqualIndex + + ) % TransformsAreEqual . Num ( ) ] = false ;
2019-06-08 17:15:34 -04:00
}
else
{
// Copy existing global matrices into prev transforms
DynamicData - > PrevTransforms = GlobalMatrices ;
// Copy global matrices over to DynamicData
CalculateGlobalMatrices ( ) ;
// if the number of matrices has changed between frames, then sync previous to current
if ( GlobalMatrices . Num ( ) ! = DynamicData - > PrevTransforms . Num ( ) )
{
DynamicData - > PrevTransforms = GlobalMatrices ;
}
DynamicData - > Transforms = GlobalMatrices ;
2019-07-16 19:12:32 -04:00
// check if previous transforms are the same as current
TransformsAreEqual [ ( TransformsAreEqualIndex + + ) % TransformsAreEqual . Num ( ) ] = IsEqual ( DynamicData - > PrevTransforms , DynamicData - > Transforms ) ;
}
}
2019-06-08 17:15:34 -04:00
}
2018-12-12 11:25:29 -05:00
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 ) ;
2020-06-23 18:40:00 -04:00
2020-07-21 15:17:07 -04:00
# if WITH_EDITOR
if ( IsRegistered ( ) & & SceneProxy & & RestCollection )
{
const bool bWantNanite = RestCollection - > EnableNanite & & GGeometryCollectionNanite ! = 0 ;
const bool bHaveNanite = SceneProxy - > IsNaniteMesh ( ) ;
bool bRecreateProxy = bWantNanite ! = bHaveNanite ;
if ( bRecreateProxy )
{
// Wait until resources are released
FlushRenderingCommands ( ) ;
FComponentReregisterContext ReregisterContext ( this ) ;
UpdateAllPrimitiveSceneInfosForSingleComponent ( this ) ;
}
}
# endif
2020-06-23 18:40:00 -04:00
# if WITH_CHAOS
2019-06-08 17:15:34 -04:00
//if (bRenderStateDirty && DynamicCollection) //todo: always send for now
2020-07-21 15:17:07 -04:00
if ( RestCollection )
2018-12-12 11:25:29 -05:00
{
2020-10-29 13:38:15 -04:00
if ( CHAOS_ENSURE ( DynamicCollection ) ) //, TEXT("No dynamic collection available for component %s during tick."), *GetName()))
2018-12-12 11:25:29 -05:00
{
2020-06-23 18:40:00 -04:00
if ( RestCollection - > HasVisibleGeometry ( ) | | DynamicCollection - > IsDirty ( ) )
2020-04-30 07:34:59 -04:00
{
2020-06-23 18:40:00 -04:00
MarkRenderTransformDirty ( ) ;
MarkRenderDynamicDataDirty ( ) ;
bRenderStateDirty = false ;
//DynamicCollection->MakeClean(); clean?
const UWorld * MyWorld = GetWorld ( ) ;
if ( MyWorld & & MyWorld - > IsGameWorld ( ) )
2020-04-30 07:34:59 -04:00
{
2020-06-23 18:40:00 -04:00
//cycle every 0xff frames
//@todo - Need way of seeing if the collection is actually changing
if ( bNavigationRelevant & & bRegistered & & ( ( ( GFrameCounter + NavmeshInvalidationTimeSliceIndex ) & 0xff ) = = 0 ) )
{
UpdateNavigationData ( ) ;
}
2020-04-30 07:34:59 -04:00
}
}
2018-12-12 11:25:29 -05:00
}
}
2020-06-23 18:40:00 -04:00
# endif
2018-12-12 11:25:29 -05:00
}
void UGeometryCollectionComponent : : OnRegister ( )
{
2020-06-23 18:40:00 -04:00
# if WITH_CHAOS
2018-12-12 11:25:29 -05:00
//UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent[%p]::OnRegister()[%p]"), this,RestCollection );
ResetDynamicCollection ( ) ;
2019-07-23 16:27:55 -04:00
2019-07-24 13:04:02 -04:00
# if WITH_EDITOR
FScopedColorEdit ColorEdit ( this ) ;
2019-07-23 16:27:55 -04:00
ColorEdit . ResetBoneSelection ( ) ;
ColorEdit . ResetHighlightedBones ( ) ;
2019-07-24 13:04:02 -04:00
# endif
2020-06-23 18:40:00 -04:00
# endif // WITH_CHAOS
2020-09-01 14:07:48 -04:00
SetIsReplicated ( bEnableReplication ) ;
2019-06-08 17:15:34 -04:00
Super : : OnRegister ( ) ;
2018-12-12 11:25:29 -05:00
}
void UGeometryCollectionComponent : : ResetDynamicCollection ( )
{
2019-06-08 17:15:34 -04:00
bool bCreateDynamicCollection = true ;
# if WITH_EDITOR
bCreateDynamicCollection = false ;
if ( UWorld * World = GetWorld ( ) )
{
if ( World - > IsGameWorld ( ) )
{
bCreateDynamicCollection = true ;
}
}
# endif
2018-12-12 11:25:29 -05:00
//UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent[%p]::ResetDynamicCollection()"), static_cast<const void*>(this));
2019-06-08 17:15:34 -04:00
if ( bCreateDynamicCollection & & RestCollection )
{
DynamicCollection = MakeUnique < FGeometryDynamicCollection > ( ) ;
for ( const auto DynamicArray : CopyOnWriteAttributeList )
{
* DynamicArray = nullptr ;
}
GetTransformArrayCopyOnWrite ( ) ;
GetParentArrayCopyOnWrite ( ) ;
GetChildrenArrayCopyOnWrite ( ) ;
GetSimulationTypeArrayCopyOnWrite ( ) ;
GetStatusFlagsArrayCopyOnWrite ( ) ;
SetRenderStateDirty ( ) ;
}
2018-12-12 11:25:29 -05:00
if ( RestCollection )
{
2019-06-08 17:15:34 -04:00
CalculateGlobalMatrices ( ) ;
CalculateLocalBounds ( ) ;
2018-12-12 11:25:29 -05:00
}
}
void UGeometryCollectionComponent : : OnCreatePhysicsState ( )
{
2020-02-21 17:40:37 -05:00
/*#if WITH_PHYSX
2019-06-08 17:15:34 -04:00
DummyBodySetup = NewObject < UBodySetup > ( this , UBodySetup : : StaticClass ( ) ) ;
DummyBodySetup - > AggGeom . BoxElems . Add ( FKBoxElem ( 1.0f ) ) ;
DummyBodyInstance . InitBody ( DummyBodySetup , GetComponentToWorld ( ) , this , nullptr ) ;
DummyBodyInstance . bNotifyRigidBodyCollision = BodyInstance . bNotifyRigidBodyCollision ;
# endif
2020-02-21 17:40:37 -05:00
*/
2018-12-12 11:25:29 -05:00
// Skip the chain - don't care about body instance setup
UActorComponent : : OnCreatePhysicsState ( ) ;
2019-06-08 17:15:34 -04:00
if ( ! Simulating ) IsObjectLoading = false ; // just mark as loaded if we are simulating.
2018-12-12 11:25:29 -05:00
2020-02-21 17:40:37 -05:00
/*#if WITH_PHYSX
2019-06-08 17:15:34 -04:00
DummyBodyInstance . SetCollisionEnabled ( ECollisionEnabled : : QueryOnly ) ;
DummyBodyInstance . SetResponseToAllChannels ( ECR_Block ) ;
# endif
2020-02-21 17:40:37 -05:00
*/
2020-06-23 18:40:00 -04:00
# if WITH_CHAOS
2020-02-21 17:40:37 -05:00
// Static mesh uses an init framework that goes through FBodyInstance. We
// do the same thing, but through the geometry collection proxy and lambdas
// defined below. FBodyInstance doesn't work for geometry collections
// because FBodyInstance manages a single particle, where we have many.
2019-08-02 09:01:58 -04:00
if ( ! PhysicsProxy )
2019-06-08 17:15:34 -04:00
{
2018-12-12 11:25:29 -05:00
# if WITH_EDITOR && WITH_EDITORONLY_DATA
2019-06-08 17:15:34 -04:00
EditorActor = nullptr ;
if ( RestCollection )
{
//hack: find a better place for this
2020-10-21 17:56:05 -04:00
UGeometryCollection * RestCollectionMutable = const_cast < UGeometryCollection * > ( ToRawPtr ( RestCollection ) ) ;
2019-06-08 17:15:34 -04:00
RestCollectionMutable - > EnsureDataIsCooked ( ) ;
}
2018-12-12 11:25:29 -05:00
# endif
2019-06-08 17:15:34 -04:00
const bool bValidWorld = GetWorld ( ) & & GetWorld ( ) - > IsGameWorld ( ) ;
const bool bValidCollection = DynamicCollection & & DynamicCollection - > Transform . Num ( ) > 0 ;
if ( bValidWorld & & bValidCollection )
2018-12-12 11:25:29 -05:00
{
2020-03-09 13:22:54 -04:00
FPhysxUserData : : Set < UPrimitiveComponent > ( & PhysicsUserData , this ) ;
2020-03-02 17:05:51 -05:00
FSimulationParameters SimulationParameters ;
{
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
SimulationParameters . Name = GetPathName ( ) ;
# endif
2020-03-06 18:17:16 -05:00
if ( RestCollection )
{
RestCollection - > GetSharedSimulationParams ( SimulationParameters . Shared ) ;
SimulationParameters . RestCollection = RestCollection - > GetGeometryCollection ( ) . Get ( ) ;
}
2020-03-02 17:05:51 -05:00
SimulationParameters . Simulating = Simulating ;
SimulationParameters . EnableClustering = EnableClustering ;
SimulationParameters . ClusterGroupIndex = EnableClustering ? ClusterGroupIndex : 0 ;
SimulationParameters . MaxClusterLevel = MaxClusterLevel ;
2021-01-29 00:35:21 -04:00
SimulationParameters . bUseSizeSpecificDamageThresholds = bUseSizeSpecificDamageThreshold ;
2020-03-02 17:05:51 -05:00
SimulationParameters . DamageThreshold = DamageThreshold ;
SimulationParameters . ClusterConnectionMethod = ( Chaos : : FClusterCreationParameters < float > : : EConnectionMethod ) ClusterConnectionType ;
SimulationParameters . CollisionGroup = CollisionGroup ;
SimulationParameters . CollisionSampleFraction = CollisionSampleFraction ;
SimulationParameters . InitialVelocityType = InitialVelocityType ;
SimulationParameters . InitialLinearVelocity = InitialLinearVelocity ;
SimulationParameters . InitialAngularVelocity = InitialAngularVelocity ;
SimulationParameters . bClearCache = true ;
SimulationParameters . ObjectType = ObjectType ;
SimulationParameters . CacheType = CacheParameters . CacheMode ;
SimulationParameters . ReverseCacheBeginTime = CacheParameters . ReverseCacheBeginTime ;
SimulationParameters . CollisionData . SaveCollisionData = CacheParameters . SaveCollisionData ;
SimulationParameters . CollisionData . DoGenerateCollisionData = CacheParameters . DoGenerateCollisionData ;
SimulationParameters . CollisionData . CollisionDataSizeMax = CacheParameters . CollisionDataSizeMax ;
SimulationParameters . CollisionData . DoCollisionDataSpatialHash = CacheParameters . DoCollisionDataSpatialHash ;
SimulationParameters . CollisionData . CollisionDataSpatialHashRadius = CacheParameters . CollisionDataSpatialHashRadius ;
SimulationParameters . CollisionData . MaxCollisionPerCell = CacheParameters . MaxCollisionPerCell ;
SimulationParameters . BreakingData . SaveBreakingData = CacheParameters . SaveBreakingData ;
SimulationParameters . BreakingData . DoGenerateBreakingData = CacheParameters . DoGenerateBreakingData ;
SimulationParameters . BreakingData . BreakingDataSizeMax = CacheParameters . BreakingDataSizeMax ;
SimulationParameters . BreakingData . DoBreakingDataSpatialHash = CacheParameters . DoBreakingDataSpatialHash ;
SimulationParameters . BreakingData . BreakingDataSpatialHashRadius = CacheParameters . BreakingDataSpatialHashRadius ;
SimulationParameters . BreakingData . MaxBreakingPerCell = CacheParameters . MaxBreakingPerCell ;
SimulationParameters . TrailingData . SaveTrailingData = CacheParameters . SaveTrailingData ;
SimulationParameters . TrailingData . DoGenerateTrailingData = CacheParameters . DoGenerateTrailingData ;
SimulationParameters . TrailingData . TrailingDataSizeMax = CacheParameters . TrailingDataSizeMax ;
SimulationParameters . TrailingData . TrailingMinSpeedThreshold = CacheParameters . TrailingMinSpeedThreshold ;
SimulationParameters . TrailingData . TrailingMinVolumeThreshold = CacheParameters . TrailingMinVolumeThreshold ;
SimulationParameters . RemoveOnFractureEnabled = SimulationParameters . Shared . RemoveOnFractureIndices . Num ( ) > 0 ;
2020-03-09 13:22:54 -04:00
SimulationParameters . WorldTransform = GetComponentToWorld ( ) ;
SimulationParameters . UserData = static_cast < void * > ( & PhysicsUserData ) ;
2020-09-24 00:43:27 -04:00
UPhysicalMaterial * EnginePhysicalMaterial = GetPhysicalMaterial ( ) ;
if ( ensure ( EnginePhysicalMaterial ) )
{
SimulationParameters . PhysicalMaterialHandle = EnginePhysicalMaterial - > GetPhysicsMaterial ( ) ;
}
2020-03-02 17:05:51 -05:00
}
2020-02-21 17:40:37 -05:00
//
// Called from FGeometryCollectionPhysicsProxy::Initialize()
//
2019-06-08 17:15:34 -04:00
auto InitFunc = [ this ] ( FSimulationParameters & InParams )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
InParams . Name = GetPathName ( ) ;
# endif
GetInitializationCommands ( InParams . InitializationCommands ) ;
UGeometryCollectionCache * Cache = CacheParameters . TargetCache ;
if ( Cache & & CacheParameters . CacheMode ! = EGeometryCollectionCacheType : : None )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
bool bCacheValid = false ;
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
switch ( CacheParameters . CacheMode )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
case EGeometryCollectionCacheType : : Record :
case EGeometryCollectionCacheType : : RecordAndPlay :
bCacheValid = Cache - > CompatibleWithForRecord ( RestCollection ) ;
break ;
case EGeometryCollectionCacheType : : Play :
bCacheValid = Cache - > CompatibleWithForPlayback ( RestCollection ) ;
break ;
default :
check ( false ) ;
break ;
}
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
if ( bCacheValid )
{
InParams . RecordedTrack = ( InParams . IsCachePlaying ( ) & & CacheParameters . TargetCache ) ? CacheParameters . TargetCache - > GetData ( ) : nullptr ;
}
else
{
// We attempted to utilize a cache that was not compatible with this component. Report and do not use
UE_LOG ( LogChaos , Error , TEXT ( " Geometry collection '%s' attempted to use cache '%s' but it is not compatible. " ) , * GetName ( ) , * ( CacheParameters . TargetCache - > GetName ( ) ) ) ;
InParams . RecordedTrack = nullptr ;
InParams . CacheType = EGeometryCollectionCacheType : : None ;
CacheParameters . CacheMode = EGeometryCollectionCacheType : : None ;
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
FMessageLog ( " PIE " ) . Error ( )
- > AddToken ( FTextToken : : Create ( NSLOCTEXT ( " GeomCollectionComponent " , " BadCache_01 " , " Geometry collection " ) ) )
- > AddToken ( FUObjectToken : : Create ( this ) )
- > AddToken ( FTextToken : : Create ( NSLOCTEXT ( " GeomCollectionComponent " , " BadCache_02 " , " attempted to use cache " ) ) )
- > AddToken ( FUObjectToken : : Create ( CacheParameters . TargetCache ) )
- > AddToken ( FTextToken : : Create ( NSLOCTEXT ( " GeomCollectionComponent " , " BadCache_03 " , " but it is not compatible " ) ) ) ;
# endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
2018-12-12 11:25:29 -05:00
}
}
2019-06-08 17:15:34 -04:00
} ;
auto CacheSyncFunc = [ this ] ( const FGeometryCollectionResults & Results )
{
# if GEOMETRYCOLLECTION_DEBUG_DRAW
const bool bHasNumParticlesChanged = ( NumParticlesAdded ! = Results . NumParticlesAdded ) ; // Needs to be evaluated before NumParticlesAdded gets updated
# endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
2019-11-08 19:47:41 -05:00
//RigidBodyIds.Init(Results.RigidBodyIds);
2019-06-08 17:15:34 -04:00
BaseRigidBodyIndex = Results . BaseIndex ;
NumParticlesAdded = Results . NumParticlesAdded ;
DisabledFlags = Results . DisabledStates ;
if ( ! IsObjectDynamic & & Results . IsObjectDynamic )
{
IsObjectDynamic = Results . IsObjectDynamic ;
NotifyGeometryCollectionPhysicsStateChange . Broadcast ( this ) ;
SwitchRenderModels ( GetOwner ( ) ) ;
}
if ( IsObjectLoading & & ! Results . IsObjectLoading )
{
IsObjectLoading = Results . IsObjectLoading ;
NotifyGeometryCollectionPhysicsLoadingStateChange . Broadcast ( this ) ;
}
WorldBounds = Results . WorldBounds ;
2020-09-01 14:07:48 -04:00
// Update replication data for clients if necessary
UpdateRepData ( ) ;
2019-06-08 17:15:34 -04:00
# if GEOMETRYCOLLECTION_DEBUG_DRAW
// Notify debug draw componentUGeometryCollectionDebugDrawComponent of particle changes
if ( bHasNumParticlesChanged )
{
if ( const AGeometryCollectionActor * const Owner = Cast < AGeometryCollectionActor > ( GetOwner ( ) ) )
{
if ( UGeometryCollectionDebugDrawComponent * const GeometryCollectionDebugDrawComponent = Owner - > GetGeometryCollectionDebugDrawComponent ( ) )
{
GeometryCollectionDebugDrawComponent - > OnClusterChanged ( ) ;
}
}
}
# endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
} ;
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
2020-03-09 13:22:54 -04:00
FPhysScene_Chaos * Scene = GetInnerChaosScene ( ) ;
2019-06-08 17:15:34 -04:00
CacheParameters . TargetCache - > PreEditChange ( nullptr ) ;
CacheParameters . TargetCache - > Modify ( ) ;
CacheParameters . TargetCache - > SetFromRawTrack ( InTrack ) ;
CacheParameters . TargetCache - > PostEditChange ( ) ;
Scene - > AddPieModifiedObject ( CacheParameters . TargetCache ) ;
if ( EditorActor )
{
UGeometryCollectionComponent * EditorComponent = Cast < UGeometryCollectionComponent > ( EditorUtilities : : FindMatchingComponentInstance ( this , EditorActor ) ) ;
if ( EditorComponent )
{
2020-03-15 10:33:45 -04:00
EditorComponent - > PreEditChange ( FindFProperty < FProperty > ( EditorComponent - > GetClass ( ) , GET_MEMBER_NAME_CHECKED ( UGeometryCollectionComponent , CacheParameters ) ) ) ;
2019-06-08 17:15:34 -04:00
EditorComponent - > Modify ( ) ;
EditorComponent - > CacheParameters . TargetCache = CacheParameters . TargetCache ;
EditorComponent - > PostEditChange ( ) ;
Scene - > AddPieModifiedObject ( EditorComponent ) ;
Scene - > AddPieModifiedObject ( EditorActor ) ;
}
EditorActor = nullptr ;
}
}
}
2018-12-12 11:25:29 -05:00
# endif
2019-06-08 17:15:34 -04:00
} ;
2018-12-12 11:25:29 -05:00
2020-12-09 18:44:07 -04:00
// If the Component is set to Dynamic, we look to the RestCollection for initial dynamic state override per transform.
2019-06-08 17:15:34 -04:00
TManagedArray < int32 > & DynamicState = DynamicCollection - > DynamicState ;
2020-12-09 18:44:07 -04:00
2019-06-08 17:15:34 -04:00
if ( ObjectType ! = EObjectStateTypeEnum : : Chaos_Object_UserDefined )
{
2020-12-09 18:44:07 -04:00
if ( RestCollection & & ( ObjectType = = EObjectStateTypeEnum : : Chaos_Object_Dynamic ) )
2019-06-08 17:15:34 -04:00
{
2020-12-09 18:44:07 -04:00
TManagedArray < int32 > & InitialDynamicState = RestCollection - > GetGeometryCollection ( ) - > InitialDynamicState ;
for ( int i = 0 ; i < DynamicState . Num ( ) ; i + + )
{
DynamicState [ i ] = ( InitialDynamicState [ i ] = = static_cast < int32 > ( Chaos : : EObjectStateType : : Uninitialized ) ) ? static_cast < int32 > ( ObjectType ) : InitialDynamicState [ i ] ;
}
}
else
{
for ( int i = 0 ; i < DynamicState . Num ( ) ; i + + )
{
DynamicState [ i ] = static_cast < int32 > ( ObjectType ) ;
}
2019-06-08 17:15:34 -04:00
}
}
2020-12-09 18:44:07 -04:00
2019-06-08 17:15:34 -04:00
TManagedArray < bool > & Active = DynamicCollection - > Active ;
{
for ( int i = 0 ; i < Active . Num ( ) ; i + + )
{
Active [ i ] = Simulating ;
}
}
TManagedArray < int32 > & CollisionGroupArray = DynamicCollection - > CollisionGroup ;
{
for ( int i = 0 ; i < CollisionGroupArray . Num ( ) ; i + + )
{
CollisionGroupArray [ i ] = CollisionGroup ;
}
}
2020-12-09 18:44:07 -04:00
2020-03-09 13:22:54 -04:00
// Set up initial filter data for our particles
2020-04-02 11:57:01 -04:00
// #BGTODO We need a dummy body setup for now to allow the body instance to generate filter information. Change body instance to operate independently.
DummyBodySetup = NewObject < UBodySetup > ( this , UBodySetup : : StaticClass ( ) ) ;
BodyInstance . BodySetup = DummyBodySetup ;
FBodyCollisionFilterData FilterData ;
FMaskFilter FilterMask = BodyInstance . GetMaskFilter ( ) ;
BodyInstance . BuildBodyFilterData ( FilterData ) ;
InitialSimFilter = FilterData . SimFilter ;
InitialQueryFilter = FilterData . QuerySimpleFilter ;
2020-03-09 13:22:54 -04:00
// Enable for complex and simple (no dual representation currently like other meshes)
InitialQueryFilter . Word3 | = ( EPDF_SimpleCollision | EPDF_ComplexCollision ) ;
InitialSimFilter . Word3 | = ( EPDF_SimpleCollision | EPDF_ComplexCollision ) ;
PhysicsProxy = new FGeometryCollectionPhysicsProxy ( this , * DynamicCollection , SimulationParameters , InitialQueryFilter , InitialSimFilter , InitFunc , CacheSyncFunc , FinalSyncFunc ) ;
FPhysScene_Chaos * Scene = GetInnerChaosScene ( ) ;
2019-08-02 09:01:58 -04:00
Scene - > AddObject ( this , PhysicsProxy ) ;
2019-06-08 17:15:34 -04:00
RegisterForEvents ( ) ;
}
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
# if WITH_PHYSX && !WITH_CHAOS_NEEDS_TO_BE_FIXED
2019-08-02 09:01:58 -04:00
if ( PhysicsProxy )
2019-06-08 17:15:34 -04:00
{
GlobalGeomCollectionAccelerator . AddComponent ( this ) ;
}
# endif
2020-06-23 18:40:00 -04:00
# endif // WITH_CHAOS
2018-12-12 11:25:29 -05:00
}
void UGeometryCollectionComponent : : OnDestroyPhysicsState ( )
{
UActorComponent : : OnDestroyPhysicsState ( ) ;
2020-06-23 18:40:00 -04:00
# if WITH_CHAOS
2019-06-08 17:15:34 -04:00
# if WITH_PHYSX && !WITH_CHAOS_NEEDS_TO_BE_FIXED
GlobalGeomCollectionAccelerator . RemoveComponent ( this ) ;
# endif
# if WITH_PHYSX
if ( DummyBodyInstance . IsValidBodyInstance ( ) )
{
DummyBodyInstance . TermBody ( ) ;
}
# endif
2019-08-02 09:01:58 -04:00
if ( PhysicsProxy )
2019-06-08 17:15:34 -04:00
{
2020-03-09 13:22:54 -04:00
FPhysScene_Chaos * Scene = GetInnerChaosScene ( ) ;
2019-08-02 09:01:58 -04:00
Scene - > RemoveObject ( PhysicsProxy ) ;
2019-06-08 17:15:34 -04:00
InitializationState = ESimulationInitializationState : : Unintialized ;
// Discard the pointer (cleanup happens through the scene or dedicated thread)
2019-08-02 09:01:58 -04:00
PhysicsProxy = nullptr ;
2018-12-12 11:25:29 -05:00
}
2020-06-23 18:40:00 -04:00
# endif
2018-12-12 11:25:29 -05:00
}
void UGeometryCollectionComponent : : SendRenderDynamicData_Concurrent ( )
{
//UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent[%p]::SendRenderDynamicData_Concurrent()"), this);
Super : : SendRenderDynamicData_Concurrent ( ) ;
2019-06-08 17:15:34 -04:00
// Only update the dynamic data if the dynamic collection is dirty
if ( SceneProxy & & ( ( DynamicCollection & & DynamicCollection - > IsDirty ( ) ) | | CachePlayback ) )
{
FGeometryCollectionDynamicData * DynamicData = : : new FGeometryCollectionDynamicData ;
InitDynamicData ( DynamicData ) ;
//
2020-11-02 21:12:24 -04:00
// Only send dynamic data if the transform arrays on the past N frames are different
2019-06-08 17:15:34 -04:00
//
bool PastFramesTransformsAreEqual = true ;
2020-11-02 21:12:24 -04:00
for ( int32 TransformIndex = 0 ; TransformIndex < TransformsAreEqual . Num ( ) ; + + TransformIndex )
2019-06-08 17:15:34 -04:00
{
2020-11-02 21:12:24 -04:00
if ( ! TransformsAreEqual [ TransformIndex ] )
{
PastFramesTransformsAreEqual = false ;
break ;
}
2019-06-08 17:15:34 -04:00
}
if ( PastFramesTransformsAreEqual )
{
delete DynamicData ;
}
else
{
2018-12-12 11:25:29 -05:00
// Enqueue command to send to render thread
2020-07-15 19:46:08 -04:00
if ( SceneProxy - > IsNaniteMesh ( ) )
{
FNaniteGeometryCollectionSceneProxy * GeometryCollectionSceneProxy = static_cast < FNaniteGeometryCollectionSceneProxy * > ( SceneProxy ) ;
ENQUEUE_RENDER_COMMAND ( SendRenderDynamicData ) (
[ GeometryCollectionSceneProxy , DynamicData ] ( FRHICommandListImmediate & RHICmdList )
2019-06-08 17:15:34 -04:00
{
2020-07-15 19:46:08 -04:00
if ( GeometryCollectionSceneProxy )
{
GeometryCollectionSceneProxy - > SetDynamicData_RenderThread ( DynamicData ) ;
GeometryCollectionSceneProxy - > GetPrimitiveSceneInfo ( ) - > RequestGPUSceneUpdate ( ) ;
}
2019-06-08 17:15:34 -04:00
}
2020-07-15 19:46:08 -04:00
) ;
}
else
{
FGeometryCollectionSceneProxy * GeometryCollectionSceneProxy = static_cast < FGeometryCollectionSceneProxy * > ( SceneProxy ) ;
ENQUEUE_RENDER_COMMAND ( SendRenderDynamicData ) (
[ GeometryCollectionSceneProxy , DynamicData ] ( FRHICommandListImmediate & RHICmdList )
{
if ( GeometryCollectionSceneProxy )
{
GeometryCollectionSceneProxy - > SetDynamicData_RenderThread ( DynamicData ) ;
}
}
) ;
}
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
// mark collection clean now that we have rendered
if ( DynamicCollection )
{
DynamicCollection - > MakeClean ( ) ;
}
2018-12-12 11:25:29 -05:00
}
}
2019-06-08 17:15:34 -04:00
void UGeometryCollectionComponent : : SetRestCollection ( const UGeometryCollection * RestCollectionIn )
2018-12-12 11:25:29 -05:00
{
//UE_LOG(UGCC_LOG, Log, TEXT("GeometryCollectionComponent[%p]::SetRestCollection()"), this);
if ( RestCollectionIn )
{
RestCollection = RestCollectionIn ;
2019-06-08 17:15:34 -04:00
CalculateGlobalMatrices ( ) ;
CalculateLocalBounds ( ) ;
//ResetDynamicCollection();
2018-12-12 11:25:29 -05:00
}
}
2019-06-20 12:19:58 -04:00
FGeometryCollectionEdit : : FGeometryCollectionEdit ( UGeometryCollectionComponent * InComponent , GeometryCollection : : EEditUpdate InEditUpdate )
2018-12-12 11:25:29 -05:00
: Component ( InComponent )
2019-06-20 12:19:58 -04:00
, EditUpdate ( InEditUpdate )
2018-12-12 11:25:29 -05:00
{
bHadPhysicsState = Component - > HasValidPhysicsState ( ) ;
2019-06-20 12:19:58 -04:00
if ( EnumHasAnyFlags ( EditUpdate , GeometryCollection : : EEditUpdate : : Physics ) & & bHadPhysicsState )
2018-12-12 11:25:29 -05:00
{
Component - > DestroyPhysicsState ( ) ;
}
}
FGeometryCollectionEdit : : ~ FGeometryCollectionEdit ( )
{
2019-06-08 17:15:34 -04:00
# if WITH_EDITOR
2019-06-20 12:19:58 -04:00
if ( ! ! EditUpdate )
2018-12-12 11:25:29 -05:00
{
2019-06-20 12:19:58 -04:00
if ( EnumHasAnyFlags ( EditUpdate , GeometryCollection : : EEditUpdate : : Dynamic ) )
{
Component - > ResetDynamicCollection ( ) ;
}
if ( EnumHasAnyFlags ( EditUpdate , GeometryCollection : : EEditUpdate : : Rest ) & & GetRestCollection ( ) )
2018-12-12 11:25:29 -05:00
{
GetRestCollection ( ) - > Modify ( ) ;
}
2019-06-20 12:19:58 -04:00
if ( EnumHasAnyFlags ( EditUpdate , GeometryCollection : : EEditUpdate : : Physics ) & & bHadPhysicsState )
2018-12-12 11:25:29 -05:00
{
Component - > RecreatePhysicsState ( ) ;
}
}
2019-06-08 17:15:34 -04:00
# endif
2018-12-12 11:25:29 -05:00
}
UGeometryCollection * FGeometryCollectionEdit : : GetRestCollection ( )
{
if ( Component )
{
2020-10-21 17:56:05 -04:00
return const_cast < UGeometryCollection * > ( ToRawPtr ( Component - > RestCollection ) ) ; //const cast is ok here since we are explicitly in edit mode. Should all this editor code be in an editor module?
2018-12-12 11:25:29 -05:00
}
return nullptr ;
}
2019-06-08 17:15:34 -04:00
# if WITH_EDITOR
2018-12-12 11:25:29 -05:00
TArray < FLinearColor > FScopedColorEdit : : RandomColors ;
2019-06-08 17:15:34 -04:00
FScopedColorEdit : : FScopedColorEdit ( UGeometryCollectionComponent * InComponent , bool bForceUpdate ) : bUpdated ( bForceUpdate ) , Component ( InComponent )
2018-12-12 11:25:29 -05:00
{
if ( RandomColors . Num ( ) = = 0 )
{
2019-06-08 17:15:34 -04:00
FMath : : RandInit ( 2019 ) ;
2018-12-12 11:25:29 -05:00
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 ( )
{
2019-06-08 17:15:34 -04:00
if ( bUpdated )
{
UpdateBoneColors ( ) ;
}
2018-12-12 11:25:29 -05:00
}
void FScopedColorEdit : : SetShowBoneColors ( bool ShowBoneColorsIn )
{
2019-06-08 17:15:34 -04:00
if ( Component - > bShowBoneColors ! = ShowBoneColorsIn )
{
bUpdated = true ;
Component - > bShowBoneColors = ShowBoneColorsIn ;
}
2018-12-12 11:25:29 -05:00
}
bool FScopedColorEdit : : GetShowBoneColors ( ) const
{
2019-06-08 17:15:34 -04:00
return Component - > bShowBoneColors ;
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
void FScopedColorEdit : : SetEnableBoneSelection ( bool ShowSelectedBonesIn )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
if ( Component - > bEnableBoneSelection ! = ShowSelectedBonesIn )
{
bUpdated = true ;
Component - > bEnableBoneSelection = ShowSelectedBonesIn ;
}
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
bool FScopedColorEdit : : GetEnableBoneSelection ( ) const
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
return Component - > bEnableBoneSelection ;
2018-12-12 11:25:29 -05:00
}
bool FScopedColorEdit : : IsBoneSelected ( int BoneIndex ) const
{
return Component - > SelectedBones . Contains ( BoneIndex ) ;
}
void FScopedColorEdit : : SetSelectedBones ( const TArray < int32 > & SelectedBonesIn )
{
2019-06-08 17:15:34 -04:00
bUpdated = true ;
2018-12-12 11:25:29 -05:00
Component - > SelectedBones = SelectedBonesIn ;
}
void FScopedColorEdit : : AppendSelectedBones ( const TArray < int32 > & SelectedBonesIn )
{
2019-06-08 17:15:34 -04:00
bUpdated = true ;
2018-12-12 11:25:29 -05:00
Component - > SelectedBones . Append ( SelectedBonesIn ) ;
}
2019-06-08 17:15:34 -04:00
void FScopedColorEdit : : ToggleSelectedBones ( const TArray < int32 > & SelectedBonesIn )
{
bUpdated = true ;
for ( int32 BoneIndex : SelectedBonesIn )
{
if ( Component - > SelectedBones . Contains ( BoneIndex ) )
{
Component - > SelectedBones . Remove ( BoneIndex ) ;
}
else
{
Component - > SelectedBones . Add ( BoneIndex ) ;
}
}
}
2018-12-12 11:25:29 -05:00
void FScopedColorEdit : : AddSelectedBone ( int32 BoneIndex )
{
2019-06-08 17:15:34 -04:00
if ( ! Component - > SelectedBones . Contains ( BoneIndex ) )
{
bUpdated = true ;
Component - > SelectedBones . Push ( BoneIndex ) ;
}
2018-12-12 11:25:29 -05:00
}
void FScopedColorEdit : : ClearSelectedBone ( int32 BoneIndex )
{
2019-06-08 17:15:34 -04:00
if ( Component - > SelectedBones . Contains ( BoneIndex ) )
{
bUpdated = true ;
Component - > SelectedBones . Remove ( BoneIndex ) ;
}
2018-12-12 11:25:29 -05:00
}
const TArray < int32 > & FScopedColorEdit : : GetSelectedBones ( ) const
{
return Component - > GetSelectedBones ( ) ;
}
void FScopedColorEdit : : ResetBoneSelection ( )
{
2019-06-08 17:15:34 -04:00
if ( Component - > SelectedBones . Num ( ) > 0 )
{
bUpdated = true ;
}
2018-12-12 11:25:29 -05:00
Component - > SelectedBones . Empty ( ) ;
}
void FScopedColorEdit : : SelectBones ( GeometryCollection : : ESelectionMode SelectionMode )
{
check ( Component ) ;
2019-06-08 17:15:34 -04:00
2018-12-12 11:25:29 -05:00
const UGeometryCollection * GeometryCollection = Component - > GetRestCollection ( ) ;
if ( GeometryCollection )
{
2019-06-08 17:15:34 -04:00
TSharedPtr < FGeometryCollection , ESPMode : : ThreadSafe > GeometryCollectionPtr = GeometryCollection - > GetGeometryCollection ( ) ;
2018-12-12 11:25:29 -05:00
switch ( SelectionMode )
{
case GeometryCollection : : ESelectionMode : : None :
ResetBoneSelection ( ) ;
break ;
case GeometryCollection : : ESelectionMode : : AllGeometry :
{
TArray < int32 > Roots ;
FGeometryCollectionClusteringUtility : : GetRootBones ( GeometryCollectionPtr . Get ( ) , Roots ) ;
ResetBoneSelection ( ) ;
for ( int32 RootElement : Roots )
{
TArray < int32 > LeafBones ;
2021-01-28 23:22:16 -04:00
FGeometryCollectionClusteringUtility : : GetLeafBones ( GeometryCollectionPtr . Get ( ) , RootElement , true , LeafBones ) ;
2018-12-12 11:25:29 -05:00
AppendSelectedBones ( LeafBones ) ;
}
}
break ;
case GeometryCollection : : ESelectionMode : : InverseGeometry :
{
TArray < int32 > Roots ;
FGeometryCollectionClusteringUtility : : GetRootBones ( GeometryCollectionPtr . Get ( ) , Roots ) ;
TArray < int32 > NewSelection ;
for ( int32 RootElement : Roots )
{
TArray < int32 > LeafBones ;
2021-01-28 23:22:16 -04:00
FGeometryCollectionClusteringUtility : : GetLeafBones ( GeometryCollectionPtr . Get ( ) , RootElement , true , LeafBones ) ;
2018-12-12 11:25:29 -05:00
for ( int32 Element : LeafBones )
{
if ( ! IsBoneSelected ( Element ) )
{
NewSelection . Push ( Element ) ;
}
}
}
ResetBoneSelection ( ) ;
AppendSelectedBones ( NewSelection ) ;
}
break ;
2019-06-08 17:15:34 -04:00
case GeometryCollection : : ESelectionMode : : Neighbors :
{
if ( ensureMsgf ( GeometryCollectionPtr - > HasAttribute ( " Proximity " , FGeometryCollection : : GeometryGroup ) ,
TEXT ( " Must build breaking group for neighbor based selection " ) ) )
{
const TManagedArray < int32 > & TransformIndex = GeometryCollectionPtr - > TransformIndex ;
const TManagedArray < int32 > & TransformToGeometryIndex = GeometryCollectionPtr - > TransformToGeometryIndex ;
const TManagedArray < TSet < int32 > > & Proximity = GeometryCollectionPtr - > GetAttribute < TSet < int32 > > ( " Proximity " , FGeometryCollection : : GeometryGroup ) ;
const TArray < int32 > SelectedBones = GetSelectedBones ( ) ;
TArray < int32 > NewSelection ;
for ( int32 Bone : SelectedBones )
{
NewSelection . AddUnique ( Bone ) ;
const TSet < int32 > & Neighbors = Proximity [ TransformToGeometryIndex [ Bone ] ] ;
for ( int32 NeighborGeometryIndex : Neighbors )
{
NewSelection . AddUnique ( TransformIndex [ NeighborGeometryIndex ] ) ;
}
}
ResetBoneSelection ( ) ;
AppendSelectedBones ( NewSelection ) ;
}
}
break ;
case GeometryCollection : : ESelectionMode : : Siblings :
{
const TManagedArray < int32 > & Parents = GeometryCollectionPtr - > Parent ;
const TManagedArray < TSet < int32 > > & Children = GeometryCollectionPtr - > Children ;
const TArray < int32 > SelectedBones = GetSelectedBones ( ) ;
TArray < int32 > NewSelection ;
for ( int32 Bone : SelectedBones )
{
int32 ParentBone = Parents [ Bone ] ;
if ( ParentBone ! = FGeometryCollection : : Invalid )
{
for ( int32 Child : Children [ ParentBone ] )
{
NewSelection . AddUnique ( Child ) ;
}
}
}
ResetBoneSelection ( ) ;
AppendSelectedBones ( NewSelection ) ;
}
break ;
case GeometryCollection : : ESelectionMode : : AllInCluster :
{
const TManagedArray < int32 > & Parents = GeometryCollectionPtr - > Parent ;
const TArray < int32 > SelectedBones = GetSelectedBones ( ) ;
TArray < int32 > NewSelection ;
for ( int32 Bone : SelectedBones )
{
int32 ParentBone = Parents [ Bone ] ;
TArray < int32 > LeafBones ;
2021-01-28 23:22:16 -04:00
FGeometryCollectionClusteringUtility : : GetLeafBones ( GeometryCollectionPtr . Get ( ) , ParentBone , true , LeafBones ) ;
2019-06-08 17:15:34 -04:00
for ( int32 Element : LeafBones )
{
NewSelection . AddUnique ( Element ) ;
}
}
ResetBoneSelection ( ) ;
AppendSelectedBones ( NewSelection ) ;
}
break ;
2018-12-12 11:25:29 -05:00
default :
check ( false ) ; // unexpected selection mode
break ;
}
2019-06-08 17:15:34 -04:00
const TArray < int32 > & SelectedBones = GetSelectedBones ( ) ;
2018-12-12 11:25:29 -05:00
SetHighlightedBones ( SelectedBones ) ;
}
}
bool FScopedColorEdit : : IsBoneHighlighted ( int BoneIndex ) const
{
return Component - > HighlightedBones . Contains ( BoneIndex ) ;
}
void FScopedColorEdit : : SetHighlightedBones ( const TArray < int32 > & HighlightedBonesIn )
{
2019-06-08 17:15:34 -04:00
if ( Component - > HighlightedBones ! = HighlightedBonesIn )
{
bUpdated = true ;
Component - > HighlightedBones = HighlightedBonesIn ;
}
2018-12-12 11:25:29 -05:00
}
void FScopedColorEdit : : AddHighlightedBone ( int32 BoneIndex )
{
Component - > HighlightedBones . Push ( BoneIndex ) ;
}
const TArray < int32 > & FScopedColorEdit : : GetHighlightedBones ( ) const
{
return Component - > GetHighlightedBones ( ) ;
}
void FScopedColorEdit : : ResetHighlightedBones ( )
{
2019-06-08 17:15:34 -04:00
if ( Component - > HighlightedBones . Num ( ) > 0 )
{
bUpdated = true ;
Component - > HighlightedBones . Empty ( ) ;
}
2018-12-12 11:25:29 -05:00
}
void FScopedColorEdit : : SetLevelViewMode ( int ViewLevelIn )
{
2019-06-08 17:15:34 -04:00
if ( Component - > ViewLevel ! = ViewLevelIn )
{
bUpdated = true ;
Component - > ViewLevel = ViewLevelIn ;
}
2018-12-12 11:25:29 -05:00
}
int FScopedColorEdit : : GetViewLevel ( )
{
return Component - > ViewLevel ;
}
void FScopedColorEdit : : UpdateBoneColors ( )
{
2019-06-08 17:15:34 -04:00
// @todo FractureTools - For large fractures updating colors this way is extremely slow because the render state (and thus all buffers) must be recreated.
// It would be better to push the update to the proxy via a render command and update the existing buffer directly
2019-07-22 14:27:12 -04:00
FGeometryCollectionEdit GeometryCollectionEdit = Component - > EditRestCollection ( GeometryCollection : : EEditUpdate : : None ) ;
2018-12-12 11:25:29 -05:00
UGeometryCollection * GeometryCollection = GeometryCollectionEdit . GetRestCollection ( ) ;
2019-06-08 17:15:34 -04:00
if ( GeometryCollection )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
FGeometryCollection * Collection = GeometryCollection - > GetGeometryCollection ( ) . Get ( ) ;
FLinearColor BlankColor ( FColor ( 80 , 80 , 80 , 50 ) ) ;
const TManagedArray < int > & Parents = Collection - > Parent ;
bool HasLevelAttribute = Collection - > HasAttribute ( " Level " , FTransformCollection : : TransformGroup ) ;
TManagedArray < int > * Levels = nullptr ;
if ( HasLevelAttribute )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
Levels = & Collection - > GetAttribute < int32 > ( " Level " , FTransformCollection : : TransformGroup ) ;
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
TManagedArray < FLinearColor > & BoneColors = Collection - > BoneColor ;
2019-07-23 16:27:55 -04:00
for ( int32 BoneIndex = 0 , NumBones = Parents . Num ( ) ; BoneIndex < NumBones ; + + BoneIndex )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
FLinearColor BoneColor = FLinearColor ( FColor : : Black ) ;
2018-12-12 11:25:29 -05:00
if ( Component - > ViewLevel = = - 1 )
{
BoneColor = RandomColors [ BoneIndex % RandomColors . Num ( ) ] ;
}
else
{
2019-06-08 17:15:34 -04:00
if ( HasLevelAttribute & & ( * Levels ) [ BoneIndex ] > = Component - > ViewLevel )
2018-12-12 11:25:29 -05:00
{
// go up until we find parent at the required ViewLevel
int32 Bone = BoneIndex ;
2019-06-08 17:15:34 -04:00
while ( Bone ! = - 1 & & ( * Levels ) [ Bone ] > Component - > ViewLevel )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
Bone = Parents [ Bone ] ;
2018-12-12 11:25:29 -05:00
}
int32 ColorIndex = Bone + 1 ; // parent can be -1 for root, range [-1..n]
BoneColor = RandomColors [ ColorIndex % RandomColors . Num ( ) ] ;
2019-06-08 17:15:34 -04:00
BoneColor . LinearRGBToHSV ( ) ;
BoneColor . B * = .5 ;
BoneColor . HSVToLinearRGB ( ) ;
2018-12-12 11:25:29 -05:00
}
else
{
BoneColor = BlankColor ;
}
}
2019-06-08 17:15:34 -04:00
// store the bone selected toggle in alpha so we can use it in the shader
BoneColor . A = IsBoneHighlighted ( BoneIndex ) ? 1 : 0 ;
BoneColors [ BoneIndex ] = BoneColor ;
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
Component - > MarkRenderStateDirty ( ) ;
Component - > MarkRenderDynamicDataDirty ( ) ;
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
}
# endif
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
void UGeometryCollectionComponent : : ApplyKinematicField ( float Radius , FVector Position )
{
FName TargetName = GetGeometryCollectionPhysicsTypeName ( EGeometryCollectionPhysicsTypeEnum : : Chaos_DynamicState ) ;
2021-02-03 14:57:28 -04:00
DispatchFieldCommand ( { TargetName , new FRadialIntMask ( Radius , Position , ( int32 ) Chaos : : EObjectStateType : : Dynamic ,
( int32 ) Chaos : : EObjectStateType : : Kinematic , ESetMaskConditionType : : Field_Set_IFF_NOT_Interior ) } ) ;
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
void UGeometryCollectionComponent : : ApplyPhysicsField ( bool Enabled , EGeometryCollectionPhysicsTypeEnum Target , UFieldSystemMetaData * MetaData , UFieldNodeBase * Field )
{
if ( Enabled & & Field )
2018-12-12 11:25:29 -05:00
{
2021-02-03 14:57:28 -04:00
FFieldSystemCommand Command = FFieldObjectCommands : : CreateFieldCommand ( GetGeometryCollectionPhysicsTypeName ( Target ) , Field , MetaData ) ;
DispatchFieldCommand ( Command ) ;
2018-12-12 11:25:29 -05:00
}
}
2021-02-03 14:57:28 -04:00
void UGeometryCollectionComponent : : DispatchFieldCommand ( const FFieldSystemCommand & InCommand )
2019-06-08 17:15:34 -04:00
{
2019-08-02 09:01:58 -04:00
if ( PhysicsProxy )
2019-06-08 17:15:34 -04:00
{
2020-09-24 00:43:27 -04:00
FChaosSolversModule * ChaosModule = FChaosSolversModule : : GetModule ( ) ;
2019-06-08 17:15:34 -04:00
checkSlow ( ChaosModule ) ;
2020-09-24 00:43:27 -04:00
auto Solver = PhysicsProxy - > GetSolver < Chaos : : FPBDRigidsSolver > ( ) ;
Solver - > EnqueueCommandImmediate ( [ Solver , PhysicsProxy = this - > PhysicsProxy , NewCommand = InCommand ] ( )
2019-06-08 17:15:34 -04:00
{
// Pass through nullptr here as geom component commands can never affect other solvers
2020-09-24 00:43:27 -04:00
PhysicsProxy - > BufferCommand ( Solver , NewCommand ) ;
2019-06-08 17:15:34 -04:00
} ) ;
}
}
void UGeometryCollectionComponent : : GetInitializationCommands ( TArray < FFieldSystemCommand > & CombinedCommmands )
{
CombinedCommmands . Reset ( ) ;
2021-02-03 14:57:28 -04:00
for ( const AFieldSystemActor * FieldSystemActor : InitializationFields )
2019-06-08 17:15:34 -04:00
{
if ( FieldSystemActor ! = nullptr )
{
2021-02-03 14:57:28 -04:00
if ( FieldSystemActor - > GetFieldSystemComponent ( ) )
2019-06-08 17:15:34 -04:00
{
2021-02-03 14:57:28 -04:00
for ( int32 CommandIndex = 0 , NumCommands = FieldSystemActor - > GetFieldSystemComponent ( ) - > ConstructionCommands . GetNumCommands ( ) ;
CommandIndex < NumCommands ; + + CommandIndex )
2019-06-08 17:15:34 -04:00
{
2021-02-03 14:57:28 -04:00
CombinedCommmands . Add ( FieldSystemActor - > GetFieldSystemComponent ( ) - > ConstructionCommands . BuildFieldCommand ( CommandIndex ) ) ;
2019-06-08 17:15:34 -04:00
}
}
}
}
}
2020-03-09 13:22:54 -04:00
FPhysScene_Chaos * UGeometryCollectionComponent : : GetInnerChaosScene ( ) const
2018-12-12 11:25:29 -05:00
{
if ( ChaosSolverActor )
{
2019-11-22 05:24:36 -05:00
return ChaosSolverActor - > GetPhysicsScene ( ) . Get ( ) ;
2018-12-12 11:25:29 -05:00
}
else
{
2019-10-02 17:27:26 -04:00
# if INCLUDE_CHAOS
2019-06-08 17:15:34 -04:00
if ( ensure ( GetOwner ( ) ) & & ensure ( GetOwner ( ) - > GetWorld ( ) ) )
{
2020-08-11 01:36:57 -04:00
return GetOwner ( ) - > GetWorld ( ) - > GetPhysicsScene ( ) ;
2019-06-08 17:15:34 -04:00
}
check ( GWorld ) ;
2020-08-11 01:36:57 -04:00
return GWorld - > GetPhysicsScene ( ) ;
2019-10-02 17:27:26 -04:00
# else
return nullptr ;
# endif
2018-12-12 11:25:29 -05:00
}
}
2019-06-08 17:15:34 -04:00
AChaosSolverActor * UGeometryCollectionComponent : : GetPhysicsSolverActor ( ) const
{
if ( ChaosSolverActor )
{
return ChaosSolverActor ;
}
else
{
2020-03-09 13:22:54 -04:00
FPhysScene_Chaos const * const Scene = GetInnerChaosScene ( ) ;
2019-06-08 17:15:34 -04:00
return Scene ? Cast < AChaosSolverActor > ( Scene - > GetSolverActor ( ) ) : nullptr ;
}
return nullptr ;
}
void UGeometryCollectionComponent : : CalculateLocalBounds ( )
{
LocalBounds . Init ( ) ;
const TManagedArray < FBox > & BoundingBoxes = GetBoundingBoxArray ( ) ;
const TManagedArray < int32 > & TransformIndices = GetTransformIndexArray ( ) ;
const int32 NumBoxes = BoundingBoxes . Num ( ) ;
for ( int32 BoxIdx = 0 ; BoxIdx < NumBoxes ; + + BoxIdx )
{
const int32 TransformIndex = TransformIndices [ BoxIdx ] ;
if ( GetRestCollection ( ) - > GetGeometryCollection ( ) - > IsGeometry ( TransformIndex ) )
{
LocalBounds + = BoundingBoxes [ BoxIdx ] ;
}
}
}
void UGeometryCollectionComponent : : CalculateGlobalMatrices ( )
{
SCOPE_CYCLE_COUNTER ( STAT_GCCUGlobalMatrices ) ;
2020-08-11 01:36:57 -04:00
2019-11-20 13:24:55 -05:00
const FGeometryCollectionResults * Results = PhysicsProxy ? PhysicsProxy - > GetConsumerResultsGT ( ) : nullptr ;
2019-06-08 17:15:34 -04:00
const int32 NumTransforms = Results ? Results - > GlobalTransforms . Num ( ) : 0 ;
if ( NumTransforms > 0 )
{
// Just calc from results
GlobalMatrices . Empty ( ) ;
GlobalMatrices . Append ( Results - > GlobalTransforms ) ;
}
else
{
// Have to fully rebuild
GeometryCollectionAlgo : : GlobalMatrices ( GetTransformArray ( ) , GetParentArray ( ) , GlobalMatrices ) ;
}
# if WITH_EDITOR
if ( GlobalMatrices . Num ( ) > 0 )
{
if ( RestCollection - > GetGeometryCollection ( ) - > HasAttribute ( " ExplodedVector " , FGeometryCollection : : TransformGroup ) )
{
const TManagedArray < FVector > & ExplodedVectors = RestCollection - > GetGeometryCollection ( ) - > GetAttribute < FVector > ( " ExplodedVector " , FGeometryCollection : : TransformGroup ) ;
check ( GlobalMatrices . Num ( ) = = ExplodedVectors . Num ( ) ) ;
for ( int32 tt = 0 , nt = GlobalMatrices . Num ( ) ; tt < nt ; + + tt )
{
GlobalMatrices [ tt ] = GlobalMatrices [ tt ] . ConcatTranslation ( ExplodedVectors [ tt ] ) ;
}
}
}
# endif
}
// #todo(dmp): for backwards compatibility with existing maps, we need to have a default of 3 materials. Otherwise
// some existing test scenes will crash
int32 UGeometryCollectionComponent : : GetNumMaterials ( ) const
{
return ! RestCollection | | RestCollection - > Materials . Num ( ) = = 0 ? 3 : RestCollection - > Materials . Num ( ) ;
}
UMaterialInterface * UGeometryCollectionComponent : : GetMaterial ( int32 MaterialIndex ) const
{
// If we have a base materials array, use that
if ( OverrideMaterials . IsValidIndex ( MaterialIndex ) & & OverrideMaterials [ MaterialIndex ] )
{
return OverrideMaterials [ MaterialIndex ] ;
}
// Otherwise get from geom collection
else
{
return RestCollection & & RestCollection - > Materials . IsValidIndex ( MaterialIndex ) ? RestCollection - > Materials [ MaterialIndex ] : nullptr ;
}
}
// #temp HACK for demo, When fracture happens (physics state changes to dynamic) then switch the visible render meshes in a blueprint/actor from static meshes to geometry collections
void UGeometryCollectionComponent : : SwitchRenderModels ( const AActor * Actor )
{
2019-06-17 19:58:42 -04:00
// Don't touch visibility if the component is not visible
if ( ! IsVisible ( ) )
{
return ;
}
2019-09-05 15:45:24 -04:00
TInlineComponentArray < UPrimitiveComponent * > PrimitiveComponents ;
Actor - > GetComponents ( PrimitiveComponents ) ;
for ( UPrimitiveComponent * PrimitiveComponent : PrimitiveComponents )
2019-06-08 17:15:34 -04:00
{
bool ValidComponent = false ;
if ( UStaticMeshComponent * StaticMeshComp = Cast < UStaticMeshComponent > ( PrimitiveComponent ) )
{
StaticMeshComp - > SetVisibility ( false ) ;
}
else if ( UGeometryCollectionComponent * GeometryCollectionComponent = Cast < UGeometryCollectionComponent > ( PrimitiveComponent ) )
{
2019-07-16 19:12:32 -04:00
if ( ! GeometryCollectionComponent - > IsVisible ( ) )
{
continue ;
}
2019-06-08 17:15:34 -04:00
GeometryCollectionComponent - > SetVisibility ( true ) ;
}
}
2019-09-05 15:45:24 -04:00
TInlineComponentArray < UChildActorComponent * > ChildActorComponents ;
Actor - > GetComponents ( ChildActorComponents ) ;
2019-06-08 17:15:34 -04:00
for ( UChildActorComponent * ChildComponent : ChildActorComponents )
{
AActor * ChildActor = ChildComponent - > GetChildActor ( ) ;
if ( ChildActor )
{
SwitchRenderModels ( ChildActor ) ;
}
}
}
bool UGeometryCollectionComponent : : IsEqual ( const TArray < FMatrix > & A , const TArray < FMatrix > & B , const float Tolerance )
{
if ( A . Num ( ) ! = B . Num ( ) )
{
return false ;
}
for ( int Index = 0 ; Index < A . Num ( ) ; + + Index )
{
if ( ! A [ Index ] . Equals ( B [ Index ] , Tolerance ) )
{
return false ;
}
}
return true ;
}
# if GEOMETRYCOLLECTION_EDITOR_SELECTION
void UGeometryCollectionComponent : : EnableTransformSelectionMode ( bool bEnable )
{
2020-07-15 19:46:08 -04:00
// TODO: Support for Nanite?
if ( SceneProxy & & ! SceneProxy - > IsNaniteMesh ( ) & & RestCollection & & RestCollection - > HasVisibleGeometry ( ) )
2019-06-08 17:15:34 -04:00
{
static_cast < FGeometryCollectionSceneProxy * > ( SceneProxy ) - > UseSubSections ( bEnable , true ) ;
}
bIsTransformSelectionModeEnabled = bEnable ;
}
# endif // #if GEOMETRYCOLLECTION_EDITOR_SELECTION