64 voxels inside one number.
This is my favourite part of the whole tool. A 3D grid grows cubically, and the naive
version stores one bool per voxel — which in C# means a whole byte to hold
one bit of information. Eight times more memory than the data actually needs, for a
structure that's already the thing most likely to blow your budget.
So: each coarse cell covers a 4 × 4 × 4 block of fine voxels. That's 64 yes/no answers.
A ulong is 64 bits. They fit exactly, which is one of those coincidences that
feels like the universe giving you permission.
4 × 4 × 464 sub-voxelsOne coarse
grid cell
→
64 BITS1 × ulongOne bit per fine
voxel
→
COMPACTDense storageLess memory
per voxel state
The cost is that reading a voxel is now a shift and a mask instead of an
array index. Worth it, and the whole coarse cell lands in cache in one go, which the search
is quietly grateful for.
Here's the bake side of it. Walk the 64 sub-voxels of one coarse cell, ask physics two
questions about each, and drop the answer into bit i of a ulong.
Most voxels are floating in open air, so "is anything even near me?" goes first and
throws most of them out before the second question gets asked.
DetectCollisionC# · once per cell · bake time
private ulong DetectCollision(int3 position)
{
ulong result = 0;
for (int i = 0; i < PathfindingHolder.DATASTRUCTSIZE; i++)
{
int3 localPosition = position * PathfindingHolder.SUBDIVIDEBOX
+ new int3(i % PathfindingHolder.SUBDIVIDEBOX,
(i / PathfindingHolder.SUBDIVIDEBOX) % PathfindingHolder.SUBDIVIDEBOX,
i / (PathfindingHolder.SUBDIVIDEBOX * PathfindingHolder.SUBDIVIDEBOX));
float3 worldPosition = PathfindingUtilities.FindWorldPosition(localPosition, holder);
// not too far from something
bool notTooFar = Physics.CheckBox(worldPosition, Vector3.one * holder._maxDistanceFromWall / 2,
Quaternion.identity, CollisionMask, QueryTriggerInteraction.Ignore);
if (!notTooFar) continue;
// not inside of something
bool notInside = !Physics.CheckBox(worldPosition, Vector3.one * holder._voxelSize / 2,
Quaternion.identity, CollisionMask, QueryTriggerInteraction.Ignore);
if (notInside) result |= 1ul << i;
}
return result;
}
i → (x, y, z) is just base-4 digits: i % 4, (i / 4) % 4, i / 16.