code: package org.sunflow.core.primitive;
  
  import org.sunflow.SunflowAPI;
  import org.sunflow.core.Instance;
  import org.sunflow.core.IntersectionState;
  import org.sunflow.core.ParameterList;
  import org.sunflow.core.PrimitiveList;
  import org.sunflow.core.Ray;
  import org.sunflow.core.ShadingState;
  import org.sunflow.math.BoundingBox;
  import org.sunflow.math.Matrix4;
  import org.sunflow.math.OrthoNormalBasis;
  import org.sunflow.math.Vector3;
  
  public abstract class CubeGrid implements PrimitiveList {
      private int nx, ny, nz;
      private float voxelwx, voxelwy, voxelwz;
      private float invVoxelwx, invVoxelwy, invVoxelwz;
      private BoundingBox bounds;
  
      public CubeGrid() {
          nx = ny = nz = 1;
          bounds = new BoundingBox(1);
      }
  
      public boolean update(ParameterList pl, SunflowAPI api) {
          nx = pl.getInt("resolutionX", nx);
          ny = pl.getInt("resolutionY", ny);
          nz = pl.getInt("resolutionZ", nz);
          voxelwx = 2.0f / nx;
          voxelwy = 2.0f / ny;
          voxelwz = 2.0f / nz;
          invVoxelwx = 1 / voxelwx;
          invVoxelwy = 1 / voxelwy;
          invVoxelwz = 1 / voxelwz;
          return true;
      }
  
      protected abstract boolean inside(int x, int y, int z);
  
      public BoundingBox getBounds() {
          return bounds;
      }
  
      public void prepareShadingState(ShadingState state) {
          state.init();
          state.getRay().getPoint(state.getPoint());
          Instance parent = state.getInstance();
          Vector3 normal;
          switch (state.getPrimitiveID()) {
              case 0:
                  normal = new Vector3(-1, 0, 0);
                  break;
              case 1:
                  normal = new Vector3(1, 0, 0);
                  break;
              case 2:
                  normal = new Vector3(0, -1, 0);
                  break;
              case 3:
                  normal = new Vector3(0, 1, 0);
                  break;
              case 4:
                  normal = new Vector3(0, 0, -1);
                  break;
              case 5:
                  normal = new Vector3(0, 0, 1);
                  break;
              default:
                  normal = new Vector3(0, 0, 0);
                  break;
          }
          state.getNormal().set(state.transformNormalObjectToWorld(normal));
          state.getGeoNormal().set(state.getNormal());
          state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal()));
          state.setShader(parent.getShader(0));
          state.setModifier(parent.getModifier(0));
      }
  
      public void intersectPrimitive(Ray r, int primID, IntersectionState state) {
          float intervalMin = r.getMin();
          float intervalMax = r.getMax();
          float orgX = r.ox;
          float orgY = r.oy;
          float orgZ = r.oz;
          float dirX = r.dx, invDirX = 1 / dirX;
          float dirY = r.dy, invDirY = 1 / dirY;
          float dirZ = r.dz, invDirZ = 1 / dirZ;
          float t1, t2;
          t1 = (-1 - orgX) * invDirX;
          t2 = (+1 - orgX) * invDirX;
          int curr = -1;
          if (invDirX > 0) {
              if (t1 > intervalMin) {
                  intervalMin = t1;
                  curr = 0;
              }
              if (t2 < intervalMax)
                  intervalMax = t2;
              if (intervalMin > intervalMax)
                  return;
          } else {
              if (t2 > intervalMin) {
                  intervalMin = t2;
                  curr = 1;
              }
              if (t1 < intervalMax)
                  intervalMax = t1;
              if (intervalMin > intervalMax)
                  return;
          }
          t1 = (-1 - orgY) * invDirY;
          t2 = (+1 - orgY) * invDirY;
          if (invDirY > 0) {
              if (t1 > intervalMin) {
                  intervalMin = t1;
                  curr = 2;
              }
              if (t2 < intervalMax)
                  intervalMax = t2;
              if (intervalMin > intervalMax)
                  return;
          } else {
              if (t2 > intervalMin) {
                  intervalMin = t2;
                  curr = 3;
              }
              if (t1 < intervalMax)
                  intervalMax = t1;
              if (intervalMin > intervalMax)
                  return;
          }
          t1 = (-1 - orgZ) * invDirZ;
          t2 = (+1 - orgZ) * invDirZ;
          if (invDirZ > 0) {
              if (t1 > intervalMin) {
                  intervalMin = t1;
                  curr = 4;
              }
              if (t2 < intervalMax)
                  intervalMax = t2;
              if (intervalMin > intervalMax)
                  return;
          } else {
              if (t2 > intervalMin) {
                  intervalMin = t2;
                  curr = 5;
              }
              if (t1 < intervalMax)
                  intervalMax = t1;
              if (intervalMin > intervalMax)
                  return;
          }
          // box is hit at [intervalMin, intervalMax]
          orgX += intervalMin * dirX;
          orgY += intervalMin * dirY;
          orgZ += intervalMin * dirZ;
          // locate starting point inside the grid
          // and set up 3D-DDA vars
          int indxX, indxY, indxZ;
          int stepX, stepY, stepZ;
          int stopX, stopY, stopZ;
          float deltaX, deltaY, deltaZ;
          float tnextX, tnextY, tnextZ;
          // stepping factors along X
          indxX = (int) ((orgX + 1) * invVoxelwx);
          if (indxX < 0)
              indxX = 0;
          else if (indxX >= nx)
              indxX = nx - 1;
          if (Math.abs(dirX) < 1e-6f) {
              stepX = 0;
              stopX = indxX;
              deltaX = 0;
              tnextX = Float.POSITIVE_INFINITY;
          } else if (dirX > 0) {
              stepX = 1;
              stopX = nx;
              deltaX = voxelwx * invDirX;
              tnextX = intervalMin + ((indxX + 1) * voxelwx - 1 - orgX) * invDirX;
          } else {
              stepX = -1;
              stopX = -1;
              deltaX = -voxelwx * invDirX;
              tnextX = intervalMin + (indxX * voxelwx - 1 - orgX) * invDirX;
          }
          // stepping factors along Y
          indxY = (int) ((orgY + 1) * invVoxelwy);
          if (indxY < 0)
              indxY = 0;
          else if (indxY >= ny)
              indxY = ny - 1;
          if (Math.abs(dirY) < 1e-6f) {
              stepY = 0;
              stopY = indxY;
              deltaY = 0;
              tnextY = Float.POSITIVE_INFINITY;
          } else if (dirY > 0) {
              stepY = 1;
              stopY = ny;
              deltaY = voxelwy * invDirY;
              tnextY = intervalMin + ((indxY + 1) * voxelwy - 1 - orgY) * invDirY;
          } else {
              stepY = -1;
              stopY = -1;
              deltaY = -voxelwy * invDirY;
              tnextY = intervalMin + (indxY * voxelwy - 1 - orgY) * invDirY;
          }
          // stepping factors along Z
          indxZ = (int) ((orgZ + 1) * invVoxelwz);
          if (indxZ < 0)
              indxZ = 0;
          else if (indxZ >= nz)
              indxZ = nz - 1;
          if (Math.abs(dirZ) < 1e-6f) {
              stepZ = 0;
              stopZ = indxZ;
              deltaZ = 0;
              tnextZ = Float.POSITIVE_INFINITY;
          } else if (dirZ > 0) {
              stepZ = 1;
              stopZ = nz;
              deltaZ = voxelwz * invDirZ;
              tnextZ = intervalMin + ((indxZ + 1) * voxelwz - 1 - orgZ) * invDirZ;
          } else {
              stepZ = -1;
              stopZ = -1;
              deltaZ = -voxelwz * invDirZ;
              tnextZ = intervalMin + (indxZ * voxelwz - 1 - orgZ) * invDirZ;
          }
          // are we starting inside the cube
          boolean isInside = inside(indxX, indxY, indxZ) && bounds.contains(r.ox, r.oy, r.oz);
          // trace through the grid
          for (;;) {
              if (inside(indxX, indxY, indxZ) != isInside) {
                  // we hit a boundary
                  r.setMax(intervalMin);
                  // if we are inside, the last bit needs to be flipped
                  if (isInside)
                      curr ^= 1;
                  state.setIntersection(curr);
                  return;
              }
              if (tnextX < tnextY && tnextX < tnextZ) {
                  curr = dirX > 0 ? 0 : 1;
                  intervalMin = tnextX;
                  if (intervalMin > intervalMax)
                      return;
                  indxX += stepX;
                  if (indxX == stopX)
                      return;
                  tnextX += deltaX;
              } else if (tnextY < tnextZ) {
                  curr = dirY > 0 ? 2 : 3;
                  intervalMin = tnextY;
                  if (intervalMin > intervalMax)
                      return;
                  indxY += stepY;
                  if (indxY == stopY)
                      return;
                  tnextY += deltaY;
              } else {
                  curr = dirZ > 0 ? 4 : 5;
                  intervalMin = tnextZ;
                  if (intervalMin > intervalMax)
                      return;
                  indxZ += stepZ;
                  if (indxZ == stopZ)
                      return;
                  tnextZ += deltaZ;
              }
          }
      }
  
      public int getNumPrimitives() {
          return 1;
      }
  
      public float getPrimitiveBound(int primID, int i) {
          return ((i & 1) == 0) ? -1 : 1;
      }
  
      public BoundingBox getWorldBounds(Matrix4 o2w) {
          if (o2w == null)
              return bounds;
          return o2w.transform(bounds);
      }
  }