2018-12-14 13:41:00 -05:00
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
2018-12-12 11:25:29 -05:00
/*=============================================================================
GeometryCollection . cpp : UGeometryCollection methods .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "GeometryCollection/GeometryCollectionObject.h"
# include "GeometryCollection/GeometryCollection.h"
# include "GeometryCollection/GeometryCollectionCache.h"
# include "UObject/DestructionObjectVersion.h"
# include "Serialization/ArchiveCountMem.h"
# include "HAL/IConsoleManager.h"
# include "UObject/Package.h"
# include "Materials/MaterialInstance.h"
2019-06-08 17:15:34 -04:00
# if WITH_EDITOR
# include "GeometryCollection/DerivedDataGeometryCollectionCooker.h"
# include "DerivedDataCacheInterface.h"
# include "Serialization/MemoryReader.h"
# endif
# include "GeometryCollection/GeometryCollectionSimulationCoreTypes.h"
# include "Chaos/ChaosArchive.h"
2018-12-12 11:25:29 -05:00
DEFINE_LOG_CATEGORY_STATIC ( UGeometryCollectionLogging , NoLogging , All ) ;
UGeometryCollection : : UGeometryCollection ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
2019-06-08 17:15:34 -04:00
, CollisionType ( ECollisionTypeEnum : : Chaos_Surface_Volumetric )
, ImplicitType ( EImplicitTypeEnum : : Chaos_Implicit_Box )
, MinLevelSetResolution ( 10 )
, MaxLevelSetResolution ( 10 )
, MinClusterLevelSetResolution ( 50 )
, MaxClusterLevelSetResolution ( 50 )
, CollisionObjectReductionPercentage ( 0.0f )
, bMassAsDensity ( false )
, Mass ( 1.0f )
, MinimumMassClamp ( 0.1f )
, CollisionParticlesFraction ( 1.0f )
2019-08-15 21:15:42 -04:00
, MaximumCollisionParticles ( 60 )
2019-06-08 17:15:34 -04:00
, EnableRemovePiecesOnFracture ( false )
2018-12-12 11:25:29 -05:00
, GeometryCollection ( new FGeometryCollection ( ) )
{
PersistentGuid = FGuid : : NewGuid ( ) ;
InvalidateCollection ( ) ;
}
2019-06-08 17:15:34 -04:00
FGeometryCollectionSizeSpecificData : : FGeometryCollectionSizeSpecificData ( )
: MaxSize ( 0.0f )
, CollisionType ( ECollisionTypeEnum : : Chaos_Surface_Volumetric )
, ImplicitType ( EImplicitTypeEnum : : Chaos_Implicit_Box )
, MinLevelSetResolution ( 5 )
, MaxLevelSetResolution ( 10 )
, MinClusterLevelSetResolution ( 25 )
, MaxClusterLevelSetResolution ( 50 )
, CollisionObjectReductionPercentage ( 0.0f )
, CollisionParticlesFraction ( 1.0f )
2019-08-15 21:15:42 -04:00
, MaximumCollisionParticles ( 60 )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
}
void FillSharedSimulationSizeSpecificData ( FSharedSimulationSizeSpecificData & ToData , const FGeometryCollectionSizeSpecificData & FromData )
{
ToData . CollisionType = FromData . CollisionType ;
ToData . ImplicitType = FromData . ImplicitType ;
ToData . MaxSize = FromData . MaxSize ;
ToData . MinLevelSetResolution = FromData . MinLevelSetResolution ;
ToData . MaxLevelSetResolution = FromData . MaxLevelSetResolution ;
ToData . MinClusterLevelSetResolution = FromData . MinClusterLevelSetResolution ;
ToData . MaxClusterLevelSetResolution = FromData . MaxClusterLevelSetResolution ;
ToData . CollisionObjectReductionPercentage = FromData . CollisionObjectReductionPercentage ;
ToData . CollisionParticlesFraction = FromData . CollisionParticlesFraction ;
2019-08-15 21:15:42 -04:00
ToData . MaximumCollisionParticles = FromData . MaximumCollisionParticles ;
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
float KgCm3ToKgM3 ( float Density )
{
return Density * 1000000 ;
}
float KgM3ToKgCm3 ( float Density )
{
return Density / 1000000 ;
}
void UGeometryCollection : : GetSharedSimulationParams ( FSharedSimulationParameters & OutParams ) const
{
OutParams . bMassAsDensity = bMassAsDensity ;
OutParams . Mass = bMassAsDensity ? KgM3ToKgCm3 ( Mass ) : Mass ; //todo(ocohen): we still have the solver working in old units. This is mainly to fix ui issues. Long term need to normalize units for best precision
OutParams . MinimumMassClamp = MinimumMassClamp ;
2019-08-15 21:15:42 -04:00
OutParams . MaximumCollisionParticleCount = MaximumCollisionParticles ;
2019-06-08 17:15:34 -04:00
FGeometryCollectionSizeSpecificData InfSize ;
InfSize . MaxSize = FLT_MAX ;
InfSize . CollisionType = CollisionType ;
InfSize . ImplicitType = ImplicitType ;
InfSize . MinLevelSetResolution = MinLevelSetResolution ;
InfSize . MaxLevelSetResolution = MaxLevelSetResolution ;
InfSize . MinClusterLevelSetResolution = MinClusterLevelSetResolution ;
InfSize . MaxClusterLevelSetResolution = MaxClusterLevelSetResolution ;
InfSize . CollisionObjectReductionPercentage = CollisionObjectReductionPercentage ;
InfSize . CollisionParticlesFraction = CollisionParticlesFraction ;
2019-08-15 21:15:42 -04:00
InfSize . MaximumCollisionParticles = MaximumCollisionParticles ;
2019-06-08 17:15:34 -04:00
OutParams . SizeSpecificData . SetNum ( SizeSpecificData . Num ( ) + 1 ) ;
FillSharedSimulationSizeSpecificData ( OutParams . SizeSpecificData [ 0 ] , InfSize ) ;
for ( int32 Idx = 0 ; Idx < SizeSpecificData . Num ( ) ; + + Idx )
{
FillSharedSimulationSizeSpecificData ( OutParams . SizeSpecificData [ Idx + 1 ] , SizeSpecificData [ Idx ] ) ;
}
if ( EnableRemovePiecesOnFracture )
{
FixupRemoveOnFractureMaterials ( OutParams ) ;
}
OutParams . SizeSpecificData . Sort ( ) ; //can we do this at editor time on post edit change?
}
void UGeometryCollection : : FixupRemoveOnFractureMaterials ( FSharedSimulationParameters & SharedParms ) const
{
// Match RemoveOnFracture materials with materials in model and record the material indices
int32 NumMaterials = Materials . Num ( ) ;
for ( int32 MaterialIndex = 0 ; MaterialIndex < NumMaterials ; MaterialIndex + + )
{
UMaterialInterface * MaterialInfo = Materials [ MaterialIndex ] ;
for ( int32 ROFMaterialIndex = 0 ; ROFMaterialIndex < RemoveOnFractureMaterials . Num ( ) ; ROFMaterialIndex + + )
{
if ( MaterialInfo = = RemoveOnFractureMaterials [ ROFMaterialIndex ] )
{
SharedParms . RemoveOnFractureIndices . Add ( MaterialIndex ) ;
break ;
}
}
}
}
2018-12-12 11:25:29 -05:00
/** AppendGeometry */
2019-06-08 17:15:34 -04:00
int32 UGeometryCollection : : AppendGeometry ( const UGeometryCollection & Element , bool ReindexAllMaterials , const FTransform & TransformRoot )
2018-12-12 11:25:29 -05:00
{
Modify ( ) ;
InvalidateCollection ( ) ;
2019-06-08 17:15:34 -04:00
// add all materials
// if there are none, we assume all material assignments in Element are shared by this GeometryCollection
// otherwise, we assume all assignments come from the contained materials
int32 MaterialIDOffset = 0 ;
if ( Element . Materials . Num ( ) > 0 )
{
MaterialIDOffset = Materials . Num ( ) ;
Materials . Append ( Element . Materials ) ;
}
return GeometryCollection - > AppendGeometry ( * Element . GetGeometryCollection ( ) , MaterialIDOffset , ReindexAllMaterials , TransformRoot ) ;
2018-12-12 11:25:29 -05:00
}
/** NumElements */
2019-06-08 17:15:34 -04:00
int32 UGeometryCollection : : NumElements ( const FName & Group ) const
2018-12-12 11:25:29 -05:00
{
return GeometryCollection - > NumElements ( Group ) ;
}
/** RemoveElements */
void UGeometryCollection : : RemoveElements ( const FName & Group , const TArray < int32 > & SortedDeletionList )
{
Modify ( ) ;
GeometryCollection - > RemoveElements ( Group , SortedDeletionList ) ;
InvalidateCollection ( ) ;
}
/** ReindexMaterialSections */
void UGeometryCollection : : ReindexMaterialSections ( )
{
Modify ( ) ;
GeometryCollection - > ReindexMaterials ( ) ;
InvalidateCollection ( ) ;
}
2019-06-08 17:15:34 -04:00
void UGeometryCollection : : InitializeMaterials ( )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
Modify ( ) ;
// Consolidate materials
// add all materials to a set
TSet < UMaterialInterface * > MaterialSet ;
for ( UMaterialInterface * Curr : Materials )
{
MaterialSet . Add ( Curr ) ;
}
// create the final material array only containing unique materials
// and one slot for each internal material
TMap < UMaterialInterface * , int32 > MaterialPtrToArrayIndex ;
TArray < UMaterialInterface * > FinalMaterials ;
for ( UMaterialInterface * Curr : MaterialSet )
{
// Add base material
TTuple < UMaterialInterface * , int32 > CurrTuple ( Curr , FinalMaterials . Add ( Curr ) ) ;
MaterialPtrToArrayIndex . Add ( CurrTuple ) ;
// Add interior material
FinalMaterials . Add ( Curr ) ;
}
TManagedArray < int32 > & MaterialID = GeometryCollection - > MaterialID ;
// Reassign materialid for each face given the new consolidated array of materials
for ( int i = 0 ; i < MaterialID . Num ( ) ; + + i )
{
if ( MaterialID [ i ] < Materials . Num ( ) )
{
UMaterialInterface * OldMaterialPtr = Materials [ MaterialID [ i ] ] ;
MaterialID [ i ] = * MaterialPtrToArrayIndex . Find ( OldMaterialPtr ) ;
}
}
// Set new material array on the collection
Materials = FinalMaterials ;
2018-12-12 11:25:29 -05:00
// Last Material is the selection one
2019-06-08 17:15:34 -04:00
UMaterialInterface * BoneSelectedMaterial = LoadObject < UMaterialInterface > ( NULL , TEXT ( " /Engine/EditorMaterials/GeometryCollection/SelectedGeometryMaterial.SelectedGeometryMaterial " ) , NULL , LOAD_None , NULL ) ;
BoneSelectedMaterialIndex = Materials . Add ( BoneSelectedMaterial ) ;
GeometryCollection - > ReindexMaterials ( ) ;
InvalidateCollection ( ) ;
2018-12-12 11:25:29 -05:00
}
/** Returns true if there is anything to render */
2019-06-08 17:15:34 -04:00
bool UGeometryCollection : : HasVisibleGeometry ( ) const
2018-12-12 11:25:29 -05:00
{
return GeometryCollection - > HasVisibleGeometry ( ) ;
}
/** Serialize */
void UGeometryCollection : : Serialize ( FArchive & Ar )
{
Ar . UsingCustomVersion ( FDestructionObjectVersion : : GUID ) ;
2019-06-08 17:15:34 -04:00
Chaos : : FChaosArchive ChaosAr ( Ar ) ;
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
# if WITH_EDITOR
//Early versions did not have tagged properties serialize first
if ( Ar . CustomVer ( FDestructionObjectVersion : : GUID ) < FDestructionObjectVersion : : GeometryCollectionInDDC )
2018-12-12 11:25:29 -05:00
{
2019-06-08 17:15:34 -04:00
GeometryCollection - > Serialize ( ChaosAr ) ;
}
if ( Ar . CustomVer ( FDestructionObjectVersion : : GUID ) < FDestructionObjectVersion : : AddedTimestampedGeometryComponentCache )
{
if ( Ar . IsLoading ( ) )
2018-12-12 11:25:29 -05:00
{
// Strip old recorded cache data
int32 DummyNumFrames ;
TArray < TArray < FTransform > > DummyTransforms ;
Ar < < DummyNumFrames ;
DummyTransforms . SetNum ( DummyNumFrames ) ;
2019-06-08 17:15:34 -04:00
for ( int32 Index = 0 ; Index < DummyNumFrames ; + + Index )
2018-12-12 11:25:29 -05:00
{
Ar < < DummyTransforms [ Index ] ;
}
}
}
else
2019-06-08 17:15:34 -04:00
# endif
2018-12-12 11:25:29 -05:00
{
// Push up the chain to hit tagged properties too
// This should have always been in here but because we have saved assets
// from before this line was here it has to be gated
Super : : Serialize ( Ar ) ;
}
2019-06-08 17:15:34 -04:00
if ( Ar . CustomVer ( FDestructionObjectVersion : : GUID ) < FDestructionObjectVersion : : DensityUnitsChanged )
{
if ( bMassAsDensity )
{
Mass = KgCm3ToKgM3 ( Mass ) ;
}
}
bool bIsCookedOrCooking = Ar . IsCooking ( ) ;
if ( Ar . CustomVer ( FDestructionObjectVersion : : GUID ) > = FDestructionObjectVersion : : GeometryCollectionInDDC )
{
Ar < < bIsCookedOrCooking ;
}
# if WITH_EDITOR
if ( Ar . CustomVer ( FDestructionObjectVersion : : GUID ) = = FDestructionObjectVersion : : GeometryCollectionInDDC )
{
//This version only saved content into DDC so skip serializing, but copy from DDC at a specific version
bool bCopyFromDDC = Ar . IsLoading ( ) ;
CreateSimulationDataImp ( bCopyFromDDC , TEXT ( " 8724C70A140146B5A2F4CF0C16083041 " ) ) ;
}
# endif
//new versions serialize geometry collection after tagged properties
if ( Ar . CustomVer ( FDestructionObjectVersion : : GUID ) > = FDestructionObjectVersion : : GeometryCollectionInDDCAndAsset )
{
# if WITH_EDITOR
if ( Ar . IsSaving ( ) & & ! Ar . IsTransacting ( ) )
{
CreateSimulationDataImp ( /*bCopyFromDDC=*/ false ) ; //make sure content is built before saving
}
# endif
GeometryCollection - > Serialize ( ChaosAr ) ;
}
if ( Ar . CustomVer ( FDestructionObjectVersion : : GUID ) < FDestructionObjectVersion : : GroupAndAttributeNameRemapping )
{
GeometryCollection - > UpdateOldAttributeNames ( ) ;
InvalidateCollection ( ) ;
# if WITH_EDITOR
CreateSimulationData ( ) ;
# endif
}
# if WITH_EDITOR
//for all versions loaded, make sure sim data is up to date
if ( Ar . IsLoading ( ) )
{
CreateSimulationDataImp ( /*bCopyFromDDC=*/ true ) ; //make sure loaded content is built
}
# endif
2018-12-12 11:25:29 -05:00
}
2019-06-08 17:15:34 -04:00
# if WITH_EDITOR
void UGeometryCollection : : CreateSimulationDataImp ( bool bCopyFromDDC , const TCHAR * OverrideVersion )
{
//Use the DDC to build simulation data. If we are loading in the editor we then serialize this data into the geometry collection
TArray < uint8 > DDCData ;
FDerivedDataGeometryCollectionCooker * GeometryCollectionCooker = new FDerivedDataGeometryCollectionCooker ( * this ) ;
GeometryCollectionCooker - > SetOverrideVersion ( OverrideVersion ) ;
if ( GeometryCollectionCooker - > CanBuild ( ) )
{
GetDerivedDataCacheRef ( ) . GetSynchronous ( GeometryCollectionCooker , DDCData ) ;
if ( bCopyFromDDC )
{
FMemoryReader Ar ( DDCData ) ;
Chaos : : FChaosArchive ChaosAr ( Ar ) ;
GeometryCollection - > Serialize ( ChaosAr ) ;
}
}
}
void UGeometryCollection : : CreateSimulationData ( )
{
CreateSimulationDataImp ( /*bCopyFromDDC=*/ false ) ;
}
# endif
2018-12-12 11:25:29 -05:00
void UGeometryCollection : : InvalidateCollection ( )
{
StateGuid = FGuid : : NewGuid ( ) ;
}
FGuid UGeometryCollection : : GetIdGuid ( ) const
{
return PersistentGuid ;
}
FGuid UGeometryCollection : : GetStateGuid ( ) const
{
return StateGuid ;
}
# if WITH_EDITOR
void UGeometryCollection : : PostEditChangeProperty ( struct FPropertyChangedEvent & PropertyChangedEvent )
{
2019-06-08 17:15:34 -04:00
if ( PropertyChangedEvent . Property & & PropertyChangedEvent . Property - > GetFName ( ) ! = GET_MEMBER_NAME_CHECKED ( UGeometryCollection , Materials ) )
{
InvalidateCollection ( ) ;
CreateSimulationData ( ) ;
}
2018-12-12 11:25:29 -05:00
}
# endif
bool UGeometryCollection : : Modify ( bool bAlwaysMarkDirty /*= true*/ )
{
bool bSuperResult = Super : : Modify ( bAlwaysMarkDirty ) ;
UPackage * Package = GetOutermost ( ) ;
if ( Package - > IsDirty ( ) )
{
InvalidateCollection ( ) ;
}
return bSuperResult ;
}
void UGeometryCollection : : PostLoad ( )
{
Super : : PostLoad ( ) ;
}
2019-06-08 17:15:34 -04:00
# if WITH_EDITOR
void UGeometryCollection : : EnsureDataIsCooked ( )
{
if ( StateGuid ! = LastBuiltGuid )
{
CreateSimulationDataImp ( /*bCopyFromDDC=*/ true ) ;
LastBuiltGuid = StateGuid ;
}
}
# endif