2022-11-25 09:18:31 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# if WITH_EDITOR
# include "SparseVolumeTextureOpenVDBUtility.h"
# include "SparseVolumeTextureOpenVDB.h"
# include "SparseVolumeTexture/SparseVolumeTexture.h"
2023-01-10 15:51:23 -05:00
# include "OpenVDBGridAdapter.h"
2022-11-25 09:18:31 -05:00
DEFINE_LOG_CATEGORY_STATIC ( LogSparseVolumeTextureOpenVDBUtility , Log , All ) ;
# if OPENVDB_AVAILABLE
2023-01-12 13:06:52 -05:00
// We are using this instead of GMaxVolumeTextureDimensions to be independent of the platform that
// the asset is imported on. 2048 should be a safe value that should be supported by all our platforms.
static constexpr int32 SVTMaxVolumeTextureDim = 2048 ;
2022-11-25 09:18:31 -05:00
namespace
{
// Utility class acting as adapter between TArray<uint8> and std::istream
class FArrayUint8StreamBuf : public std : : streambuf
{
public :
explicit FArrayUint8StreamBuf ( TArray < uint8 > & Array )
{
char * Data = ( char * ) Array . GetData ( ) ;
setg ( Data , Data , Data + Array . Num ( ) ) ;
}
} ;
}
static FOpenVDBData GetOpenVDBData ( openvdb : : GridBase : : Ptr GridBase )
{
FOpenVDBData OpenVDBData ;
openvdb : : CoordBBox VolumeActiveAABB = GridBase - > evalActiveVoxelBoundingBox ( ) ;
openvdb : : Coord VolumeActiveDim = GridBase - > evalActiveVoxelDim ( ) ;
openvdb : : Vec3d VolumeVoxelSize = GridBase - > voxelSize ( ) ;
OpenVDBData . VolumeActiveAABBMin = FVector ( VolumeActiveAABB . min ( ) . x ( ) , VolumeActiveAABB . min ( ) . y ( ) , VolumeActiveAABB . min ( ) . z ( ) ) ;
OpenVDBData . VolumeActiveAABBMax = FVector ( VolumeActiveAABB . max ( ) . x ( ) , VolumeActiveAABB . max ( ) . y ( ) , VolumeActiveAABB . max ( ) . z ( ) ) ;
OpenVDBData . VolumeActiveDim = FVector ( VolumeActiveDim . x ( ) , VolumeActiveDim . y ( ) , VolumeActiveDim . z ( ) ) ;
OpenVDBData . VolumeVoxelSize = FVector ( VolumeVoxelSize . x ( ) , VolumeVoxelSize . y ( ) , VolumeVoxelSize . z ( ) ) ;
OpenVDBData . bIsInWorldSpace = GridBase - > isInWorldSpace ( ) ;
OpenVDBData . bHasUniformVoxels = GridBase - > hasUniformVoxels ( ) ;
return OpenVDBData ;
}
# endif
2023-01-10 15:51:23 -05:00
bool IsOpenVDBDataValid ( const FOpenVDBData & OpenVDBData , const FString & Filename )
2022-11-25 09:18:31 -05:00
{
if ( OpenVDBData . VolumeActiveDim . X * OpenVDBData . VolumeActiveDim . Y * OpenVDBData . VolumeActiveDim . Z = = 0 )
{
// SVT_TODO we should gently handle that case
2023-01-10 15:51:23 -05:00
UE_LOG ( LogSparseVolumeTextureOpenVDBUtility , Warning , TEXT ( " OpenVDB grid is empty due to volume size being 0: %s " ) , * Filename ) ;
2022-11-25 09:18:31 -05:00
return false ;
}
if ( ! OpenVDBData . bHasUniformVoxels )
{
2023-01-10 15:51:23 -05:00
UE_LOG ( LogSparseVolumeTextureOpenVDBUtility , Warning , TEXT ( " OpenVDB importer cannot handle non uniform voxels: %s " ) , * Filename ) ;
2022-11-25 09:18:31 -05:00
return false ;
}
return true ;
}
bool FindDensityGridIndex ( TArray < uint8 > & SourceFile , const FString & Filename , uint32 * OutGridIndex , FOpenVDBData * OutOVDBData )
{
# if OPENVDB_AVAILABLE
FArrayUint8StreamBuf StreamBuf ( SourceFile ) ;
std : : istream IStream ( & StreamBuf ) ;
openvdb : : io : : Stream Stream ( IStream , false /*delayLoad*/ ) ;
openvdb : : GridPtrVecPtr Grids = Stream . getGrids ( ) ;
if ( ! Grids )
{
UE_LOG ( LogSparseVolumeTextureOpenVDBUtility , Warning , TEXT ( " OpenVDB file contains no grids: %s " ) , * Filename ) ;
return false ;
}
uint32 GridIndex = 0 ;
openvdb : : GridBase : : Ptr BaseGrid = nullptr ;
for ( openvdb : : GridBase : : Ptr & Grid : * Grids )
{
if ( Grid - > getName ( ) = = " density " )
{
if ( Grid - > isType < openvdb : : FloatGrid > ( ) )
{
BaseGrid = Grid ;
break ;
}
break ;
}
+ + GridIndex ;
}
// If we have not found any density map, let's pick up the first float map
if ( ! BaseGrid )
{
GridIndex = 0 ;
for ( openvdb : : GridBase : : Ptr & Grid : * Grids )
{
if ( Grid - > isType < openvdb : : FloatGrid > ( ) )
{
BaseGrid = Grid ;
break ;
}
+ + GridIndex ;
}
}
if ( BaseGrid )
{
openvdb : : FloatGrid : : Ptr DensityGrid = openvdb : : gridPtrCast < openvdb : : FloatGrid > ( BaseGrid ) ;
// Only open float grid for now
if ( DensityGrid = = nullptr )
{
UE_LOG ( LogSparseVolumeTextureOpenVDBUtility , Error , TEXT ( " Could not convert the grid to float: %s " ) , * Filename ) ;
return false ;
}
FOpenVDBData OVDBData = GetOpenVDBData ( DensityGrid ) ;
if ( ! IsOpenVDBDataValid ( OVDBData , Filename ) )
{
return false ;
}
* OutGridIndex = GridIndex ;
* OutOVDBData = OVDBData ;
return true ;
}
# endif // OPENVDB_AVAILABLE
return false ;
}
2023-01-10 15:51:23 -05:00
bool GetOpenVDBGridInfo ( TArray < uint8 > & SourceFile , TArray < FOpenVDBGridInfo > * OutGridInfo )
2022-11-30 15:06:09 -05:00
{
# if OPENVDB_AVAILABLE
FArrayUint8StreamBuf StreamBuf ( SourceFile ) ;
std : : istream IStream ( & StreamBuf ) ;
openvdb : : io : : Stream Stream ( IStream , false /*delayLoad*/ ) ;
openvdb : : GridPtrVecPtr Grids = Stream . getGrids ( ) ;
2023-01-10 15:51:23 -05:00
OutGridInfo - > Empty ( Grids - > size ( ) ) ;
2022-11-30 15:06:09 -05:00
uint32 GridIndex = 0 ;
for ( openvdb : : GridBase : : Ptr & Grid : * Grids )
{
2023-01-10 15:51:23 -05:00
// Create entry
2022-11-30 15:06:09 -05:00
FOpenVDBGridInfo GridInfo ;
2022-12-01 19:25:56 -05:00
GridInfo . Index = GridIndex + + ;
2023-01-10 15:51:23 -05:00
GridInfo . NumComponents = 0 ;
GridInfo . Type = EOpenVDBGridType : : Unknown ;
GridInfo . Name = Grid - > getName ( ) . c_str ( ) ;
GridInfo . OpenVDBData = GetOpenVDBData ( Grid ) ;
2022-11-30 15:06:09 -05:00
// Figure out the type/format of the grid
2023-01-10 15:51:23 -05:00
if ( Grid - > isType < FOpenVDBFloat1Grid > ( ) )
2022-11-30 15:06:09 -05:00
{
2023-01-10 15:51:23 -05:00
GridInfo . NumComponents = 1 ;
GridInfo . Type = EOpenVDBGridType : : Float ;
2022-11-30 15:06:09 -05:00
}
2023-01-10 15:51:23 -05:00
else if ( Grid - > isType < FOpenVDBFloat2Grid > ( ) )
2022-11-30 15:06:09 -05:00
{
2023-01-10 15:51:23 -05:00
GridInfo . NumComponents = 2 ;
GridInfo . Type = EOpenVDBGridType : : Float2 ;
2022-12-01 19:25:56 -05:00
}
2023-01-10 15:51:23 -05:00
else if ( Grid - > isType < FOpenVDBFloat3Grid > ( ) )
2022-12-01 19:25:56 -05:00
{
2023-01-10 15:51:23 -05:00
GridInfo . NumComponents = 3 ;
GridInfo . Type = EOpenVDBGridType : : Float3 ;
2022-12-01 19:25:56 -05:00
}
2023-01-10 15:51:23 -05:00
else if ( Grid - > isType < FOpenVDBFloat4Grid > ( ) )
2022-12-01 19:25:56 -05:00
{
2023-01-10 15:51:23 -05:00
GridInfo . NumComponents = 4 ;
GridInfo . Type = EOpenVDBGridType : : Float4 ;
2022-12-01 19:25:56 -05:00
}
2023-01-10 15:51:23 -05:00
else if ( Grid - > isType < FOpenVDBDouble1Grid > ( ) )
2022-12-01 19:25:56 -05:00
{
2023-01-10 15:51:23 -05:00
GridInfo . NumComponents = 1 ;
GridInfo . Type = EOpenVDBGridType : : Double ;
2022-12-01 19:25:56 -05:00
}
2023-01-10 15:51:23 -05:00
else if ( Grid - > isType < FOpenVDBDouble2Grid > ( ) )
2022-12-01 19:25:56 -05:00
{
2023-01-10 15:51:23 -05:00
GridInfo . NumComponents = 2 ;
GridInfo . Type = EOpenVDBGridType : : Double2 ;
2022-12-01 19:25:56 -05:00
}
2023-01-10 15:51:23 -05:00
else if ( Grid - > isType < FOpenVDBDouble3Grid > ( ) )
2022-12-01 19:25:56 -05:00
{
2023-01-10 15:51:23 -05:00
GridInfo . NumComponents = 3 ;
GridInfo . Type = EOpenVDBGridType : : Double3 ;
2022-12-01 19:25:56 -05:00
}
2023-01-10 15:51:23 -05:00
else if ( Grid - > isType < FOpenVDBDouble4Grid > ( ) )
2022-12-01 19:25:56 -05:00
{
2023-01-10 15:51:23 -05:00
GridInfo . NumComponents = 4 ;
GridInfo . Type = EOpenVDBGridType : : Double4 ;
2022-12-01 19:25:56 -05:00
}
2022-11-30 15:06:09 -05:00
2023-01-10 15:51:23 -05:00
FStringFormatOrderedArguments FormatArgs ;
FormatArgs . Add ( GridInfo . Index ) ;
FormatArgs . Add ( OpenVDBGridTypeToString ( GridInfo . Type ) ) ;
FormatArgs . Add ( GridInfo . Name ) ;
2022-11-30 15:06:09 -05:00
2023-01-10 15:51:23 -05:00
GridInfo . DisplayString = FString : : Format ( TEXT ( " {0}. Type: {1}, Name: \" {2} \" " ) , FormatArgs ) ;
2022-11-30 15:06:09 -05:00
2023-01-10 15:51:23 -05:00
OutGridInfo - > Add ( MoveTemp ( GridInfo ) ) ;
2022-11-30 15:06:09 -05:00
}
return true ;
# endif // OPENVDB_AVAILABLE
return false ;
}
2022-12-01 19:25:56 -05:00
static EPixelFormat GetMultiComponentFormat ( ESparseVolumePackedDataFormat Format , uint32 NumComponents )
{
switch ( Format )
{
case ESparseVolumePackedDataFormat : : Unorm8 :
{
switch ( NumComponents )
{
case 1 : return PF_R8 ;
case 2 : return PF_R8G8 ;
case 3 :
case 4 : return PF_R8G8B8A8 ;
}
break ;
}
case ESparseVolumePackedDataFormat : : Float16 :
{
switch ( NumComponents )
{
case 1 : return PF_R16F ;
case 2 : return PF_G16R16F ;
case 3 :
case 4 : return PF_FloatRGBA ;
}
break ;
}
case ESparseVolumePackedDataFormat : : Float32 :
{
switch ( NumComponents )
{
case 1 : return PF_R32_FLOAT ;
case 2 : return PF_G32R32F ;
case 3 :
case 4 : return PF_A32B32G32R32F ;
}
break ;
}
}
return PF_Unknown ;
}
2023-01-12 13:06:52 -05:00
static uint32 PackPageTableEntry ( const FIntVector3 & Coord )
{
// A page encodes the physical tile coord as unsigned int of 11 11 10 bits
// This means a page coord cannot be larger than 2047 for x and y and 1023 for z
// which mean we cannot have more than 2048*2048*1024 = 4 Giga tiles of 16^3 tiles.
uint32 Result = ( Coord . X & 0x7FF ) | ( ( Coord . Y & 0x7FF ) < < 11 ) | ( ( Coord . Z & 0x3FF ) < < 22 ) ;
return Result ;
}
static FIntVector3 UnpackPageTableEntry ( uint32 Packed )
{
FIntVector3 Result ;
Result . X = Packed & 0x7FF ;
Result . Y = ( Packed > > 11 ) & 0x7FF ;
Result . Z = ( Packed > > 22 ) & 0x3FF ;
return Result ;
}
2022-11-25 09:18:31 -05:00
bool ConvertOpenVDBToSparseVolumeTexture (
TArray < uint8 > & SourceFile ,
2023-01-11 20:56:20 -05:00
FSparseVolumeRawSourcePackedData & PackedDataA ,
FSparseVolumeRawSourcePackedData & PackedDataB ,
FOpenVDBToSVTConversionResult * OutResult ,
2022-11-25 09:18:31 -05:00
bool bOverrideActiveMinMax ,
FVector ActiveMin ,
FVector ActiveMax )
{
# if OPENVDB_AVAILABLE
2022-12-01 19:25:56 -05:00
2023-01-11 20:56:20 -05:00
constexpr uint32 NumPackedData = 2 ; // PackedDataA and PackedDataB, representing the two textures with voxel data
FSparseVolumeRawSourcePackedData * PackedData [ NumPackedData ] = { & PackedDataA , & PackedDataB } ;
// Compute some basic info about the number of components and which format to use
uint32 NumActualComponents [ NumPackedData ] = { } ;
EPixelFormat MultiCompFormat [ NumPackedData ] = { } ;
uint32 FormatSize [ NumPackedData ] = { } ;
uint32 SingleComponentFormatSize [ NumPackedData ] = { } ;
bool bNormalizedFormat [ NumPackedData ] = { } ;
bool bHasValidSourceGrids [ NumPackedData ] = { } ;
bool bAnySourceGridIndicesValid = false ;
2023-01-12 13:06:52 -05:00
2023-01-11 20:56:20 -05:00
for ( uint32 PackedDataIdx = 0 ; PackedDataIdx < NumPackedData ; + + PackedDataIdx )
2022-12-01 19:25:56 -05:00
{
2023-01-11 20:56:20 -05:00
uint32 NumRequiredComponents = 0 ;
for ( uint32 ComponentIdx = 0 ; ComponentIdx < 4 ; + + ComponentIdx )
2022-12-01 19:25:56 -05:00
{
2023-01-11 20:56:20 -05:00
if ( PackedData [ PackedDataIdx ] - > SourceGridIndex [ ComponentIdx ] ! = INDEX_NONE )
{
check ( PackedData [ PackedDataIdx ] - > SourceComponentIndex [ ComponentIdx ] ! = INDEX_NONE ) ;
NumRequiredComponents = FMath : : Max ( ComponentIdx + 1 , NumRequiredComponents ) ;
bHasValidSourceGrids [ PackedDataIdx ] = true ;
bAnySourceGridIndicesValid = true ;
}
}
if ( bHasValidSourceGrids [ PackedDataIdx ] )
{
NumActualComponents [ PackedDataIdx ] = NumRequiredComponents = = 3 ? 4 : NumRequiredComponents ; // We don't support formats with only 3 components
bNormalizedFormat [ PackedDataIdx ] = PackedData [ PackedDataIdx ] - > Format = = ESparseVolumePackedDataFormat : : Unorm8 ;
MultiCompFormat [ PackedDataIdx ] = GetMultiComponentFormat ( PackedData [ PackedDataIdx ] - > Format , NumActualComponents [ PackedDataIdx ] ) ;
if ( MultiCompFormat [ PackedDataIdx ] = = PF_Unknown )
{
2023-01-12 13:06:52 -05:00
UE_LOG ( LogSparseVolumeTextureOpenVDBUtility , Warning , TEXT ( " SparseVolumeTexture is set to use an unsupported format: %i " ) , ( int32 ) PackedData [ PackedDataIdx ] - > Format ) ;
2023-01-11 20:56:20 -05:00
return false ;
}
FormatSize [ PackedDataIdx ] = ( uint32 ) GPixelFormats [ ( SIZE_T ) MultiCompFormat [ PackedDataIdx ] ] . BlockBytes ;
SingleComponentFormatSize [ PackedDataIdx ] = FormatSize [ PackedDataIdx ] / NumActualComponents [ PackedDataIdx ] ;
2022-12-01 19:25:56 -05:00
}
}
2023-01-11 20:56:20 -05:00
// All source grid indices are INDEX_NONE, so nothing was selected for import
if ( ! bAnySourceGridIndicesValid )
2022-12-01 19:25:56 -05:00
{
2023-01-12 13:06:52 -05:00
UE_LOG ( LogSparseVolumeTextureOpenVDBUtility , Warning , TEXT ( " SparseVolumeTexture has all components set to <None>, so there is nothing to import. " ) ) ;
2022-12-01 19:25:56 -05:00
return false ;
}
// Load file
2022-11-25 09:18:31 -05:00
FArrayUint8StreamBuf StreamBuf ( SourceFile ) ;
std : : istream IStream ( & StreamBuf ) ;
openvdb : : io : : Stream Stream ( IStream , false /*delayLoad*/ ) ;
2023-01-11 20:56:20 -05:00
// Check that the source grid indices are valid
2022-11-25 09:18:31 -05:00
openvdb : : GridPtrVecPtr Grids = Stream . getGrids ( ) ;
2022-12-01 19:25:56 -05:00
const size_t NumSourceGrids = Grids ? Grids - > size ( ) : 0 ;
2023-01-11 20:56:20 -05:00
for ( uint32 PackedDataIdx = 0 ; PackedDataIdx < NumPackedData ; + + PackedDataIdx )
2022-11-25 09:18:31 -05:00
{
2023-01-11 20:56:20 -05:00
for ( uint32 CompIdx = 0 ; CompIdx < 4 ; + + CompIdx )
{
const uint32 SourceGridIndex = PackedData [ PackedDataIdx ] - > SourceGridIndex [ CompIdx ] ;
if ( SourceGridIndex ! = INDEX_NONE & & SourceGridIndex > = NumSourceGrids )
{
2023-01-12 13:06:52 -05:00
UE_LOG ( LogSparseVolumeTextureOpenVDBUtility , Warning , TEXT ( " SparseVolumeTexture has invalid index into the array of grids in the source file: %i " ) , ( int32 ) SourceGridIndex ) ;
2023-01-11 20:56:20 -05:00
return false ;
}
}
2022-11-25 09:18:31 -05:00
}
2023-01-11 20:56:20 -05:00
FSparseVolumeAssetHeader & Header = * OutResult - > Header ;
Header . PackedDataAFormat = MultiCompFormat [ 0 ] ;
Header . PackedDataBFormat = MultiCompFormat [ 1 ] ;
2022-12-01 19:25:56 -05:00
Header . SourceVolumeResolution = FIntVector : : ZeroValue ;
FIntVector SmallestAABBMin = FIntVector ( INT32_MAX ) ;
2022-11-25 09:18:31 -05:00
2022-12-01 19:25:56 -05:00
// Compute per source grid data of up to 4 different grids (one per component)
2023-01-11 20:56:20 -05:00
TArray < TSharedPtr < IOpenVDBGridAdapterBase > > UniqueGridAdapters ;
2023-01-12 13:06:52 -05:00
UniqueGridAdapters . SetNum ( ( int32 ) NumSourceGrids ) ;
2023-01-11 20:56:20 -05:00
TSharedPtr < IOpenVDBGridAdapterBase > GridAdapters [ NumPackedData ] [ 4 ] { } ;
float GridBackgroundValues [ NumPackedData ] [ 4 ] { } ;
float NormalizeScale [ NumPackedData ] [ 4 ] { } ;
float NormalizeBias [ NumPackedData ] [ 4 ] { } ;
for ( uint32 PackedDataIdx = 0 ; PackedDataIdx < NumPackedData ; + + PackedDataIdx )
2022-12-01 19:25:56 -05:00
{
2023-01-11 20:56:20 -05:00
for ( uint32 CompIdx = 0 ; CompIdx < 4 ; + + CompIdx )
2022-12-01 19:25:56 -05:00
{
2023-01-11 20:56:20 -05:00
NormalizeScale [ PackedDataIdx ] [ CompIdx ] = 1.0f ;
const uint32 SourceGridIndex = PackedData [ PackedDataIdx ] - > SourceGridIndex [ CompIdx ] ;
const uint32 SourceComponentIndex = PackedData [ PackedDataIdx ] - > SourceComponentIndex [ CompIdx ] ;
if ( SourceGridIndex = = INDEX_NONE )
{
continue ;
}
2022-12-01 19:25:56 -05:00
2023-01-11 20:56:20 -05:00
openvdb : : GridBase : : Ptr GridBase = ( * Grids ) [ SourceGridIndex ] ;
2022-12-01 19:25:56 -05:00
2023-01-11 20:56:20 -05:00
// Try to reuse adapters. Internally they use caching to accelerate read accesses,
// so using three different adapters to access the three components of a single grid would be wasteful.
if ( UniqueGridAdapters [ SourceGridIndex ] = = nullptr )
{
UniqueGridAdapters [ SourceGridIndex ] = CreateOpenVDBGridAdapter ( GridBase ) ;
if ( ! UniqueGridAdapters [ SourceGridIndex ] )
{
return false ;
}
}
2022-12-01 19:25:56 -05:00
2023-01-11 20:56:20 -05:00
GridAdapters [ PackedDataIdx ] [ CompIdx ] = UniqueGridAdapters [ SourceGridIndex ] ;
2022-12-01 19:25:56 -05:00
2023-01-11 20:56:20 -05:00
FOpenVDBData OVDBData = GetOpenVDBData ( GridBase ) ;
if ( ! IsOpenVDBDataValid ( OVDBData , TEXT ( " " ) ) )
{
return false ;
}
2022-12-01 19:25:56 -05:00
2023-01-11 20:56:20 -05:00
if ( bOverrideActiveMinMax )
{
OVDBData . VolumeActiveAABBMin = ActiveMin ;
OVDBData . VolumeActiveAABBMax = ActiveMax ;
2023-01-12 13:06:52 -05:00
OVDBData . VolumeActiveDim = ActiveMax + 1 - ActiveMin ;
2023-01-11 20:56:20 -05:00
}
Header . SourceVolumeResolution . X = FMath : : Max ( Header . SourceVolumeResolution . X , OVDBData . VolumeActiveDim . X ) ;
Header . SourceVolumeResolution . Y = FMath : : Max ( Header . SourceVolumeResolution . Y , OVDBData . VolumeActiveDim . Y ) ;
Header . SourceVolumeResolution . Z = FMath : : Max ( Header . SourceVolumeResolution . Z , OVDBData . VolumeActiveDim . Z ) ;
SmallestAABBMin . X = FMath : : Min ( SmallestAABBMin . X , OVDBData . VolumeActiveAABBMin . X ) ;
SmallestAABBMin . Y = FMath : : Min ( SmallestAABBMin . Y , OVDBData . VolumeActiveAABBMin . Y ) ;
SmallestAABBMin . Z = FMath : : Min ( SmallestAABBMin . Z , OVDBData . VolumeActiveAABBMin . Z ) ;
GridBackgroundValues [ PackedDataIdx ] [ CompIdx ] = GridAdapters [ PackedDataIdx ] [ CompIdx ] - > GetBackgroundValue ( SourceComponentIndex ) ;
if ( bNormalizedFormat [ PackedDataIdx ] & & PackedData [ PackedDataIdx ] - > bRemapInputForUnorm )
{
float MinVal = 0.0f ;
float MaxVal = 0.0f ;
GridAdapters [ PackedDataIdx ] [ CompIdx ] - > GetMinMaxValue ( SourceComponentIndex , & MinVal , & MaxVal ) ;
const float Diff = MaxVal - MinVal ;
NormalizeScale [ PackedDataIdx ] [ CompIdx ] = MaxVal > SMALL_NUMBER ? ( 1.0f / Diff ) : 1.0f ;
NormalizeBias [ PackedDataIdx ] [ CompIdx ] = - MinVal * NormalizeScale [ PackedDataIdx ] [ CompIdx ] ;
}
2022-12-01 19:25:56 -05:00
}
}
2023-01-12 13:06:52 -05:00
FIntVector3 PageTableVolumeResolution = FIntVector3 (
2022-11-25 09:18:31 -05:00
FMath : : DivideAndRoundUp ( Header . SourceVolumeResolution . X , SPARSE_VOLUME_TILE_RES ) ,
FMath : : DivideAndRoundUp ( Header . SourceVolumeResolution . Y , SPARSE_VOLUME_TILE_RES ) ,
FMath : : DivideAndRoundUp ( Header . SourceVolumeResolution . Z , SPARSE_VOLUME_TILE_RES ) ) ;
2023-01-12 13:06:52 -05:00
if ( PageTableVolumeResolution . X > SVTMaxVolumeTextureDim
| | PageTableVolumeResolution . Y > SVTMaxVolumeTextureDim
| | PageTableVolumeResolution . Z > SVTMaxVolumeTextureDim )
{
UE_LOG ( LogSparseVolumeTextureOpenVDBUtility , Warning , TEXT ( " SparseVolumeTexture page table texture dimensions exceed limit (%ix%ix%i): %ix%ix%i " ) , SVTMaxVolumeTextureDim , SVTMaxVolumeTextureDim , SVTMaxVolumeTextureDim , PageTableVolumeResolution . X , PageTableVolumeResolution . Y , PageTableVolumeResolution . Z ) ;
return false ;
}
Header . PageTableVolumeResolution = PageTableVolumeResolution ;
2022-11-25 09:18:31 -05:00
Header . TileDataVolumeResolution = FIntVector : : ZeroValue ; // unknown for now
2023-01-12 13:06:52 -05:00
// Tag all pages with valid data
TBitArray PagesWithData ( false , Header . PageTableVolumeResolution . Z * Header . PageTableVolumeResolution . Y * Header . PageTableVolumeResolution . X ) ;
for ( uint32 GridIdx = 0 ; GridIdx < NumSourceGrids ; + + GridIdx )
{
if ( ! UniqueGridAdapters [ GridIdx ] )
{
continue ;
}
UniqueGridAdapters [ GridIdx ] - > IteratePhysical (
[ & ] ( const FIntVector3 & Coord , uint32 NumComponents , float * VoxelValues )
{
// Check if the source grid component is used at all
bool bValueIsUsed = false ;
for ( uint32 PackedDataIdx = 0 ; PackedDataIdx < NumPackedData & & ! bValueIsUsed ; + + PackedDataIdx )
{
for ( uint32 DstCompIdx = 0 ; DstCompIdx < NumActualComponents [ PackedDataIdx ] & & ! bValueIsUsed ; + + DstCompIdx )
{
for ( uint32 SrcCompIdx = 0 ; SrcCompIdx < NumComponents & & ! bValueIsUsed ; + + SrcCompIdx )
{
const bool bIsBackgroundValue = ( VoxelValues [ SrcCompIdx ] = = GridBackgroundValues [ PackedDataIdx ] [ DstCompIdx ] ) ;
bValueIsUsed | = ! bIsBackgroundValue & & ( PackedData [ PackedDataIdx ] - > SourceGridIndex [ DstCompIdx ] = = GridIdx ) & & ( PackedData [ PackedDataIdx ] - > SourceComponentIndex [ DstCompIdx ] = = SrcCompIdx ) ;
}
}
}
if ( ! bValueIsUsed )
{
return ;
}
const FIntVector3 GridCoord = Coord - SmallestAABBMin ;
check ( GridCoord . X > = 0 & & GridCoord . Y > = 0 & & GridCoord . Z > = 0 ) ;
check ( GridCoord . X < Header . SourceVolumeResolution . X & & GridCoord . Y < Header . SourceVolumeResolution . Y & & GridCoord . Z < Header . SourceVolumeResolution . Z ) ;
const FIntVector3 PageCoord = GridCoord / SPARSE_VOLUME_TILE_RES ;
check ( PageCoord . X > = 0 & & PageCoord . Y > = 0 & & PageCoord . Z > = 0 ) ;
check ( PageCoord . X < Header . PageTableVolumeResolution . X & & PageCoord . Y < Header . PageTableVolumeResolution . Y & & PageCoord . Z < Header . PageTableVolumeResolution . Z ) ;
const int32 PageIndex = PageCoord . Z * ( Header . PageTableVolumeResolution . Y * Header . PageTableVolumeResolution . X ) + PageCoord . Y * Header . PageTableVolumeResolution . X + PageCoord . X ;
PagesWithData [ PageIndex ] = true ;
} ) ;
}
2022-11-25 09:18:31 -05:00
// Allocate some memory for temp data (worst case)
TArray < FIntVector3 > LinearAllocatedPages ;
LinearAllocatedPages . SetNum ( Header . PageTableVolumeResolution . X * Header . PageTableVolumeResolution . Y * Header . PageTableVolumeResolution . Z ) ;
2022-12-01 19:25:56 -05:00
// Go over each potential page from the source data and push allocate it if it has any data.
2022-11-25 09:18:31 -05:00
// Otherwise point to the default empty page.
bool bAnyEmptyPageExists = false ;
2023-01-12 13:06:52 -05:00
uint32 NumAllocatedPages = 0 ;
2022-11-25 09:18:31 -05:00
for ( int32_t PageZ = 0 ; PageZ < Header . PageTableVolumeResolution . Z ; + + PageZ )
{
for ( int32_t PageY = 0 ; PageY < Header . PageTableVolumeResolution . Y ; + + PageY )
{
for ( int32_t PageX = 0 ; PageX < Header . PageTableVolumeResolution . X ; + + PageX )
{
2023-01-12 13:06:52 -05:00
const int32 PageIndex = PageZ * ( Header . PageTableVolumeResolution . Y * Header . PageTableVolumeResolution . X ) + PageY * Header . PageTableVolumeResolution . X + PageX ;
bool bHasAnyData = PagesWithData [ PageIndex ] ;
2022-11-25 09:18:31 -05:00
if ( bHasAnyData )
{
LinearAllocatedPages [ NumAllocatedPages ] = FIntVector3 ( PageX , PageY , PageZ ) ;
NumAllocatedPages + + ;
}
bAnyEmptyPageExists | = ! bHasAnyData ;
}
}
}
// Compute Page and Tile VolumeResolution from allocated pages
const uint32 EffectivelyAllocatedPageEntries = NumAllocatedPages + ( bAnyEmptyPageExists ? 1 : 0 ) ;
uint32 TileVolumeResolutionCube = 1 ;
while ( TileVolumeResolutionCube * TileVolumeResolutionCube * TileVolumeResolutionCube < EffectivelyAllocatedPageEntries )
{
TileVolumeResolutionCube + + ; // We use a simple loop to compute the minimum resolution of a cube to store all the tile data
}
Header . TileDataVolumeResolution = FIntVector3 ( TileVolumeResolutionCube , TileVolumeResolutionCube , TileVolumeResolutionCube ) ;
while ( Header . TileDataVolumeResolution . X * Header . TileDataVolumeResolution . Y * ( Header . TileDataVolumeResolution . Z - 1 ) > int32 ( EffectivelyAllocatedPageEntries ) )
{
Header . TileDataVolumeResolution . Z - - ; // We then trim an edge to get back space.
}
const FIntVector3 TileCoordResolution = Header . TileDataVolumeResolution ;
Header . TileDataVolumeResolution = Header . TileDataVolumeResolution * SPARSE_VOLUME_TILE_RES ;
2023-01-12 13:06:52 -05:00
if ( Header . TileDataVolumeResolution . X > SVTMaxVolumeTextureDim
| | Header . TileDataVolumeResolution . Y > SVTMaxVolumeTextureDim
| | Header . TileDataVolumeResolution . Z > SVTMaxVolumeTextureDim )
{
UE_LOG ( LogSparseVolumeTextureOpenVDBUtility , Warning , TEXT ( " SparseVolumeTexture tile data texture dimensions exceed limit (%ix%ix%i): %ix%ix%i " ) , SVTMaxVolumeTextureDim , SVTMaxVolumeTextureDim , SVTMaxVolumeTextureDim , Header . TileDataVolumeResolution . X , Header . TileDataVolumeResolution . Y , Header . TileDataVolumeResolution . Z ) ;
return false ;
}
2022-11-25 09:18:31 -05:00
// Initialise the SparseVolumeTexture page and tile.
2023-01-11 20:56:20 -05:00
OutResult - > PageTable - > SetNumZeroed ( Header . PageTableVolumeResolution . X * Header . PageTableVolumeResolution . Y * Header . PageTableVolumeResolution . Z ) ;
OutResult - > PhysicalTileDataA - > SetNumZeroed ( Header . TileDataVolumeResolution . X * Header . TileDataVolumeResolution . Y * Header . TileDataVolumeResolution . Z * FormatSize [ 0 ] * ( bHasValidSourceGrids [ 0 ] ? 1 : 0 ) ) ;
OutResult - > PhysicalTileDataB - > SetNumZeroed ( Header . TileDataVolumeResolution . X * Header . TileDataVolumeResolution . Y * Header . TileDataVolumeResolution . Z * FormatSize [ 1 ] * ( bHasValidSourceGrids [ 1 ] ? 1 : 0 ) ) ;
uint32 * PageTablePtr = OutResult - > PageTable - > GetData ( ) ;
uint8 * PhysicalTileDataPtrs [ ] = { OutResult - > PhysicalTileDataA - > GetData ( ) , OutResult - > PhysicalTileDataB - > GetData ( ) } ;
2022-11-25 09:18:31 -05:00
2023-01-12 13:06:52 -05:00
// Generate page table and tile volume data by splatting the data
2022-11-25 09:18:31 -05:00
{
2023-01-12 13:06:52 -05:00
FIntVector3 DstTileCoord = FIntVector3 : : ZeroValue ;
auto GoToNextTileCoord = [ & ] ( )
2022-11-25 09:18:31 -05:00
{
2023-01-12 13:06:52 -05:00
DstTileCoord . X + + ;
if ( DstTileCoord . X > = TileCoordResolution . X )
{
DstTileCoord . X = 0 ;
DstTileCoord . Y + + ;
}
if ( DstTileCoord . Y > = TileCoordResolution . Y )
{
DstTileCoord . Y = 0 ;
DstTileCoord . Z + + ;
}
} ;
2022-11-25 09:18:31 -05:00
2023-01-12 13:06:52 -05:00
// Add an empty tile is needed, reserve slot at coord 0
if ( bAnyEmptyPageExists )
{
// PageTable is all cleared to zero, simply skip a tile
GoToNextTileCoord ( ) ;
}
for ( uint32 i = 0 ; i < NumAllocatedPages ; + + i )
{
FIntVector3 PageCoordToSplat = LinearAllocatedPages [ i ] ;
uint32 DestinationTileCoord32bit = PackPageTableEntry ( DstTileCoord ) ;
// Setup the page table entry
PageTablePtr
[
PageCoordToSplat . Z * Header . PageTableVolumeResolution . X * Header . PageTableVolumeResolution . Y +
PageCoordToSplat . Y * Header . PageTableVolumeResolution . X +
PageCoordToSplat . X
] = DestinationTileCoord32bit ;
// Set the next tile to be written to
GoToNextTileCoord ( ) ;
}
2022-11-25 09:18:31 -05:00
}
2023-01-12 13:06:52 -05:00
for ( uint32 GridIdx = 0 ; GridIdx < NumSourceGrids ; + + GridIdx )
2022-11-25 09:18:31 -05:00
{
2023-01-12 13:06:52 -05:00
if ( ! UniqueGridAdapters [ GridIdx ] )
2022-11-25 09:18:31 -05:00
{
2023-01-12 13:06:52 -05:00
continue ;
}
UniqueGridAdapters [ GridIdx ] - > IteratePhysical (
[ & ] ( const FIntVector3 & Coord , uint32 NumComponents , float * VoxelValues )
2022-11-25 09:18:31 -05:00
{
2023-01-12 13:06:52 -05:00
const FIntVector3 GridCoord = Coord - SmallestAABBMin ;
check ( GridCoord . X > = 0 & & GridCoord . Y > = 0 & & GridCoord . Z > = 0 ) ;
check ( GridCoord . X < Header . SourceVolumeResolution . X & & GridCoord . Y < Header . SourceVolumeResolution . Y & & GridCoord . Z < Header . SourceVolumeResolution . Z ) ;
const FIntVector3 PageCoord = GridCoord / SPARSE_VOLUME_TILE_RES ;
check ( PageCoord . X > = 0 & & PageCoord . Y > = 0 & & PageCoord . Z > = 0 ) ;
check ( PageCoord . X < Header . PageTableVolumeResolution . X & & PageCoord . Y < Header . PageTableVolumeResolution . Y & & PageCoord . Z < Header . PageTableVolumeResolution . Z ) ;
const int32 PageIndex = PageCoord . Z * ( Header . PageTableVolumeResolution . Y * Header . PageTableVolumeResolution . X ) + PageCoord . Y * Header . PageTableVolumeResolution . X + PageCoord . X ;
if ( ! PagesWithData [ PageIndex ] )
2022-11-25 09:18:31 -05:00
{
2023-01-12 13:06:52 -05:00
return ;
}
const FIntVector3 DstTileCoord = UnpackPageTableEntry ( PageTablePtr [ PageIndex ] ) ;
const FIntVector3 TileLocalCoord = GridCoord % SPARSE_VOLUME_TILE_RES ;
// Check all output components and splat VoxelValue if they map to this source grid/component
for ( uint32 PackedDataIdx = 0 ; PackedDataIdx < NumPackedData ; + + PackedDataIdx )
{
for ( uint32 DstCompIdx = 0 ; DstCompIdx < NumActualComponents [ PackedDataIdx ] ; + + DstCompIdx )
2022-12-01 19:25:56 -05:00
{
2023-01-12 13:06:52 -05:00
for ( uint32 SrcCompIdx = 0 ; SrcCompIdx < NumComponents ; + + SrcCompIdx )
2022-12-01 19:25:56 -05:00
{
2023-01-12 13:06:52 -05:00
if ( ( PackedData [ PackedDataIdx ] - > SourceGridIndex [ DstCompIdx ] ! = GridIdx ) | | ( PackedData [ PackedDataIdx ] - > SourceComponentIndex [ DstCompIdx ] ! = SrcCompIdx ) )
2023-01-11 20:56:20 -05:00
{
2023-01-12 13:06:52 -05:00
continue ;
2023-01-11 20:56:20 -05:00
}
2022-12-01 19:25:56 -05:00
2023-01-12 13:06:52 -05:00
const float VoxelValueNormalized = FMath : : Clamp ( VoxelValues [ SrcCompIdx ] * NormalizeScale [ PackedDataIdx ] [ DstCompIdx ] + NormalizeBias [ PackedDataIdx ] [ DstCompIdx ] , 0.0f , 1.0f ) ;
const size_t DstVoxelIndex =
( DstTileCoord . Z * SPARSE_VOLUME_TILE_RES + TileLocalCoord . Z ) * Header . TileDataVolumeResolution . X * Header . TileDataVolumeResolution . Y +
( DstTileCoord . Y * SPARSE_VOLUME_TILE_RES + TileLocalCoord . Y ) * Header . TileDataVolumeResolution . X +
( DstTileCoord . X * SPARSE_VOLUME_TILE_RES + TileLocalCoord . X ) ;
const size_t DstCoord = DstVoxelIndex * FormatSize [ PackedDataIdx ] + DstCompIdx * SingleComponentFormatSize [ PackedDataIdx ] ;
2022-12-01 19:25:56 -05:00
2023-01-11 20:56:20 -05:00
switch ( PackedData [ PackedDataIdx ] - > Format )
{
case ESparseVolumePackedDataFormat : : Unorm8 :
{
2023-01-12 13:06:52 -05:00
PhysicalTileDataPtrs [ PackedDataIdx ] [ DstCoord ] = uint8 ( VoxelValueNormalized * 255.0f ) ;
2023-01-11 20:56:20 -05:00
break ;
}
case ESparseVolumePackedDataFormat : : Float16 :
{
2023-01-12 13:06:52 -05:00
const uint16 VoxelValue16FEncoded = FFloat16 ( VoxelValues [ SrcCompIdx ] ) . Encoded ;
* ( ( uint16 * ) ( & PhysicalTileDataPtrs [ PackedDataIdx ] [ DstCoord ] ) ) = VoxelValue16FEncoded ;
2023-01-11 20:56:20 -05:00
break ;
}
case ESparseVolumePackedDataFormat : : Float32 :
{
2023-01-12 13:06:52 -05:00
* ( ( float * ) ( & PhysicalTileDataPtrs [ PackedDataIdx ] [ DstCoord ] ) ) = VoxelValues [ SrcCompIdx ] ;
2023-01-11 20:56:20 -05:00
break ;
}
default :
checkNoEntry ( ) ;
break ;
}
2022-12-01 19:25:56 -05:00
}
}
2022-11-25 09:18:31 -05:00
}
2023-01-12 13:06:52 -05:00
} ) ;
2022-11-25 09:18:31 -05:00
}
return true ;
# else
return false ;
# endif // OPENVDB_AVAILABLE
}
2023-01-10 15:51:23 -05:00
const TCHAR * OpenVDBGridTypeToString ( EOpenVDBGridType Type )
2022-11-30 15:06:09 -05:00
{
2023-01-10 15:51:23 -05:00
switch ( Type )
2022-11-30 15:06:09 -05:00
{
2023-01-10 15:51:23 -05:00
case EOpenVDBGridType : : Float :
2022-11-30 15:06:09 -05:00
return TEXT ( " Float " ) ;
2023-01-10 15:51:23 -05:00
case EOpenVDBGridType : : Float2 :
return TEXT ( " Float2 " ) ;
case EOpenVDBGridType : : Float3 :
return TEXT ( " Float3 " ) ;
case EOpenVDBGridType : : Float4 :
return TEXT ( " Float4 " ) ;
case EOpenVDBGridType : : Double :
2022-12-01 19:25:56 -05:00
return TEXT ( " Double " ) ;
2023-01-10 15:51:23 -05:00
case EOpenVDBGridType : : Double2 :
return TEXT ( " Double2 " ) ;
case EOpenVDBGridType : : Double3 :
return TEXT ( " Double3 " ) ;
case EOpenVDBGridType : : Double4 :
return TEXT ( " Double4 " ) ;
2022-11-30 15:06:09 -05:00
default :
return TEXT ( " Unknown " ) ;
}
}
2022-11-25 09:18:31 -05:00
# endif // WITH_EDITOR