// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "/Engine/Public/Platform.ush" ByteAddressBuffer BlockBuffer; Texture3D 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]; }