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.MathUtils;
  import org.sunflow.math.Matrix4;
  import org.sunflow.math.OrthoNormalBasis;
  import org.sunflow.math.Point3;
  import org.sunflow.math.Solvers;
  import org.sunflow.math.Vector3;
  
  public class Torus implements PrimitiveList {
      private float ri2, ro2;
      private float ri, ro;
  
      public Torus() {
          ri = 0.25f;
          ro = 1;
          ri2 = ri * ri;
          ro2 = ro * ro;
  
      }
  
      public boolean update(ParameterList pl, SunflowAPI api) {
          ri = pl.getFloat("radiusInner", ri);
          ro = pl.getFloat("radiusOuter", ro);
          ri2 = ri * ri;
          ro2 = ro * ro;
          return true;
      }
  
      public BoundingBox getWorldBounds(Matrix4 o2w) {
          BoundingBox bounds = new BoundingBox(-ro - ri, -ro - ri, -ri);
          bounds.include(ro + ri, ro + ri, ri);
          if (o2w != null)
              bounds = o2w.transform(bounds);
          return bounds;
      }
  
      public float getPrimitiveBound(int primID, int i) {
          switch (i) {
              case 0:
              case 2:
                  return -ro - ri;
              case 1:
              case 3:
                  return ro + ri;
              case 4:
                  return -ri;
              case 5:
                  return ri;
              default:
                  return 0;
          }
      }
  
      public int getNumPrimitives() {
          return 1;
      }
  
      public void prepareShadingState(ShadingState state) {
          state.init();
          state.getRay().getPoint(state.getPoint());
          Instance parent = state.getInstance();
          // get local point
          Point3 p = state.transformWorldToObject(state.getPoint());
          // compute local normal
          float deriv = p.x * p.x + p.y * p.y + p.z * p.z - ri2 - ro2;
          state.getNormal().set(p.x * deriv, p.y * deriv, p.z * deriv + 2 * ro2 * p.z);
          state.getNormal().normalize();
  
          double phi = Math.asin(MathUtils.clamp(p.z / ri, -1, 1));
          double theta = Math.atan2(p.y, p.x);
          if (theta < 0)
              theta += 2 * Math.PI;
          state.getUV().x = (float) (theta / (2 * Math.PI));
          state.getUV().y = (float) ((phi + Math.PI / 2) / Math.PI);
          state.setShader(parent.getShader(0));
          state.setModifier(parent.getModifier(0));
          // into world space
          Vector3 worldNormal = state.transformNormalObjectToWorld(state.getNormal());
          state.getNormal().set(worldNormal);
          state.getNormal().normalize();
          state.getGeoNormal().set(state.getNormal());
          // make basis in world space
          state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal()));
  
      }
  
      public void intersectPrimitive(Ray r, int primID, IntersectionState state) {
          // intersect in local space
          float rd2x = r.dx * r.dx;
          float rd2y = r.dy * r.dy;
          float rd2z = r.dz * r.dz;
          float ro2x = r.ox * r.ox;
          float ro2y = r.oy * r.oy;
          float ro2z = r.oz * r.oz;
          // compute some common factors
          double alpha = rd2x + rd2y + rd2z;
          double beta = 2 * (r.ox * r.dx + r.oy * r.dy + r.oz * r.dz);
          double gamma = (ro2x + ro2y + ro2z) - ri2 - ro2;
          // setup quartic coefficients
          double A = alpha * alpha;
          double B = 2 * alpha * beta;
          double C = beta * beta + 2 * alpha * gamma + 4 * ro2 * rd2z;
          double D = 2 * beta * gamma + 8 * ro2 * r.oz * r.dz;
          double E = gamma * gamma + 4 * ro2 * ro2z - 4 * ro2 * ri2;
          // solve equation
          double[] t = Solvers.solveQuartic(A, B, C, D, E);
          if (t != null) {
              // early rejection
              if (t[0] >= r.getMax() || t[t.length - 1] <= r.getMin())
                  return;
              // find first intersection in front of the ray
              for (int i = 0; i < t.length; i++) {
                  if (t[i] > r.getMin()) {
                      r.setMax((float) t[i]);
                      state.setIntersection(0);
                      return;
                  }
              }
          }
      }
  
      public PrimitiveList getBakingPrimitives() {
          return null;
      }
  }