+/*
+ * Sixth 3D engine. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.sixth.e3d.renderer.octree;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.Ray;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+import static java.lang.Integer.max;
+import static java.lang.Integer.min;
+
+/**
+ * Sparse voxel octree for 3D volume storage and ray tracing.
+ *
+ * <p>The octree represents a 3D volume with three cell types:</p>
+ * <ul>
+ * <li><b>UNUSED</b> - Empty cell, not yet allocated</li>
+ * <li><b>SOLID</b> - Contains color and illumination data</li>
+ * <li><b>CLUSTER</b> - Contains pointers to 8 child cells (for subdivision)</li>
+ * </ul>
+ *
+ * <p>Cell data is stored in parallel arrays ({@code cell1} through {@code cell8})
+ * for memory efficiency. Each array stores different aspects of cell data.</p>
+ *
+ * @see eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.RayTracer
+ * @see eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.Ray
+ */
+public class OctreeVolume {
+
+ /** Return value indicating no intersection during ray tracing. */
+ public static final int TRACE_NO_HIT = -1;
+
+ /** Cell state marker for solid cells. */
+ private static final int CELL_STATE_SOLID = -2;
+
+ /** Cell state marker for unused/empty cells. */
+ private static final int CELL_STATE_UNUSED = -1;
+
+ /** Cell data array 1: stores cell state and first child pointer. */
+ public int[] cell1;
+ /** Cell data array 2: stores color values. */
+ public int[] cell2;
+ /** Cell data array 3: stores illumination values. */
+ public int[] cell3;
+ /** Cell data array 4: stores child pointer 4. */
+ public int[] cell4;
+ /** Cell data array 5: stores child pointer 5. */
+ public int[] cell5;
+ /** Cell data array 6: stores child pointer 6. */
+ public int[] cell6;
+ /** Cell data array 7: stores child pointer 7. */
+ public int[] cell7;
+ /** Cell data array 8: stores child pointer 8. */
+ public int[] cell8;
+
+ /**
+ * Pointer to the next unused cell in the allocation buffer.
+ */
+ public int cellAllocationPointer = 0;
+
+ /** Number of currently allocated cells. */
+ public int usedCellsCount = 0;
+
+ /** Size of the root (master) cell in world units. */
+ public int masterCellSize;
+
+ /**
+ * Creates a new octree volume with default buffer size (1.5M cells)
+ * and master cell size of 256*64 units.
+ */
+ public OctreeVolume() {
+ initWorld(1500000, 256 * 64);
+ }
+
+ /**
+ * Subdivides a solid cell into 8 child cells, each with the same color and illumination.
+ *
+ * @param pointer the cell to break up
+ */
+ public void breakSolidCell(final int pointer) {
+ final int color = getCellColor(pointer);
+ final int illumination = getCellIllumination(pointer);
+
+ cell1[pointer] = makeNewCell(color, illumination);
+ cell2[pointer] = makeNewCell(color, illumination);
+ cell3[pointer] = makeNewCell(color, illumination);
+ cell4[pointer] = makeNewCell(color, illumination);
+ cell5[pointer] = makeNewCell(color, illumination);
+ cell6[pointer] = makeNewCell(color, illumination);
+ cell7[pointer] = makeNewCell(color, illumination);
+ cell8[pointer] = makeNewCell(color, illumination);
+ }
+
+ /**
+ * Clears the cell.
+ * @param pointer Pointer to the cell.
+ */
+ public void clearCell(final int pointer) {
+ cell1[pointer] = 0;
+ cell2[pointer] = 0;
+ cell3[pointer] = 0;
+ cell4[pointer] = 0;
+
+ cell5[pointer] = 0;
+ cell6[pointer] = 0;
+ cell7[pointer] = 0;
+ cell8[pointer] = 0;
+ }
+
+ /**
+ * Marks a cell as deleted and returns it to the unused pool.
+ *
+ * @param cellPointer the cell to delete
+ */
+ public void deleteCell(final int cellPointer) {
+ clearCell(cellPointer);
+ cell1[cellPointer] = CELL_STATE_UNUSED;
+ usedCellsCount--;
+ }
+
+ /**
+ * Tests whether a ray intersects with a cubic region.
+ *
+ * @param cubeX the X center of the cube
+ * @param cubeY the Y center of the cube
+ * @param cubeZ the Z center of the cube
+ * @param cubeSize the half-size of the cube
+ * @param r the ray to test
+ * @return intersection type code, or 0 if no intersection
+ */
+ public int doesIntersect(final int cubeX, final int cubeY, final int cubeZ,
+ final int cubeSize, final Ray r) {
+
+ // ray starts inside the cube
+ if ((cubeX - cubeSize) < r.origin.x)
+ if ((cubeX + cubeSize) > r.origin.x)
+ if ((cubeY - cubeSize) < r.origin.y)
+ if ((cubeY + cubeSize) > r.origin.y)
+ if ((cubeZ - cubeSize) < r.origin.z)
+ if ((cubeZ + cubeSize) > r.origin.z) {
+ r.hitPoint = r.origin.clone();
+ return 1;
+ }
+ // back face
+ if (r.direction.z > 0)
+ if ((cubeZ - cubeSize) > r.origin.z) {
+ final double mult = ((cubeZ - cubeSize) - r.origin.z) / r.direction.z;
+ final double hitX = (r.direction.x * mult) + r.origin.x;
+ if ((cubeX - cubeSize) < hitX)
+ if ((cubeX + cubeSize) > hitX) {
+ final double hitY = (r.direction.y * mult) + r.origin.y;
+ if ((cubeY - cubeSize) < hitY)
+ if ((cubeY + cubeSize) > hitY) {
+ r.hitPoint = new Point3D(hitX, hitY, cubeZ
+ - cubeSize);
+ return 2;
+ }
+ }
+ }
+
+ // up face
+ if (r.direction.y > 0)
+ if ((cubeY - cubeSize) > r.origin.y) {
+ final double mult = ((cubeY - cubeSize) - r.origin.y) / r.direction.y;
+ final double hitX = (r.direction.x * mult) + r.origin.x;
+ if ((cubeX - cubeSize) < hitX)
+ if ((cubeX + cubeSize) > hitX) {
+ final double hitZ = (r.direction.z * mult) + r.origin.z;
+ if ((cubeZ - cubeSize) < hitZ)
+ if ((cubeZ + cubeSize) > hitZ) {
+ r.hitPoint = new Point3D(hitX, cubeY - cubeSize,
+ hitZ);
+ return 3;
+ }
+ }
+ }
+
+ // left face
+ if (r.direction.x > 0)
+ if ((cubeX - cubeSize) > r.origin.x) {
+ final double mult = ((cubeX - cubeSize) - r.origin.x) / r.direction.x;
+ final double hitY = (r.direction.y * mult) + r.origin.y;
+ if ((cubeY - cubeSize) < hitY)
+ if ((cubeY + cubeSize) > hitY) {
+ final double hitZ = (r.direction.z * mult) + r.origin.z;
+ if ((cubeZ - cubeSize) < hitZ)
+ if ((cubeZ + cubeSize) > hitZ) {
+ r.hitPoint = new Point3D(cubeX - cubeSize, hitY,
+ hitZ);
+ return 4;
+ }
+ }
+ }
+
+ // front face
+ if (r.direction.z < 0)
+ if ((cubeZ + cubeSize) < r.origin.z) {
+ final double mult = ((cubeZ + cubeSize) - r.origin.z) / r.direction.z;
+ final double hitX = (r.direction.x * mult) + r.origin.x;
+ if ((cubeX - cubeSize) < hitX)
+ if ((cubeX + cubeSize) > hitX) {
+ final double hitY = (r.direction.y * mult) + r.origin.y;
+ if ((cubeY - cubeSize) < hitY)
+ if ((cubeY + cubeSize) > hitY) {
+ r.hitPoint = new Point3D(hitX, hitY, cubeZ
+ + cubeSize);
+ return 5;
+ }
+ }
+ }
+
+ // down face
+ if (r.direction.y < 0)
+ if ((cubeY + cubeSize) < r.origin.y) {
+ final double mult = ((cubeY + cubeSize) - r.origin.y) / r.direction.y;
+ final double hitX = (r.direction.x * mult) + r.origin.x;
+ if ((cubeX - cubeSize) < hitX)
+ if ((cubeX + cubeSize) > hitX) {
+ final double hitZ = (r.direction.z * mult) + r.origin.z;
+ if ((cubeZ - cubeSize) < hitZ)
+ if ((cubeZ + cubeSize) > hitZ) {
+ r.hitPoint = new Point3D(hitX, cubeY + cubeSize,
+ hitZ);
+ return 6;
+ }
+ }
+ }
+
+ // right face
+ if (r.direction.x < 0)
+ if ((cubeX + cubeSize) < r.origin.x) {
+ final double mult = ((cubeX + cubeSize) - r.origin.x) / r.direction.x;
+ final double hitY = (r.direction.y * mult) + r.origin.y;
+ if ((cubeY - cubeSize) < hitY)
+ if ((cubeY + cubeSize) > hitY) {
+ final double hitZ = (r.direction.z * mult) + r.origin.z;
+ if ((cubeZ - cubeSize) < hitZ)
+ if ((cubeZ + cubeSize) > hitZ) {
+ r.hitPoint = new Point3D(cubeX + cubeSize, hitY,
+ hitZ);
+ return 7;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Fills a 3D rectangular region with solid cells of the given color.
+ *
+ * @param p1 one corner of the rectangle
+ * @param p2 the opposite corner of the rectangle
+ * @param color the color to fill with
+ */
+ public void fillRectangle(IntegerPoint p1, IntegerPoint p2, Color color) {
+
+ int x1 = min(p1.x, p2.x);
+ int x2 = max(p1.x, p2.x);
+ int y1 = min(p1.y, p2.y);
+ int y2 = max(p1.y, p2.y);
+ int z1 = min(p1.z, p2.z);
+ int z2 = max(p1.z, p2.z);
+
+ for (int x = x1; x <= x2; x++)
+ for (int y = y1; y <= y2; y++)
+ for (int z = z1; z <= z2; z++)
+ putCell(x, y, z, 0, 0, 0, masterCellSize, 0, color);
+ }
+
+ /**
+ * Returns the color value stored in a solid cell.
+ *
+ * @param pointer the cell pointer
+ * @return the packed RGB color value
+ */
+ public int getCellColor(final int pointer) {
+ return cell2[pointer];
+ }
+
+ /**
+ * Returns the illumination value stored in a solid cell.
+ *
+ * @param pointer the cell pointer
+ * @return the packed RGB illumination value
+ */
+ public int getCellIllumination(final int pointer) {
+ return cell3[pointer];
+ }
+
+ /**
+ * Initializes the octree storage arrays with the specified buffer size and root cell size.
+ *
+ * @param bufferLength the number of cells to allocate space for
+ * @param masterCellSize the size of the root cell in world units
+ */
+ public void initWorld(final int bufferLength, final int masterCellSize) {
+ // System.out.println("Initializing new world");
+
+ // initialize world storage buffer
+ this.masterCellSize = masterCellSize;
+
+ cell1 = new int[bufferLength];
+ cell2 = new int[bufferLength];
+ cell3 = new int[bufferLength];
+ cell4 = new int[bufferLength];
+
+ cell5 = new int[bufferLength];
+ cell6 = new int[bufferLength];
+ cell7 = new int[bufferLength];
+ cell8 = new int[bufferLength];
+
+ for (int i = 0; i < bufferLength; i++)
+ cell1[i] = CELL_STATE_UNUSED;
+
+ // initialize master cell
+ clearCell(0);
+ }
+
+ /**
+ * Checks if the cell at the given pointer is a solid (leaf) cell.
+ *
+ * @param pointer the cell pointer to check
+ * @return {@code true} if the cell is solid
+ */
+ public boolean isCellSolid(final int pointer) {
+ return cell1[pointer] == CELL_STATE_SOLID;
+ }
+
+ /**
+ * Scans cells arrays and returns pointer to found unused cell.
+ * @return pointer to found unused cell
+ */
+ public int getNewCellPointer() {
+ while (true) {
+ // ensure that cell allocation pointer is in bounds
+ if (cellAllocationPointer >= cell1.length)
+ cellAllocationPointer = 0;
+
+ if (cell1[cellAllocationPointer] == CELL_STATE_UNUSED) {
+ // unused cell found
+ clearCell(cellAllocationPointer);
+
+ usedCellsCount++;
+ return cellAllocationPointer;
+ } else
+ cellAllocationPointer++;
+ }
+ }
+
+ /**
+ * Allocates a new solid cell with the given color and illumination.
+ *
+ * @param color the color value for the new cell
+ * @param illumination the illumination value for the new cell
+ * @return the pointer to the newly allocated cell
+ */
+ public int makeNewCell(final int color, final int illumination) {
+ final int pointer = getNewCellPointer();
+ markCellAsSolid(pointer);
+ setCellColor(pointer, color);
+ setCellIllumination(pointer, illumination);
+ return pointer;
+ }
+
+ /**
+ * Mark cell as solid.
+ *
+ * @param pointer pointer to cell
+ */
+ public void markCellAsSolid(final int pointer) {
+ cell1[pointer] = CELL_STATE_SOLID;
+ }
+
+ /**
+ * Stores a voxel at the given world coordinates with the specified color.
+ *
+ * @param x the X coordinate
+ * @param y the Y coordinate
+ * @param z the Z coordinate
+ * @param color the color of the voxel
+ */
+ public void putCell(final int x, final int y, final int z, final Color color) {
+ putCell(x, y, z, 0, 0, 0, masterCellSize, 0, color);
+ }
+
+ private void putCell(final int x, final int y, final int z,
+ final int cellX, final int cellY, final int cellZ,
+ final int cellSize, final int cellPointer, final Color color) {
+
+ if (cellSize > 1) {
+
+ // if case of big cell
+ if (isCellSolid(cellPointer)) {
+
+ // if cell is already a needed color, do nothing
+ if (getCellColor(cellPointer) == color.toInt())
+ return;
+
+ // otherwise break cell up
+ breakSolidCell(cellPointer);
+
+ // continue, as if it is cluster now
+ }
+
+ // decide which subcube to use
+ int[] subCubeArray;
+ int subX, subY, subZ;
+
+ if (x > cellX) {
+ subX = (cellSize / 2) + cellX;
+ if (y > cellY) {
+ subY = (cellSize / 2) + cellY;
+ if (z > cellZ) {
+ subZ = (cellSize / 2) + cellZ;
+ // 7
+ subCubeArray = cell7;
+ } else {
+ subZ = (-cellSize / 2) + cellZ;
+ // 3
+ subCubeArray = cell3;
+ }
+ } else {
+ subY = (-cellSize / 2) + cellY;
+ if (z > cellZ) {
+ subZ = (cellSize / 2) + cellZ;
+ // 6
+ subCubeArray = cell6;
+ } else {
+ subZ = (-cellSize / 2) + cellZ;
+ // 2
+ subCubeArray = cell2;
+ }
+ }
+ } else {
+ subX = (-cellSize / 2) + cellX;
+ if (y > cellY) {
+ subY = (cellSize / 2) + cellY;
+ if (z > cellZ) {
+ subZ = (cellSize / 2) + cellZ;
+ // 8
+ subCubeArray = cell8;
+ } else {
+ subZ = (-cellSize / 2) + cellZ;
+ // 4
+ subCubeArray = cell4;
+ }
+ } else {
+ subY = (-cellSize / 2) + cellY;
+ if (z > cellZ) {
+ subZ = (cellSize / 2) + cellZ;
+ // 5
+ subCubeArray = cell5;
+ } else {
+ subZ = (-cellSize / 2) + cellZ;
+ // 1
+ subCubeArray = cell1;
+ }
+ }
+ }
+
+ int subCubePointer;
+ if (subCubeArray[cellPointer] == 0) {
+ // create empty cluster
+ subCubePointer = getNewCellPointer();
+ subCubeArray[cellPointer] = subCubePointer;
+ } else
+ subCubePointer = subCubeArray[cellPointer];
+
+ putCell(x, y, z, subX, subY, subZ, cellSize / 2, subCubePointer,
+ color);
+ } else {
+ cell1[cellPointer] = CELL_STATE_SOLID;
+ cell2[cellPointer] = color.toInt();
+ cell3[cellPointer] = CELL_STATE_UNUSED;
+ // System.out.println("Cell written!");
+ }
+ }
+
+ /**
+ * Sets the color value for the cell at the given pointer.
+ *
+ * @param pointer the cell pointer
+ * @param color the color value to set
+ */
+ public void setCellColor(final int pointer, final int color) {
+ cell2[pointer] = color;
+ }
+
+ /**
+ * Sets the illumination value for the cell at the given pointer.
+ *
+ * @param pointer the cell pointer
+ * @param illumination the illumination value to set
+ */
+ public void setCellIllumination(final int pointer, final int illumination) {
+ cell3[pointer] = illumination;
+ }
+
+ /**
+ * Traces a ray through the octree to find an intersecting solid cell.
+ *
+ * @param cellX the X coordinate of the current cell center
+ * @param cellY the Y coordinate of the current cell center
+ * @param cellZ the Z coordinate of the current cell center
+ * @param cellSize the size of the current cell
+ * @param pointer the pointer to the current cell
+ * @param ray the ray to trace
+ * @return pointer to intersecting cell or TRACE_NO_HIT if no intersection
+ */
+ public int traceCell(final int cellX, final int cellY, final int cellZ,
+ final int cellSize, final int pointer, final Ray ray) {
+ if (isCellSolid(pointer)) {
+ // solid cell
+ if (doesIntersect(cellX, cellY, cellZ, cellSize, ray) != 0) {
+ ray.hitCellSize = cellSize;
+ ray.hitCellX = cellX;
+ ray.hitCellY = cellY;
+ ray.hitCellZ = cellZ;
+ return pointer;
+ }
+ return TRACE_NO_HIT;
+ } else // cluster
+ if (doesIntersect(cellX, cellY, cellZ, cellSize, ray) != 0) {
+ final int halfOfCellSize = cellSize / 2;
+ int rayIntersectionResult;
+
+ if (ray.origin.x > cellX) {
+ if (ray.origin.y > cellY) {
+ if (ray.origin.z > cellZ) {
+ // 7
+ // 6 8 3 5 2 4 1
+
+ if (cell7[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize,
+ cellZ + halfOfCellSize, halfOfCellSize,
+ cell7[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell6[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize,
+ cellZ + halfOfCellSize, halfOfCellSize,
+ cell6[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell8[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize,
+ cellZ + halfOfCellSize, halfOfCellSize,
+ cell8[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell3[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize,
+ cellZ - halfOfCellSize, halfOfCellSize,
+ cell3[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell2[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize,
+ cellZ - halfOfCellSize, halfOfCellSize,
+ cell2[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell4[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize,
+ cellZ - halfOfCellSize, halfOfCellSize,
+ cell4[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell5[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize,
+ cellZ + halfOfCellSize, halfOfCellSize,
+ cell5[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell1[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize,
+ cellZ - halfOfCellSize, halfOfCellSize,
+ cell1[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ } else {
+ // 3
+ // 2 4 7 1 6 8 5
+ if (cell3[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize,
+ cellZ - halfOfCellSize, halfOfCellSize,
+ cell3[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell2[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize,
+ cellZ - halfOfCellSize, halfOfCellSize,
+ cell2[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell4[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize,
+ cellZ - halfOfCellSize, halfOfCellSize,
+ cell4[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell7[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize,
+ cellZ + halfOfCellSize, halfOfCellSize,
+ cell7[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell6[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize,
+ cellZ + halfOfCellSize, halfOfCellSize,
+ cell6[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell8[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize,
+ cellZ + halfOfCellSize, halfOfCellSize,
+ cell8[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell1[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize,
+ cellZ - halfOfCellSize, halfOfCellSize,
+ cell1[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell5[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize,
+ cellZ + halfOfCellSize, halfOfCellSize,
+ cell5[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ }
+ } else if (ray.origin.z > cellZ) {
+ // 6
+ // 5 2 7 8 1 3 4
+ if (cell6[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell6[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell7[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell7[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell2[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell2[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell5[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell5[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell8[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell8[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell3[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell3[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell1[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell1[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell4[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell4[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ } else {
+ // 2
+ // 1 3 6 5 4 7 8
+ if (cell2[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell2[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell3[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell3[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell1[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell1[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell6[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell6[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell7[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell7[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell5[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell5[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell4[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell4[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell8[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell8[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ }
+ } else if (ray.origin.y > cellY) {
+ if (ray.origin.z > cellZ) {
+ // 8
+ // 5 7 4 1 6 3 2
+
+ if (cell8[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell8[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell7[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell7[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell5[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell5[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell4[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell4[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell3[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell3[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell1[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell1[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell6[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell6[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell2[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell2[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ } else {
+ // 4
+ // 1 3 8 5 7 2 6
+
+ if (cell4[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell4[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell8[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY + halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell8[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell3[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell3[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell1[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell1[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell7[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY + halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell7[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell5[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ - halfOfCellSize, cellY - halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell5[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell2[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize, cellZ
+ - halfOfCellSize, halfOfCellSize, cell2[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell6[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX
+ + halfOfCellSize, cellY - halfOfCellSize, cellZ
+ + halfOfCellSize, halfOfCellSize, cell6[pointer],
+ ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ }
+ } else if (ray.origin.z > cellZ) {
+ // 5
+ // 1 6 8 4 2 7 3
+
+ if (cell5[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+ cellY - halfOfCellSize, cellZ + halfOfCellSize,
+ halfOfCellSize, cell5[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell1[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+ cellY - halfOfCellSize, cellZ - halfOfCellSize,
+ halfOfCellSize, cell1[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell6[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+ cellY - halfOfCellSize, cellZ + halfOfCellSize,
+ halfOfCellSize, cell6[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell8[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+ cellY + halfOfCellSize, cellZ + halfOfCellSize,
+ halfOfCellSize, cell8[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell4[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+ cellY + halfOfCellSize, cellZ - halfOfCellSize,
+ halfOfCellSize, cell4[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell7[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+ cellY + halfOfCellSize, cellZ + halfOfCellSize,
+ halfOfCellSize, cell7[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell2[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+ cellY - halfOfCellSize, cellZ - halfOfCellSize,
+ halfOfCellSize, cell2[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell3[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+ cellY + halfOfCellSize, cellZ - halfOfCellSize,
+ halfOfCellSize, cell3[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ } else {
+ // 1
+ // 5 2 4 8 6 3 7
+
+ if (cell1[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+ cellY - halfOfCellSize, cellZ - halfOfCellSize,
+ halfOfCellSize, cell1[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell5[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+ cellY - halfOfCellSize, cellZ + halfOfCellSize,
+ halfOfCellSize, cell5[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell2[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+ cellY - halfOfCellSize, cellZ - halfOfCellSize,
+ halfOfCellSize, cell2[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell4[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+ cellY + halfOfCellSize, cellZ - halfOfCellSize,
+ halfOfCellSize, cell4[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell6[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+ cellY - halfOfCellSize, cellZ + halfOfCellSize,
+ halfOfCellSize, cell6[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell8[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX - halfOfCellSize,
+ cellY + halfOfCellSize, cellZ + halfOfCellSize,
+ halfOfCellSize, cell8[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+
+ if (cell3[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+ cellY + halfOfCellSize, cellZ - halfOfCellSize,
+ halfOfCellSize, cell3[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ if (cell7[pointer] != 0) {
+ rayIntersectionResult = traceCell(cellX + halfOfCellSize,
+ cellY + halfOfCellSize, cellZ + halfOfCellSize,
+ halfOfCellSize, cell7[pointer], ray);
+ if (rayIntersectionResult >= 0)
+ return rayIntersectionResult;
+ }
+ }
+ }
+ return TRACE_NO_HIT;
+ }
+
+}