836 lines
20 KiB
HLSL
836 lines
20 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "/Engine/Public/Platform.ush"
|
|
|
|
ByteAddressBuffer BlockBuffer;
|
|
Texture3D<float> DistValues;
|
|
|
|
void BlockBounds( uint2 Block, out uint3 Min, out uint3 Max )
|
|
{
|
|
// Block is 64bits split into 2 uints to represent 4x4x4.
|
|
// Bit = x + y * 4 + z * 16;
|
|
|
|
// Min is inclusive. Max is exclusive.
|
|
|
|
const uint Low = Block.x ? Block.x : Block.y;
|
|
const uint High = Block.y ? Block.y : Block.x;
|
|
Min.z = ( firstbitlow( Low ) >> 4 ) + ( Block.x ? 0 : 2 ); //TODO: Could be slightly more efficient using 64-bit intrinsic (where available)
|
|
Max.z = ( firstbithigh( High ) >> 4 ) + ( Block.y ? 3 : 1 );
|
|
|
|
uint Bits = Block.x | Block.y;
|
|
Bits = ( Bits | ( Bits << 16 ) );
|
|
Min.y = firstbitlow( Bits >> 16 ) >> 2;
|
|
Max.y = (firstbithigh( Bits ) >> 2 ) - 4 + 1;
|
|
|
|
Bits = ( Bits | ( Bits << 8 ) );
|
|
Bits = ( Bits | ( Bits << 4 ) );
|
|
Min.x = firstbitlow( Bits >> 28 );
|
|
Max.x = firstbithigh( Bits ) - 28 + 1;
|
|
}
|
|
|
|
struct FRay
|
|
{
|
|
float3 Origin;
|
|
float3 Direction;
|
|
|
|
float2 Time;
|
|
};
|
|
|
|
struct FDDA
|
|
{
|
|
float2 Time;
|
|
|
|
bool3 IsPositive;
|
|
int3 Step;
|
|
uint VoxelIndex;
|
|
|
|
float3 InvDir;
|
|
float3 Next;
|
|
};
|
|
|
|
FDDA InitDDA( FRay Ray )
|
|
{
|
|
FDDA DDA;
|
|
DDA.Time = Ray.Time;
|
|
DDA.IsPositive = Ray.Direction >= 0;
|
|
DDA.Step = select( DDA.IsPositive, int3( 1, 4, 16 ), -int3( 1, 4, 16 ) );
|
|
DDA.VoxelIndex = 0;
|
|
DDA.InvDir = rcp( Ray.Direction );
|
|
return DDA;
|
|
}
|
|
|
|
void StartDDA( inout FDDA DDA, const uint Dimension, const FRay Ray )
|
|
{
|
|
float3 Position = Ray.Origin + Ray.Direction * DDA.Time[0];
|
|
int3 Voxel = floor( Position ); // v_cvt_flr_i32_f32
|
|
|
|
DDA.VoxelIndex = uint( ( Voxel.x + Voxel.y * 4 ) + Voxel.z * 16 );
|
|
DDA.InvDir = rcp( Ray.Direction );
|
|
|
|
float3 Frac = select_internal( DDA.IsPositive, Dimension, 0.0f ) - frac( Position );
|
|
|
|
DDA.Next = DDA.Time[0] + Frac * DDA.InvDir;
|
|
}
|
|
|
|
float NextTime( FDDA DDA )
|
|
{
|
|
return min3( DDA.Next.x, DDA.Next.y, DDA.Next.z );
|
|
}
|
|
|
|
void StepDDA( inout FDDA DDA, const uint Dimension )
|
|
{
|
|
DDA.Time[0] = NextTime( DDA );
|
|
|
|
#if 0
|
|
#if 0
|
|
bool3 bLeast = DDA.Next.xyz <= min( DDA.Next.yzx, DDA.Next.zxy );
|
|
//float3 Mask = float3( (DDA.Next.xyz < DDA.Next.yzx) & (DDA.Next.xyz <= DDA.Next.zxy) );
|
|
//#elif 0
|
|
//bool3 bLeast = DDA.Next.xxy < DDA.Next.yzz;
|
|
//bLeast.x = bLeast.x && bLeast.y;
|
|
//bLeast.y = !bLeast.x && bLeast.z;
|
|
//bLeast.z = !bLeast.x && !bLeast.y;
|
|
float3 Mask = bLeast ? Dimension : 0;
|
|
#else
|
|
float3 Mask = 0;
|
|
FLATTEN
|
|
if( DDA.Next.x < min( DDA.Next.y, DDA.Next.z ) )
|
|
Mask.x = Dimension;
|
|
else if( DDA.Next.y < DDA.Next.z )
|
|
Mask.y = Dimension;
|
|
else
|
|
Mask.z = Dimension;
|
|
#endif
|
|
|
|
DDA.Next += Mask * abs( DDA.InvDir );
|
|
DDA.VoxelIndex += dot( Mask, DDA.Step );
|
|
#else
|
|
|
|
BRANCH
|
|
if( DDA.Next.x == DDA.Time[0] )
|
|
{
|
|
DDA.Next.x += abs( DDA.InvDir.x );
|
|
DDA.VoxelIndex += DDA.Step.x;
|
|
}
|
|
else
|
|
{
|
|
BRANCH
|
|
if( DDA.Next.y == DDA.Time[0] )
|
|
{
|
|
DDA.Next.y += abs( DDA.InvDir.y );
|
|
DDA.VoxelIndex += DDA.Step.y;
|
|
}
|
|
else
|
|
{
|
|
DDA.Next.z += abs( DDA.InvDir.z );
|
|
DDA.VoxelIndex += DDA.Step.z;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uint PrefixSum64( uint2 Occupency, uint Index, uint Mask )
|
|
{
|
|
uint Offset;
|
|
Mask -= 1;
|
|
Offset = countbits( Occupency.x & ( Index < 32 ? Mask : ~0u ) );
|
|
Offset += countbits( Occupency.y & ( Index < 32 ? 0 : Mask ) );
|
|
return Offset;
|
|
}
|
|
|
|
uint PrefixSum64( uint2 Occupency, uint Index )
|
|
{
|
|
uint Mask = 1u << ( Index & 31 );
|
|
return PrefixSum64( Occupency, Index, Mask );
|
|
}
|
|
|
|
float2 RayUnitBox( FRay Ray )
|
|
{
|
|
#if 0
|
|
float3 Origin = Ray.Direction >= 0 ? -Ray.Origin : Ray.Origin;
|
|
float3 InvDir = rcp( abs( Ray.Direction ) );
|
|
float3 MinIntersection = ( Origin - 1 ) * InvDir;
|
|
float3 MaxIntersection = ( Origin + 1 ) * InvDir;
|
|
#else
|
|
float3 InvDir = rcp( Ray.Direction );
|
|
float3 Center = -Ray.Origin * InvDir;
|
|
float3 Extent = abs( InvDir );
|
|
float3 MinIntersection = Center - Extent;
|
|
float3 MaxIntersection = Center + Extent;
|
|
#endif
|
|
Ray.Time[0] = max( Ray.Time[0], max3( MinIntersection.x, MinIntersection.y, MinIntersection.z ) );
|
|
Ray.Time[1] = min( Ray.Time[1], min3( MaxIntersection.x, MaxIntersection.y, MaxIntersection.z ) );
|
|
return Ray.Time;
|
|
}
|
|
|
|
float2 Intersect( FRay Ray, float3 BoundsCenter, float3 BoundsExtent )
|
|
{
|
|
Ray.Origin -= BoundsCenter;
|
|
|
|
float3 InvDir = rcp( Ray.Direction );
|
|
float3 Center = -Ray.Origin * InvDir;
|
|
float3 Extent = abs( InvDir ) * BoundsExtent;
|
|
float3 MinIntersection = Center - Extent;
|
|
float3 MaxIntersection = Center + Extent;
|
|
|
|
return float2(
|
|
max3( MinIntersection.x, MinIntersection.y, MinIntersection.z ),
|
|
min3( MaxIntersection.x, MaxIntersection.y, MaxIntersection.z ) );
|
|
}
|
|
|
|
float2 Intersect( FRay Ray, float3 Bounds[2] )
|
|
{
|
|
float3 InvDir = rcp( Ray.Direction );
|
|
float3 PlaneIntersect0 = ( Bounds[0] - Ray.Origin ) * InvDir;
|
|
float3 PlaneIntersect1 = ( Bounds[1] - Ray.Origin ) * InvDir;
|
|
float3 MinIntersection = min( PlaneIntersect0, PlaneIntersect1 );
|
|
float3 MaxIntersection = max( PlaneIntersect0, PlaneIntersect1 );
|
|
|
|
Ray.Time[0] = max( Ray.Time[0], max3( MinIntersection.x, MinIntersection.y, MinIntersection.z ) );
|
|
Ray.Time[1] = min( Ray.Time[1], min3( MaxIntersection.x, MaxIntersection.y, MaxIntersection.z ) );
|
|
|
|
return Ray.Time;
|
|
}
|
|
|
|
|
|
struct FRayCastCounters
|
|
{
|
|
int Tests;
|
|
uint Steps;
|
|
uint StepIn;
|
|
uint StepOut;
|
|
};
|
|
|
|
// Assumes Ray in local 0-1 space
|
|
float RayCastBrick_L1( FRay Ray, uint BrickOffset, inout FRayCastCounters Counters )
|
|
{
|
|
const float Epsilon = 1e-4;
|
|
|
|
uint2 NodeBlock = BlockBuffer.Load2( 8 * BrickOffset );
|
|
|
|
Ray.Time += float2( Epsilon, -Epsilon );
|
|
|
|
Ray.Origin *= 4;
|
|
Ray.Direction *= 4;
|
|
|
|
FDDA DDA = InitDDA( Ray );
|
|
StartDDA( DDA, 1, Ray );
|
|
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= 4*3 )
|
|
{
|
|
Counters.Tests++;
|
|
|
|
uint VoxelMask = 1u << ( DDA.VoxelIndex & 31 );
|
|
bool bVoxelExists = ( DDA.VoxelIndex < 32 ? NodeBlock.x : NodeBlock.y ) & VoxelMask;
|
|
|
|
if( bVoxelExists )
|
|
{
|
|
return DDA.Time[0];
|
|
#if 0
|
|
#if 0
|
|
uint ValuesOffset = 2;
|
|
ValuesOffset = countbits( RootBlock.x );
|
|
ValuesOffset += countbits( RootBlock.y );
|
|
|
|
uint BlockOffset = PrefixSum64( NodeBlock, VoxelIndex, VoxelMask );
|
|
#endif
|
|
|
|
float2 Time = { DDA.Time[0], NextTime( DDA ) };
|
|
//Time += float2( Epsilon, -Epsilon );
|
|
|
|
// TODO transform to volume space
|
|
|
|
float Value[2];
|
|
Value[0] = DistValues.SampleLevel( View.SharedBilinearClampedSampler, Ray.Origin + Ray.Direction * Time[0], 0 ) - 0.5;
|
|
Value[1] = DistValues.SampleLevel( View.SharedBilinearClampedSampler, Ray.Origin + Ray.Direction * Time[1], 0 ) - 0.5;
|
|
|
|
// Zero crossing
|
|
//if( Value[0] * Value[1] < 0 )
|
|
if( Value[1] < 0 ) // Assume Value[0] > 0
|
|
{
|
|
Ray.Time[0] = Time[0] + ( Time[1] - Time[0] ) * Value[0] / ( Value[0] - Value[1] );
|
|
return true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Counters.Steps++;
|
|
#if 1
|
|
StepDDA( DDA, 1 );
|
|
#elif 1
|
|
DDA.Time[0] = NextTime( DDA );
|
|
|
|
if( DDA.Time[0] >= DDA.Time[1] )
|
|
break;
|
|
|
|
bool3 bLeast = DDA.Next.xyz <= min( DDA.Next.yzx, DDA.Next.zxy );
|
|
float3 Mask = bLeast;
|
|
|
|
DDA.Next += Mask * abs( DDA.InvDir );
|
|
DDA.VoxelIndex += dot( Mask, DDA.Step );
|
|
#else
|
|
{
|
|
DDA.Time[0] = NextTime( DDA );
|
|
|
|
FLATTEN
|
|
if( DDA.Next.x < DDA.Next.y && DDA.Next.x < DDA.Next.z )
|
|
{
|
|
DDA.Next.x += abs( DDA.InvDir.x );
|
|
DDA.VoxelIndex += DDA.Step.x;
|
|
}
|
|
else if( DDA.Next.y < DDA.Next.z )
|
|
{
|
|
DDA.Next.y += abs( DDA.InvDir.y );
|
|
DDA.VoxelIndex += DDA.Step.y;
|
|
}
|
|
else
|
|
{
|
|
DDA.Next.z += abs( DDA.InvDir.z );
|
|
DDA.VoxelIndex += DDA.Step.z;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return Ray.Time[1] + 1.0;
|
|
}
|
|
|
|
// Assumes Ray in local 0-1 space
|
|
float RayCastBrick_L1_YZ( FRay Ray, uint BrickOffset, inout FRayCastCounters Counters )
|
|
{
|
|
const float Epsilon = 1e-4;
|
|
|
|
uint2 NodeBlock = BlockBuffer.Load2( 8 * BrickOffset );
|
|
|
|
Ray.Time += float2( Epsilon, -Epsilon );
|
|
|
|
Ray.Origin *= 4;
|
|
Ray.Direction *= 4;
|
|
|
|
FDDA DDA = InitDDA( Ray );
|
|
//StartDDA( DDA, 1, Ray );
|
|
|
|
float3 Position = Ray.Origin + Ray.Direction * DDA.Time[0];
|
|
float3 Voxel = floor( Position );
|
|
|
|
DDA.VoxelIndex = uint( Voxel.y + Voxel.z * 4 ) << 2;
|
|
DDA.Next = DDA.Time[0] + ( ( Voxel + DDA.IsPositive ) - Position ) * DDA.InvDir;
|
|
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= 4*3 )
|
|
{
|
|
Counters.Tests++;
|
|
|
|
float ColumnTime0 = DDA.Time[0];
|
|
float ColumnTime1 = min( DDA.Next.y, DDA.Next.z );
|
|
|
|
float ColumnX0 = Ray.Origin.x + Ray.Direction.x * ColumnTime0;
|
|
float ColumnX1 = Ray.Origin.x + Ray.Direction.x * ColumnTime1;
|
|
|
|
uint x0 = uint( clamp( floor( ColumnX0 ), 0, 4 ) );
|
|
uint x1 = uint( clamp( ceil( ColumnX1 ), 0, 4 ) );
|
|
uint Mask0 = ( 0b1111u << x0 ) & 0b1111u;
|
|
uint Mask1 = ( 0b1111u << x1 ) & 0b1111u;
|
|
uint LineMask = Mask0 & ~Mask1;
|
|
|
|
uint RayMask = LineMask << ( DDA.VoxelIndex & 31 );
|
|
uint HitMask = ( DDA.VoxelIndex < 32 ? NodeBlock.x : NodeBlock.y ) & RayMask;
|
|
if( HitMask )
|
|
{
|
|
if( DDA.IsPositive.x )
|
|
HitMask ^= HitMask - 1;
|
|
uint HitIndex = firstbithigh( HitMask );
|
|
|
|
return DDA.Time[0];
|
|
}
|
|
|
|
Counters.Steps++;
|
|
|
|
{
|
|
//DDA.Time[0] = NextTime( DDA );
|
|
DDA.Time[0] = min( DDA.Next.y, DDA.Next.z );
|
|
|
|
FLATTEN
|
|
if( DDA.Next.y < DDA.Next.z )
|
|
{
|
|
DDA.Next.y += abs( DDA.InvDir.y );
|
|
DDA.VoxelIndex += DDA.Step.y;
|
|
}
|
|
else
|
|
{
|
|
DDA.Next.z += abs( DDA.InvDir.z );
|
|
DDA.VoxelIndex += DDA.Step.z;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Ray.Time[1] + 1.0;
|
|
}
|
|
|
|
// Assumes Ray in local 0-1 space
|
|
float RayCastBrick_L2_Flat( FRay Ray, uint BrickOffset, inout FRayCastCounters Counters )
|
|
{
|
|
const float Epsilon = 1e-4;
|
|
|
|
uint2 RootBlock = BlockBuffer.Load2( 8 * BrickOffset );
|
|
|
|
Ray.Time += float2( Epsilon, -Epsilon );
|
|
|
|
Ray.Origin *= 16;
|
|
Ray.Direction *= 16;
|
|
|
|
#if 0
|
|
FDDA DDA = InitDDA( Ray );
|
|
StartDDA( DDA, 1, Ray );
|
|
#else
|
|
FDDA DDA;
|
|
DDA.Time = Ray.Time;
|
|
DDA.IsPositive = Ray.Direction >= 0;
|
|
DDA.Step = select( DDA.IsPositive, int3( 1, 4, 16 ), -int3( 1, 4, 16 ) );
|
|
DDA.InvDir = rcp( Ray.Direction );
|
|
|
|
float3 Position = Ray.Origin + Ray.Direction * DDA.Time[0];
|
|
//float3 Voxel = floor( Position );
|
|
uint3 Voxel = Position;
|
|
|
|
float3 DDA_Sign = select( DDA.IsPositive, 1.0, -1.0 );
|
|
|
|
//DDA.VoxelIndex = uint( ( Voxel.x + Voxel.y * 4 ) + Voxel.z * 16 );
|
|
|
|
//float3 MinCorner = Voxel;
|
|
//float3 FarCorner = MinCorner + select( DDA.IsPositive, 1.0, 0.0 );
|
|
//DDA.Next = DDA.Time[0] + ( FarCorner - Position ) * DDA.InvDir;
|
|
DDA.Next = DDA.Time[0] + ( ( Voxel - Position + 0.5 ) * DDA_Sign + 0.5 ) * abs( DDA.InvDir );
|
|
#endif
|
|
|
|
const uint MaxTests = 3*16 + 3*4;
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= 4*3 )
|
|
{
|
|
uint BlockIndex;
|
|
uint BlockMask;
|
|
bool bBlockExists;
|
|
|
|
do
|
|
{
|
|
Counters.Tests++;
|
|
|
|
uint3 BlockPos = Voxel >> 2;
|
|
BlockIndex = BlockPos.x + BlockPos.y * 4 + BlockPos.z * 16;
|
|
BlockMask = 1u << ( BlockIndex & 31 );
|
|
|
|
bBlockExists = ( BlockIndex < 32 ? RootBlock.x : RootBlock.y ) & BlockMask;
|
|
|
|
if( bBlockExists )
|
|
break;
|
|
|
|
DDA.Time[0] = NextTime( DDA );
|
|
|
|
if( DDA.Time[0] >= DDA.Time[1] )
|
|
break;
|
|
|
|
Counters.Steps++;
|
|
|
|
float3 VoxelPos = Voxel & 3;
|
|
float3 Step4 = select( DDA.IsPositive, 3.0 - VoxelPos, VoxelPos );
|
|
float3 Next = DDA.Next + abs( DDA.InvDir ) * Step4;
|
|
|
|
float3 Step = floor( 1.0 + ( min3( Next.x, Next.y, Next.z ) - DDA.Next ) * abs( Ray.Direction ) );
|
|
|
|
DDA.Next += Step * abs( DDA.InvDir );
|
|
Voxel += Step * DDA_Sign;
|
|
}
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= MaxTests );
|
|
|
|
if( !bBlockExists )
|
|
break;
|
|
|
|
#if 1
|
|
return DDA.Time[0];
|
|
#else
|
|
float3 VoxelPos = Voxel & 3;
|
|
float3 Next = DDA.Next + abs( DDA.InvDir ) * select( DDA.IsPositive, 3.0 - VoxelPos, VoxelPos );
|
|
|
|
float LeafEnd = min3( Next.x, Next.y, Next.z );
|
|
LeafEnd = min( LeafEnd, DDA.Time[1] );
|
|
|
|
uint BlockOffset = PrefixSum64( RootBlock, BlockIndex, BlockMask );
|
|
uint2 NodeBlock = BlockBuffer.Load2( 8 * ( BrickOffset + 1 + BlockOffset ) );
|
|
|
|
// Step into leaf block
|
|
Counters.StepIn++;
|
|
|
|
do
|
|
{
|
|
Counters.Tests++;
|
|
|
|
uint3 VoxelPos = Voxel & 3;
|
|
uint VoxelIndex = uint( VoxelPos.x + VoxelPos.y * 4 + VoxelPos.z * 16 );
|
|
uint VoxelMask = 1u << ( VoxelIndex & 31 );
|
|
|
|
bool bVoxelExists = ( VoxelIndex < 32 ? NodeBlock.x : NodeBlock.y ) & VoxelMask;
|
|
if( bVoxelExists )
|
|
{
|
|
Counters.Tests = -Counters.Tests;
|
|
return DDA.Time[0];
|
|
//break;
|
|
}
|
|
|
|
Counters.Steps++;
|
|
|
|
DDA.Time[0] = NextTime( DDA );
|
|
|
|
//if( DDA.Time[0] >= DDA.Time[1] )
|
|
// break;
|
|
|
|
bool3 bLeast = DDA.Next.xyz <= min( DDA.Next.yzx, DDA.Next.zxy );
|
|
float3 Step = bLeast;
|
|
|
|
DDA.Next += Step * abs( DDA.InvDir );
|
|
Voxel += Step * DDA_Sign;
|
|
}
|
|
while( DDA.Time[0] < LeafEnd && Counters.Tests <= MaxTests );
|
|
#endif
|
|
}
|
|
|
|
return Ray.Time[1] + 1.0;
|
|
}
|
|
|
|
// Assumes Ray in local 0-1 space
|
|
float RayCastBrick_L2( FRay Ray, uint BrickOffset, inout FRayCastCounters Counters )
|
|
{
|
|
const float Epsilon = 1e-4;
|
|
|
|
bool bRoot = true;
|
|
|
|
uint2 RootBlock = BlockBuffer.Load2( 8 * BrickOffset );
|
|
uint2 NodeBlock = RootBlock;
|
|
|
|
Ray.Time += float2( Epsilon, -Epsilon );
|
|
|
|
const uint RootShift = 0;
|
|
|
|
Ray.Origin *= 4u << RootShift;
|
|
Ray.Direction *= 4u << RootShift;
|
|
|
|
FDDA RootDDA;
|
|
|
|
FDDA DDA = InitDDA( Ray );
|
|
StartDDA( DDA, 1u << RootShift, Ray );
|
|
|
|
const uint MaxTests = 3*16 + 3*4;
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= MaxTests )
|
|
{
|
|
Counters.Tests++;
|
|
|
|
uint VoxelMask = 1u << ( DDA.VoxelIndex & 31 );
|
|
bool bVoxelExists = ( DDA.VoxelIndex < 32 ? NodeBlock.x : NodeBlock.y ) & VoxelMask;
|
|
|
|
if( bVoxelExists )
|
|
{
|
|
if( bRoot )
|
|
{
|
|
// Step into leaf block
|
|
Counters.StepIn++;
|
|
|
|
RootDDA = DDA;
|
|
|
|
bRoot = false;
|
|
|
|
uint BlockOffset = PrefixSum64( RootBlock, DDA.VoxelIndex, VoxelMask );
|
|
NodeBlock = BlockBuffer.Load2( 8 * ( BrickOffset + 1 + BlockOffset ) );
|
|
|
|
DDA.Time[1] = NextTime( DDA );
|
|
DDA.Time += float2( Epsilon, -Epsilon );
|
|
|
|
FRay LocalRay = Ray;
|
|
//LocalRay.Origin -= DDA.Voxel;
|
|
LocalRay.Origin.x -= ( DDA.VoxelIndex >> 0 ) & 3;
|
|
LocalRay.Origin.y -= ( DDA.VoxelIndex >> 2 ) & 3;
|
|
LocalRay.Origin.z -= ( DDA.VoxelIndex >> 4 ) & 3;
|
|
LocalRay.Origin *= 4;
|
|
LocalRay.Direction *= 4;
|
|
|
|
StartDDA( DDA, 1, LocalRay );
|
|
}
|
|
else
|
|
{
|
|
return DDA.Time[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Counters.Steps++;
|
|
StepDDA( DDA, bRoot ? (1u << RootShift) : 1u );
|
|
}
|
|
|
|
if( DDA.Time[0] >= DDA.Time[1] && !bRoot )
|
|
{
|
|
// Step out, back to root.
|
|
Counters.StepOut++;
|
|
|
|
bRoot = true;
|
|
NodeBlock = RootBlock;
|
|
DDA.Time[1] = Ray.Time[1];
|
|
#if 0
|
|
StartDDA( DDA, (1u << RootShift), Ray );
|
|
#else
|
|
DDA.InvDir = rcp( Ray.Direction );
|
|
DDA.VoxelIndex = RootDDA.VoxelIndex;
|
|
DDA.Next = RootDDA.Next;
|
|
|
|
StepDDA( DDA, 1u << RootShift );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return Ray.Time[1] + 1.0;
|
|
}
|
|
|
|
#if 1
|
|
void StartDDA2( inout FDDA DDA, const float Scale, const FRay Ray )
|
|
{
|
|
float3 Position = Ray.Origin + Ray.Direction * DDA.Time[0];
|
|
float3 Voxel = floor( Position / Scale );
|
|
|
|
DDA.VoxelIndex = uint( ( Voxel.x + Voxel.y * 4 ) + Voxel.z * 16 );
|
|
DDA.Next = DDA.Time[0] + ( ( Voxel + DDA.IsPositive ) * Scale - Position ) * DDA.InvDir;
|
|
}
|
|
|
|
void StepDDA2( inout FDDA DDA, const float Scale )
|
|
{
|
|
FLATTEN
|
|
if( DDA.Next.x < min( DDA.Next.y, DDA.Next.z ) )
|
|
{
|
|
DDA.Next.x += abs( DDA.InvDir.x ) * Scale;
|
|
DDA.VoxelIndex += DDA.Step.x;
|
|
}
|
|
else if( DDA.Next.y < DDA.Next.z )
|
|
{
|
|
DDA.Next.y += abs( DDA.InvDir.y ) * Scale;
|
|
DDA.VoxelIndex += DDA.Step.y;
|
|
}
|
|
else
|
|
{
|
|
DDA.Next.z += abs( DDA.InvDir.z ) * Scale;
|
|
DDA.VoxelIndex += DDA.Step.z;
|
|
}
|
|
}
|
|
|
|
// Assumes Ray in local 0-1 space
|
|
float RayCastBrick_L2_WhileWhile( FRay Ray, uint BrickOffset, inout FRayCastCounters Counters )
|
|
{
|
|
const float Epsilon = 1e-4;
|
|
|
|
uint2 RootBlock = BlockBuffer.Load2( 8 * BrickOffset );
|
|
|
|
Ray.Time += float2( Epsilon, -Epsilon );
|
|
|
|
Ray.Origin *= 16;
|
|
Ray.Direction *= 16;
|
|
|
|
#if 0
|
|
FDDA DDA = InitDDA( Ray );
|
|
StartDDA( DDA, 1, Ray );
|
|
#else
|
|
FDDA DDA;
|
|
DDA.Time = Ray.Time;
|
|
DDA.IsPositive = Ray.Direction >= 0;
|
|
DDA.Step = select( DDA.IsPositive, int3( 1, 4, 16 ), -int3( 1, 4, 16 ) );
|
|
DDA.InvDir = rcp( Ray.Direction );
|
|
#endif
|
|
|
|
StartDDA2( DDA, 4, Ray );
|
|
|
|
const uint MaxTests = 3*16 + 3*4;
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= MaxTests )
|
|
{
|
|
uint BlockMask;
|
|
bool bBlockExists;
|
|
|
|
//StartDDA2( DDA, 4, Ray );
|
|
|
|
do
|
|
{
|
|
Counters.Tests++;
|
|
|
|
BlockMask = 1u << ( DDA.VoxelIndex & 31 );
|
|
bBlockExists = ( DDA.VoxelIndex < 32 ? RootBlock.x : RootBlock.y ) & BlockMask;
|
|
|
|
if( bBlockExists )
|
|
break;
|
|
|
|
DDA.Time[0] = NextTime( DDA );
|
|
|
|
if( DDA.Time[0] >= DDA.Time[1] )
|
|
break;
|
|
|
|
StepDDA2( DDA, 4 );
|
|
Counters.Steps++;
|
|
}
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= MaxTests );
|
|
|
|
if( !bBlockExists )
|
|
break;
|
|
|
|
//return DDA.Time[0];
|
|
|
|
uint BlockOffset = PrefixSum64( RootBlock, DDA.VoxelIndex, BlockMask );
|
|
uint2 NodeBlock = BlockBuffer.Load2( 8 * ( BrickOffset + 1 + BlockOffset ) );
|
|
|
|
DDA.Time[0] += Epsilon;
|
|
DDA.Time[1] = NextTime( DDA ) - Epsilon * 2; // 2 to counter the previous Epsilon
|
|
|
|
uint RootIndex = DDA.VoxelIndex;
|
|
float3 RootNext = DDA.Next;
|
|
|
|
#if 0
|
|
FRay LocalRay = Ray;
|
|
LocalRay.Origin.x -= ( DDA.VoxelIndex << 2 ) & 12;
|
|
LocalRay.Origin.y -= ( DDA.VoxelIndex >> 0 ) & 12;
|
|
LocalRay.Origin.z -= ( DDA.VoxelIndex >> 2 ) & 12;
|
|
|
|
uint3 Min, Max;
|
|
BlockBounds( NodeBlock, Min, Max );
|
|
float3 Bounds[2] = { Min, Max };
|
|
|
|
DDA.Time = Intersect( LocalRay, Bounds );
|
|
DDA.Time += float2( Epsilon, -Epsilon );
|
|
|
|
StartDDA2( DDA, 1, LocalRay );
|
|
#else
|
|
float3 Position = Ray.Origin + Ray.Direction * DDA.Time[0];
|
|
uint3 Voxel = int3( Position ) & 3;
|
|
|
|
DDA.VoxelIndex = uint( ( Voxel.x + Voxel.y * 4 ) + Voxel.z * 16 );
|
|
DDA.Next = DDA.Time[0] + ( DDA.IsPositive - frac( Position ) ) * DDA.InvDir;
|
|
#endif
|
|
|
|
// Step into leaf block
|
|
Counters.StepIn++;
|
|
|
|
do
|
|
{
|
|
Counters.Tests++;
|
|
|
|
uint VoxelMask = 1u << ( DDA.VoxelIndex & 31 );
|
|
bool bVoxelExists = ( DDA.VoxelIndex < 32 ? NodeBlock.x : NodeBlock.y ) & VoxelMask;
|
|
|
|
if( bVoxelExists )
|
|
return DDA.Time[0];
|
|
|
|
DDA.Time[0] = NextTime( DDA );
|
|
|
|
if( DDA.Time[0] >= DDA.Time[1] )
|
|
break;
|
|
|
|
StepDDA2( DDA, 1 );
|
|
Counters.Steps++;
|
|
}
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= MaxTests );
|
|
|
|
// Step out, back to root.
|
|
Counters.StepOut++;
|
|
|
|
DDA.Time[1] = Ray.Time[1];
|
|
DDA.VoxelIndex = RootIndex;
|
|
DDA.Next = RootNext;
|
|
|
|
StepDDA2( DDA, 4 );
|
|
Counters.Steps++;
|
|
}
|
|
|
|
return Ray.Time[1] + 1.0;
|
|
}
|
|
#endif
|
|
|
|
// Assumes Ray in local 0-1 space
|
|
float RayCastBrick_L2_WhileIf( FRay Ray, uint BrickOffset, inout FRayCastCounters Counters )
|
|
{
|
|
const float Epsilon = 1e-4;
|
|
|
|
bool bRoot = true;
|
|
|
|
uint2 RootBlock = BlockBuffer.Load2( 8 * BrickOffset );
|
|
uint2 NodeBlock = RootBlock;
|
|
|
|
Ray.Time += float2( Epsilon, -Epsilon );
|
|
|
|
Ray.Origin *= 4;
|
|
Ray.Direction *= 4;
|
|
|
|
FDDA DDA = InitDDA( Ray );
|
|
StartDDA( DDA, 4, Ray );
|
|
|
|
const uint MaxTests = 3*16 + 3*4;
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= MaxTests )
|
|
{
|
|
uint VoxelIndex;
|
|
uint VoxelMask;
|
|
bool bVoxelExists;
|
|
|
|
do
|
|
{
|
|
Counters.Tests++;
|
|
|
|
VoxelIndex = DDA.VoxelIndex;
|
|
//VoxelIndex = uint( DDA.Voxel.x + DDA.Voxel.y * 4 + DDA.Voxel.z * 16 );
|
|
VoxelMask = 1u << ( DDA.VoxelIndex & 31u );
|
|
|
|
bVoxelExists = ( VoxelIndex < 32 ? NodeBlock.x : NodeBlock.y ) & VoxelMask;
|
|
|
|
if( bVoxelExists )
|
|
break;
|
|
|
|
Counters.Steps++;
|
|
StepDDA( DDA, bRoot ? 4 : 1 );
|
|
}
|
|
while( DDA.Time[0] < DDA.Time[1] && Counters.Tests <= MaxTests );
|
|
|
|
if( bVoxelExists )
|
|
{
|
|
if( bRoot )
|
|
{
|
|
// Step into leaf block
|
|
Counters.StepIn++;
|
|
|
|
bRoot = false;
|
|
|
|
uint BlockOffset = PrefixSum64( RootBlock, VoxelIndex, VoxelMask );
|
|
NodeBlock = BlockBuffer.Load2( 8 * ( BrickOffset + 1 + BlockOffset ) );
|
|
|
|
DDA.Time[1] = NextTime( DDA );
|
|
DDA.Time += float2( Epsilon, -Epsilon );
|
|
|
|
FRay LocalRay = Ray;
|
|
//LocalRay.Origin -= DDA.Voxel;
|
|
LocalRay.Origin.x -= ( VoxelIndex >> 0 ) & 3;
|
|
LocalRay.Origin.y -= ( VoxelIndex >> 2 ) & 3;
|
|
LocalRay.Origin.z -= ( VoxelIndex >> 4 ) & 3;
|
|
LocalRay.Origin *= 4;
|
|
LocalRay.Direction *= 4;
|
|
|
|
StartDDA( DDA, 1, LocalRay );
|
|
}
|
|
else
|
|
{
|
|
Counters.Tests = -Counters.Tests;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( DDA.Time[0] >= DDA.Time[1] && !bRoot )
|
|
{
|
|
// Step out, back to root.
|
|
Counters.StepOut++;
|
|
|
|
bRoot = true;
|
|
NodeBlock = RootBlock;
|
|
DDA.Time[1] = Ray.Time[1];
|
|
|
|
StartDDA( DDA, 4, Ray );
|
|
}
|
|
}
|
|
|
|
return DDA.Time[0];
|
|
} |