2022-10-21 19:51:57 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# pragma once
# include "Components/SceneComponent.h"
# include "Containers/Map.h"
2023-03-21 17:54:35 -04:00
# include "InstancedStaticMeshDelegates.h"
2023-04-03 23:33:01 -04:00
# include "Materials/MaterialInterface.h"
2022-10-21 19:51:57 -04:00
# include "GeometryCollectionISMPoolComponent.generated.h"
class AActor ;
class UInstancedStaticMeshComponent ;
class UGeometryCollectionISMPoolComponent ;
2023-04-14 09:07:04 -04:00
/**
* Structure containing a set of allocated instance ranges in an FGeometryCollectionISM which is the manager for a single ISM component .
* The instance ranges don ' t change once allocated , and aren ' t the same as the actual render indices in the ISM .
* The reason that we don ' t store the the actual ISM render indices is that ISM component is free to reorder its instances whenever it likes .
*/
2022-10-21 19:51:57 -04:00
struct FInstanceGroups
{
using FInstanceGroupId = int32 ;
2023-04-14 09:07:04 -04:00
/** A single continuous range associated with an FInstanceGroupId. */
2022-10-21 19:51:57 -04:00
struct FInstanceGroupRange
{
FInstanceGroupRange ( int32 InStart , int32 InCount )
2023-04-14 09:07:04 -04:00
: Start ( InStart )
, Count ( InCount )
2023-03-21 17:54:35 -04:00
{
}
2023-04-14 09:07:04 -04:00
int32 Start = 0 ;
int32 Count = 0 ;
2022-10-21 19:51:57 -04:00
} ;
2023-04-14 09:07:04 -04:00
/** Reset all contents. */
void Reset ( )
{
TotalInstanceCount = 0 ;
TotalFreeInstanceCount = 0 ;
GroupRanges . Empty ( ) ;
FreeList . Empty ( ) ;
}
/** Returns true if no groups ranges are in use. */
2023-04-06 17:29:18 -04:00
bool IsEmpty ( ) const
{
2023-04-14 09:07:04 -04:00
return GroupRanges . Num ( ) = = FreeList . Num ( ) ;
2023-04-06 17:29:18 -04:00
}
2023-04-14 09:07:04 -04:00
/** Returns the maximum instance index that we have allocated in a group. */
int32 GetMaxInstanceIndex ( ) const
{
return TotalInstanceCount ;
}
/** Add a new group range. */
2022-10-21 19:51:57 -04:00
FInstanceGroupId AddGroup ( int32 Count )
{
2023-04-14 09:07:04 -04:00
// First check to see if we can recycle a group from the free list.
for ( int32 Index = 0 ; Index < FreeList . Num ( ) ; + + Index )
{
const FInstanceGroupId GroupId = FreeList [ Index ] ;
// todo: Could also allow allocating a subrange if Count is less than the group range count.
if ( Count = = GroupRanges [ GroupId ] . Count )
{
TotalFreeInstanceCount - = Count ;
FreeList . RemoveAtSwap ( Index , 1 , false ) ;
return GroupId ;
}
}
// Create a new group.
const int32 StartIndex = TotalInstanceCount ;
TotalInstanceCount + = Count ;
const FInstanceGroupId GroupId = GroupRanges . Add ( FInstanceGroupRange ( StartIndex , Count ) ) ;
return GroupId ;
2022-10-21 19:51:57 -04:00
}
2023-04-14 09:07:04 -04:00
/** Remove a group range. */
2022-10-21 19:51:57 -04:00
void RemoveGroup ( FInstanceGroupId GroupId )
{
2023-04-14 09:07:04 -04:00
// Remove the group by putting on free list for reuse.
// Actually removing it would require too much shuffling of the render instance index remapping.
TotalFreeInstanceCount + = GroupRanges [ GroupId ] . Count ;
FreeList . Add ( GroupId ) ;
2022-10-21 19:51:57 -04:00
}
2023-04-14 09:07:04 -04:00
int32 TotalInstanceCount = 0 ;
int32 TotalFreeInstanceCount = 0 ;
TArray < FInstanceGroupRange > GroupRanges ;
TArray < FInstanceGroupId > FreeList ;
2022-10-21 19:51:57 -04:00
} ;
2023-04-14 09:07:04 -04:00
/**
* A description for an ISM component .
*/
2023-04-03 23:33:01 -04:00
struct FISMComponentDescription
{
bool bUseHISM = false ;
2023-04-06 17:29:18 -04:00
bool bReverseCulling = false ;
2023-04-03 23:33:01 -04:00
bool bIsStaticMobility = false ;
bool bAffectShadow = true ;
bool bAffectDistanceFieldLighting = false ;
bool bAffectDynamicIndirectLighting = false ;
int32 NumCustomDataFloats = 0 ;
int32 StartCullDistance = 0 ;
int32 EndCullDistance = 0 ;
2023-04-10 23:19:28 -04:00
int32 MinLod = 0 ;
2023-04-03 23:33:01 -04:00
float LodScale = 1.f ;
bool operator = = ( const FISMComponentDescription & Other ) const
{
return bUseHISM = = Other . bUseHISM & &
2023-04-06 17:29:18 -04:00
bReverseCulling = = Other . bReverseCulling & &
2023-04-03 23:33:01 -04:00
bIsStaticMobility = = Other . bIsStaticMobility & &
bAffectShadow = = Other . bAffectShadow & &
bAffectDistanceFieldLighting = = Other . bAffectDistanceFieldLighting & &
bAffectDynamicIndirectLighting = = Other . bAffectDistanceFieldLighting & &
NumCustomDataFloats = = Other . NumCustomDataFloats & &
StartCullDistance = = Other . StartCullDistance & &
EndCullDistance = = Other . EndCullDistance & &
2023-04-10 23:19:28 -04:00
MinLod = = Other . MinLod & &
2023-04-03 23:33:01 -04:00
LodScale = = Other . LodScale ;
}
} ;
FORCEINLINE uint32 GetTypeHash ( const FISMComponentDescription & Desc )
{
2023-04-06 17:29:18 -04:00
const uint32 PackedBools = ( Desc . bUseHISM ? 1 : 0 ) | ( Desc . bReverseCulling ? 2 : 0 ) | ( Desc . bIsStaticMobility ? 4 : 0 ) | ( Desc . bAffectShadow ? 8 : 0 ) | ( Desc . bAffectDistanceFieldLighting ? 16 : 0 ) | ( Desc . bAffectDynamicIndirectLighting ? 32 : 0 ) ;
2023-04-03 23:33:01 -04:00
uint32 Hash = HashCombine ( GetTypeHash ( PackedBools ) , GetTypeHash ( Desc . NumCustomDataFloats ) ) ;
Hash = HashCombine ( Hash , GetTypeHash ( Desc . StartCullDistance ) ) ;
Hash = HashCombine ( Hash , GetTypeHash ( Desc . EndCullDistance ) ) ;
2023-04-10 23:19:28 -04:00
Hash = HashCombine ( Hash , GetTypeHash ( Desc . MinLod ) ) ;
2023-04-03 23:33:01 -04:00
return HashCombine ( Hash , GetTypeHash ( Desc . LodScale ) ) ;
}
2022-10-21 19:51:57 -04:00
/**
2023-04-14 09:07:04 -04:00
* A mesh with potentially overriden materials and ISM property description .
* We batch instances into ISMs that have equivalent values for this structure .
*/
2022-10-21 19:51:57 -04:00
struct FGeometryCollectionStaticMeshInstance
{
UStaticMesh * StaticMesh = nullptr ;
TArray < UMaterialInterface * > MaterialsOverrides ;
2023-04-03 23:33:01 -04:00
FISMComponentDescription Desc ;
2022-10-21 19:51:57 -04:00
bool operator = = ( const FGeometryCollectionStaticMeshInstance & Other ) const
{
2023-04-03 23:33:01 -04:00
if ( StaticMesh = = Other . StaticMesh & & Desc = = Other . Desc )
2022-10-21 19:51:57 -04:00
{
if ( MaterialsOverrides . Num ( ) = = Other . MaterialsOverrides . Num ( ) )
{
for ( int32 MatIndex = 0 ; MatIndex < MaterialsOverrides . Num ( ) ; MatIndex + + )
{
const FName MatName = MaterialsOverrides [ MatIndex ] ? MaterialsOverrides [ MatIndex ] - > GetFName ( ) : NAME_None ;
const FName OtherName = Other . MaterialsOverrides [ MatIndex ] ? Other . MaterialsOverrides [ MatIndex ] - > GetFName ( ) : NAME_None ;
if ( MatName ! = OtherName )
{
return false ;
}
}
return true ;
}
}
return false ;
}
} ;
FORCEINLINE uint32 GetTypeHash ( const FGeometryCollectionStaticMeshInstance & MeshInstance )
{
uint32 CombinedHash = GetTypeHash ( MeshInstance . StaticMesh ) ;
CombinedHash = HashCombine ( CombinedHash , GetTypeHash ( MeshInstance . MaterialsOverrides . Num ( ) ) ) ;
for ( const UMaterialInterface * Material : MeshInstance . MaterialsOverrides )
{
CombinedHash = HashCombine ( CombinedHash , GetTypeHash ( Material ) ) ;
}
2023-04-03 23:33:01 -04:00
CombinedHash = HashCombine ( CombinedHash , GetTypeHash ( MeshInstance . Desc ) ) ;
2022-10-21 19:51:57 -04:00
return CombinedHash ;
}
2023-04-14 09:07:04 -04:00
/** Describes a group of instances within an ISM. */
2022-10-21 19:51:57 -04:00
struct FGeometryCollectionMeshInfo
{
int32 ISMIndex ;
2023-04-14 09:07:04 -04:00
FInstanceGroups : : FInstanceGroupId InstanceGroupIndex ;
2022-10-21 19:51:57 -04:00
} ;
struct FGeometryCollectionISMPool ;
/**
2023-04-14 09:07:04 -04:00
* A mesh group which is a collection of meshes and their related FGeometryCollectionMeshInfo .
* We group these with a single handle with the expectation that a client will want to own multiple meshs and release them together .
*/
2022-10-21 19:51:57 -04:00
struct FGeometryCollectionMeshGroup
{
using FMeshId = int32 ;
2023-04-14 09:07:04 -04:00
/** Adds a new mesh with instance count. We expect to only add a unique mesh instance once to each group. Returns a ID that can be used to update the instances. */
2022-10-21 19:51:57 -04:00
FMeshId AddMesh ( const FGeometryCollectionStaticMeshInstance & MeshInstance , int32 InstanceCount , const FGeometryCollectionMeshInfo & ISMInstanceInfo ) ;
2023-04-14 09:07:04 -04:00
/** Update instance transforms for a group of instances. */
2022-10-21 19:51:57 -04:00
bool BatchUpdateInstancesTransforms ( FGeometryCollectionISMPool & ISMPool , FMeshId MeshId , int32 StartInstanceIndex , const TArray < FTransform > & NewInstancesTransforms , bool bWorldSpace , bool bMarkRenderStateDirty , bool bTeleport ) ;
2023-04-14 09:07:04 -04:00
/** Remove all of our managed meshes and associated instances. */
2022-10-21 19:51:57 -04:00
void RemoveAllMeshes ( FGeometryCollectionISMPool & ISMPool ) ;
2023-04-14 09:07:04 -04:00
/** Array of allocated mesh infos. */
2022-10-21 19:51:57 -04:00
TArray < FGeometryCollectionMeshInfo > MeshInfos ;
2023-04-14 09:07:04 -04:00
/** Map from mesh instance description to the its index in the MeshInfo array. */
TMap < FGeometryCollectionStaticMeshInstance , FMeshId > Meshes ;
2022-10-21 19:51:57 -04:00
} ;
2023-04-14 09:07:04 -04:00
/** Structure containting all info for a single ISM. */
2022-10-21 19:51:57 -04:00
struct FGeometryCollectionISM
{
2023-04-06 17:29:18 -04:00
FGeometryCollectionISM ( AActor * OwmingActor , const FGeometryCollectionStaticMeshInstance & InMeshInstance ) ;
2022-10-21 19:51:57 -04:00
2023-04-14 09:07:04 -04:00
/** Add a group to the ISM. Returns the group index. */
FInstanceGroups : : FInstanceGroupId AddInstanceGroup ( int32 InstanceCount , TArrayView < const float > CustomDataFloats ) ;
2022-10-21 19:51:57 -04:00
2023-04-14 09:07:04 -04:00
/** Unique description of ISM component settings. */
2023-04-06 17:29:18 -04:00
FGeometryCollectionStaticMeshInstance MeshInstance ;
2023-04-14 09:07:04 -04:00
/** Created ISM component. Will be nullptr when this slot has been recycled to FGeometryCollectionISMPool FreeList. */
TObjectPtr < UInstancedStaticMeshComponent > ISMComponent ;
/** Groups of instances allocated in the ISM. */
2022-10-21 19:51:57 -04:00
FInstanceGroups InstanceGroups ;
2023-04-14 09:07:04 -04:00
/** Mapping from our instance index to the ISM Component index. */
TArray < int32 > InstanceIndexToRenderIndex ;
/** Mapping from the ISM Component index to our instance index . */
TArray < int32 > RenderIndexToInstanceIndex ;
2022-10-21 19:51:57 -04:00
} ;
2023-04-14 09:07:04 -04:00
/** A pool of ISMs. */
2022-10-21 19:51:57 -04:00
struct FGeometryCollectionISMPool
{
using FISMIndex = int32 ;
2023-04-03 23:33:01 -04:00
FGeometryCollectionMeshInfo AddISM ( UGeometryCollectionISMPoolComponent * OwningComponent , const FGeometryCollectionStaticMeshInstance & MeshInstance , int32 InstanceCount , TArrayView < const float > CustomDataFloats ) ;
2022-10-21 19:51:57 -04:00
bool BatchUpdateInstancesTransforms ( FGeometryCollectionMeshInfo & MeshInfo , int32 StartInstanceIndex , const TArray < FTransform > & NewInstancesTransforms , bool bWorldSpace , bool bMarkRenderStateDirty , bool bTeleport ) ;
void RemoveISM ( const FGeometryCollectionMeshInfo & MeshInfo ) ;
2023-03-21 17:54:35 -04:00
2022-10-21 19:51:57 -04:00
/** Clear all ISM components and associated data */
void Clear ( ) ;
TMap < FGeometryCollectionStaticMeshInstance , FISMIndex > MeshToISMIndex ;
2023-03-21 17:54:35 -04:00
TMap < UInstancedStaticMeshComponent * , FISMIndex > ISMComponentToISMIndex ;
2022-10-21 19:51:57 -04:00
TArray < FGeometryCollectionISM > ISMs ;
2023-04-06 17:29:18 -04:00
TArray < int32 > FreeList ;
2022-10-21 19:51:57 -04:00
} ;
/**
2023-04-14 09:07:04 -04:00
* UGeometryCollectionISMPoolComponent .
* Component that manages a pool of ISM components in order to allow multiple client components that use the same meshes to the share ISMs .
2022-10-21 19:51:57 -04:00
*/
UCLASS ( meta = ( BlueprintSpawnableComponent ) )
class GEOMETRYCOLLECTIONENGINE_API UGeometryCollectionISMPoolComponent : public USceneComponent
{
GENERATED_UCLASS_BODY ( )
public :
using FMeshGroupId = int32 ;
using FMeshId = int32 ;
2023-03-22 19:44:46 -04:00
//~ Begin UActorComponent Interface
2023-04-14 09:07:04 -04:00
virtual void GetResourceSizeEx ( FResourceSizeEx & CumulativeResourceSize ) override ;
2023-03-22 19:44:46 -04:00
//~ End UActorComponent Interface
2022-10-21 19:51:57 -04:00
/**
* Create an Mesh group which represent an arbitrary set of mesh with their instance
* no resources are created until the meshes are added for this group
* return a mesh group Id used to add and update instances
*/
FMeshGroupId CreateMeshGroup ( ) ;
/** destroy a mesh group and its associated resources */
void DestroyMeshGroup ( FMeshGroupId MeshGroupId ) ;
/** Add a static mesh for a nmesh group */
2023-04-03 23:33:01 -04:00
FMeshId AddMeshToGroup ( FMeshGroupId MeshGroupId , const FGeometryCollectionStaticMeshInstance & MeshInstance , int32 InstanceCount , TArrayView < const float > CustomDataFloats ) ;
2022-10-21 19:51:57 -04:00
2023-04-14 09:07:04 -04:00
/** Add a static mesh for a mesh group */
2022-10-21 19:51:57 -04:00
bool BatchUpdateInstancesTransforms ( FMeshGroupId MeshGroupId , FMeshId MeshId , int32 StartInstanceIndex , const TArray < FTransform > & NewInstancesTransforms , bool bWorldSpace = false , bool bMarkRenderStateDirty = false , bool bTeleport = false ) ;
private :
uint32 NextMeshGroupId = 0 ;
TMap < FMeshGroupId , FGeometryCollectionMeshGroup > MeshGroups ;
FGeometryCollectionISMPool Pool ;
2023-03-09 00:00:20 -05:00
// Expose internals for debug draw support.
friend class UGeometryCollectionISMPoolDebugDrawComponent ;
2022-10-21 19:51:57 -04:00
} ;