2019-12-26 14:45:42 -05:00
// Copyright 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"
2020-07-15 01:49:28 -04:00
# include "UObject/UE5MainStreamObjectVersion.h"
2018-12-12 11:25:29 -05:00
# include "Serialization/ArchiveCountMem.h"
# include "HAL/IConsoleManager.h"
# include "UObject/Package.h"
# include "Materials/MaterialInstance.h"
2020-03-27 17:52:09 -04:00
# include "ProfilingDebugging/CookStats.h"
2018-12-12 11:25:29 -05:00
2019-06-08 17:15:34 -04:00
# if WITH_EDITOR
# include "GeometryCollection/DerivedDataGeometryCollectionCooker.h"
2020-07-21 15:17:07 -04:00
# include "GeometryCollection/GeometryCollectionComponent.h"
2019-06-08 17:15:34 -04:00
# include "DerivedDataCacheInterface.h"
# include "Serialization/MemoryReader.h"
2020-07-15 01:49:28 -04:00
# include "NaniteBuilder.h"
# include "Rendering/NaniteResources.h"
2020-07-15 03:39:13 -04:00
// TODO: Temp until new asset-agnostic builder API
# include "StaticMeshResources.h"
2019-06-08 17:15:34 -04:00
# endif
# include "GeometryCollection/GeometryCollectionSimulationCoreTypes.h"
# include "Chaos/ChaosArchive.h"
2020-03-11 17:01:25 -04:00
# include "GeometryCollectionProxyData.h"
2019-06-08 17:15:34 -04:00
2018-12-12 11:25:29 -05:00
DEFINE_LOG_CATEGORY_STATIC ( UGeometryCollectionLogging , NoLogging , All ) ;
2020-03-27 17:52:09 -04:00
# if ENABLE_COOK_STATS
namespace GeometryCollectionCookStats
{
static FCookStats : : FDDCResourceUsageStats UsageStats ;
static FCookStatsManager : : FAutoRegisterCallback RegisterCookStats ( [ ] ( FCookStatsManager : : AddStatFuncRef AddStat )
{
UsageStats . LogStats ( AddStat , TEXT ( " GeometryCollection.Usage " ) , TEXT ( " " ) ) ;
} ) ;
}
# endif
2018-12-12 11:25:29 -05:00
UGeometryCollection : : UGeometryCollection ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
2020-07-15 01:49:28 -04:00
, EnableNanite ( false )
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:16:13 -04:00
, MaximumCollisionParticles ( 60 )
2019-06-08 17:15:34 -04:00
, EnableRemovePiecesOnFracture ( false )
2020-07-15 01:49:28 -04:00
, bRenderingResourcesInitialized ( 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:16:13 -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:16:13 -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:16:13 -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:16:13 -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 ;
2020-07-22 13:12:31 -04:00
// Reassign material ID for each face given the new consolidated array of materials
for ( int32 Material = 0 ; Material < MaterialID . Num ( ) ; + + Material )
2019-06-08 17:15:34 -04:00
{
2020-07-22 13:12:31 -04:00
if ( MaterialID [ Material ] < Materials . Num ( ) )
2019-06-08 17:15:34 -04:00
{
2020-07-22 13:12:31 -04:00
UMaterialInterface * OldMaterialPtr = Materials [ MaterialID [ Material ] ] ;
MaterialID [ Material ] = * MaterialPtrToArrayIndex . Find ( OldMaterialPtr ) ;
2019-06-08 17:15:34 -04:00
}
}
// Set new material array on the collection
Materials = FinalMaterials ;
2018-12-12 11:25:29 -05:00
// Last Material is the selection one
2020-07-22 13:12:31 -04:00
UMaterialInterface * BoneSelectedMaterial = LoadObject < UMaterialInterface > ( nullptr , GetSelectedMaterialPath ( ) , nullptr , LOAD_None , nullptr ) ;
2019-06-08 17:15:34 -04:00
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
{
2020-06-23 18:40:00 -04:00
if ( ensureMsgf ( GeometryCollection . IsValid ( ) , TEXT ( " Geometry Collection %s has an invalid internal collection " ) ) )
{
return GeometryCollection - > HasVisibleGeometry ( ) ;
}
return false ;
2018-12-12 11:25:29 -05:00
}
/** Serialize */
void UGeometryCollection : : Serialize ( FArchive & Ar )
{
Ar . UsingCustomVersion ( FDestructionObjectVersion : : GUID ) ;
2020-07-15 01:49:28 -04:00
Ar . UsingCustomVersion ( FUE5MainStreamObjectVersion : : 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 ) ;
2020-03-11 17:01:25 -04:00
// Fix up the type change for implicits here, previously they were unique ptrs, now they're shared
TManagedArray < TUniquePtr < Chaos : : FImplicitObject > > * OldAttr = GeometryCollection - > FindAttributeTyped < TUniquePtr < Chaos : : FImplicitObject > > ( FGeometryDynamicCollection : : ImplicitsAttribute , FTransformCollection : : TransformGroup ) ;
TManagedArray < TSharedPtr < Chaos : : FImplicitObject , ESPMode : : ThreadSafe > > * NewAttr = GeometryCollection - > FindAttributeTyped < TSharedPtr < Chaos : : FImplicitObject , ESPMode : : ThreadSafe > > ( FGeometryDynamicCollection : : SharedImplicitsAttribute , FTransformCollection : : TransformGroup ) ;
if ( OldAttr )
{
if ( ! NewAttr )
{
NewAttr = & GeometryCollection - > AddAttribute < TSharedPtr < Chaos : : FImplicitObject , ESPMode : : ThreadSafe > > ( FGeometryDynamicCollection : : SharedImplicitsAttribute , FTransformCollection : : TransformGroup ) ;
const int32 NumElems = GeometryCollection - > NumElements ( FTransformCollection : : TransformGroup ) ;
for ( int32 Index = 0 ; Index < NumElems ; + + Index )
{
( * NewAttr ) [ Index ] = TSharedPtr < Chaos : : FImplicitObject , ESPMode : : ThreadSafe > ( ( * OldAttr ) [ Index ] . Release ( ) ) ;
}
}
GeometryCollection - > RemoveAttribute ( FGeometryDynamicCollection : : ImplicitsAttribute , FTransformCollection : : TransformGroup ) ;
}
2019-06-08 17:15:34 -04:00
}
if ( Ar . CustomVer ( FDestructionObjectVersion : : GUID ) < FDestructionObjectVersion : : GroupAndAttributeNameRemapping )
{
2020-07-15 01:49:28 -04:00
GeometryCollection - > UpdateOldAttributeNames ( ) ;
2019-06-08 17:15:34 -04:00
InvalidateCollection ( ) ;
# if WITH_EDITOR
CreateSimulationData ( ) ;
# endif
}
2020-07-15 01:49:28 -04:00
if ( Ar . CustomVer ( FUE5MainStreamObjectVersion : : GUID ) > = FUE5MainStreamObjectVersion : : GeometryCollectionNaniteData )
{
int32 NumNaniteResources = Ar . IsLoading ( ) ? 0 : NaniteResources . Num ( ) ;
if ( ! EnableNanite )
{
// Avoid serializing stale Nanite data when support is disabled.
// TODO: Should instead do this on toggle of the property?
// TODO: Might never even need this, as NaniteResources may always be empty due to CreateNaniteData() running before
NumNaniteResources = 0 ;
}
Ar < < NumNaniteResources ;
if ( NumNaniteResources = = 0 )
{
NaniteResources . Empty ( ) ;
}
else
{
if ( Ar . IsLoading ( ) )
{
NaniteResources . Reset ( NumNaniteResources ) ;
NaniteResources . AddDefaulted ( NumNaniteResources ) ;
}
for ( int32 ResourceIndex = 0 ; ResourceIndex < NumNaniteResources ; + + ResourceIndex )
{
NaniteResources [ ResourceIndex ] . Serialize ( ChaosAr , this ) ;
}
// Nanite data is currently 1:1 with each geometry group in the collection.
const int32 NumGeometryGroups = GeometryCollection - > NumElements ( FGeometryCollection : : GeometryGroup ) ;
if ( NumGeometryGroups ! = NumNaniteResources )
{
Ar . SetError ( ) ;
}
}
if ( ! EnableNanite )
{
// Avoid serializing stale Nanite data when support is disabled.
// TODO: Should instead do this on toggle of the property?
NaniteResources . Empty ( ) ;
}
}
2019-06-08 17:15:34 -04:00
# if WITH_EDITOR
//for all versions loaded, make sure sim data is up to date
if ( Ar . IsLoading ( ) )
{
2020-08-28 16:47:53 -04:00
EnsureDataIsCooked ( ) ; //make sure loaded content is built
2019-06-08 17:15:34 -04:00
}
# endif
2018-12-12 11:25:29 -05:00
}
2020-07-22 13:12:31 -04:00
const TCHAR * UGeometryCollection : : GetSelectedMaterialPath ( ) const
{
return TEXT ( " /Engine/EditorMaterials/GeometryCollection/SelectedGeometryMaterial.SelectedGeometryMaterial " ) ;
}
2019-06-08 17:15:34 -04:00
# if WITH_EDITOR
void UGeometryCollection : : CreateSimulationDataImp ( bool bCopyFromDDC , const TCHAR * OverrideVersion )
{
2020-03-27 17:52:09 -04:00
COOK_STAT ( auto Timer = GeometryCollectionCookStats : : UsageStats . TimeSyncWork ( ) ) ;
2020-03-11 17:01:25 -04:00
// Skips the DDC fetch entirely for testing the builder without adding to the DDC
const static bool bSkipDDC = false ;
2019-06-08 17:15:34 -04:00
//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 ( ) )
{
2020-03-27 17:52:09 -04:00
if ( bSkipDDC )
2020-03-11 17:01:25 -04:00
{
GeometryCollectionCooker - > Build ( DDCData ) ;
2020-03-27 17:52:09 -04:00
COOK_STAT ( Timer . AddMiss ( DDCData . Num ( ) ) ) ;
2020-03-11 17:01:25 -04:00
}
else
{
2020-03-27 17:52:09 -04:00
bool bBuilt = false ;
const bool bSuccess = GetDerivedDataCacheRef ( ) . GetSynchronous ( GeometryCollectionCooker , DDCData , & bBuilt ) ;
COOK_STAT ( Timer . AddHitOrMiss ( ! bSuccess | | bBuilt ? FCookStats : : CallStats : : EHitOrMiss : : Miss : FCookStats : : CallStats : : EHitOrMiss : : Hit , DDCData . Num ( ) ) ) ;
2020-03-11 17:01:25 -04:00
}
2020-03-27 17:52:09 -04:00
if ( bCopyFromDDC )
2019-06-08 17:15:34 -04:00
{
2020-08-28 16:47:53 -04:00
FMemoryReader Ar ( DDCData , true ) ; // Must be persistent for BulkData to serialize
2019-06-08 17:15:34 -04:00
Chaos : : FChaosArchive ChaosAr ( Ar ) ;
GeometryCollection - > Serialize ( ChaosAr ) ;
2020-08-28 16:47:53 -04:00
for ( Nanite : : FResources & NaniteResource : NaniteResources )
{
NaniteResource . Serialize ( ChaosAr , this ) ;
}
2019-06-08 17:15:34 -04:00
}
}
}
void UGeometryCollection : : CreateSimulationData ( )
{
CreateSimulationDataImp ( /*bCopyFromDDC=*/ false ) ;
}
2020-07-15 01:49:28 -04:00
TArray < Nanite : : FResources > & UGeometryCollection : : CreateNaniteData ( FGeometryCollection * Collection )
{
TRACE_CPUPROFILER_EVENT_SCOPE_TEXT ( TEXT ( " FDerivedDataGeometryCollectionCooker::Build::Nanite " ) ) ;
NaniteResources . Empty ( ) ;
if ( ! EnableNanite )
{
return NaniteResources ;
}
Nanite : : IBuilderModule & NaniteBuilderModule = Nanite : : IBuilderModule : : Get ( ) ;
// Transform Group
const TManagedArray < int32 > & TransformToGeometryIndexArray = Collection - > TransformToGeometryIndex ;
const TManagedArray < int32 > & SimulationTypeArray = Collection - > SimulationType ;
const TManagedArray < int32 > & StatusFlagsArray = Collection - > StatusFlags ;
// Vertices Group
const TManagedArray < FVector > & VertexArray = Collection - > Vertex ;
const TManagedArray < FVector2D > & UVArray = Collection - > UV ;
const TManagedArray < FLinearColor > & ColorArray = Collection - > Color ;
const TManagedArray < FVector > & TangentUArray = Collection - > TangentU ;
const TManagedArray < FVector > & TangentVArray = Collection - > TangentV ;
const TManagedArray < FVector > & NormalArray = Collection - > Normal ;
const TManagedArray < int32 > & BoneMapArray = Collection - > BoneMap ;
// Faces Group
const TManagedArray < FIntVector > & IndicesArray = Collection - > Indices ;
const TManagedArray < bool > & VisibleArray = Collection - > Visible ;
const TManagedArray < int32 > & MaterialIndexArray = Collection - > MaterialIndex ;
const TManagedArray < int32 > & MaterialIDArray = Collection - > MaterialID ;
// Geometry Group
const TManagedArray < int32 > & TransformIndexArray = Collection - > TransformIndex ;
const TManagedArray < FBox > & BoundingBoxArray = Collection - > BoundingBox ;
const TManagedArray < float > & InnerRadiusArray = Collection - > InnerRadius ;
const TManagedArray < float > & OuterRadiusArray = Collection - > OuterRadius ;
const TManagedArray < int32 > & VertexStartArray = Collection - > VertexStart ;
const TManagedArray < int32 > & VertexCountArray = Collection - > VertexCount ;
const TManagedArray < int32 > & FaceStartArray = Collection - > FaceStart ;
const TManagedArray < int32 > & FaceCountArray = Collection - > FaceCount ;
// Material Group
const TManagedArray < FGeometryCollectionSection > & Sections = Collection - > Sections ;
int32 NumGeometry = Collection - > NumElements ( FGeometryCollection : : GeometryGroup ) ;
NaniteResources . AddDefaulted ( NumGeometry ) ;
for ( int32 GeometryGroupIndex = 0 ; GeometryGroupIndex < NumGeometry ; GeometryGroupIndex + + )
{
Nanite : : FResources & NaniteResource = NaniteResources [ GeometryGroupIndex ] ;
NaniteResource = { } ;
uint32 NumTexCoords = 1 ; // NumTextureCoord;
2020-07-15 05:35:35 -04:00
bool bHasColors = ColorArray . Num ( ) > 0 ;
2020-07-15 01:49:28 -04:00
const int32 VertexStart = VertexStartArray [ GeometryGroupIndex ] ;
const int32 VertexCount = VertexCountArray [ GeometryGroupIndex ] ;
TArray < FStaticMeshBuildVertex > BuildVertices ;
BuildVertices . Reserve ( VertexCount ) ;
for ( int32 VertexIndex = 0 ; VertexIndex < VertexCount ; + + VertexIndex )
{
FStaticMeshBuildVertex & Vertex = BuildVertices . Emplace_GetRef ( ) ;
Vertex . Position = VertexArray [ VertexStart + VertexIndex ] ;
2020-07-15 05:35:35 -04:00
Vertex . Color = bHasColors ? ColorArray [ VertexStart + VertexIndex ] . ToFColor ( false /* sRGB */ ) : FColor : : White ;
Vertex . TangentX = FVector : : ZeroVector ;
Vertex . TangentY = FVector : : ZeroVector ;
Vertex . TangentZ = NormalArray [ VertexStart + VertexIndex ] ;
2020-07-15 01:49:28 -04:00
Vertex . UVs [ 0 ] = UVArray [ VertexStart + VertexIndex ] ;
2020-07-15 05:35:35 -04:00
if ( Vertex . UVs [ 0 ] . ContainsNaN ( ) )
{
Vertex . UVs [ 0 ] = FVector2D : : ZeroVector ;
}
2020-07-15 01:49:28 -04:00
}
const int32 FaceStart = FaceStartArray [ GeometryGroupIndex ] ;
const int32 FaceCount = FaceCountArray [ GeometryGroupIndex ] ;
// TODO: Respect multiple materials like in FGeometryCollectionConversion::AppendStaticMesh
2020-07-21 17:01:45 -04:00
TArray < int32 > MaterialIndices ;
MaterialIndices . Reserve ( FaceCount ) ;
2020-07-15 01:49:28 -04:00
TArray < uint32 > BuildIndices ;
BuildIndices . Reserve ( FaceCount * 3 ) ;
for ( int32 FaceIndex = 0 ; FaceIndex < FaceCount ; + + FaceIndex )
{
2020-07-21 15:17:07 -04:00
if ( ! VisibleArray [ FaceStart + FaceIndex ] ) // TODO: Always in range?
{
continue ;
}
2020-07-15 01:49:28 -04:00
const FIntVector FaceIndices = IndicesArray [ FaceStart + FaceIndex ] ;
2020-07-21 15:17:07 -04:00
BuildIndices . Add ( FaceIndices . X - VertexStart ) ;
BuildIndices . Add ( FaceIndices . Y - VertexStart ) ;
BuildIndices . Add ( FaceIndices . Z - VertexStart ) ;
2020-07-15 01:49:28 -04:00
2020-07-21 17:01:45 -04:00
const int32 MaterialIndex = MaterialIDArray [ FaceStart + FaceIndex ] ;
MaterialIndices . Add ( MaterialIndex ) ;
2020-07-15 01:49:28 -04:00
}
2020-07-21 15:17:07 -04:00
if ( BuildIndices . Num ( ) = = 0 )
{
// No visible faces of entire geometry, skip any building/rendering.
continue ;
}
2020-07-15 01:49:28 -04:00
FMeshNaniteSettings NaniteSettings = { } ;
NaniteSettings . bEnabled = true ;
NaniteSettings . PercentTriangles = 1.0f ; // 100% - no reduction
2020-07-21 17:01:45 -04:00
if ( ! NaniteBuilderModule . Build ( NaniteResource , BuildVertices , BuildIndices , MaterialIndices , NumTexCoords , bHasColors , NaniteSettings ) )
2020-07-15 01:49:28 -04:00
{
UE_LOG ( LogStaticMesh , Error , TEXT ( " Failed to build Nanite for geometry collection. See previous line(s) for details. " ) ) ;
}
}
return NaniteResources ;
}
2019-06-08 17:15:34 -04:00
# endif
2020-07-15 01:49:28 -04:00
void UGeometryCollection : : InitResources ( )
{
//LLM_SCOPE(ELLMTag::GeometryCollection);
ReleaseResources ( ) ;
2020-07-21 15:17:07 -04:00
if ( EnableNanite )
2020-07-15 01:49:28 -04:00
{
2020-07-21 15:17:07 -04:00
for ( Nanite : : FResources & Resource : NaniteResources )
2020-07-15 01:49:28 -04:00
{
2020-07-21 15:17:07 -04:00
// Skip resources that have their render data stripped
if ( Resource . PageStreamingStates . Num ( ) > 0 )
{
Resource . InitResources ( ) ;
}
2020-07-15 01:49:28 -04:00
}
}
bRenderingResourcesInitialized = true ;
}
void UGeometryCollection : : ReleaseResources ( )
{
if ( bRenderingResourcesInitialized )
{
for ( Nanite : : FResources & Resource : NaniteResources )
{
if ( Resource . PageStreamingStates . Num ( ) > 0 )
{
Resource . ReleaseResources ( ) ;
}
}
bRenderingResourcesInitialized = false ;
}
}
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 )
{
2020-07-21 15:17:07 -04:00
if ( PropertyChangedEvent . Property )
2019-06-08 17:15:34 -04:00
{
2020-07-21 15:17:07 -04:00
if ( PropertyChangedEvent . Property - > GetFName ( ) ! = GET_MEMBER_NAME_CHECKED ( UGeometryCollection , Materials ) )
{
InvalidateCollection ( ) ;
CreateSimulationData ( ) ;
}
if ( PropertyChangedEvent . Property - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UGeometryCollection , EnableNanite ) )
{
if ( FApp : : CanEverRender ( ) )
{
InitResources ( ) ;
}
}
2019-06-08 17:15:34 -04:00
}
2018-12-12 11:25:29 -05:00
}
bool UGeometryCollection : : Modify ( bool bAlwaysMarkDirty /*= true*/ )
{
bool bSuperResult = Super : : Modify ( bAlwaysMarkDirty ) ;
UPackage * Package = GetOutermost ( ) ;
if ( Package - > IsDirty ( ) )
{
InvalidateCollection ( ) ;
}
return bSuperResult ;
}
2019-06-08 17:15:34 -04:00
void UGeometryCollection : : EnsureDataIsCooked ( )
{
if ( StateGuid ! = LastBuiltGuid )
{
CreateSimulationDataImp ( /*bCopyFromDDC=*/ true ) ;
LastBuiltGuid = StateGuid ;
}
}
# endif
2020-07-15 01:49:28 -04:00
void UGeometryCollection : : PostLoad ( )
{
Super : : PostLoad ( ) ;
2020-07-21 15:17:07 -04:00
// Initialize rendering resources.
2020-07-15 01:49:28 -04:00
if ( FApp : : CanEverRender ( ) )
{
InitResources ( ) ;
}
}
void UGeometryCollection : : BeginDestroy ( )
{
Super : : BeginDestroy ( ) ;
ReleaseResources ( ) ;
}