code: package org.sunflow.core;
  
  import org.sunflow.SunflowAPI;
  import org.sunflow.core.accel.NullAccelerator;
  import org.sunflow.math.BoundingBox;
  import org.sunflow.math.Matrix4;
  import org.sunflow.system.UI;
  import org.sunflow.system.UI.Module;
  
  /**
   * This class represent a geometric object in its native object space. These
   * object are not rendered directly, they must be instanced via {@link Instance}.
   * This class performs all the bookkeeping needed for on-demand tesselation and
   * acceleration structure building.
   */
  public class Geometry implements RenderObject {
      private Tesselatable tesselatable;
      private PrimitiveList primitives;
      private AccelerationStructure accel;
      private int builtAccel;
      private int builtTess;
      private String acceltype;
  
      /**
       * Create a geometry from the specified tesselatable object. The actual
       * renderable primitives will be generated on demand.
       * 
       * @param tesselatable tesselation object
       */
      public Geometry(Tesselatable tesselatable) {
          this.tesselatable = tesselatable;
          primitives = null;
          accel = null;
          builtAccel = 0;
          builtTess = 0;
          acceltype = null;
      }
  
      /**
       * Create a geometry from the specified primitive aggregate. The
       * acceleration structure for this object will be built on demand.
       * 
       * @param primitives primitive list object
       */
      public Geometry(PrimitiveList primitives) {
          tesselatable = null;
          this.primitives = primitives;
          accel = null;
          builtAccel = 0;
          builtTess = 1; // already tesselated
      }
  
      public boolean update(ParameterList pl, SunflowAPI api) {
          acceltype = pl.getString("accel", acceltype);
          // clear up old tesselation if it exists
          if (tesselatable != null) {
              primitives = null;
              builtTess = 0;
          }
          // clear acceleration structure so it will be rebuilt
          accel = null;
          builtAccel = 0;
          if (tesselatable != null)
              return tesselatable.update(pl, api);
          // update primitives
          return primitives.update(pl, api);
      }
  
      int getNumPrimitives() {
          return primitives == null ? 0 : primitives.getNumPrimitives();
      }
  
      BoundingBox getWorldBounds(Matrix4 o2w) {
          if (primitives == null) {
  
              BoundingBox b = tesselatable.getWorldBounds(o2w);
              if (b != null)
                  return b;
              if (builtTess == 0)
                  tesselate();
              if (primitives == null)
                  return null; // failed tesselation, return infinite bounding
              // box
          }
          return primitives.getWorldBounds(o2w);
      }
  
      void intersect(Ray r, IntersectionState state) {
          if (builtTess == 0)
              tesselate();
          if (builtAccel == 0)
              build();
          accel.intersect(r, state);
      }
  
      private synchronized void tesselate() {
          // double check flag
          if (builtTess != 0)
              return;
          if (tesselatable != null && primitives == null) {
              UI.printInfo(Module.GEOM, "Tesselating geometry ...");
              primitives = tesselatable.tesselate();
              if (primitives == null)
                  UI.printError(Module.GEOM, "Tesselation failed - geometry will be discarded");
              else
                  UI.printDetailed(Module.GEOM, "Tesselation produced %d primitives", primitives.getNumPrimitives());
          }
          builtTess = 1;
      }
  
      private synchronized void build() {
          // double check flag
          if (builtAccel != 0)
              return;
          if (primitives != null) {
              int n = primitives.getNumPrimitives();
              if (n >= 1000)
                  UI.printInfo(Module.GEOM, "Building acceleration structure for %d primitives ...", n);
              accel = AccelerationStructureFactory.create(acceltype, n, true);
              accel.build(primitives);
          } else {
              // create an empty accelerator to avoid having to check for null
              // pointers in the intersect method
              accel = new NullAccelerator();
          }
          builtAccel = 1;
      }
  
      void prepareShadingState(ShadingState state) {
          primitives.prepareShadingState(state);
      }
  
      PrimitiveList getBakingPrimitives() {
          if (builtTess == 0)
              tesselate();
          if (primitives == null)
              return null;
          return primitives.getBakingPrimitives();
      }
  
      PrimitiveList getPrimitiveList() {
          return primitives;
      }
  }