2022-10-16 02:28:34 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "SparseVolumeTextureFactory.h"
# include "SparseVolumeTexture/SparseVolumeTexture.h"
2023-03-01 16:56:41 -05:00
# include "SparseVolumeTexture/SparseVolumeTextureData.h"
2022-10-16 02:28:34 -04:00
# if WITH_EDITOR
# include "SparseVolumeTextureOpenVDB.h"
2022-11-25 09:18:31 -05:00
# include "SparseVolumeTextureOpenVDBUtility.h"
2023-01-31 01:11:48 -05:00
# include "OpenVDBImportOptions.h"
2022-10-16 02:28:34 -04:00
# include "Misc/Paths.h"
# include "Misc/ScopedSlowTask.h"
2022-11-25 09:18:31 -05:00
# include "Misc/FileHelper.h"
2023-02-16 10:42:36 -05:00
# include "Async/Async.h"
2023-02-22 09:39:24 -05:00
# include "Async/ParallelFor.h"
2022-10-16 02:28:34 -04:00
2024-01-26 04:47:20 -05:00
# include "AssetImportTask.h"
2022-10-16 02:28:34 -04:00
# include "Editor.h"
2023-06-15 09:39:59 -04:00
# include "EditorFramework/AssetImportData.h"
2023-09-11 11:09:14 -04:00
# include "ObjectTools.h"
2022-10-16 02:28:34 -04:00
2022-11-30 15:06:09 -05:00
# include "OpenVDBImportWindow.h"
# include "HAL/PlatformApplicationMisc.h"
2023-02-16 10:42:36 -05:00
# include "HAL/Event.h"
# include "HAL/PlatformProcess.h"
2022-11-30 15:06:09 -05:00
# include "Interfaces/IMainFrameModule.h"
2023-02-16 10:42:36 -05:00
# include <atomic>
# include <mutex>
2022-10-16 02:28:34 -04:00
# define LOCTEXT_NAMESPACE "USparseVolumeTextureFactory"
DEFINE_LOG_CATEGORY_STATIC ( LogSparseVolumeTextureFactory , Log , All ) ;
2023-01-31 01:11:48 -05:00
static void ComputeDefaultOpenVDBGridAssignment ( const TArray < TSharedPtr < FOpenVDBGridComponentInfo > > & GridComponentInfo , int32 NumFiles , FOpenVDBImportOptions * ImportOptions )
2023-01-30 11:48:00 -05:00
{
2023-01-31 01:11:48 -05:00
for ( FOpenVDBSparseVolumeAttributesDesc & AttributesDesc : ImportOptions - > Attributes )
2023-01-30 11:48:00 -05:00
{
2023-01-31 01:11:48 -05:00
for ( FOpenVDBSparseVolumeComponentMapping & Mapping : AttributesDesc . Mappings )
{
Mapping . SourceGridIndex = INDEX_NONE ;
Mapping . SourceComponentIndex = INDEX_NONE ;
}
2023-07-31 11:23:48 -04:00
AttributesDesc . Format = ESparseVolumeAttributesFormat : : Float16 ;
2023-01-30 11:48:00 -05:00
}
// Assign the components of the input grids to the components of the output SVT.
2023-07-31 11:23:48 -04:00
const TSharedPtr < FOpenVDBGridComponentInfo > * DensityComponentInfoPtr = GridComponentInfo . FindByPredicate ( [ ] ( const TSharedPtr < FOpenVDBGridComponentInfo > & GridComponent ) { return GridComponent - > Name = = TEXT ( " density " ) ; } ) ;
const int32 NumNonDensityComponentInfos = GridComponentInfo . Num ( ) - 1 - ( DensityComponentInfoPtr ? 1 : 0 ) ; // -1 because there is always a <None> element in the list
// Optimized density assignment: density as 8bit unorm in Attributes A and all other components in Attributes B as 16bit float. This only works if there is a maximum of 4 non-density components.
// We also don't use this assignment if there are 3 non-density components as these will be padded to a 4 component format anyways, so we might as well put all 4 components into a single texture.
const bool bOptimizedDensityAssignment = ( NumNonDensityComponentInfos < = 4 & & NumNonDensityComponentInfos ! = 3 ) & & DensityComponentInfoPtr ! = nullptr ;
if ( bOptimizedDensityAssignment )
2023-01-30 11:48:00 -05:00
{
2023-07-31 11:23:48 -04:00
// Assign density to the first channel of Attributes A and set format to 8 bit unorm
ImportOptions - > Attributes [ 0 ] . Mappings [ 0 ] . SourceGridIndex = ( * DensityComponentInfoPtr ) - > Index ;
ImportOptions - > Attributes [ 0 ] . Mappings [ 0 ] . SourceComponentIndex = ( * DensityComponentInfoPtr ) - > ComponentIndex ;
ImportOptions - > Attributes [ 0 ] . Format = ESparseVolumeAttributesFormat : : Unorm8 ;
// All the other components go into Attributes B with 16 bit float
uint32 DstComponentIdx = 0 ;
for ( const TSharedPtr < FOpenVDBGridComponentInfo > & GridComponent : GridComponentInfo )
2023-01-30 11:48:00 -05:00
{
2023-07-31 11:23:48 -04:00
if ( ! ensure ( DstComponentIdx < = 3 ) | | GridComponent - > Index = = INDEX_NONE | | & GridComponent = = DensityComponentInfoPtr )
2023-01-30 11:48:00 -05:00
{
2023-07-31 11:23:48 -04:00
continue ;
}
ImportOptions - > Attributes [ 1 ] . Mappings [ DstComponentIdx ] . SourceGridIndex = GridComponent - > Index ;
ImportOptions - > Attributes [ 1 ] . Mappings [ DstComponentIdx ] . SourceComponentIndex = GridComponent - > ComponentIndex ;
+ + DstComponentIdx ;
}
ImportOptions - > Attributes [ 1 ] . Format = ESparseVolumeAttributesFormat : : Float16 ;
}
else
{
uint32 DstAttributesIdx = 0 ;
uint32 DstComponentIdx = 0 ;
for ( const TSharedPtr < FOpenVDBGridComponentInfo > & GridComponent : GridComponentInfo )
{
if ( GridComponent - > Index = = INDEX_NONE )
{
continue ;
}
ImportOptions - > Attributes [ DstAttributesIdx ] . Mappings [ DstComponentIdx ] . SourceGridIndex = GridComponent - > Index ;
ImportOptions - > Attributes [ DstAttributesIdx ] . Mappings [ DstComponentIdx ] . SourceComponentIndex = GridComponent - > ComponentIndex ;
+ + DstComponentIdx ;
if ( DstComponentIdx = = 4 )
{
DstComponentIdx = 0 ;
+ + DstAttributesIdx ;
if ( DstAttributesIdx = = 2 )
{
break ;
}
2023-01-30 11:48:00 -05:00
}
}
}
2023-07-31 11:23:48 -04:00
2023-01-31 01:11:48 -05:00
ImportOptions - > bIsSequence = NumFiles > 1 ;
2023-01-30 11:48:00 -05:00
}
2023-09-11 11:09:14 -04:00
static FString GetVDBSequenceBaseFileName ( const FString & FileName , bool bDiscardNumbersOnly )
2023-06-19 10:56:00 -04:00
{
const FString CleanFileName = FPaths : : GetCleanFilename ( FileName ) ;
2023-09-11 11:09:14 -04:00
int32 NumValidChars = CleanFileName . Len ( ) - 4 ; // chop off the file extension
// Remove any digits at the end
NumValidChars = CleanFileName . FindLastCharByPredicate ( [ & ] ( TCHAR Letter ) { return ! FChar : : IsDigit ( Letter ) ; } , NumValidChars ) + 1 ;
// Optionally remove other unwanted chars like underscores and invalid object name chars that would later be replaced by underscores
if ( ! bDiscardNumbersOnly )
{
NumValidChars = CleanFileName . FindLastCharByPredicate ( [ & ] ( TCHAR Letter )
{
// INVALID_OBJECTNAME_CHARACTERS is defined in NameTypes.h and is a string literal containing all the invalid chars.
// The number at the end of a filename in a sequence is often separated by an underscore or other special character,
// so when we want to get the base filename for deriving the new asset name, we also discard these characters.
// Underscores are not part of the invalid chars string literal, so we append them here to also discard these chars.
2024-01-25 07:34:28 -05:00
const TCHAR * InvalidChars = TEXT ( " _ " ) INVALID_OBJECTNAME_CHARACTERS ;
2023-09-11 11:09:14 -04:00
while ( * InvalidChars )
{
if ( Letter = = * InvalidChars )
{
return false ;
}
+ + InvalidChars ;
}
return true ;
} , NumValidChars ) + 1 ;
}
const FString CleanFileNameWithoutSuffix = CleanFileName . Left ( NumValidChars ) ;
2023-06-19 10:56:00 -04:00
return CleanFileNameWithoutSuffix ;
}
2023-01-30 11:48:00 -05:00
static TArray < FString > FindOpenVDBSequenceFileNames ( const FString & Filename )
{
TArray < FString > SequenceFilenames ;
// The file is potentially a sequence if the character before the `.vdb` is a number.
const bool bIsFilePotentiallyPartOfASequence = FChar : : IsDigit ( Filename [ Filename . Len ( ) - 5 ] ) ;
if ( ! bIsFilePotentiallyPartOfASequence )
{
SequenceFilenames . Add ( Filename ) ;
}
else
{
const FString Path = FPaths : : GetPath ( Filename ) ;
const FString CleanFilename = FPaths : : GetCleanFilename ( Filename ) ;
2023-09-11 11:09:14 -04:00
const FString CleanFilenameWithoutSuffix = GetVDBSequenceBaseFileName ( Filename , true /*bDiscardNumbersOnly*/ ) ;
2023-01-30 11:48:00 -05:00
// Find all files potentially part of the sequence
TArray < FString > PotentialSequenceFilenames ;
IFileManager : : Get ( ) . FindFiles ( PotentialSequenceFilenames , * Path , TEXT ( " *.vdb " ) ) ;
2023-09-11 11:09:14 -04:00
PotentialSequenceFilenames = PotentialSequenceFilenames . FilterByPredicate ( [ & CleanFilenameWithoutSuffix ] ( const FString & Str )
{
if ( ! CleanFilenameWithoutSuffix . IsEmpty ( ) )
{
// Check for the same base filename
return Str . StartsWith ( CleanFilenameWithoutSuffix ) ;
}
else
{
// Removing the digits at the end of the input file resulted in an empty string, so we are looking for numeric filenames only (excluding the 4 chars file extension)
return Str . LeftChop ( 4 ) . IsNumeric ( ) ;
}
} ) ;
2023-01-30 11:48:00 -05:00
auto GetFilenameNumberSuffix = [ ] ( const FString & Filename ) - > int32
{
const FString FilenameWithoutExt = Filename . LeftChop ( 4 ) ;
const int32 LastNonDigitIndex = FilenameWithoutExt . FindLastCharByPredicate ( [ ] ( TCHAR Letter ) { return ! FChar : : IsDigit ( Letter ) ; } ) + 1 ;
const FString NumberSuffixStr = FilenameWithoutExt . RightChop ( LastNonDigitIndex ) ;
int32 Number = INDEX_NONE ;
if ( NumberSuffixStr . IsNumeric ( ) )
{
TTypeFromString < int32 > : : FromString ( Number , * NumberSuffixStr ) ;
}
return Number ;
} ;
// Find range of number suffixes
int32 LowestIndex = INT32_MAX ;
int32 HighestIndex = INT32_MIN ;
for ( FString & ItemFilename : PotentialSequenceFilenames )
{
const int32 Index = GetFilenameNumberSuffix ( ItemFilename ) ;
if ( Index = = INDEX_NONE )
{
ItemFilename . Empty ( ) ;
continue ;
}
LowestIndex = FMath : : Min ( LowestIndex , Index ) ;
HighestIndex = FMath : : Max ( HighestIndex , Index ) ;
}
check ( HighestIndex > = LowestIndex ) ;
// Sort the filenames into the result array
SequenceFilenames . SetNum ( HighestIndex - LowestIndex + 1 ) ;
for ( const FString & ItemFilename : PotentialSequenceFilenames )
{
const int32 Index = ItemFilename . IsEmpty ( ) ? INDEX_NONE : GetFilenameNumberSuffix ( ItemFilename ) ;
if ( Index = = INDEX_NONE )
{
continue ;
}
SequenceFilenames [ Index - LowestIndex ] = Path / ItemFilename ;
}
// Chop off any items after finding the first gap
for ( int32 i = 0 ; i < SequenceFilenames . Num ( ) ; + + i )
{
if ( SequenceFilenames [ i ] . IsEmpty ( ) )
{
SequenceFilenames . SetNum ( i ) ;
break ;
}
}
}
check ( ! SequenceFilenames . IsEmpty ( ) ) ;
return SequenceFilenames ;
}
2024-01-26 04:47:20 -05:00
bool LoadOpenVDBPreviewData ( const FString & Filename , FOpenVDBPreviewData * OutPreviewData )
2023-01-24 21:44:51 -05:00
{
FOpenVDBPreviewData & Result = * OutPreviewData ;
2023-01-30 11:48:00 -05:00
check ( Result . LoadedFile . IsEmpty ( ) ) ;
check ( Result . GridInfo . IsEmpty ( ) ) ;
check ( Result . GridInfoPtrs . IsEmpty ( ) ) ;
check ( Result . GridComponentInfoPtrs . IsEmpty ( ) ) ;
check ( Result . SequenceFilenames . IsEmpty ( ) ) ;
2023-01-24 21:44:51 -05:00
if ( ! FFileHelper : : LoadFileToArray ( Result . LoadedFile , * Filename ) )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " OpenVDB file could not be loaded: %s " ) , * Filename ) ;
return false ;
}
if ( ! GetOpenVDBGridInfo ( Result . LoadedFile , true /*bCreateStrings*/ , & Result . GridInfo ) )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Failed to read OpenVDB file: %s " ) , * Filename ) ;
return false ;
}
if ( Result . GridInfo . IsEmpty ( ) )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " OpenVDB file contains no grids: %s " ) , * Filename ) ;
return false ;
}
// We need a <None> option to leave channels empty
FOpenVDBGridComponentInfo NoneGridComponentInfo ;
NoneGridComponentInfo . Index = INDEX_NONE ;
NoneGridComponentInfo . ComponentIndex = INDEX_NONE ;
NoneGridComponentInfo . Name = TEXT ( " <None> " ) ;
NoneGridComponentInfo . DisplayString = TEXT ( " <None> " ) ;
Result . GridComponentInfoPtrs . Add ( MakeShared < FOpenVDBGridComponentInfo > ( NoneGridComponentInfo ) ) ;
// Create individual entries for each component of all valid source grids.
// This is an array of TSharedPtr because SComboBox requires its input to be wrapped in TSharedPtr.
bool bFoundSupportedGridType = false ;
for ( const FOpenVDBGridInfo & Grid : Result . GridInfo )
{
2023-01-30 11:48:00 -05:00
// Append all grids, even if we don't actually support them
Result . GridInfoPtrs . Add ( MakeShared < FOpenVDBGridInfo > ( Grid ) ) ;
2023-01-24 21:44:51 -05:00
if ( Grid . Type = = EOpenVDBGridType : : Unknown | | ! IsOpenVDBGridValid ( Grid , Filename ) )
{
continue ;
}
bFoundSupportedGridType = true ;
// Create one entry per component
for ( uint32 ComponentIdx = 0 ; ComponentIdx < Grid . NumComponents ; + + ComponentIdx )
{
FOpenVDBGridComponentInfo ComponentInfo ;
ComponentInfo . Index = Grid . Index ;
ComponentInfo . ComponentIndex = ComponentIdx ;
ComponentInfo . Name = Grid . Name ;
const TCHAR * ComponentNames [ ] = { TEXT ( " .X " ) , TEXT ( " .Y " ) , TEXT ( " .Z " ) , TEXT ( " .W " ) } ;
FStringFormatOrderedArguments FormatArgs ;
FormatArgs . Add ( ComponentInfo . Index ) ;
FormatArgs . Add ( ComponentInfo . Name ) ;
FormatArgs . Add ( Grid . NumComponents = = 1 ? TEXT ( " " ) : ComponentNames [ ComponentIdx ] ) ;
ComponentInfo . DisplayString = FString : : Format ( TEXT ( " {0}. {1}{2} " ) , FormatArgs ) ;
Result . GridComponentInfoPtrs . Add ( MakeShared < FOpenVDBGridComponentInfo > ( MoveTemp ( ComponentInfo ) ) ) ;
}
}
if ( ! bFoundSupportedGridType )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " OpenVDB file contains no grids of supported type: %s " ) , * Filename ) ;
return false ;
}
2023-01-30 11:48:00 -05:00
Result . SequenceFilenames = FindOpenVDBSequenceFileNames ( Filename ) ;
2023-01-31 01:11:48 -05:00
ComputeDefaultOpenVDBGridAssignment ( Result . GridComponentInfoPtrs , Result . SequenceFilenames . Num ( ) , & Result . DefaultImportOptions ) ;
2023-01-30 11:48:00 -05:00
2023-01-24 21:44:51 -05:00
return true ;
}
2023-01-30 11:48:00 -05:00
static bool ShowOpenVDBImportWindow ( const FString & Filename , const FOpenVDBPreviewData & PreviewData , FOpenVDBImportOptions * OutImportOptions )
2023-01-24 21:44:51 -05:00
{
TSharedPtr < SWindow > ParentWindow ;
if ( FModuleManager : : Get ( ) . IsModuleLoaded ( " MainFrame " ) )
{
IMainFrameModule & MainFrame = FModuleManager : : LoadModuleChecked < IMainFrameModule > ( " MainFrame " ) ;
ParentWindow = MainFrame . GetParentWindow ( ) ;
}
// Compute centered window position based on max window size, which include when all categories are expanded
const float ImportWindowWidth = 450.0f ;
const float ImportWindowHeight = 750.0f ;
FVector2D ImportWindowSize = FVector2D ( ImportWindowWidth , ImportWindowHeight ) ; // Max window size it can get based on current slate
FSlateRect WorkAreaRect = FSlateApplicationBase : : Get ( ) . GetPreferredWorkArea ( ) ;
FVector2D DisplayTopLeft ( WorkAreaRect . Left , WorkAreaRect . Top ) ;
FVector2D DisplaySize ( WorkAreaRect . Right - WorkAreaRect . Left , WorkAreaRect . Bottom - WorkAreaRect . Top ) ;
float ScaleFactor = FPlatformApplicationMisc : : GetDPIScaleFactorAtPoint ( DisplayTopLeft . X , DisplayTopLeft . Y ) ;
ImportWindowSize * = ScaleFactor ;
FVector2D WindowPosition = ( DisplayTopLeft + ( DisplaySize - ImportWindowSize ) / 2.0f ) / ScaleFactor ;
TSharedRef < SWindow > Window = SNew ( SWindow )
. Title ( NSLOCTEXT ( " UnrealEd " , " OpenVDBImportOptionsTitle " , " OpenVDB Import Options " ) )
. SizingRule ( ESizingRule : : Autosized )
. AutoCenter ( EAutoCenter : : None )
. ClientSize ( ImportWindowSize )
. ScreenPosition ( WindowPosition ) ;
2023-01-31 01:11:48 -05:00
TArray < TSharedPtr < ESparseVolumeAttributesFormat > > SupportedFormats =
2023-01-24 21:44:51 -05:00
{
2023-01-31 01:11:48 -05:00
MakeShared < ESparseVolumeAttributesFormat > ( ESparseVolumeAttributesFormat : : Float32 ) ,
MakeShared < ESparseVolumeAttributesFormat > ( ESparseVolumeAttributesFormat : : Float16 ) ,
MakeShared < ESparseVolumeAttributesFormat > ( ESparseVolumeAttributesFormat : : Unorm8 )
2023-01-24 21:44:51 -05:00
} ;
TSharedPtr < SOpenVDBImportWindow > OpenVDBOptionWindow ;
Window - > SetContent
(
SAssignNew ( OpenVDBOptionWindow , SOpenVDBImportWindow )
2023-01-31 01:11:48 -05:00
. ImportOptions ( OutImportOptions )
. DefaultImportOptions ( & PreviewData . DefaultImportOptions )
2023-01-30 11:48:00 -05:00
. NumFoundFiles ( PreviewData . SequenceFilenames . Num ( ) )
. OpenVDBGridInfo ( & PreviewData . GridInfoPtrs )
. OpenVDBGridComponentInfo ( & PreviewData . GridComponentInfoPtrs )
2023-01-24 21:44:51 -05:00
. OpenVDBSupportedTargetFormats ( & SupportedFormats )
. WidgetWindow ( Window )
. FullPath ( FText : : FromString ( Filename ) )
. MaxWindowHeight ( ImportWindowHeight )
. MaxWindowWidth ( ImportWindowWidth )
) ;
FSlateApplication : : Get ( ) . AddModalWindow ( Window , ParentWindow , false ) ;
2023-01-30 11:48:00 -05:00
OutImportOptions - > bIsSequence = OpenVDBOptionWindow - > ShouldImportAsSequence ( ) ;
2023-01-24 21:44:51 -05:00
2023-01-30 11:48:00 -05:00
return OpenVDBOptionWindow - > ShouldImport ( ) ;
}
static bool ValidateImportOptions ( const FOpenVDBImportOptions & ImportOptions , const TArray < FOpenVDBGridInfo > & GridInfo )
{
2023-01-31 01:11:48 -05:00
const int32 NumGrids = GridInfo . Num ( ) ;
for ( const FOpenVDBSparseVolumeAttributesDesc & AttributesDesc : ImportOptions . Attributes )
2023-01-30 11:48:00 -05:00
{
2023-01-31 01:11:48 -05:00
for ( const FOpenVDBSparseVolumeComponentMapping & Mapping : AttributesDesc . Mappings )
2023-01-30 11:48:00 -05:00
{
2023-01-31 01:11:48 -05:00
const int32 SourceGridIndex = Mapping . SourceGridIndex ;
const int32 SourceComponentIndex = Mapping . SourceComponentIndex ;
if ( Mapping . SourceGridIndex ! = INDEX_NONE )
2023-01-30 11:48:00 -05:00
{
if ( SourceGridIndex > = NumGrids )
{
return false ; // Invalid grid index
}
2023-01-31 01:11:48 -05:00
if ( SourceComponentIndex = = INDEX_NONE | | SourceComponentIndex > = ( int32 ) GridInfo [ SourceGridIndex ] . NumComponents )
2023-01-30 11:48:00 -05:00
{
return false ; // Invalid component index
}
}
}
}
return true ;
2023-01-24 21:44:51 -05:00
}
2022-10-16 02:28:34 -04:00
USparseVolumeTextureFactory : : USparseVolumeTextureFactory ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
{
bCreateNew = true ;
bEditAfterNew = true ;
bEditorImport = true ;
2023-06-15 09:39:59 -04:00
SupportedClass = nullptr ; // This factory supports multiple classes, so SupportedClass needs to be nullptr
2022-10-16 02:28:34 -04:00
Formats . Add ( TEXT ( " vdb;OpenVDB Format " ) ) ;
}
FText USparseVolumeTextureFactory : : GetDisplayName ( ) const
{
return LOCTEXT ( " SparseVolumeTextureFactoryDescription " , " Sparse Volume Texture " ) ;
}
bool USparseVolumeTextureFactory : : ConfigureProperties ( )
{
return true ;
}
bool USparseVolumeTextureFactory : : ShouldShowInNewMenu ( ) const
{
return false ;
}
///////////////////////////////////////////////////////////////////////////////
// Create asset
bool USparseVolumeTextureFactory : : CanCreateNew ( ) const
{
return false ; // To be able to import files and call
}
UObject * USparseVolumeTextureFactory : : FactoryCreateNew ( UClass * InClass , UObject * InParent , FName InName , EObjectFlags Flags , UObject * Context , FFeedbackContext * Warn )
{
USparseVolumeTexture * Object = NewObject < USparseVolumeTexture > ( InParent , InClass , InName , Flags ) ;
// SVT_TODO initialize similarly to UTexture2DFactoryNew
return Object ;
}
///////////////////////////////////////////////////////////////////////////////
// Import asset
bool USparseVolumeTextureFactory : : DoesSupportClass ( UClass * Class )
{
2023-06-15 09:39:59 -04:00
return Class = = USparseVolumeTexture : : StaticClass ( ) | | Class = = UStaticSparseVolumeTexture : : StaticClass ( ) | | Class = = UAnimatedSparseVolumeTexture : : StaticClass ( ) ;
2022-10-16 02:28:34 -04:00
}
UClass * USparseVolumeTextureFactory : : ResolveSupportedClass ( )
{
2023-06-15 09:39:59 -04:00
// SVT_TODO: Do we need to return UStaticSparseVolumeTexture::StaticClass() or UAnimatedSparseVolumeTexture::StaticClass() here instead? Using the base class seems to work.
2022-10-16 02:28:34 -04:00
return USparseVolumeTexture : : StaticClass ( ) ;
}
bool USparseVolumeTextureFactory : : FactoryCanImport ( const FString & Filename )
{
const FString Extension = FPaths : : GetExtension ( Filename ) ;
if ( Extension = = TEXT ( " vdb " ) )
{
return true ;
}
return false ;
}
void USparseVolumeTextureFactory : : CleanUp ( )
{
Super : : CleanUp ( ) ;
}
UObject * USparseVolumeTextureFactory : : FactoryCreateFile ( UClass * InClass , UObject * InParent , FName InName , EObjectFlags Flags , const FString & Filename ,
const TCHAR * Parms , FFeedbackContext * Warn , bool & bOutOperationCanceled )
2023-06-15 09:39:59 -04:00
{
2023-06-19 10:56:00 -04:00
return ImportInternal ( InClass , InParent , InName , Flags , Filename , Parms , bOutOperationCanceled , false /*bIsReimport*/ ) ;
2023-06-15 09:39:59 -04:00
}
bool USparseVolumeTextureFactory : : CanReimport ( UObject * Obj , TArray < FString > & OutFilenames )
{
# if OPENVDB_AVAILABLE
UStreamableSparseVolumeTexture * StreamableSVT = Cast < UStreamableSparseVolumeTexture > ( Obj ) ;
if ( StreamableSVT & & StreamableSVT - > AssetImportData )
{
StreamableSVT - > AssetImportData - > ExtractFilenames ( OutFilenames ) ;
return true ;
}
# endif // OPENVDB_AVAILABLE
return false ;
}
void USparseVolumeTextureFactory : : SetReimportPaths ( UObject * Obj , const TArray < FString > & NewReimportPaths )
{
# if OPENVDB_AVAILABLE
UStreamableSparseVolumeTexture * StreamableSVT = Cast < UStreamableSparseVolumeTexture > ( Obj ) ;
if ( StreamableSVT & & ensure ( NewReimportPaths . Num ( ) = = 1 ) )
{
StreamableSVT - > AssetImportData - > UpdateFilenameOnly ( NewReimportPaths [ 0 ] ) ;
}
# endif // OPENVDB_AVAILABLE
}
EReimportResult : : Type USparseVolumeTextureFactory : : Reimport ( UObject * Obj )
{
# if OPENVDB_AVAILABLE
UStreamableSparseVolumeTexture * StreamableSVT = Cast < UStreamableSparseVolumeTexture > ( Obj ) ;
if ( ! StreamableSVT )
{
return EReimportResult : : Failed ;
}
// Make sure file is valid and exists
const FString Filename = StreamableSVT - > AssetImportData - > GetFirstFilename ( ) ;
if ( ! Filename . Len ( ) | | IFileManager : : Get ( ) . FileSize ( * Filename ) = = INDEX_NONE | | ! FactoryCanImport ( Filename ) )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Reimport failed! Filename '%s' is invalid or no such file exists. " ) , * Filename ) ;
return EReimportResult : : Failed ;
}
bool OutCanceled = false ;
2023-06-19 10:56:00 -04:00
if ( ! ImportInternal ( StreamableSVT - > GetClass ( ) , StreamableSVT - > GetOuter ( ) , * StreamableSVT - > GetName ( ) , RF_Public | RF_Standalone , Filename , nullptr , OutCanceled , true /*bIsReimport*/ ) )
2023-06-15 09:39:59 -04:00
{
if ( OutCanceled )
{
return EReimportResult : : Cancelled ;
}
return EReimportResult : : Failed ;
}
return EReimportResult : : Succeeded ;
# else
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Cannot import OpenVDB asset any platform other than Windows. " ) ) ;
return EReimportResult : : Failed ;
# endif // OPENVDB_AVAILABLE
}
2023-06-19 10:56:00 -04:00
UObject * USparseVolumeTextureFactory : : ImportInternal ( UClass * InClass , UObject * InParent , FName InName , EObjectFlags Flags , const FString & Filename , const TCHAR * Parms , bool & bOutOperationCanceled , bool bIsReimport )
2022-10-16 02:28:34 -04:00
{
# if OPENVDB_AVAILABLE
GEditor - > GetEditorSubsystem < UImportSubsystem > ( ) - > BroadcastAssetPreImport ( this , InClass , InParent , InName , Parms ) ;
TArray < UObject * > ResultAssets ;
2023-01-30 11:48:00 -05:00
bOutOperationCanceled = false ;
const bool bIsUnattended = ( IsAutomatedImport ( )
| | FApp : : IsUnattended ( )
| | IsRunningCommandlet ( )
| | GIsRunningUnattendedScript ) ;
2023-01-24 21:44:51 -05:00
FOpenVDBPreviewData PreviewData ;
2024-01-26 04:47:20 -05:00
// Use the provided preview data, if any
bool bCollectedData = false ;
if ( bIsUnattended & & AssetImportTask )
2022-10-16 02:28:34 -04:00
{
2024-01-26 04:47:20 -05:00
if ( UOpenVDBImportOptionsObject * TaskOptions = Cast < UOpenVDBImportOptionsObject > ( AssetImportTask - > Options ) )
{
PreviewData = TaskOptions - > PreviewData ;
bCollectedData = true ;
}
}
// Otherwise, load file and get info about each contained grid
if ( ! bCollectedData )
{
if ( ! LoadOpenVDBPreviewData ( Filename , & PreviewData ) )
{
return nullptr ;
}
2023-01-24 21:44:51 -05:00
}
2023-01-31 01:11:48 -05:00
FOpenVDBImportOptions ImportOptions = PreviewData . DefaultImportOptions ;
2023-06-15 09:39:59 -04:00
2023-01-30 11:48:00 -05:00
if ( ! bIsUnattended )
2023-01-24 21:44:51 -05:00
{
2023-01-30 11:48:00 -05:00
// Show dialog for import options
if ( ! ShowOpenVDBImportWindow ( Filename , PreviewData , & ImportOptions ) )
{
bOutOperationCanceled = true ;
return nullptr ;
}
}
if ( ! ValidateImportOptions ( ImportOptions , PreviewData . GridInfo ) )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Import options are invalid! This is likely due to invalid/out-of-bounds grid or component indices. " ) ) ;
2023-01-24 21:44:51 -05:00
return nullptr ;
}
// Utility function for computing the bounding box encompassing the bounds of all frames in the SVT.
2024-02-08 16:08:23 -05:00
auto ExpandVolumeBounds = [ ] ( const FOpenVDBImportOptions & ImportOptions , const TArray < FOpenVDBGridInfo > & GridInfoArray , FIntVector3 & VolumeBoundsMin , FIntVector3 & VolumeBoundsMax )
2023-01-24 21:44:51 -05:00
{
2023-01-31 01:11:48 -05:00
for ( const FOpenVDBSparseVolumeAttributesDesc & Attributes : ImportOptions . Attributes )
2023-01-24 21:44:51 -05:00
{
2023-01-31 01:11:48 -05:00
for ( const FOpenVDBSparseVolumeComponentMapping & Mapping : Attributes . Mappings )
2023-01-24 21:44:51 -05:00
{
2023-01-31 01:11:48 -05:00
if ( Mapping . SourceGridIndex ! = INDEX_NONE )
2023-01-24 21:44:51 -05:00
{
2023-01-31 01:11:48 -05:00
const FOpenVDBGridInfo & GridInfo = GridInfoArray [ Mapping . SourceGridIndex ] ;
2023-02-22 09:39:24 -05:00
VolumeBoundsMin . X = FMath : : Min ( VolumeBoundsMin . X , GridInfo . VolumeActiveAABBMin . X ) ;
VolumeBoundsMin . Y = FMath : : Min ( VolumeBoundsMin . Y , GridInfo . VolumeActiveAABBMin . Y ) ;
VolumeBoundsMin . Z = FMath : : Min ( VolumeBoundsMin . Z , GridInfo . VolumeActiveAABBMin . Z ) ;
2023-01-24 21:44:51 -05:00
2023-02-22 09:39:24 -05:00
VolumeBoundsMax . X = FMath : : Max ( VolumeBoundsMax . X , GridInfo . VolumeActiveAABBMax . X ) ;
VolumeBoundsMax . Y = FMath : : Max ( VolumeBoundsMax . Y , GridInfo . VolumeActiveAABBMax . Y ) ;
VolumeBoundsMax . Z = FMath : : Max ( VolumeBoundsMax . Z , GridInfo . VolumeActiveAABBMax . Z ) ;
2023-01-24 21:44:51 -05:00
}
}
}
2022-10-16 02:28:34 -04:00
} ;
2023-02-22 09:39:24 -05:00
FIntVector3 VolumeBoundsMin = FIntVector3 ( INT32_MAX , INT32_MAX , INT32_MAX ) ;
FIntVector3 VolumeBoundsMax = FIntVector3 ( INT32_MIN , INT32_MIN , INT32_MIN ) ;
2023-01-24 21:44:51 -05:00
// Import as either single static SVT or a sequence of frames, making up an animated SVT
if ( ! ImportOptions . bIsSequence )
2022-10-16 02:28:34 -04:00
{
// Import as a static sparse volume texture asset.
FScopedSlowTask ImportTask ( 1.0f , LOCTEXT ( " ImportingVDBStatic " , " Importing static OpenVDB " ) ) ;
ImportTask . MakeDialog ( true ) ;
2024-02-08 16:08:23 -05:00
ExpandVolumeBounds ( ImportOptions , PreviewData . GridInfo , VolumeBoundsMin , VolumeBoundsMax ) ;
2023-01-24 21:44:51 -05:00
2023-05-31 08:14:22 -04:00
UE : : SVT : : FTextureData TextureData { } ;
2024-02-08 16:08:23 -05:00
FTransform FrameTransform = FTransform : : Identity ;
const bool bConversionSuccess = ConvertOpenVDBToSparseVolumeTexture ( PreviewData . LoadedFile , ImportOptions , VolumeBoundsMin , TextureData , FrameTransform ) ;
2023-01-31 01:11:48 -05:00
if ( ! bConversionSuccess )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Failed to convert OpenVDB file to SparseVolumeTexture: %s " ) , * Filename ) ;
return nullptr ;
}
2022-11-25 09:18:31 -05:00
2023-03-24 12:42:28 -04:00
UStaticSparseVolumeTexture * StaticSVTexture = NewObject < UStaticSparseVolumeTexture > ( InParent , UStaticSparseVolumeTexture : : StaticClass ( ) , InName , Flags ) ;
2024-02-08 16:08:23 -05:00
const bool bInitSuccess = StaticSVTexture - > Initialize ( MakeArrayView ( & TextureData , 1 ) , MakeArrayView ( & FrameTransform , 1 ) ) ;
2023-03-24 12:42:28 -04:00
if ( ! bInitSuccess )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Failed to initialize SparseVolumeTexture: %s " ) , * Filename ) ;
return nullptr ;
}
2022-10-16 02:28:34 -04:00
if ( ImportTask . ShouldCancel ( ) )
{
2023-01-31 01:11:48 -05:00
bOutOperationCanceled = true ;
2022-10-16 02:28:34 -04:00
return nullptr ;
}
ImportTask . EnterProgressFrame ( 1.0f , LOCTEXT ( " ConvertingVDBStatic " , " Converting static OpenVDB " ) ) ;
ResultAssets . Add ( StaticSVTexture ) ;
}
2023-01-24 21:44:51 -05:00
else
{
// Import as an animated sparse volume texture asset.
2023-06-15 09:39:59 -04:00
2023-01-24 21:44:51 -05:00
// Data from original file is no longer needed; we iterate over all frames later
PreviewData . LoadedFile . Empty ( ) ;
2023-06-15 09:39:59 -04:00
2023-01-30 11:48:00 -05:00
const int32 NumFrames = PreviewData . SequenceFilenames . Num ( ) ;
2023-01-24 21:44:51 -05:00
2023-02-22 09:39:24 -05:00
FScopedSlowTask ImportTask ( NumFrames + 1 , LOCTEXT ( " ImportingVDBAnim " , " Importing OpenVDB animation " ) ) ;
2023-01-24 21:44:51 -05:00
ImportTask . MakeDialog ( true ) ;
// Allocate space for each frame
2023-05-31 08:14:22 -04:00
TArray < UE : : SVT : : FTextureData > UncookedFramesData ;
2024-02-08 16:08:23 -05:00
TArray < FTransform > FrameTransforms ;
2023-03-24 12:42:28 -04:00
UncookedFramesData . SetNum ( NumFrames ) ;
2024-02-08 16:08:23 -05:00
FrameTransforms . SetNum ( NumFrames ) ;
2023-01-24 21:44:51 -05:00
2023-02-16 10:42:36 -05:00
std : : atomic_bool bErrored = false ;
std : : atomic_bool bCanceled = false ;
2023-02-22 09:39:24 -05:00
// Compute volume bounds and check sequence files for compatiblity
std : : mutex VolumeBoundsMutex ;
2024-02-08 16:08:23 -05:00
ParallelFor ( NumFrames , [ & bErrored , & VolumeBoundsMutex , & VolumeBoundsMin , & VolumeBoundsMax , & ExpandVolumeBounds , & PreviewData , & ImportOptions ] ( int32 FrameIdx )
2023-02-22 09:39:24 -05:00
{
if ( bErrored . load ( ) )
{
return ;
}
// Load file and get info about each contained grid
const FString & FrameFilename = PreviewData . SequenceFilenames [ FrameIdx ] ;
2023-02-23 14:53:04 -05:00
TArray64 < uint8 > LoadedFrameFile ;
2023-02-22 09:39:24 -05:00
if ( ! FFileHelper : : LoadFileToArray ( LoadedFrameFile , * FrameFilename ) )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " OpenVDB file could not be loaded: %s " ) , * FrameFilename ) ;
bErrored . store ( true ) ;
return ;
}
TArray < FOpenVDBGridInfo > FrameGridInfo ;
if ( ! GetOpenVDBGridInfo ( LoadedFrameFile , true , & FrameGridInfo ) )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Failed to read OpenVDB file: %s " ) , * FrameFilename ) ;
bErrored . store ( true ) ;
return ;
}
// Sanity check for compatibility
for ( const FOpenVDBSparseVolumeAttributesDesc & AttributesDesc : ImportOptions . Attributes )
{
for ( const FOpenVDBSparseVolumeComponentMapping & Mapping : AttributesDesc . Mappings )
{
const uint32 SourceGridIndex = Mapping . SourceGridIndex ;
if ( SourceGridIndex ! = INDEX_NONE )
{
if ( ( int32 ) SourceGridIndex > = FrameGridInfo . Num ( ) )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " OpenVDB file is incompatible with other frames in the sequence: %s " ) , * FrameFilename ) ;
bErrored . store ( true ) ;
return ;
}
const FOpenVDBGridInfo & OrigSourceGrid = PreviewData . GridInfo [ SourceGridIndex ] ;
const FOpenVDBGridInfo & FrameSourceGrid = FrameGridInfo [ SourceGridIndex ] ;
if ( OrigSourceGrid . Type ! = FrameSourceGrid . Type | | OrigSourceGrid . Name ! = FrameSourceGrid . Name )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " OpenVDB file is incompatible with other frames in the sequence: %s " ) , * FrameFilename ) ;
bErrored . store ( true ) ;
return ;
}
}
}
}
// Update sequence volume bounds and increment ProcessedFramesCounter
{
std : : lock_guard < std : : mutex > Lock ( VolumeBoundsMutex ) ;
2024-02-08 16:08:23 -05:00
ExpandVolumeBounds ( ImportOptions , FrameGridInfo , VolumeBoundsMin , VolumeBoundsMax ) ;
2023-02-22 09:39:24 -05:00
}
} ) ;
if ( bErrored . load ( ) )
{
return nullptr ;
}
ImportTask . EnterProgressFrame ( 1.0f , LOCTEXT ( " ConvertingVDBAnim " , " Converting OpenVDB animation " ) ) ;
FEvent * AllTasksFinishedEvent = FPlatformProcess : : GetSynchEventFromPool ( ) ;
2023-02-16 10:42:36 -05:00
std : : atomic_int FinishedTasksCounter = 0 ; // Will be incremented even if frame processing failed
std : : atomic_int ProcessedFramesCounter = 0 ;
2023-01-24 21:44:51 -05:00
2023-02-22 09:39:24 -05:00
// Load individual frames, process/convert them and append them to the resulting asset
2023-01-24 21:44:51 -05:00
for ( int32 FrameIdx = 0 ; FrameIdx < NumFrames ; + + FrameIdx )
{
2023-02-16 10:42:36 -05:00
// Increments the atomic counter when going out of scope. Triggers an event once the counter reaches a given value.
struct FScopedIncrementer
2023-01-24 21:44:51 -05:00
{
2023-02-16 10:42:36 -05:00
std : : atomic_int & Counter ;
int32 MaxValue ;
FEvent * Event ;
2023-06-15 09:39:59 -04:00
explicit FScopedIncrementer ( std : : atomic_int & InCounter , int32 InMaxValue , FEvent * InEvent )
2023-02-16 10:42:36 -05:00
: Counter ( InCounter ) , MaxValue ( InMaxValue ) , Event ( InEvent ) { }
2023-06-15 09:39:59 -04:00
~ FScopedIncrementer ( )
{
2023-02-16 10:42:36 -05:00
if ( ( Counter . fetch_add ( 1 ) + 1 ) = = MaxValue )
2023-01-24 21:44:51 -05:00
{
2023-02-16 10:42:36 -05:00
Event - > Trigger ( ) ;
2023-01-24 21:44:51 -05:00
}
}
2023-02-16 10:42:36 -05:00
} ;
2023-01-24 21:44:51 -05:00
2023-06-15 09:39:59 -04:00
AsyncTask ( ENamedThreads : : AnyNormalThreadNormalTask ,
2024-02-08 16:08:23 -05:00
[ FrameIdx , NumFrames , & PreviewData , & ImportOptions , & UncookedFramesData , & FrameTransforms ,
AllTasksFinishedEvent , & bErrored , & bCanceled , & FinishedTasksCounter , & ProcessedFramesCounter , & VolumeBoundsMin ] ( )
2023-02-16 10:42:36 -05:00
{
// Ensure the FinishedTasksCounter will be incremented in all cases
FScopedIncrementer Incremeter ( FinishedTasksCounter , NumFrames , AllTasksFinishedEvent ) ;
2023-02-01 06:20:15 -05:00
2023-02-16 10:42:36 -05:00
if ( bErrored . load ( ) | | bCanceled . load ( ) )
{
return ;
}
2023-01-31 01:11:48 -05:00
2023-02-16 10:42:36 -05:00
const FString & FrameFilename = PreviewData . SequenceFilenames [ FrameIdx ] ;
2023-01-31 01:11:48 -05:00
2023-02-16 10:42:36 -05:00
UE_LOG ( LogSparseVolumeTextureFactory , Display , TEXT ( " Loading OpenVDB sequence frame #%i %s. " ) , FrameIdx , * FrameFilename ) ;
2023-01-31 01:11:48 -05:00
2023-02-22 09:39:24 -05:00
// Load file
2023-02-23 14:53:04 -05:00
TArray64 < uint8 > LoadedFrameFile ;
2023-02-16 10:42:36 -05:00
if ( ! FFileHelper : : LoadFileToArray ( LoadedFrameFile , * FrameFilename ) )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " OpenVDB file could not be loaded: %s " ) , * FrameFilename ) ;
bErrored . store ( true ) ;
return ;
}
2023-05-31 08:14:22 -04:00
UE : : SVT : : FTextureData TextureData { } ;
2024-02-08 16:08:23 -05:00
FTransform FrameTransform = FTransform : : Identity ;
const bool bConversionSuccess = ConvertOpenVDBToSparseVolumeTexture ( LoadedFrameFile , ImportOptions , VolumeBoundsMin , TextureData , FrameTransform ) ;
2023-02-16 10:42:36 -05:00
if ( ! bConversionSuccess )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Failed to convert OpenVDB file to SparseVolumeTexture: %s " ) , * FrameFilename ) ;
bErrored . store ( true ) ;
return ;
}
2023-03-24 12:42:28 -04:00
UncookedFramesData [ FrameIdx ] = MoveTemp ( TextureData ) ;
2024-02-08 16:08:23 -05:00
FrameTransforms [ FrameIdx ] = FrameTransform ;
2023-02-16 10:42:36 -05:00
2023-02-22 09:39:24 -05:00
// Increment ProcessedFramesCounter
2023-02-16 10:42:36 -05:00
ProcessedFramesCounter . fetch_add ( 1 ) ;
} ) ;
}
// Wait for frames to be processed
{
int NumFinishedTasks = 0 ;
int NumProcessedFrames = 0 ;
2023-06-15 09:39:59 -04:00
2023-02-16 10:42:36 -05:00
while ( NumFinishedTasks < NumFrames )
2023-01-31 01:11:48 -05:00
{
2023-02-16 10:42:36 -05:00
// We can't block here because we want to regularly update the progress bar and check for user input.
const uint32 WaitTimeMS = 2 ;
AllTasksFinishedEvent - > Wait ( WaitTimeMS ) ;
2023-01-24 21:44:51 -05:00
2023-02-16 10:42:36 -05:00
if ( ! bCanceled . load ( ) & & ! bErrored . load ( ) & & ImportTask . ShouldCancel ( ) )
{
bCanceled . store ( true ) ;
}
2023-01-24 21:44:51 -05:00
2023-02-16 10:42:36 -05:00
const int NewNumFinishedTasks = FinishedTasksCounter . load ( ) ;
if ( NewNumFinishedTasks > NumFinishedTasks )
{
const int NewNumProcessedFrames = ProcessedFramesCounter . load ( ) ;
if ( NewNumProcessedFrames > NumProcessedFrames & & ! bErrored . load ( ) )
{
const float Progress = float ( NewNumProcessedFrames - NumProcessedFrames ) ;
ImportTask . EnterProgressFrame ( Progress , LOCTEXT ( " ConvertingVDBAnim " , " Converting OpenVDB animation " ) ) ;
}
NumFinishedTasks = NewNumFinishedTasks ;
NumProcessedFrames = NewNumProcessedFrames ;
}
2023-01-24 21:44:51 -05:00
}
2023-02-16 10:42:36 -05:00
}
FPlatformProcess : : ReturnSynchEventToPool ( AllTasksFinishedEvent ) ;
if ( bCanceled . load ( ) )
{
bOutOperationCanceled = true ;
return nullptr ;
}
if ( bErrored . load ( ) )
{
return nullptr ;
2023-01-24 21:44:51 -05:00
}
2023-06-19 10:56:00 -04:00
// By default, the resulting package (already created) and object (about to be) will have the name of the imported file.
// However, since the file is part of an entire sequence that we imported, it would be confusing if the resulting asset was named "file_0000" instead of "file",
// so we attempt to rename both package and object here.
// Don't try to rename the SVT if we are doing a reimport.
FName NewObjectName = InName ;
if ( ! bIsReimport )
{
2023-09-11 11:09:14 -04:00
FString NewFileName = GetVDBSequenceBaseFileName ( Filename , false /*bDiscardNumbersOnly*/ ) ;
// GetVDBSequenceBaseFileName() discards the number as well as underscores and invalid chars at the end of the filename.
// We still need to ensure that there are no additional invalid characters in the filename.
NewFileName = ObjectTools : : SanitizeObjectName ( NewFileName ) ;
2023-06-19 10:56:00 -04:00
2023-09-11 11:09:14 -04:00
// Don't try to rename the package if it's the transient package or the new filename is empty
if ( ! NewFileName . IsEmpty ( ) & & ( InParent ! = GetTransientPackage ( ) & & InParent - > IsA < UPackage > ( ) ) )
2023-06-19 10:56:00 -04:00
{
const FString PackageName = InParent - > GetName ( ) ; // Contains name with path
int32 LastSeparatorIndex = 0 ;
const bool bFoundSeparator = PackageName . FindLastChar ( TEXT ( ' / ' ) , LastSeparatorIndex ) ;
// Get the substring containing the path only, without the file name
const FString PackagePath = bFoundSeparator ? PackageName . Left ( LastSeparatorIndex ) : FString ( ) ;
const FString NewPackageName = PackagePath / NewFileName ;
UPackage * ExistingPackage = FindPackage ( InParent - > GetOuter ( ) , * NewPackageName ) ;
if ( ! ExistingPackage )
{
2024-06-17 11:55:02 -04:00
InParent - > Rename ( * NewPackageName , nullptr , REN_DontCreateRedirectors ) ;
2023-06-19 10:56:00 -04:00
NewObjectName = * NewFileName ;
}
}
}
UAnimatedSparseVolumeTexture * AnimatedSVTexture = NewObject < UAnimatedSparseVolumeTexture > ( InParent , UAnimatedSparseVolumeTexture : : StaticClass ( ) , NewObjectName , Flags ) ;
2024-02-08 16:08:23 -05:00
const bool bInitSuccess = AnimatedSVTexture - > Initialize ( UncookedFramesData , FrameTransforms ) ;
2023-03-24 12:42:28 -04:00
if ( ! bInitSuccess )
{
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Failed to initialize SparseVolumeTexture: %s " ) , * Filename ) ;
return nullptr ;
}
2023-01-24 21:44:51 -05:00
ResultAssets . Add ( AnimatedSVTexture ) ;
}
2022-10-16 02:28:34 -04:00
// Now notify the system about the imported/updated/created assets
2023-02-07 08:59:18 -05:00
check ( ResultAssets . Num ( ) = = 1 ) ;
2023-06-15 09:39:59 -04:00
CastChecked < UStreamableSparseVolumeTexture > ( ResultAssets [ 0 ] ) - > AssetImportData - > Update ( Filename ) ;
2023-02-07 08:59:18 -05:00
GEditor - > GetEditorSubsystem < UImportSubsystem > ( ) - > BroadcastAssetPostImport ( this , ResultAssets [ 0 ] ) ;
2022-10-16 02:28:34 -04:00
2023-02-07 08:59:18 -05:00
return ResultAssets [ 0 ] ;
2022-10-16 02:28:34 -04:00
# else // OPENVDB_AVAILABLE
// SVT_TODO Make sure we can also import on more platforms such as Linux. See SparseVolumeTextureOpenVDB.h
UE_LOG ( LogSparseVolumeTextureFactory , Error , TEXT ( " Cannot import OpenVDB asset any platform other than Windows. " ) ) ;
return nullptr ;
# endif // OPENVDB_AVAILABLE
}
# endif // WITH_EDITORONLY_DATA
# undef LOCTEXT_NAMESPACE