2019-12-26 15:32:37 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-10-04 13:11:45 -04:00
# include "DatasmithMeshExporter.h"
2022-03-23 16:31:37 -04:00
# include "DatasmithCore.h"
2020-09-24 00:43:27 -04:00
# include "DatasmithExporterManager.h"
2019-10-04 13:11:45 -04:00
# include "DatasmithMesh.h"
# include "DatasmithMeshUObject.h"
2022-03-25 14:12:48 -04:00
# include "DatasmithMeshSerialization.h"
2019-10-04 13:11:45 -04:00
# include "DatasmithSceneFactory.h"
# include "DatasmithUtils.h"
2020-02-17 13:28:31 -05:00
# include "Containers/LockFreeList.h"
2019-10-04 13:11:45 -04:00
# include "HAL/FileManager.h"
2020-03-18 11:01:39 -04:00
# include "Misc/Guid.h"
2019-10-04 13:11:45 -04:00
# include "Misc/Paths.h"
# include "Serialization/MemoryWriter.h"
2021-03-05 19:27:14 -04:00
/**
2022-05-20 13:21:20 -04:00
* Number of datasmith meshes pending deletion for which the Garbage Collection will be triggered .
2021-03-05 19:27:14 -04:00
*/
constexpr int32 DSMeshGarbageCollectionThreshold { 2000 } ;
2022-03-23 16:31:37 -04:00
struct FDatasmithMeshExporterOptions
{
FDatasmithMeshExporterOptions ( const FString & InFullPath , FDatasmithMesh & InMesh , EDSExportLightmapUV InLightmapUV , FDatasmithMesh * InCollisionMesh = nullptr )
: MeshFullPath ( InFullPath )
, Mesh ( InMesh )
, LightmapUV ( InLightmapUV )
, CollisionMesh ( InCollisionMesh )
{ }
FString MeshFullPath ;
FDatasmithMesh & Mesh ;
EDSExportLightmapUV LightmapUV ;
FDatasmithMesh * CollisionMesh ;
} ;
2020-02-17 13:28:31 -05:00
/**
2022-05-20 13:21:20 -04:00
* Implementation class of the DatasmithMeshExporter
* We use a lockfree UDatasmithMesh pool to avoid creating new UObject when exporting and reduces our memory footprint .
2020-02-17 13:28:31 -05:00
*/
2022-03-25 14:12:48 -04:00
class FDatasmithMeshExporterLegacyImpl
2019-10-04 13:11:45 -04:00
{
2020-02-17 13:28:31 -05:00
public :
2020-09-24 00:43:27 -04:00
2022-03-25 14:12:48 -04:00
FDatasmithMeshExporterLegacyImpl ( ) :
2020-03-18 11:01:39 -04:00
UniqueID ( FGuid : : NewGuid ( ) )
{ }
2022-03-25 14:12:48 -04:00
~ FDatasmithMeshExporterLegacyImpl ( )
2020-03-18 11:01:39 -04:00
{
ClearUDatasmithMeshPool ( ) ;
}
2019-10-04 13:11:45 -04:00
2020-02-17 13:28:31 -05:00
/**
2020-03-18 11:01:39 -04:00
* Generate and fill a UDatasmithMesh from a FDatasmithMesh .
2020-11-24 18:42:39 -04:00
* Note : The returned TSharedPtr uses a special destructor to return the UDatasmithMesh to a pool instead of destroying it ( which wouldn ' t work anyways ) .
2020-02-17 13:28:31 -05:00
*/
2020-03-18 11:01:39 -04:00
TSharedPtr < UDatasmithMesh > GeneratePooledUDatasmithMeshFromFDatasmithMesh ( const FDatasmithMesh & Mesh , bool bIsCollisionMesh ) ;
2020-02-17 13:28:31 -05:00
2020-09-24 00:43:27 -04:00
bool DoExport ( TSharedPtr < IDatasmithMeshElement > & MeshElement , const FDatasmithMeshExporterOptions & ExportOptions ) ;
void PreExport ( const FDatasmithMeshExporterOptions & ExporterOptions ) ;
bool ExportMeshes ( const FDatasmithMeshExporterOptions & ExporterOptions , FMD5Hash & OutHash ) ;
void PostExport ( const FDatasmithMesh & DatasmithMesh , TSharedPtr < IDatasmithMeshElement > & MeshElement ) ;
2020-02-17 13:28:31 -05:00
FString LastError ;
private :
2020-03-18 11:01:39 -04:00
/**
* This function allows reusing an instanced UDatasmithMesh . Reusing the same object will avoid creating new garbage in memory .
*/
void FillUDatasmithMeshFromFDatasmithMesh ( TSharedPtr < UDatasmithMesh > & UMesh , const FDatasmithMesh & Mesh , bool bValidateRawMesh ) ;
TSharedPtr < UDatasmithMesh > GetPooledUDatasmithMesh ( bool bIsCollisionMesh ) ;
void ReturnUDatasmithMeshToPool ( UDatasmithMesh * & UMesh ) ;
void ClearUDatasmithMeshPool ( ) ;
/**
* A pool of UDatasmithMesh that we use to avoid creating new UObject , this greatly reduce the garbage created when one instance of FDatasmithMeshExporter is used to export multiple Meshes .
*/
2020-02-17 13:28:31 -05:00
TLockFreePointerListLIFO < UDatasmithMesh > DatasmithMeshUObjectPool ;
static TAtomic < int32 > NumberOfUMeshPendingGC ;
2020-03-18 11:01:39 -04:00
/**
* This exporter ' s UniqueID . Used to make sure pooled meshes are not using the same names across different threads .
*/
FGuid UniqueID ;
/**
* Custom deleter used by the TSharedPtr of a pooled UDatasmithMesh to return it to the pool instead of deleting them .
*/
struct FPooledUDatasmithMeshDeleter
{
2022-03-25 14:12:48 -04:00
FPooledUDatasmithMeshDeleter ( FDatasmithMeshExporterLegacyImpl * InExporterPtr )
2020-03-18 11:01:39 -04:00
: ExporterPtr ( InExporterPtr )
{ }
FORCEINLINE void operator ( ) ( UDatasmithMesh * Object ) const
{
check ( Object ! = nullptr & & ExporterPtr ! = nullptr ) ;
ExporterPtr - > ReturnUDatasmithMeshToPool ( Object ) ;
}
2022-03-25 14:12:48 -04:00
FDatasmithMeshExporterLegacyImpl * ExporterPtr ;
2020-03-18 11:01:39 -04:00
} ;
2020-02-17 13:28:31 -05:00
} ;
2022-03-25 14:12:48 -04:00
TAtomic < int32 > FDatasmithMeshExporterLegacyImpl : : NumberOfUMeshPendingGC ( 0 ) ;
2020-02-17 13:28:31 -05:00
2022-03-23 16:31:37 -04:00
2022-03-25 14:12:48 -04:00
class FDatasmithMeshExporterImpl
2020-02-17 13:28:31 -05:00
{
2022-03-23 16:31:37 -04:00
public :
bool DoExport ( TSharedPtr < IDatasmithMeshElement > & MeshElement , const FDatasmithMeshExporterOptions & ExportOptions ) ;
FString LastError ;
private :
bool WriteMeshFile ( const FDatasmithMeshExporterOptions & ExporterOptions , FMD5Hash & OutHash ) ;
} ;
2022-03-25 14:12:48 -04:00
bool FDatasmithMeshExporterImpl : : DoExport ( TSharedPtr < IDatasmithMeshElement > & MeshElement , const FDatasmithMeshExporterOptions & ExportOptions )
2022-03-23 16:31:37 -04:00
{
FDatasmithMesh & Mesh = ExportOptions . Mesh ;
// If the mesh doesn't have a name, use the filename as its name
if ( FCString : : Strlen ( Mesh . GetName ( ) ) = = 0 )
{
Mesh . SetName ( * FPaths : : GetBaseFilename ( ExportOptions . MeshFullPath ) ) ;
}
// make sure we have at least one UV channels on each source models
2022-03-25 14:12:48 -04:00
FDatasmithMeshUtils : : CreateDefaultUVsWithLOD ( Mesh ) ;
2022-03-23 16:31:37 -04:00
FMD5Hash Hash ;
if ( WriteMeshFile ( ExportOptions , Hash ) )
{
// If no existing MeshElement provided, create one.
if ( ! MeshElement )
{
MeshElement = FDatasmithSceneFactory : : CreateMesh ( Mesh . GetName ( ) ) ;
}
MeshElement - > SetFile ( * ExportOptions . MeshFullPath ) ;
MeshElement - > SetFileHash ( Hash ) ;
2022-03-28 16:55:59 -04:00
FBox3f Extents = Mesh . GetExtents ( ) ;
2022-03-23 16:31:37 -04:00
float Width = Extents . Max [ 0 ] - Extents . Min [ 0 ] ;
float Height = Extents . Max [ 2 ] - Extents . Min [ 2 ] ;
float Depth = Extents . Max [ 1 ] - Extents . Min [ 1 ] ;
MeshElement - > SetDimensions ( Mesh . ComputeArea ( ) , Width , Height , Depth ) ;
MeshElement - > SetLightmapSourceUV ( Mesh . GetLightmapSourceUVChannel ( ) ) ;
return true ;
}
else
{
UE_LOG ( LogDatasmith , Warning , TEXT ( " Cannot export mesh %s " ) , Mesh . GetName ( ) ) ;
}
return false ;
2020-02-17 13:28:31 -05:00
}
2019-10-04 13:11:45 -04:00
2022-03-25 14:12:48 -04:00
bool FDatasmithMeshExporterLegacyImpl : : ExportMeshes ( const FDatasmithMeshExporterOptions & ExporterOptions , FMD5Hash & OutHash )
2020-09-24 00:43:27 -04:00
{
2020-03-18 11:01:39 -04:00
TArray < TSharedPtr < UDatasmithMesh > , TInlineAllocator < 2 > > MeshesToExport ;
2019-10-04 13:11:45 -04:00
2020-02-17 13:28:31 -05:00
// Static mesh, we keep a static UDatasmithMesh alive as a utility object and re-use it for every export instead of creating a new one every time. This avoid creating garbage in memory.
2020-03-18 11:01:39 -04:00
bool bIsCollisionMesh = false ;
2020-09-24 00:43:27 -04:00
MeshesToExport . Add ( GeneratePooledUDatasmithMeshFromFDatasmithMesh ( ExporterOptions . Mesh , bIsCollisionMesh ) ) ;
2019-10-04 13:11:45 -04:00
// Collision mesh
2020-09-24 00:43:27 -04:00
if ( ExporterOptions . CollisionMesh )
2019-10-04 13:11:45 -04:00
{
2020-03-18 11:01:39 -04:00
bIsCollisionMesh = true ;
2020-09-24 00:43:27 -04:00
MeshesToExport . Add ( GeneratePooledUDatasmithMeshFromFDatasmithMesh ( * ExporterOptions . CollisionMesh , bIsCollisionMesh ) ) ;
2019-10-04 13:11:45 -04:00
}
2020-09-24 00:43:27 -04:00
TUniquePtr < FArchive > Archive ( IFileManager : : Get ( ) . CreateFileWriter ( * ExporterOptions . MeshFullPath ) ) ;
2019-10-04 13:11:45 -04:00
if ( ! Archive . IsValid ( ) )
{
2020-09-24 00:43:27 -04:00
LastError = FString : : Printf ( TEXT ( " Failed writing to file %s " ) , * ExporterOptions . MeshFullPath ) ;
2019-10-04 13:11:45 -04:00
2020-09-24 00:43:27 -04:00
return false ;
2019-10-04 13:11:45 -04:00
}
int32 NumMeshes = MeshesToExport . Num ( ) ;
* Archive < < NumMeshes ;
FMD5 MD5 ;
2020-03-18 11:01:39 -04:00
for ( TSharedPtr < UDatasmithMesh > & MeshToExport : MeshesToExport )
2019-10-04 13:11:45 -04:00
{
TArray < uint8 > Bytes ;
FMemoryWriter MemoryWriter ( Bytes , true ) ;
MemoryWriter . ArIgnoreClassRef = false ;
MemoryWriter . ArIgnoreArchetypeRef = false ;
MemoryWriter . ArNoDelta = false ;
2020-09-24 00:43:27 -04:00
MemoryWriter . SetWantBinaryPropertySerialization ( true ) ;
2019-10-04 13:11:45 -04:00
MeshToExport - > Serialize ( MemoryWriter ) ;
// Calculate the Hash of all the mesh to export
2020-09-24 00:43:27 -04:00
for ( FDatasmithMeshSourceModel & Model : MeshToExport - > SourceModels )
2019-10-04 13:11:45 -04:00
{
uint8 * Buffer = ( uint8 * ) Model . RawMeshBulkData . GetBulkData ( ) . LockReadOnly ( ) ;
2020-09-24 00:43:27 -04:00
MD5 . Update ( Buffer , Model . RawMeshBulkData . GetBulkData ( ) . GetBulkDataSize ( ) ) ;
2019-10-04 13:11:45 -04:00
Model . RawMeshBulkData . GetBulkData ( ) . Unlock ( ) ;
}
* Archive < < Bytes ;
}
2020-09-24 00:43:27 -04:00
OutHash . Set ( MD5 ) ;
return true ;
}
2019-10-04 13:11:45 -04:00
2022-03-23 16:31:37 -04:00
2022-06-02 12:07:51 -04:00
FDatasmithMeshExporter : : FDatasmithMeshExporter ( bool bAllowOldSerialization )
2022-03-25 14:12:48 -04:00
: LegacyImpl ( MakeUnique < FDatasmithMeshExporterLegacyImpl > ( ) )
, Impl ( MakeUnique < FDatasmithMeshExporterImpl > ( ) )
2022-06-02 12:07:51 -04:00
, bAllowOldSerialization ( bAllowOldSerialization )
2022-03-23 16:31:37 -04:00
{ }
FDatasmithMeshExporter : : ~ FDatasmithMeshExporter ( ) = default ;
FString GetMeshFilePath ( const TCHAR * Filepath , const TCHAR * Filename )
{
FString NormalizedFilepath = Filepath ;
FPaths : : NormalizeDirectoryName ( NormalizedFilepath ) ;
FString NormalizedFilename = Filename ;
FPaths : : NormalizeFilename ( NormalizedFilename ) ;
return FPaths : : Combine ( * NormalizedFilepath , FPaths : : SetExtension ( NormalizedFilename , UDatasmithMesh : : GetFileExtension ( ) ) ) ;
}
2022-03-25 14:12:48 -04:00
bool FDatasmithMeshExporterImpl : : WriteMeshFile ( const FDatasmithMeshExporterOptions & ExporterOptions , FMD5Hash & OutHash )
2022-03-23 16:31:37 -04:00
{
FDatasmithPackedMeshes Pack ;
auto PackMeshModels = [ & ] ( FDatasmithMesh & Mesh , bool bIsCollisionMesh ) - > FDatasmithMeshModels
{
FDatasmithMeshModels Models ;
Models . bIsCollisionMesh = bIsCollisionMesh ;
Models . MeshName = Mesh . GetName ( ) ;
Models . SourceModels . Reserve ( Mesh . GetLODsCount ( ) + 1 ) ;
FMeshDescription & BaseMeshDescription = Models . SourceModels . AddDefaulted_GetRef ( ) ;
FDatasmithMeshUtils : : ToMeshDescription ( Mesh , BaseMeshDescription ) ;
for ( int32 LodIndex = 0 ; LodIndex < Mesh . GetLODsCount ( ) ; + + LodIndex )
{
if ( const FDatasmithMesh * LodMesh = Mesh . GetLOD ( LodIndex ) )
{
FMeshDescription & LodMeshDescription = Models . SourceModels . AddDefaulted_GetRef ( ) ;
FDatasmithMeshUtils : : ToMeshDescription ( * LodMesh , LodMeshDescription ) ;
}
}
return Models ;
} ;
2022-05-23 08:40:15 -04:00
Pack . Meshes . Add ( PackMeshModels ( ExporterOptions . Mesh , false ) ) ;
2022-03-23 16:31:37 -04:00
if ( ExporterOptions . CollisionMesh )
{
2022-05-23 08:40:15 -04:00
Pack . Meshes . Add ( PackMeshModels ( * ExporterOptions . CollisionMesh , true ) ) ;
2022-03-23 16:31:37 -04:00
}
TUniquePtr < FArchive > Archive ( IFileManager : : Get ( ) . CreateFileWriter ( * ExporterOptions . MeshFullPath ) ) ;
if ( ! Archive . IsValid ( ) )
{
LastError = FString : : Printf ( TEXT ( " Failed writing to file %s " ) , * ExporterOptions . MeshFullPath ) ;
return false ;
}
int32 LegacyMeshCount = 0 ;
* Archive < < LegacyMeshCount ; // the legacy importer expect a mesh count on the first bytes. Just in case a new file would end up parsed by the legacy code...
2022-04-01 11:50:17 -04:00
OutHash = Pack . Serialize ( * Archive ) ;
2022-03-23 16:31:37 -04:00
return ! Archive - > IsError ( ) ;
}
2022-04-01 11:50:17 -04:00
TSharedPtr < IDatasmithMeshElement > FDatasmithMeshExporter : : ExportToUObject ( const TCHAR * Filepath , const TCHAR * Filename , FDatasmithMesh & Mesh , FDatasmithMesh * CollisionMesh , EDSExportLightmapUV LightmapUV , bool NewFormat )
2020-09-24 00:43:27 -04:00
{
FString FullPath ( GetMeshFilePath ( Filepath , Filename ) ) ;
2019-10-04 13:11:45 -04:00
2020-09-24 00:43:27 -04:00
TSharedPtr < IDatasmithMeshElement > ExportedMeshElement ;
2022-03-23 16:31:37 -04:00
FDatasmithMeshExporterOptions ExportOptions ( MoveTemp ( FullPath ) , Mesh , LightmapUV , CollisionMesh ) ;
if ( NewFormat )
{
2022-03-25 14:12:48 -04:00
Impl - > DoExport ( ExportedMeshElement , ExportOptions ) ;
2022-03-23 16:31:37 -04:00
}
else
{
2022-06-02 12:07:51 -04:00
ensureMsgf ( bAllowOldSerialization , TEXT ( " Old serialization used for mesh %s. It is no longer supported. " ) , * FullPath ) ;
2022-03-25 14:12:48 -04:00
LegacyImpl - > DoExport ( ExportedMeshElement , ExportOptions ) ;
2022-03-23 16:31:37 -04:00
}
2020-11-24 18:42:39 -04:00
2020-09-24 00:43:27 -04:00
return ExportedMeshElement ;
}
2022-04-01 11:50:17 -04:00
bool FDatasmithMeshExporter : : ExportToUObject ( TSharedPtr < IDatasmithMeshElement > & MeshElement , const TCHAR * Filepath , FDatasmithMesh & Mesh , FDatasmithMesh * CollisionMesh , EDSExportLightmapUV LightmapUV , bool NewFormat )
2020-09-24 00:43:27 -04:00
{
FString FullPath ( GetMeshFilePath ( Filepath , MeshElement - > GetName ( ) ) ) ;
2022-03-23 16:31:37 -04:00
FDatasmithMeshExporterOptions ExportOptions ( FullPath , Mesh , LightmapUV , CollisionMesh ) ;
2020-11-24 18:42:39 -04:00
2022-04-01 11:50:17 -04:00
if ( NewFormat )
{
return Impl - > DoExport ( MeshElement , ExportOptions ) ;
}
else
{
2022-06-02 12:07:51 -04:00
ensureMsgf ( bAllowOldSerialization , TEXT ( " Old serialization used for mesh %s. It is no longer supported. " ) , MeshElement - > GetName ( ) ) ;
2022-04-01 11:50:17 -04:00
return LegacyImpl - > DoExport ( MeshElement , ExportOptions ) ;
}
2019-10-04 13:11:45 -04:00
}
2022-03-23 16:31:37 -04:00
2020-02-17 13:28:31 -05:00
FString FDatasmithMeshExporter : : GetLastError ( ) const
{
2022-03-25 14:12:48 -04:00
return LegacyImpl - > LastError . IsEmpty ( ) ? Impl - > LastError : LegacyImpl - > LastError ;
2020-02-17 13:28:31 -05:00
}
2022-03-25 14:12:48 -04:00
bool FDatasmithMeshExporterLegacyImpl : : DoExport ( TSharedPtr < IDatasmithMeshElement > & MeshElement , const FDatasmithMeshExporterOptions & ExportOptions )
2019-10-04 13:11:45 -04:00
{
2020-09-24 00:43:27 -04:00
FMD5Hash Hash ;
PreExport ( ExportOptions ) ;
if ( ExportMeshes ( ExportOptions , Hash ) )
{
// If no existing MeshElement provided, create one.
if ( ! MeshElement )
{
FString BaseFileName = FPaths : : GetBaseFilename ( ExportOptions . MeshFullPath ) ;
MeshElement = FDatasmithSceneFactory : : CreateMesh ( * BaseFileName ) ;
}
MeshElement - > SetFile ( * ExportOptions . MeshFullPath ) ;
MeshElement - > SetFileHash ( Hash ) ;
PostExport ( ExportOptions . Mesh , MeshElement ) ;
return true ;
}
return false ;
}
2022-03-25 14:12:48 -04:00
void FDatasmithMeshExporterLegacyImpl : : PreExport ( const FDatasmithMeshExporterOptions & ExporterOptions )
2020-09-24 00:43:27 -04:00
{
FDatasmithMesh & Mesh = ExporterOptions . Mesh ;
2019-10-04 13:11:45 -04:00
// If the mesh doesn't have a name, use the filename as its name
if ( FCString : : Strlen ( Mesh . GetName ( ) ) = = 0 )
{
2020-09-24 00:43:27 -04:00
Mesh . SetName ( * FPaths : : GetBaseFilename ( ExporterOptions . MeshFullPath ) ) ;
2019-10-04 13:11:45 -04:00
}
2022-03-25 14:12:48 -04:00
FDatasmithMeshUtils : : CreateDefaultUVsWithLOD ( Mesh ) ;
2019-10-04 13:11:45 -04:00
}
2022-03-25 14:12:48 -04:00
void FDatasmithMeshExporterLegacyImpl : : PostExport ( const FDatasmithMesh & DatasmithMesh , TSharedPtr < IDatasmithMeshElement > & MeshElement )
2019-10-04 13:11:45 -04:00
{
2022-03-28 16:55:59 -04:00
FBox3f Extents = DatasmithMesh . GetExtents ( ) ;
2019-10-04 13:11:45 -04:00
float Width = Extents . Max [ 0 ] - Extents . Min [ 0 ] ;
float Height = Extents . Max [ 2 ] - Extents . Min [ 2 ] ;
float Depth = Extents . Max [ 1 ] - Extents . Min [ 1 ] ;
MeshElement - > SetDimensions ( DatasmithMesh . ComputeArea ( ) , Width , Height , Depth ) ;
MeshElement - > SetLightmapSourceUV ( DatasmithMesh . GetLightmapSourceUVChannel ( ) ) ;
}
2022-03-25 14:12:48 -04:00
TSharedPtr < UDatasmithMesh > FDatasmithMeshExporterLegacyImpl : : GeneratePooledUDatasmithMeshFromFDatasmithMesh ( const FDatasmithMesh & Mesh , bool bIsCollisionMesh )
2020-02-17 13:28:31 -05:00
{
2020-03-18 11:01:39 -04:00
TSharedPtr < UDatasmithMesh > PooledMesh = GetPooledUDatasmithMesh ( bIsCollisionMesh ) ;
FillUDatasmithMeshFromFDatasmithMesh ( PooledMesh , Mesh , ! bIsCollisionMesh ) ;
PooledMesh - > bIsCollisionMesh = bIsCollisionMesh ;
return PooledMesh ;
}
/**
* This function allows reusing an instanced UDatasmithMesh . Reusing the same object will avoid creating new garbage in memory .
*/
2022-03-25 14:12:48 -04:00
void FDatasmithMeshExporterLegacyImpl : : FillUDatasmithMeshFromFDatasmithMesh ( TSharedPtr < UDatasmithMesh > & UMesh , const FDatasmithMesh & Mesh , bool bValidateRawMesh )
2020-03-18 11:01:39 -04:00
{
UMesh - > MeshName = Mesh . GetName ( ) ;
FRawMesh RawMesh ;
FDatasmithMeshUtils : : ToRawMesh ( Mesh , RawMesh , bValidateRawMesh ) ;
FDatasmithMeshSourceModel BaseModel ;
BaseModel . RawMeshBulkData . SaveRawMesh ( RawMesh ) ;
UMesh - > SourceModels . Add ( BaseModel ) ;
for ( int32 LODIndex = 0 ; LODIndex < Mesh . GetLODsCount ( ) ; + + LODIndex )
2020-02-17 13:28:31 -05:00
{
2021-02-03 14:57:28 -04:00
if ( const FDatasmithMesh * LODMesh = Mesh . GetLOD ( LODIndex ) )
{
FDatasmithMeshUtils : : ToRawMesh ( * LODMesh , RawMesh , bValidateRawMesh ) ;
2020-03-18 11:01:39 -04:00
2021-02-03 14:57:28 -04:00
FRawMeshBulkData LODRawMeshBulkData ;
LODRawMeshBulkData . SaveRawMesh ( RawMesh ) ;
2020-03-18 11:01:39 -04:00
2021-02-03 14:57:28 -04:00
FDatasmithMeshSourceModel LODModel ;
LODModel . RawMeshBulkData = LODRawMeshBulkData ;
2020-03-18 11:01:39 -04:00
2021-02-03 14:57:28 -04:00
UMesh - > SourceModels . Add ( LODModel ) ;
}
2020-03-18 11:01:39 -04:00
}
}
2022-03-25 14:12:48 -04:00
TSharedPtr < UDatasmithMesh > FDatasmithMeshExporterLegacyImpl : : GetPooledUDatasmithMesh ( bool bIsCollisionMesh )
2020-03-18 11:01:39 -04:00
{
const FString GuidString = UniqueID . ToString ( ) ;
const FString CollisionString = bIsCollisionMesh ? TEXT ( " _Collision " ) : TEXT ( " " ) ;
const FString PooledMeshName = FString : : Printf ( TEXT ( " DatasmithExporter_%s_TransientPooledUDatasmithMesh%s " ) , * GuidString , * CollisionString ) ;
if ( UDatasmithMesh * PooledMesh = DatasmithMeshUObjectPool . Pop ( ) )
{
PooledMesh - > Rename ( * PooledMeshName ) ;
return TSharedPtr < UDatasmithMesh > ( PooledMesh , FPooledUDatasmithMeshDeleter ( this ) ) ;
2020-02-17 13:28:31 -05:00
}
2020-11-24 18:42:39 -04:00
{
2021-02-03 14:57:28 -04:00
// Can't create new objects while the GC is running.
FGCScopeGuard GCGuard ;
2020-11-24 18:42:39 -04:00
return TSharedPtr < UDatasmithMesh > ( NewObject < UDatasmithMesh > ( ( UObject * ) GetTransientPackage ( ) , * PooledMeshName , RF_Transient | RF_MarkAsRootSet ) , FPooledUDatasmithMeshDeleter ( this ) ) ;
}
2020-02-17 13:28:31 -05:00
}
2022-03-25 14:12:48 -04:00
void FDatasmithMeshExporterLegacyImpl : : ReturnUDatasmithMeshToPool ( UDatasmithMesh * & UMesh )
2020-02-17 13:28:31 -05:00
{
//Clear the UDatasmithMesh.
UMesh - > SourceModels . Empty ( ) ;
//Put it back into the pool
DatasmithMeshUObjectPool . Push ( UMesh ) ;
//Null the reference to make sure we don't use the object returned to the pool.
UMesh = nullptr ;
}
2022-03-25 14:12:48 -04:00
void FDatasmithMeshExporterLegacyImpl : : ClearUDatasmithMeshPool ( )
2020-02-17 13:28:31 -05:00
{
TArray < UDatasmithMesh * > PooledMeshes ;
DatasmithMeshUObjectPool . PopAll ( PooledMeshes ) ;
//Keep track of the number of garbage UObject generated by clearing the cache so that we can trigger the GC after a while.
NumberOfUMeshPendingGC + = PooledMeshes . Num ( ) ;
2021-03-05 19:27:14 -04:00
2021-03-18 15:20:03 -04:00
auto ClearUObjectFlags = [ PooledMeshes ( MoveTemp ( PooledMeshes ) ) ] ( )
2021-03-05 19:27:14 -04:00
{
for ( UDatasmithMesh * UMesh : PooledMeshes )
{
UMesh - > RemoveFromRoot ( ) ;
2021-11-18 14:37:34 -05:00
UMesh - > MarkAsGarbage ( ) ;
2021-03-05 19:27:14 -04:00
//This object won't be reused, we must make sure there is no Async flag so that the GC knows it can safely collect it.
UMesh - > ClearInternalFlags ( EInternalObjectFlags : : Async ) ;
}
} ;
# if IS_PROGRAM
2021-03-18 15:20:03 -04:00
if ( FDatasmithExporterManager : : WasInitializedWithGameThread ( ) )
{
FSimpleDelegate Command ;
Command . BindLambda ( MoveTemp ( ClearUObjectFlags ) ) ;
2021-03-05 19:27:14 -04:00
2021-03-18 15:20:03 -04:00
// No need to synchronize to the game thread for this, we can lazily update the object flags by pushing a command, that way the flags will be updated before the next GC without blocking the current thread.
const bool bWakeUpGameThread = false ;
FDatasmithExporterManager : : PushCommandIntoGameThread ( MoveTemp ( Command ) , bWakeUpGameThread ) ;
}
else
2021-03-05 19:27:14 -04:00
# endif
2021-03-18 15:20:03 -04:00
if ( IsInGameThread ( ) )
{
ClearUObjectFlags ( ) ;
}
else
{
// Can't modify the GC flags while it's running.
FGCScopeGuard GCGuard ;
ClearUObjectFlags ( ) ;
}
2021-03-05 19:27:14 -04:00
//Even if our UObjects are basically empty at this point and don't have a big memory footprint, the engine will assert when reaching around 131k UObjects.
//So we need to call the GC before that point.
if ( NumberOfUMeshPendingGC > DSMeshGarbageCollectionThreshold )
2020-02-17 13:28:31 -05:00
{
2020-11-24 18:42:39 -04:00
if ( FDatasmithExporterManager : : RunGarbageCollection ( ) )
{
NumberOfUMeshPendingGC = 0 ;
}
2020-02-17 13:28:31 -05:00
}
}