2021-06-10 18:37:57 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "UDynamicMesh.h"
# include "Changes/MeshVertexChange.h"
# include "Changes/MeshChange.h"
# include "Changes/MeshReplacementChange.h"
# include "Misc/Base64.h"
# include "Serialization/MemoryWriter.h"
# include "Serialization/MemoryReader.h"
# include "UObject/UE5MainStreamObjectVersion.h"
# include "HAL/IConsoleManager.h"
# include "Engine/Engine.h"
# include "Generators/MinimalBoxMeshGenerator.h"
2024-10-01 19:50:31 -04:00
# if WITH_EDITOR
# include "Widgets/Notifications/SNotificationList.h"
# include "Framework/Notifications/NotificationManager.h"
# endif
2022-09-24 13:57:58 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(UDynamicMesh)
2024-10-01 19:50:31 -04:00
# define LOCTEXT_NAMESPACE "UDynamicMesh"
2021-06-10 18:37:57 -04:00
using namespace UE : : Geometry ;
// these cvars are used to support T3D encoding of the internal FDynamicMesh3, see ::ExportCustomProperties() and ::ImportCustomProperties()
static TAutoConsoleVariable < int32 > CVarDynamicMeshTextBasedDupeTriangleCountThreshold (
TEXT ( " geometry.DynamicMesh.TextBasedDupeTriThreshold " ) ,
2024-10-01 19:50:31 -04:00
200000 ,
2021-06-10 18:37:57 -04:00
TEXT ( " Triangle count threshold for text-based UDynamicMesh duplication using Base64. Large values are quite slow. " ) ) ;
static TAutoConsoleVariable < int32 > CVarDynamicMeshDupeHelperTimeout (
TEXT ( " geometry.DynamicMesh.DupeStashTimeout " ) ,
5 * 60 ,
TEXT ( " Timeout in seconds for references held by internal UDynamicMesh duplication helper system. See FDynamicMeshCopyHelper. " ) ) ;
2024-10-01 19:50:31 -04:00
namespace UE : : Private : : UDynamicMeshLocal
{
static void DisplayCriticalWarningMessage ( const FText & InMessage , float ExpireDuration = 5.0f )
{
# if WITH_EDITOR
FNotificationInfo Info ( InMessage ) ;
Info . ExpireDuration = ExpireDuration ;
FSlateNotificationManager : : Get ( ) . AddNotification ( Info ) ;
# endif
UE_LOG ( LogGeometry , Warning , TEXT ( " %s " ) , * InMessage . ToString ( ) ) ;
}
}
2021-06-10 18:37:57 -04:00
UDynamicMesh : : UDynamicMesh ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
{
InitializeNewMesh ( ) ;
}
void UDynamicMesh : : InitializeNewMesh ( )
{
Mesh = MakeUnique < FDynamicMesh3 > ( EMeshComponents : : FaceGroups ) ;
InitializeMesh ( ) ;
}
2021-09-15 21:24:22 -04:00
UDynamicMesh * UDynamicMesh : : Reset ( )
2021-06-10 18:37:57 -04:00
{
FDynamicMeshChangeInfo ChangeInfo ;
ChangeInfo . Type = EDynamicMeshChangeType : : GeneralEdit ;
EditMeshInternal ( [ this ] ( FDynamicMesh3 & EditMesh )
{
check ( & EditMesh = = Mesh . Get ( ) ) ; // assuming that EditMesh is internal mesh here...
InitializeMesh ( ) ;
} , ChangeInfo ) ;
2021-09-15 21:24:22 -04:00
return this ;
2021-06-10 18:37:57 -04:00
}
2021-09-15 21:24:22 -04:00
UDynamicMesh * UDynamicMesh : : ResetToCube ( )
2021-06-10 18:37:57 -04:00
{
FDynamicMeshChangeInfo ChangeInfo ;
ChangeInfo . Type = EDynamicMeshChangeType : : GeneralEdit ;
EditMeshInternal ( [ this ] ( FDynamicMesh3 & EditMesh )
{
EditMesh . Clear ( ) ;
FMinimalBoxMeshGenerator BoxGen ;
2021-08-26 09:31:28 -04:00
BoxGen . Box = UE : : Geometry : : FOrientedBox3d ( FVector3d : : Zero ( ) , 50.0 * FVector3d : : One ( ) ) ;
2021-06-10 18:37:57 -04:00
EditMesh = FDynamicMesh3 ( & BoxGen . Generate ( ) ) ;
EditMesh . EnableTriangleGroups ( ) ;
EditMesh . Attributes ( ) - > EnableMaterialID ( ) ;
} , ChangeInfo ) ;
2021-09-15 21:24:22 -04:00
return this ;
2021-06-10 18:37:57 -04:00
}
void UDynamicMesh : : InitializeMesh ( )
{
Mesh - > Clear ( ) ;
Mesh - > EnableTriangleGroups ( ) ;
Mesh - > EnableAttributes ( ) ;
Mesh - > Attributes ( ) - > EnableMaterialID ( ) ;
if ( MeshGenerator ! = nullptr & & bEnableMeshGenerator )
{
MeshGenerator - > Generate ( * Mesh ) ;
}
}
2021-06-20 18:12:30 -04:00
bool UDynamicMesh : : IsEmpty ( ) const
{
return Mesh - > TriangleCount ( ) = = 0 ;
}
int32 UDynamicMesh : : GetTriangleCount ( ) const
{
return Mesh - > TriangleCount ( ) ;
}
2021-06-10 18:37:57 -04:00
void UDynamicMesh : : SetMesh ( const UE : : Geometry : : FDynamicMesh3 & MoveMesh )
{
FDynamicMeshChangeInfo ChangeInfo ;
ChangeInfo . Type = EDynamicMeshChangeType : : GeneralEdit ;
EditMeshInternal ( [ & ] ( FDynamicMesh3 & EditMesh ) {
EditMesh = MoveMesh ;
} , ChangeInfo ) ;
}
void UDynamicMesh : : SetMesh ( UE : : Geometry : : FDynamicMesh3 & & MoveMesh )
{
FDynamicMeshChangeInfo ChangeInfo ;
ChangeInfo . Type = EDynamicMeshChangeType : : GeneralEdit ;
EditMeshInternal ( [ & ] ( FDynamicMesh3 & EditMesh ) {
EditMesh = MoveTemp ( MoveMesh ) ;
} , ChangeInfo ) ;
}
void UDynamicMesh : : ProcessMesh ( TFunctionRef < void ( const UE : : Geometry : : FDynamicMesh3 & ) > ProcessFunc ) const
{
ProcessFunc ( GetMeshRef ( ) ) ;
}
2021-06-11 22:42:32 -04:00
void UDynamicMesh : : EditMesh ( TFunctionRef < void ( FDynamicMesh3 & ) > EditFunc ,
EDynamicMeshChangeType ChangeType ,
EDynamicMeshAttributeChangeFlags ChangeFlags ,
2022-01-14 15:39:06 -05:00
bool bDeferChangeEvents )
2021-06-10 18:37:57 -04:00
{
FDynamicMeshChangeInfo ChangeInfo ;
2021-06-11 22:42:32 -04:00
ChangeInfo . Type = ChangeType ;
ChangeInfo . Flags = ChangeFlags ;
2022-01-14 15:39:06 -05:00
EditMeshInternal ( EditFunc , ChangeInfo , bDeferChangeEvents ) ;
2021-06-10 18:37:57 -04:00
}
2022-01-14 15:39:06 -05:00
void UDynamicMesh : : EditMeshInternal ( TFunctionRef < void ( FDynamicMesh3 & ) > EditFunc , const FDynamicMeshChangeInfo & ChangeInfo , bool bDeferChangeEvents )
2021-06-10 18:37:57 -04:00
{
2021-06-11 22:42:32 -04:00
if ( ! bDeferChangeEvents )
{
PreMeshChangedEvent . Broadcast ( this , ChangeInfo ) ;
}
2021-06-10 18:37:57 -04:00
EditFunc ( GetMeshRef ( ) ) ;
2021-06-20 18:12:30 -04:00
2022-01-14 15:39:06 -05:00
// Enforce our mesh attribute invariants. This should probably be optional to support compute-only UDynamicMeshes....
if ( Mesh - > HasTriangleGroups ( ) = = false )
2021-06-20 18:12:30 -04:00
{
2022-01-14 15:39:06 -05:00
Mesh - > EnableTriangleGroups ( ) ;
}
if ( Mesh - > HasAttributes ( ) = = false )
{
Mesh - > EnableAttributes ( ) ;
}
if ( Mesh - > Attributes ( ) - > HasMaterialID ( ) = = false )
{
Mesh - > Attributes ( ) - > EnableMaterialID ( ) ;
2021-06-20 18:12:30 -04:00
}
2021-06-11 22:42:32 -04:00
if ( ! bDeferChangeEvents )
{
MeshChangedEvent . Broadcast ( this , ChangeInfo ) ;
MeshModifiedBPEvent . Broadcast ( this ) ;
}
2021-06-10 18:37:57 -04:00
}
TUniquePtr < FDynamicMesh3 > UDynamicMesh : : ExtractMesh ( )
{
TUniquePtr < FDynamicMesh3 > ReturnMesh = MoveTemp ( Mesh ) ;
InitializeNewMesh ( ) ;
return ReturnMesh ;
}
void UDynamicMesh : : SetMeshGenerator ( TObjectPtr < UDynamicMeshGenerator > NewGenerator )
{
MeshGenerator = NewGenerator ;
}
void UDynamicMesh : : ClearMeshGenerator ( )
{
MeshGenerator = nullptr ;
}
void UDynamicMesh : : Regenerate ( )
{
if ( MeshGenerator ! = nullptr & & bEnableMeshGenerator )
{
Reset ( ) ;
}
}
void UDynamicMesh : : PostRealtimeUpdate ( )
{
MeshRealtimeUpdateEvent . Broadcast ( this ) ;
}
void UDynamicMesh : : ApplyChange ( const FMeshVertexChange * Change , bool bRevert )
{
FDynamicMeshChangeInfo ChangeInfo ;
ChangeInfo . Type = EDynamicMeshChangeType : : MeshVertexChange ;
ChangeInfo . VertexChange = Change ;
ChangeInfo . bIsRevertChange = bRevert ;
EditMeshInternal ( [ & ] ( FDynamicMesh3 & EditMesh )
{
bool bHavePositions = Change - > bHaveVertexPositions ;
bool bHaveColors = Change - > bHaveVertexColors & & EditMesh . HasVertexColors ( ) ;
int32 NV = Change - > Vertices . Num ( ) ;
const TArray < FVector3d > & Positions = ( bRevert ) ? Change - > OldPositions : Change - > NewPositions ;
const TArray < FVector3f > & Colors = ( bRevert ) ? Change - > OldColors : Change - > NewColors ;
for ( int32 k = 0 ; k < NV ; + + k )
{
int32 vid = Change - > Vertices [ k ] ;
if ( EditMesh . IsVertex ( vid ) )
{
if ( bHavePositions )
{
EditMesh . SetVertex ( vid , Positions [ k ] ) ;
}
if ( bHaveColors )
{
EditMesh . SetVertexColor ( vid , Colors [ k ] ) ;
}
}
}
if ( Change - > bHaveOverlayNormals & & EditMesh . HasAttributes ( ) & & EditMesh . Attributes ( ) - > PrimaryNormals ( ) )
{
FDynamicMeshNormalOverlay * Overlay = EditMesh . Attributes ( ) - > PrimaryNormals ( ) ;
int32 NumNormals = Change - > Normals . Num ( ) ;
const TArray < FVector3f > & UseNormals = ( bRevert ) ? Change - > OldNormals : Change - > NewNormals ;
for ( int32 k = 0 ; k < NumNormals ; + + k )
{
int32 elemid = Change - > Normals [ k ] ;
if ( Overlay - > IsElement ( elemid ) )
{
Overlay - > SetElement ( elemid , UseNormals [ k ] ) ;
}
}
}
2024-02-15 14:50:54 -05:00
if ( Change - > bHaveOverlayUVs & & EditMesh . HasAttributes ( ) & & EditMesh . Attributes ( ) - > PrimaryUV ( ) )
{
FDynamicMeshUVOverlay * Overlay = EditMesh . Attributes ( ) - > PrimaryUV ( ) ;
int32 NumUVs = Change - > UVs . Num ( ) ;
const TArray < FVector2f > & UseUVs = ( bRevert ) ? Change - > OldUVs : Change - > NewUVs ;
for ( int32 k = 0 ; k < NumUVs ; + + k )
{
int32 elemid = Change - > UVs [ k ] ;
if ( Overlay - > IsElement ( elemid ) )
{
Overlay - > SetElement ( elemid , UseUVs [ k ] ) ;
}
}
}
2021-06-10 18:37:57 -04:00
} , ChangeInfo ) ;
}
void UDynamicMesh : : ApplyChange ( const FMeshChange * Change , bool bRevert )
{
FDynamicMeshChangeInfo ChangeInfo ;
ChangeInfo . Type = EDynamicMeshChangeType : : MeshChange ;
ChangeInfo . MeshChange = Change ;
ChangeInfo . bIsRevertChange = bRevert ;
EditMeshInternal ( [ & ] ( FDynamicMesh3 & EditMesh )
{
Change - > ApplyChangeToMesh ( & EditMesh , bRevert ) ;
} , ChangeInfo ) ;
}
void UDynamicMesh : : ApplyChange ( const FMeshReplacementChange * Change , bool bRevert )
{
FDynamicMeshChangeInfo ChangeInfo ;
ChangeInfo . Type = EDynamicMeshChangeType : : MeshReplacementChange ;
ChangeInfo . ReplaceChange = Change ;
ChangeInfo . bIsRevertChange = bRevert ;
EditMeshInternal ( [ & ] ( FDynamicMesh3 & EditMesh )
{
EditMesh . Copy ( * Change - > GetMesh ( bRevert ) ) ;
} , ChangeInfo ) ;
}
void UDynamicMesh : : Serialize ( FArchive & Ar )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( UDynamicMesh : : Serialize ) ;
Super : : Serialize ( Ar ) ;
// do not serialize mesh for transactions
if ( Ar . IsTransacting ( ) )
{
return ;
}
Ar . UsingCustomVersion ( FUE5MainStreamObjectVersion : : GUID ) ;
if ( Ar . IsLoading ( ) )
{
InitializeNewMesh ( ) ;
}
Ar < < * Mesh ;
}
/**
* This is an internal singleton used to support UDynamicMesh : : ExportCustomProperties ( ) and UDynamicMesh : : ImportCustomProperties ( ) below .
*
*/
namespace UE
{
namespace Local
{
class FDynamicMeshCopyHelper
{
protected :
struct FStashedMesh
{
TSoftObjectPtr < UDynamicMesh > SourceMesh ;
FDateTime Timestamp ;
} ;
static TMap < int32 , FStashedMesh > StashedMeshes ;
static FRandomStream KeyGenerator ;
public :
static void Initialize ( )
{
static bool bInitialized = false ;
if ( ! bInitialized )
{
KeyGenerator = FRandomStream ( ( int32 ) FDateTime : : UtcNow ( ) . ToUnixTimestamp ( ) ) ;
bInitialized = true ;
}
}
static void DiscardExpiredMeshes ( )
{
const int32 ExpiryTimeoutInSeconds = CVarDynamicMeshDupeHelperTimeout . GetValueOnGameThread ( ) ;
TArray < int32 > ToRemove ;
for ( TPair < int32 , FStashedMesh > & Pair : StashedMeshes )
{
if ( ( FDateTime : : Now ( ) - Pair . Value . Timestamp ) . GetTotalSeconds ( ) > ( double ) ExpiryTimeoutInSeconds )
{
ToRemove . Add ( Pair . Key ) ;
}
}
for ( int32 id : ToRemove )
{
StashedMeshes . Remove ( id ) ;
}
}
static int32 StashMeshReference ( UDynamicMesh * SourceMesh )
{
Initialize ( ) ;
int32 MeshKey = FMath : : Abs ( ( int32 ) KeyGenerator . GetUnsignedInt ( ) ) ;
FStashedMesh NewMesh ;
NewMesh . Timestamp = FDateTime : : Now ( ) ;
NewMesh . SourceMesh = SourceMesh ;
StashedMeshes . Add ( MeshKey , MoveTemp ( NewMesh ) ) ;
DiscardExpiredMeshes ( ) ;
return MeshKey ;
}
static TUniquePtr < FDynamicMesh3 > ExtractStashedMesh ( int32 MeshKey )
{
TUniquePtr < FDynamicMesh3 > ReturnMesh ;
FStashedMesh * Found = StashedMeshes . Find ( MeshKey ) ;
if ( Found ! = nullptr & & Found - > SourceMesh . IsValid ( ) )
{
ReturnMesh = MakeUnique < FDynamicMesh3 > ( ) ;
UDynamicMesh * SourceMesh = Found - > SourceMesh . Get ( ) ;
if ( SourceMesh )
{
// should we Cast<UDynamicMesh> here to make sure this is still the same UClass? (due to recyling)
SourceMesh - > ProcessMesh ( [ & ] ( const FDynamicMesh3 & EditMesh )
{
* ReturnMesh = EditMesh ;
} ) ;
}
}
DiscardExpiredMeshes ( ) ;
return ReturnMesh ;
}
} ;
TMap < int32 , FDynamicMeshCopyHelper : : FStashedMesh > FDynamicMeshCopyHelper : : StashedMeshes ;
FRandomStream FDynamicMeshCopyHelper : : KeyGenerator ;
} } // end namespace UE::Local
void UDynamicMesh : : ExportCustomProperties ( FOutputDevice & Out , uint32 Indent )
{
// ignore empty meshes
if ( Mesh - > TriangleCount ( ) = = 0 & & Mesh - > VertexCount ( ) = = 0 )
{
return ;
}
// In the Editor, Copy/Paste of Actors and Components is not based on serialization,
// but rather on T3D structured text records. This is what allows things to be copied
// between Editor sessions, for example. Duplicate functionality in the Editor is implemented
// as Copy and Paste, so for Duplicate to work, any non-UProperty data must be serialized via text.
//
// This poses a problem for large data, such as large meshes. Serializing a mesh with millions of
// triangles as structured text is very expensive. An alternative is to serialize it to binary and
// then use Base64 encoding. This is implemented below, however even that is quite slow.
//
// So, we employ a second strategy (read: hack), of just passing the source UObject pointer
// via text. Instead of directly sending the pointer, we store it in FDynamicMeshCopyHelper, and pass
// an integer key instead. This allows for somewhat better handling, eg the copy helper could for example
// keep the UDynamicMeshes alive (so far this has not been necessary) and the keys are randomized so
// even in the unlikely even that two Editor instances end up with the same keys, it would just
// result in an correct pasted object, rather than accessing garbage pointers.
//
// FDynamicMeshCopyHelper does attempt to discard "old" references, the CVar geometry.DynamicMesh.DupeStashTimeout
// controls the definition of old (currenly 5 minutes). One effect this can have is if one does a
// copy and then a paste after the timeout, the mesh will not be found. This could also be problematic
// for large full-scene copies of hundreds of objects that takes longer than the timeout (resolvable via the CVar)
//
// Obviously this does not work between Editor sessions. So we also optionally do a Base64 binary encoding if
// the mesh triangle count is below a CVar threshold (geometry.DynamicMesh.TextBasedDupeTriThreshold)
// defaulting to 1000. Larger meshes can be supported by increasing the CVar value if need be.
//
// If the mesh is not found in the FDynamicMeshCopyHelper, and is too large to text-copy, then instead
// of leaving an empty mesh, we emit a cube, as empty meshes can be problematic. A warning is also
// printed to the Output Log, pointing the user to the CVars.
//
// Possible todos: UObject Reuse/Recycling
//
Super : : ExportCustomProperties ( Out , Indent ) ;
Out . Logf ( TEXT ( " %sCustomProperties " ) , FCString : : Spc ( Indent ) ) ;
Out . Logf ( TEXT ( " MeshData " ) ) ;
// stash copy to circumvent expensive T3D generation/parsing
int32 StashedKey = UE : : Local : : FDynamicMeshCopyHelper : : StashMeshReference ( this ) ;
Out . Logf ( TEXT ( " MESHKEY=%d " ) , StashedKey ) ;
if ( Mesh - > TriangleCount ( ) < CVarDynamicMeshTextBasedDupeTriangleCountThreshold . GetValueOnGameThread ( ) )
{
// serialize our mesh
TArray < uint8 > MeshWriteBuffer ;
FMemoryWriter MemWriter ( MeshWriteBuffer ) ;
Mesh - > Serialize ( MemWriter ) ;
FString Base64String = FBase64 : : Encode ( MeshWriteBuffer ) ;
//TArray<uint8> MeshReadBuffer;
//ensure(FBase64::Decode(Base64String, MeshReadBuffer)); // test decode
// Base64 encoding uses the '/' character, but T3D interprets '//' as some kind of
// terminator (?). If it occurs then the string passed to ImportCustomProperties() will
// come back as full of nullptrs. So we will swap in '-' here, and swap back to '/' in ImportCustomProperties()
for ( int32 k = 0 ; k < Base64String . Len ( ) ; + + k )
{
if ( Base64String [ k ] = = ' / ' )
{
Base64String [ k ] = ' - ' ;
}
}
Out . Logf ( TEXT ( " MESHDATALEN=%d MESHDATA=%s " ) , Base64String . Len ( ) , * Base64String ) ;
}
Out . Logf ( TEXT ( " \r \n " ) ) ;
}
void UDynamicMesh : : ImportCustomProperties ( const TCHAR * SourceText , FFeedbackContext * Warn )
{
Super : : ImportCustomProperties ( SourceText , Warn ) ;
if ( FParse : : Command ( & SourceText , TEXT ( " MeshData " ) ) )
{
static const TCHAR MeshKeyToken [ ] = TEXT ( " MESHKEY= " ) ;
const TCHAR * FoundMeshKeyStart = FCString : : Strifind ( SourceText , MeshKeyToken ) ;
if ( FoundMeshKeyStart )
{
SourceText = FoundMeshKeyStart + FCString : : Strlen ( MeshKeyToken ) ;
int32 MeshKey = FCString : : Atoi ( SourceText ) ;
TUniquePtr < FDynamicMesh3 > FoundMesh = UE : : Local : : FDynamicMeshCopyHelper : : ExtractStashedMesh ( MeshKey ) ;
if ( FoundMesh . IsValid ( ) & & FoundMesh - > TriangleCount ( ) > 0 )
{
InitializeNewMesh ( ) ;
SetMesh ( MoveTemp ( * FoundMesh ) ) ;
return ;
}
}
static const TCHAR MeshDataLenToken [ ] = TEXT ( " MESHDATALEN= " ) ;
const TCHAR * FoundMeshDataLenStart = FCString : : Strifind ( SourceText , MeshDataLenToken ) ;
if ( FoundMeshDataLenStart )
{
SourceText = FoundMeshDataLenStart + FCString : : Strlen ( MeshDataLenToken ) ;
int32 MeshDataLen = FCString : : Atoi ( SourceText ) ;
static const TCHAR MeshDataToken [ ] = TEXT ( " MESHDATA= " ) ;
const TCHAR * FoundMeshDataStart = FCString : : Strifind ( SourceText , MeshDataToken ) ;
if ( FoundMeshDataStart )
{
SourceText = FoundMeshDataStart + FCString : : Strlen ( MeshDataToken ) ;
2024-06-24 09:25:50 -04:00
FString MeshData = FString : : ConstructFromPtrSize ( SourceText , MeshDataLen ) ;
2021-06-10 18:37:57 -04:00
// fix-up the hack applied to the Base64-encoded string in ExportCustomProperties()
for ( int32 k = 0 ; k < MeshData . Len ( ) ; + + k )
{
if ( MeshData [ k ] = = ' - ' )
{
MeshData [ k ] = ' / ' ;
}
}
TArray < uint8 > MeshReadBuffer ;
bool bDecoded = FBase64 : : Decode ( MeshData , MeshReadBuffer ) ;
if ( bDecoded )
{
FMemoryReader MemReader ( MeshReadBuffer ) ;
FDynamicMesh3 NewMesh ;
NewMesh . Serialize ( MemReader ) ;
InitializeNewMesh ( ) ;
SetMesh ( MoveTemp ( NewMesh ) ) ;
return ;
}
}
}
// if we got here we failed. Rather than produce an empty mesh, we generate a small cube
2024-10-01 19:50:31 -04:00
UE : : Private : : UDynamicMeshLocal : : DisplayCriticalWarningMessage ( LOCTEXT ( " DynamicMeshPasteFailed " , " Dynamic Mesh paste failed! See log for details. " ) ) ;
2021-09-08 21:49:07 -04:00
UE_LOG ( LogGeometry , Warning , TEXT ( " UDynamicMesh text-based property serialization incomplete, generating box as placeholder. Try increasing geometry.DynamicMesh.TextBasedDupeTriThreshold, or geometry.DynamicMesh.DupeStashTimeout. " ) )
2021-06-10 18:37:57 -04:00
FMinimalBoxMeshGenerator BoxGen ;
2021-08-26 09:31:28 -04:00
BoxGen . Box = UE : : Geometry : : FOrientedBox3d ( FVector3d : : Zero ( ) , 50.0 * FVector3d : : One ( ) ) ;
2021-06-10 18:37:57 -04:00
FDynamicMesh3 GenMesh ( & BoxGen . Generate ( ) ) ;
GenMesh . Attributes ( ) - > EnableMaterialID ( ) ;
SetMesh ( MoveTemp ( GenMesh ) ) ;
}
}
//
// Pool support for blueprints
//
static TAutoConsoleVariable < int32 > CVarDynamicMeshPoolMaxPoolSizeThreshold (
TEXT ( " geometry.DynamicMesh.MaxPoolSize " ) ,
1000 ,
TEXT ( " Maximum number of meshes a UDynamicMeshPool will allow to be in the pool before running garbage collection " ) ) ;
UDynamicMesh * UDynamicMeshPool : : RequestMesh ( )
{
if ( CachedMeshes . Num ( ) > 0 )
{
2024-01-19 16:41:35 -05:00
return CachedMeshes . Pop ( EAllowShrinking : : No ) ;
2021-06-10 18:37:57 -04:00
}
UDynamicMesh * NewMesh = NewObject < UDynamicMesh > ( ) ;
// If we have allocated more meshes than our safety threshold, drop our holds on the existing meshes.
// This will allow them to be garbage-collected (eventually)
if ( ! ensure ( AllCreatedMeshes . Num ( ) < CVarDynamicMeshPoolMaxPoolSizeThreshold . GetValueOnGameThread ( ) ) )
{
2021-09-08 21:49:07 -04:00
UE_LOG ( LogGeometry , Warning , TEXT ( " UDynamicMeshPool Threshold of %d Allocated Meshes exceeded! Releasing references to all current meshes and forcing a garbage collection. " ) , CVarDynamicMeshPoolMaxPoolSizeThreshold . GetValueOnGameThread ( ) ) ;
2021-06-10 18:37:57 -04:00
AllCreatedMeshes . Reset ( ) ;
GEngine - > ForceGarbageCollection ( true ) ;
}
AllCreatedMeshes . Add ( NewMesh ) ;
return NewMesh ;
}
void UDynamicMeshPool : : ReturnMesh ( UDynamicMesh * Mesh )
{
2021-09-08 21:49:07 -04:00
if ( ensure ( Mesh ) & & ensure ( AllCreatedMeshes . Contains ( Mesh ) ) )
2021-06-10 18:37:57 -04:00
{
Mesh - > Reset ( ) ;
if ( ensure ( CachedMeshes . Contains ( Mesh ) = = false ) )
{
CachedMeshes . Add ( Mesh ) ;
}
}
}
void UDynamicMeshPool : : ReturnAllMeshes ( )
{
CachedMeshes = AllCreatedMeshes ;
for ( UDynamicMesh * Mesh : CachedMeshes )
{
if ( Mesh )
{
Mesh - > Reset ( ) ;
}
}
// TODO: this may be vestigial code, unclear how it could be hit
int32 Removed = CachedMeshes . RemoveAll ( [ ] ( UDynamicMesh * Mesh ) { return Mesh = = nullptr ; } ) ;
ensure ( Removed = = 0 ) ;
}
void UDynamicMeshPool : : FreeAllMeshes ( )
{
CachedMeshes . Reset ( ) ;
AllCreatedMeshes . Reset ( ) ;
2022-09-24 13:57:58 -04:00
}
2024-10-01 19:50:31 -04:00
# undef LOCTEXT_NAMESPACE