Files
UnrealEngineUWP/Engine/Source/Developer/NaniteBuilder/Private/ImposterAtlas.cpp
Brian Karis dba24ab6be FDynamicBVH now supports a forest with a spatial hash at the highest level to support LWC without the need for doubles for bounding boxes.
Switched FDynamicBVH to use greedy insertion because branch and bound perf significantly degrades with heavily overlapped leaves.

Added closest point queries using branch and bound.

FBounds is now templated as TBounds.

#rb graham.wihlidal
#robomerge FNNC
#preflight 6216dbf9104496cff8abfc36

[CL 19107993 by Brian Karis in ue5-main branch]
2022-02-23 21:17:53 -05:00

164 lines
5.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ImposterAtlas.h"
#include "Rasterizer.h"
#include "Cluster.h"
inline FVector3f OctahedronToUnitVector( const FVector2D& Oct )
{
FVector3f N( Oct.X, Oct.Y, 1.0f - FMath::Abs( Oct.X ) - FMath::Abs( Oct.Y ) );
float t = FMath::Max( -N.Z, 0.0f );
N.X += N.X >= 0.0f ? -t : t;
N.Y += N.Y >= 0.0f ? -t : t;
return N.GetUnsafeNormal();
}
namespace Nanite
{
FImposterAtlas::FImposterAtlas( TArray< uint16 >& InPixels, const FBounds3f& Bounds )
: Pixels( InPixels )
{
BoundsCenter = 0.5f * ( Bounds.Max + Bounds.Min );
BoundsExtent = 0.5f * ( Bounds.Max - Bounds.Min );
Pixels.AddZeroed( FMath::Square( AtlasSize * TileSize ) );
}
FMatrix44f FImposterAtlas::GetLocalToImposter( const FIntPoint& TilePos ) const
{
FVector2D Oct = ( FVector2D( TilePos ) + 0.5f ) / AtlasSize * 2.0f - 1.0f;
FVector3f ImposterZ = OctahedronToUnitVector( Oct );
#if 0
// [Frisvad 2012, "Building an Orthonormal Basis from a 3D Unit Vector Without Normalization"]
// Invalid for ImposterZ.z == -1
float A = 1.0f / ( 1.0f + ImposterZ.Z );
float B = -ImposterZ.X * ImposterZ.Y * A;
FVector3f ImposterX( 1.0f - FMath::Square( ImposterZ.X ) * A, B, -ImposterZ.X );
FVector3f ImposterY( B, 1.0f - FMath::Square( ImposterZ.Y ) * A, -ImposterZ.Y );
#else
FVector3f ImposterX( 0.0f, 0.0f, 1.0f );
ImposterX -= ImposterZ.Z * ImposterZ;
ImposterX.Normalize();
FVector3f ImposterY = ImposterZ ^ ImposterX;
#endif
FVector3f ImposterExtent(
BoundsExtent | ImposterX.GetAbs(),
BoundsExtent | ImposterY.GetAbs(),
BoundsExtent | ImposterZ.GetAbs() );
ImposterExtent = FVector3f::Max(ImposterExtent, FVector3f(0.001f)); // Prevent division by zero
FMatrix44f LocalToImposter = FMatrix44f(
ImposterX,
ImposterY,
ImposterZ,
FVector3f::ZeroVector ).GetTransposed();
return FTranslationMatrix44f( -BoundsCenter ) * LocalToImposter * FScaleMatrix44f( FVector3f::OneVector / ImposterExtent );
}
void FImposterAtlas::Rasterize( const FIntPoint& TilePos, const FCluster& Cluster, uint32 ClusterIndex )
{
constexpr uint32 ViewSize = TileSize;// * SuperSample;
FIntRect Scissor( 0, 0, ViewSize, ViewSize );
FMatrix44f LocalToImposter = GetLocalToImposter( TilePos );
TArray< FVector3f, TInlineAllocator<128> > Positions;
Positions.SetNum( Cluster.NumVerts, false );
for( uint32 VertIndex = 0; VertIndex < Cluster.NumVerts; VertIndex++ )
{
FVector3f Position = Cluster.GetPosition( VertIndex );
//checkSlow( FBox( BoundsCenter - BoundsExtent, BoundsCenter + BoundsExtent ).ExpandBy( KINDA_SMALL_NUMBER ).IsInside( Position ) );
Position = LocalToImposter.TransformPosition( Position );
//checkSlow( Position.GetAbsMax() < 1.0001f );
// TODO Bake into matrix
Positions[ VertIndex ].X = ( Position.X * 0.5f + 0.5f ) * ViewSize;
Positions[ VertIndex ].Y = ( Position.Y * 0.5f + 0.5f ) * ViewSize;
Positions[ VertIndex ].Z = ( Position.Z * 0.5f + 0.5f ) * 254.0f + 1.0f; // zero is reserved as masked
}
for( uint32 TriIndex = 0; TriIndex < Cluster.NumTris; TriIndex++ )
{
FVector3f Verts[3];
Verts[0] = Positions[ Cluster.Indexes[ TriIndex * 3 + 0 ] ];
Verts[1] = Positions[ Cluster.Indexes[ TriIndex * 3 + 1 ] ];
Verts[2] = Positions[ Cluster.Indexes[ TriIndex * 3 + 2 ] ];
RasterizeTri( Verts, Scissor, 0,
[&]( int32 x, int32 y, float z )
{
uint32 Depth = FMath::RoundToInt( FMath::Clamp( z, 1.0f, 255.0f ) );
uint16 PixelValue = ( Depth << 8 ) | ( ClusterIndex << 7 ) | TriIndex;
//uint32 PixelIndex = x + y * ViewSize;
uint32 PixelIndex = x + ( y + ( TilePos.X + TilePos.Y * AtlasSize ) * TileSize ) * TileSize;
Pixels[ PixelIndex ] = FMath::Max( Pixels[ PixelIndex ], PixelValue );
} );
}
}
#if 0
void FImposterAtlas::DownSample( const FIntPoint& TilePos, TArray< uint16 >& Atlas ) const
{
constexpr uint32 ViewSize = TileSize * SuperSample;
for( int32 y = 0; y < TileSize; y++ )
{
for( int32 x = 0; x < TileSize; x++ )
{
TArray< uint8, TFixedAllocator< SuperSample * SuperSample > > UniqueTris;
uint8 Counts[ SuperSample * SuperSample ] = {};
uint32 SumDepth = 0;
uint32 SumCount = 0;
for( int32 sy = 0; sy < SuperSample; sy++ )
{
for( int32 sx = 0; sx < SuperSample; sx++ )
{
uint32 PixelIndex = ( sx + x * SuperSample ) + ( sy + y * SuperSample ) * ViewSize;
uint32 PixelValue = Pixels[ PixelIndex ];
uint32 TriIndex = PixelValue & 0xff;
uint32 Depth = PixelValue >> 8;
if( Depth )
{
Counts[ UniqueTris.AddUnique( TriIndex ) ]++;
SumDepth += Depth;
SumCount++;
}
}
}
uint16 PixelValue = 0;
if( SumCount >= SuperSample * SuperSample / 2 )
{
uint32 Depth = FMath::Clamp( ( SumDepth + (SumCount >> 1) ) / SumCount, 1u, 255u );
uint32 TriIndex = 0;
uint32 MaxCount = 0;
for( int i = 0; i < UniqueTris.Num(); i++ )
{
if( Counts[i] > MaxCount )
TriIndex = UniqueTris[i];
}
PixelValue = ( Depth << 8 ) | TriIndex;
}
uint32 AtlasIndex = x + ( y + ( TilePos.X + TilePos.Y * AtlasSize ) * TileSize ) * TileSize;
Atlas[ AtlasIndex ] = PixelValue;
}
}
}
#endif
} // namespace Nanite