Package org.mt4j.components.bounds

Source Code of org.mt4j.components.bounds.BoundingSphere

/*
   * Copyright (c) 2003-2009 jMonkeyEngine
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met:
   *
   * * Redistributions of source code must retain the above copyright
   *   notice, this list of conditions and the following disclaimer.
   *
   * * Redistributions in binary form must reproduce the above copyright
   *   notice, this list of conditions and the following disclaimer in the
   *   documentation and/or other materials provided with the distribution.
   *
   * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
   *   may be used to endorse or promote products derived from this software
   *   without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
package org.mt4j.components.bounds;

import java.nio.FloatBuffer;

import org.mt4j.components.MTComponent;
import org.mt4j.components.TransformSpace;
import org.mt4j.components.visibleComponents.shapes.AbstractShape;
import org.mt4j.components.visibleComponents.shapes.mesh.MTTriangleMesh;
import org.mt4j.components.visibleComponents.shapes.mesh.Triangle;
import org.mt4j.util.camera.IFrustum;
import org.mt4j.util.math.ToolsMath;
import org.mt4j.util.math.Matrix;
import org.mt4j.util.math.Plane;
import org.mt4j.util.math.Ray;
import org.mt4j.util.math.ToolsBuffers;
import org.mt4j.util.math.Vector3D;

import processing.core.PGraphics;



  /**
   * <code>BoundingSphere</code> defines a sphere that defines a container for a
   * group of vertices of a particular piece of geometry. This sphere defines a
   * radius and a center. <br>
   * <br>
   * A typical usage is to allow the class define the center and radius by calling
   * either <code>containAABB</code> or <code>averagePoints</code>. A call to
   * <code>computeFramePoint</code> in turn calls <code>containAABB</code>.
   *
   * @author Mark Powell, Christopher Ruff
   */
  public class BoundingSphere implements IBoundingShape{
//      private static final Logger logger = Logger.getLogger(BoundingSphere.class.getName());
     
      public float radius;
     
      private static final long serialVersionUID = 2L;

    static final private float radiusEpsilon = 1f + 0.00001f;

    static final private FloatBuffer _mergeBuf = ToolsBuffers.createVector3Buffer(8);

      static private final Vector3D[] verts = new Vector3D[3];
     
      protected Vector3D center = new Vector3D();

    private MTComponent peerComponent;
     
      /** The Constant _compVect1. */
    protected static final transient Vector3D _compVect1 = new Vector3D();

//      private Vector3D centerPointObjSpace;
   
    private Vector3D[] worldVecs;
    private boolean worldVecsDirty;
    private Vector3D centerPointWorld;
    private boolean centerWorldDirty;
   
    private float radiusWorld;
    private boolean radiusWorldDirty;

//      /**
//       * Default contstructor instantiates a new <code>BoundingSphere</code>
//       * object.
//       */
//      public BoundingSphere() {
//      }
//
//      /**
//       * Constructor instantiates a new <code>BoundingSphere</code> object.
//       *
//       * @param r
//       *            the radius of the sphere.
//       * @param c
//       *            the center of the sphere.
//       */
//      public BoundingSphere(float r, Vector3D c) {
//          this.center.setValues(c);
//          this.radius = r;
//      }
     
      /**
       * The Constructor.
       *
       * @param peerComponent the peer component
       */
      public BoundingSphere(AbstractShape peerComponent){
        this.peerComponent = peerComponent;
//        this.computeFromVertices(peerComponent.getGeometryInfo().getVertices());
//        this.computeFromPoints(peerComponent.getGeometryInfo().getVertBuff());
//        this.computeFromPoints(Vector3D.getDeepVertexArrayCopy(peerComponent.getGeometryInfo().getVertices()));
       
        if (peerComponent instanceof MTTriangleMesh){
          MTTriangleMesh mesh = (MTTriangleMesh)peerComponent;
          Triangle[] tris = mesh.getTriangles();
          this.computeFromTris(tris, 0, tris.length);
        }else{
          this.computeFromPoints(Vector3D.getDeepVertexArrayCopy(peerComponent.getGeometryInfo().getVertices())); //FIXME can we avoid the copy??
//          this.computeFromPoints(peerComponent.getGeometryInfo().getVertBuff()); //FIXME can we avoid the copy??
        }
       
        this.worldVecsDirty   = true;
      this.centerWorldDirty   = true;
//      this.worldVecs       = this.getVectorsGlobal();
//      this.centerPointWorld   = this.getCenterPointGlobal();
      this.radiusWorldDirty = true;
//      this.radiusWorld = this.getRadiusWorld();
      }
     
      /**
       * The Constructor.
       *
       * @param peerComponent the peer component
       * @param vectors the vectors
       */
      public BoundingSphere(MTComponent peerComponent, Vector3D[] vectors){
        this.peerComponent = peerComponent;
        this.computeFromPoints(vectors);
       
        this.worldVecsDirty   = true;
      this.centerWorldDirty   = true;
//      this.worldVecs       = this.getVectorsGlobal(); //Do it only if requested to save memory (no cached values)
//      this.centerPointWorld   = this.getCenterPointGlobal();
      this.radiusWorldDirty = true;
//      this.radiusWorld = this.getRadiusWorld();
      }
     
      /**
       * The Constructor.
       *
       * @param peerComponent the peer component
       * @param center the center
       * @param radius the radius
       */
      public BoundingSphere(MTComponent peerComponent, Vector3D center, float radius){
        this.peerComponent = peerComponent;
        this.radius = radius;
        this.center = new Vector3D(center);
       
        this.worldVecsDirty   = true;
      this.centerWorldDirty   = true;
//      this.worldVecs       = this.getVectorsGlobal();
//      this.centerPointWorld   = this.getCenterPointGlobal();
      this.radiusWorldDirty = true;
//      this.radiusWorld = this.getRadiusWorld();
      }

     
      /* (non-Javadoc)
       * @see org.mt4j.components.bounds.IBoundingShape#setGlobalBoundsChanged()
       */
      public void setGlobalBoundsChanged(){
        this.worldVecsDirty = true;
        this.centerWorldDirty = true;
        this.radiusWorldDirty = true;
      }
     
     
    /**
     * Draw bounds.
     *
     * @param g the g
     */
    public void drawBounds(PGraphics g){
      g.pushMatrix();
      g.pushStyle();
      g.fill(150,180);
     
      Vector3D l = this.getCenterPointLocal();
      g.translate(l.x, l.y, l.z);
      g.sphere(this.getRadius());
     
      g.popStyle();
      g.popMatrix();
    }
     

      /**
       * <code>getRadius</code> returns the radius of the bounding sphere. (in object space)
       *
       * @return the radius of the bounding sphere.
       */
      public float getRadius() {
          return radius;
      }

      /**
       * <code>setRadius</code> sets the radius of this bounding sphere.
       *
       * @param radius
       *            the new radius of the bounding sphere.
       */
      public void setRadius(float radius) {
          this.radius = radius;
      }

     
      /**
       * <code>computeFromPoints</code> creates a new Bounding Sphere from a
       * given set of points. It uses the <code>calcWelzl</code> method as
       * default.
       *
       * @param points
       *            the points to contain.
       */
      public void computeFromPoints(Vector3D[] points) {
          calcWelzl(points);
      }
     
     
      /**
       * <code>computeFromPoints</code> creates a new Bounding Sphere from a
       * given set of points. It uses the <code>calcWelzl</code> method as
       * default.
       *
       * @param points
       *            the points to contain.
       */
      public void computeFromPoints(FloatBuffer points) {
          calcWelzl(points);
      }

      /**
       * <code>computeFromTris</code> creates a new Bounding Box from a given
       * set of triangles. It is used in OBBTree calculations.
       *
       * @param tris
       * @param start
       * @param end
       */
      public void computeFromTris(Triangle[] tris, int start, int end) {
          if (end - start <= 0) {
              return;
          }

          Vector3D[] vertList = new Vector3D[(end - start) * 3];
         
          int count = 0;
          for (int i = start; i < end; i++) {
//            vertList[count++] = tris[i].get(0);
//            vertList[count++] = tris[i].get(1);
//            vertList[count++] = tris[i].get(2);
            vertList[count++] = tris[i].v0;
            vertList[count++] = tris[i].v1;
            vertList[count++] = tris[i].v2;
          }
          averagePoints(vertList);
      }
     
//      /**
//       * <code>computeFromTris</code> creates a new Bounding Box from a given
//       * set of triangles. It is used in OBBTree calculations.
//       *
//     * @param indices
//     * @param mesh
//       * @param start
//       * @param end
//       */
//      public void computeFromTris(int[] indices, MTTriangleMesh mesh, int start, int end) {
//        if (end - start <= 0) {
//              return;
//          }
//       
//        Vector3D[] vertList = new Vector3D[(end - start) * 3];
//         
//        Triangle[] tris = mesh.getTriangles();
//       
//          int count = 0;
//          for (int i = start; i < end; i++) {
//            mesh.getTriangle(indices[i], verts);
//            vertList[count++] = new Vector3D(verts[0]);
//            vertList[count++] = new Vector3D(verts[1]);
//            vertList[count++] = new Vector3D(verts[2]);
//          }
//         
//          averagePoints(vertList);
//      }

      /**
       * Calculates a minimum bounding sphere for the set of points. The algorithm
       * was originally found at
       * http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1
       * in C++ and translated to java by Cep21
       *
       * @param points
       *            The points to calculate the minimum bounds from.
       */
      public void calcWelzl(FloatBuffer points) {
         if (center == null){
                center = new Vector3D();
            }
        
            FloatBuffer buf = ToolsBuffers.createFloatBuffer(points.limit());
            points.rewind();
           
            float[] pointsArr = ToolsBuffers.getFloatArray(points);
            buf.put(pointsArr);
            buf.flip();
            this.recurseMini(buf, buf.limit() / 3, 0, 0);
      }
     
      /**
       * Calculates a minimum bounding sphere for the set of points. The algorithm
       * was originally found at
       * http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1
       * in C++ and translated to java by Cep21
       *
       * @param points
       *            The points to calculate the minimum bounds from.
       */
      public void calcWelzl(Vector3D[] points) {
        if (center == null){
          center = new Vector3D();
        }
        this.recurseMini(points, points.length, 0, 0);
      }

      private static Vector3D tempA = new Vector3D(), tempB = new Vector3D(), tempC = new Vector3D(), tempD = new Vector3D();
      /**
       * Used from calcWelzl. This function recurses to calculate a minimum
       * bounding sphere a few points at a time.
       *
       * @param points
       *            The array of points to look through.
       * @param p
       *            The size of the list to be used.
       * @param b
       *            The number of points currently considering to include with the
       *            sphere.
       * @param ap
       *            A variable simulating pointer arithmatic from C++, and offset
       *            in <code>points</code>.
       */
      private void recurseMini(FloatBuffer points, int p, int b, int ap) {
          switch (b) {
          case 0:
              this.radius = 0;
              this.center.setXYZ(0, 0, 0);
              break;
          case 1:
              this.radius = 1f - radiusEpsilon;
              ToolsBuffers.populateFromBuffer(center, points, ap-1);
              break;
          case 2:
              ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
              ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
              setSphere(tempA, tempB);
              break;
          case 3:
              ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
              ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
              ToolsBuffers.populateFromBuffer(tempC, points, ap-3);
              setSphere(tempA, tempB, tempC);
              break;
          case 4:
              ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
              ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
              ToolsBuffers.populateFromBuffer(tempC, points, ap-3);
              ToolsBuffers.populateFromBuffer(tempD, points, ap-4);
              setSphere(tempA, tempB, tempC, tempD);
              return;
          }
          for (int i = 0; i < p; i++) {
              ToolsBuffers.populateFromBuffer(tempA, points, i+ap);
              if (Vector3D.distanceSquared(tempA, center) - (radius * radius) > radiusEpsilon - 1f) {
                  for (int j = i; j > 0; j--) {
                      ToolsBuffers.populateFromBuffer(tempB, points, j + ap);
                      ToolsBuffers.populateFromBuffer(tempC, points, j - 1 + ap);
                      ToolsBuffers.setInBuffer(tempC, points, j + ap);
                      ToolsBuffers.setInBuffer(tempB, points, j - 1 + ap);
                  }
                  recurseMini(points, i, b + 1, ap + 1);
              }
          }
      }
     
     
      /**
       * Used from calcWelzl. This function recurses to calculate a minimum
       * bounding sphere a few points at a time.
       *
       * @param points
       *            The array of points to look through.
       * @param p
       *            The size of the list to be used.
       * @param b
       *            The number of points currently considering to include with the
       *            sphere.
       * @param ap
       *            A variable simulating pointer arithmatic from C++, and offset
       *            in <code>points</code>.
       */
      private void recurseMini(Vector3D[] points, int p, int b, int ap) {
          switch (b) {
          case 0:
              this.radius = 0;
              this.center.setXYZ(0, 0, 0);
              break;
          case 1:
              this.radius = 1f - radiusEpsilon;
//              ToolsBuffers.populateFromBuffer(center, points, ap-1);
              this.center.setXYZ(points[ap-1].x, points[ap-1].y, points[ap-1].z);
              break;
          case 2:
            tempA.setXYZ(points[ap-1].x, points[ap-1].y, points[ap-1].z);
            tempB.setXYZ(points[ap-2].x, points[ap-2].y, points[ap-2].z);
//              ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
//              ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
              setSphere(tempA, tempB);
              break;
          case 3:
//              ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
//              ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
//              ToolsBuffers.populateFromBuffer(tempC, points, ap-3);
            tempA.setXYZ(points[ap-1].x, points[ap-1].y, points[ap-1].z);
            tempB.setXYZ(points[ap-2].x, points[ap-2].y, points[ap-2].z);
            tempC.setXYZ(points[ap-3].x, points[ap-3].y, points[ap-3].z);
              setSphere(tempA, tempB, tempC);
              break;
          case 4:
//              ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
//              ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
//              ToolsBuffers.populateFromBuffer(tempC, points, ap-3);
//              ToolsBuffers.populateFromBuffer(tempD, points, ap-4);
              tempA.setXYZ(points[ap-1].x, points[ap-1].y, points[ap-1].z);
            tempB.setXYZ(points[ap-2].x, points[ap-2].y, points[ap-2].z);
            tempC.setXYZ(points[ap-3].x, points[ap-3].y, points[ap-3].z);
            tempD.setXYZ(points[ap-4].x, points[ap-4].y, points[ap-4].z);
              setSphere(tempA, tempB, tempC, tempD);
              return;
          }
          for (int i = 0; i < p; i++) {
//              ToolsBuffers.populateFromBuffer(tempA, points, i+ap);
            tempA.setXYZ(points[i+ap].x, points[i+ap].y, points[i+ap].z);
              if (Vector3D.distanceSquared(tempA, center) - (radius * radius) > radiusEpsilon - 1f) {
                  for (int j = i; j > 0; j--) {
//                      ToolsBuffers.populateFromBuffer(tempB, points, j + ap);
//                      ToolsBuffers.populateFromBuffer(tempC, points, j - 1 + ap);
                    tempB.setXYZ(points[j + ap].x, points[j + ap].y, points[j + ap].z);
                    tempC.setXYZ(points[j - 1 + ap].x, points[j - 1 + ap].y, points[j - 1 + ap].z);
                   
//                    ToolsBuffers.setInBuffer(tempC, points, j + ap);
//                    ToolsBuffers.setInBuffer(tempB, points, j - 1 + ap);
                    points[j+ap].setXYZ(tempC.x, tempC.y, tempC.z);
                      points[j - 1 + ap].setXYZ(tempB.x, tempB.y, tempB.z);
                  }
                  recurseMini(points, i, b + 1, ap + 1);
              }
          }
      }


      /**
       * Calculates the minimum bounding sphere of 4 points. Used in welzl's
       * algorithm.
       *
       * @param O
       *            The 1st point inside the sphere.
       * @param A
       *            The 2nd point inside the sphere.
       * @param B
       *            The 3rd point inside the sphere.
       * @param C
       *            The 4th point inside the sphere.
       * @see #calcWelzl(java.nio.FloatBuffer)
       */
      private void setSphere(Vector3D O, Vector3D A, Vector3D B, Vector3D C) {
          Vector3D a = A.getSubtracted(O);
          Vector3D b = B.getSubtracted(O);
          Vector3D c = C.getSubtracted(O);

          float Denominator = 2.0f * (a.x * (b.y * c.z - c.y * b.z) - b.x
                  * (a.y * c.z - c.y * a.z) + c.x * (a.y * b.z - b.y * a.z));
          if (Denominator == 0) {
              center.setXYZ(0, 0, 0);
              radius = 0;
          } else {
//              Vector3D o = a.cross(b).multLocal(c.lengthSquared()).addLocal(
//                      c.cross(a).multLocal(b.lengthSquared())).addLocal(
//                      b.cross(c).multLocal(a.lengthSquared())).divideLocal(
//                      Denominator);
            Vector3D o = a.getCross(b).scaleLocal(c.lengthSquared()).addLocal(
                      c.getCross(a).scaleLocal(b.lengthSquared())).addLocal(
                      b.getCross(c).scaleLocal(a.lengthSquared())).scaleLocal(
                      1f/Denominator);

              radius = o.length() * radiusEpsilon;
             
//              O.add(o, center);
             
              center.setValues(O.getAdded(o));
          }
      }

      /**
       * Calculates the minimum bounding sphere of 3 points. Used in welzl's
       * algorithm.
       *
       * @param O
       *            The 1st point inside the sphere.
       * @param A
       *            The 2nd point inside the sphere.
       * @param B
       *            The 3rd point inside the sphere.
       * @see #calcWelzl(java.nio.FloatBuffer)
       */
      private void setSphere(Vector3D O, Vector3D A, Vector3D B) {
          Vector3D a = A.getSubtracted(O);
          Vector3D b = B.getSubtracted(O);
          Vector3D acrossB = a.getCross(b);

          float Denominator = 2.0f * acrossB.dot(acrossB);

          if (Denominator == 0) {
              center.setXYZ(0, 0, 0);
              radius = 0;
          } else {
//              Vector3D o = acrossB.cross(a).multLocal(b.lengthSquared())
//                      .addLocal(b.cross(acrossB).multLocal(a.lengthSquared()))
//                      .divideLocal(Denominator);
            Vector3D o = acrossB.getCross(a).scaleLocal(b.lengthSquared())
                .addLocal(b.getCross(acrossB).scaleLocal(a.lengthSquared()))
                .divideLocal(Denominator);
//                .scaleLocal(1f/Denominator);
           
              radius = o.length() * radiusEpsilon;
             
//              O.add(o, center);
              center.setValues(O.getAdded(o));
          }
      }

      /**
       * Calculates the minimum bounding sphere of 2 points. Used in welzl's
       * algorithm.
       *
       * @param O
       *            The 1st point inside the sphere.
       * @param A
       *            The 2nd point inside the sphere.
       * @see #calcWelzl(java.nio.FloatBuffer)
       */
      private void setSphere(Vector3D O, Vector3D A) {
          radius = ToolsMath.sqrt(((A.x - O.x) * (A.x - O.x) + (A.y - O.y)
                  * (A.y - O.y) + (A.z - O.z) * (A.z - O.z)) / 4f) + radiusEpsilon - 1f;
         
//          center.interpolate(O, A, .5f);
          center.setValues(this.interpolate(O, A, .5f));
      }
     
     
      /**
       * Sets this vector to the interpolation by changeAmnt from beginVec to finalVec
       * this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
       * @param beginVec the beging vector (changeAmnt=0)
       * @param finalVec The final vector to interpolate towards
       * @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
       *  change from beginVec towards finalVec
       */
      public Vector3D interpolate(Vector3D beginVec,Vector3D finalVec, float changeAmnt) {
        float x =(1-changeAmnt)*beginVec.x + changeAmnt*finalVec.x;
        float y =(1-changeAmnt)*beginVec.y + changeAmnt*finalVec.y;
        float z =(1-changeAmnt)*beginVec.z + changeAmnt*finalVec.z;
        return new Vector3D(x,y,z);
      }


      /**
       * <code>averagePoints</code> selects the sphere center to be the average
       * of the points and the sphere radius to be the smallest value to enclose
       * all points.
       *
       * @param points
       *            the list of points to contain.
       */
      public void averagePoints(Vector3D[] points) {
          //logger.info("Bounding Sphere calculated using average points.");
          center = points[0].getCopy();

          for (int i = 1; i < points.length; i++) {
              center.addLocal(points[i]);
          }
         
          float quantity = 1.0f / points.length;
//          center.multLocal(quantity);
          center.scaleLocal(quantity);

          float maxRadiusSqr = 0;
          for (int i = 0; i < points.length; i++) {
              Vector3D diff = points[i].getSubtracted(center);
              float radiusSqr = diff.lengthSquared();
              if (radiusSqr > maxRadiusSqr) {
                  maxRadiusSqr = radiusSqr;
              }
          }

          radius = (float) Math.sqrt(maxRadiusSqr) + radiusEpsilon - 1f;

      }


      private float getMaxAxis(Vector3D scale) {
          float x = ToolsMath.abs(scale.x);
          float y = ToolsMath.abs(scale.y);
          float z = ToolsMath.abs(scale.z);
         
          if (x >= y) {
              if (x >= z)
                  return x;
              return z;
          }
         
          if (y >= z)
              return y;
         
          return z;
      }

//      /**
//       * <code>whichSide</code> takes a plane (typically provided by a view
//       * frustum) to determine which side this bound is on.
//       *
//       * @param plane
//       *            the plane to check against.
//       * @return side
//       */
//      public Side whichSide(Plane plane) {
//          float distance = plane.pseudoDistance(center);
//          if (distance <= -radius) { return Side.NEGATIVE; }
//          if (distance >=  radius) { return Side.POSITIVE; }
//          return Side.NONE;
//      }
     
      /**
       * <code>whichSide</code> takes a plane (typically provided by a view
       * frustum) to determine which side this bound is on.
       *
       * @param plane the plane to check against.
       *
       * @return int
       */
      public int whichSide(Plane plane) {
//        float distance = plane.pseudoDistance(center);
//        if (distance <= -radius) { return Side.NEGATIVE; }
//        if (distance >=  radius) { return Side.POSITIVE; }
//        return Side.NONE;
        int side = plane.classifyPoint(center);
        return side;
      }

     
//      /**
//       * <code>merge</code> combines this sphere with a second bounding sphere.
//       * This new sphere contains both bounding spheres and is returned.
//       *
//       * @param volume
//       *            the sphere to combine with this sphere.
//       * @return a new sphere
//       */
//      public BoundingVolume merge(BoundingVolume volume) {
//          if (volume == null) {
//              return this;
//          }
//
//          switch(volume.getType()) {
//
//          case Sphere: {
//            BoundingSphere sphere = (BoundingSphere) volume;
//              float temp_radius = sphere.getRadius();
//              Vector3D temp_center = sphere.getCenter();
//              BoundingSphere rVal = new BoundingSphere();
//              return merge(temp_radius, temp_center, rVal);
//          }
//         
//          case Capsule: {
//            BoundingCapsule capsule = (BoundingCapsule) volume;
//              float temp_radius = capsule.getRadius()
//                + capsule.getLineSegment().getExtent();
//              Vector3D temp_center = capsule.getCenter();
//              BoundingSphere rVal = new BoundingSphere();
//              return merge(temp_radius, temp_center, rVal);
//          }
//
//          case AABB: {
//            BoundingBox box = (BoundingBox) volume;
//              Vector3D radVect = new Vector3D(box.xExtent, box.yExtent,
//                      box.zExtent);
//              Vector3D temp_center = box.center;
//              BoundingSphere rVal = new BoundingSphere();
//              return merge(radVect.length(), temp_center, rVal);
//          }
//
//          case OBB: {
//            OrientedBoundingBox box = (OrientedBoundingBox) volume;
//              BoundingSphere rVal = (BoundingSphere) this.clone(null);
//              return rVal.mergeOBB(box);
//          }
//
//          default:
//            return null;
//
//          }
//      }

      private Vector3D tmpRadVect = new Vector3D();

//      /**
//       * <code>mergeLocal</code> combines this sphere with a second bounding
//       * sphere locally. Altering this sphere to contain both the original and the
//       * additional sphere volumes;
//       *
//       * @param volume
//       *            the sphere to combine with this sphere.
//       * @return this
//       */
//      public BoundingVolume mergeLocal(BoundingVolume volume) {
//          if (volume == null) {
//              return this;
//          }
//
//          switch (volume.getType()) {
//
//          case Sphere: {
//            BoundingSphere sphere = (BoundingSphere) volume;
//              float temp_radius = sphere.getRadius();
//              Vector3D temp_center = sphere.getCenter();
//              return merge(temp_radius, temp_center, this);
//          }
//
//          case AABB: {
//            BoundingBox box = (BoundingBox) volume;
//              Vector3D radVect = tmpRadVect;
//              radVect.set(box.xExtent, box.yExtent, box.zExtent);
//              Vector3D temp_center = box.center;
//              return merge(radVect.length(), temp_center, this);
//          }
//
//          case OBB: {
//            return mergeOBB((OrientedBoundingBox) volume);
//          }
//         
//          case Capsule: {
//            BoundingCapsule capsule = (BoundingCapsule) volume;
//            return merge(capsule.getRadius() + capsule.getLineSegment().getExtent(),
//                capsule.getCenter(), this);
//          }
//
//          default:
//            return null;
//          }
//      }

//      /**
//       * Merges this sphere with the given OBB.
//       *
//       * @param volume
//       *            The OBB to merge.
//       * @return This sphere, after merging.
//       */
//      private BoundingSphere mergeOBB(OrientedBoundingBox volume) {
//          // compute edge points from the obb
//          if (!volume.correctCorners)
//              volume.computeCorners();
//          _mergeBuf.rewind();
//          for (int i = 0; i < 8; i++) {
//              _mergeBuf.put(volume.vectorStore[i].x);
//              _mergeBuf.put(volume.vectorStore[i].y);
//              _mergeBuf.put(volume.vectorStore[i].z);
//          }
//
//          // remember old radius and center
//          float oldRadius = radius;
//          Vector3D oldCenter = _compVect2.set( center );
//
//          // compute new radius and center from obb points
//          computeFromPoints(_mergeBuf);
//          Vector3D newCenter = _compVect3.set( center );
//          float newRadius = radius;
//
//          // restore old center and radius
//          center.set( oldCenter );
//          radius = oldRadius;
//
//          //merge obb points result
//          merge( newRadius, newCenter, this );
//
//          return this;
//      }
//
//      private BoundingVolume merge(float temp_radius, Vector3D temp_center,
//              BoundingSphere rVal) {
//          Vector3D diff = temp_center.subtract(center, _compVect1);
//          float lengthSquared = diff.lengthSquared();
//          float radiusDiff = temp_radius - radius;
//
//          float fRDiffSqr = radiusDiff * radiusDiff;
//
//          if (fRDiffSqr >= lengthSquared) {
//              if (radiusDiff <= 0.0f) {
//                  return this;
//              }
//                 
//              Vector3D rCenter = rVal.getCenter();
//              if ( rCenter == null ) {
//                  rVal.setCenter( rCenter = new Vector3D() );
//              }
//              rCenter.set(temp_center);
//              rVal.setRadius(temp_radius);
//              return rVal;
//          }
//
//          float length = (float) Math.sqrt(lengthSquared);
//
//          Vector3D rCenter = rVal.getCenter();
//          if ( rCenter == null ) {
//              rVal.setCenter( rCenter = new Vector3D() );
//          }
//          if (length > radiusEpsilon) {
//              float coeff = (length + radiusDiff) / (2.0f * length);
//              rCenter.set(center.addLocal(diff.multLocal(coeff)));
//          } else {
//              rCenter.set(center);
//          }
//
//          rVal.setRadius(0.5f * (length + radius + temp_radius));
//          return rVal;
//      }


      public Vector3D getCenter() {
          return center;
      }


//      /*
//       * (non-Javadoc)
//       *
//       * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
//       */
//      public boolean intersectsSphere(BoundingSphere bs) {
//          if (!Vector3D.isValidVector(center) || !Vector3D.isValidVector(bs.center)) return false;
//
//          Vector3D diff = getCenter().subtract(bs.getCenter(), _compVect1);
//          float rsum = getRadius() + bs.getRadius();
//          return (diff.dot(diff) <= rsum * rsum);
//      }
//
//      /*
//       * (non-Javadoc)
//       *
//       * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)
//       */
//      public boolean intersectsBoundingBox(BoundingBox bb) {
//          if (!Vector3D.isValidVector(center) || !Vector3D.isValidVector(bb.center)) return false;
//
//          if (FastMath.abs(bb.center.x - getCenter().x) < getRadius()
//                  + bb.xExtent
//                  && FastMath.abs(bb.center.y - getCenter().y) < getRadius()
//                          + bb.yExtent
//                  && FastMath.abs(bb.center.z - getCenter().z) < getRadius()
//                          + bb.zExtent)
//              return true;
//
//          return false;
//      }
//
//      /*
//       * (non-Javadoc)
//       *
//       * @see com.jme.bounding.BoundingVolume#intersectsOrientedBoundingBox(com.jme.bounding.OrientedBoundingBox)
//       */
//      public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) {
//          return obb.intersectsSphere(this);
//      }
//     
//      public boolean intersectsCapsule(BoundingCapsule bc) {
//        return bc.intersectsSphere(this);
//      }

      /**
       * Check a vector... if it is null or its floats are NaN or infinite,
       * return false.  Else return true.
       * @param vector the vector to check
       * @return true or false as stated above.
       */
      public static boolean isValidVector(Vector3D vector) {
        if (vector == null) return false;
        if (Float.isNaN(vector.x) ||
            Float.isNaN(vector.y) ||
            Float.isNaN(vector.z)) return false;
        if (Float.isInfinite(vector.x) ||
            Float.isInfinite(vector.y) ||
            Float.isInfinite(vector.z)) return false;
        return true;
      }

     
      /*
       * (non-Javadoc)
       *
       * @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray)
       */
      public boolean intersects(Ray ray) {
          if (!isValidVector(center))
            return false;

//          Vector3D diff = _compVect1.set(ray.getOrigin()).subtractLocal(getCenter());
         
          Vector3D diff = _compVect1.setValues(ray.getRayStartPoint()).subtractLocal(getCenter());
         
          float radiusSquared = getRadius() * getRadius();
          float a = diff.dot(diff) - radiusSquared;
          if (a <= 0.0) {
              // in sphere
              return true;
          }

          // outside sphere
          float b = ray.getDirection().dot(diff);
          if (b >= 0.0) {
              return false;
          }
          return b*b >= a;
      }

     
    
      public Vector3D getIntersectionLocal(Ray ray){
        Vector3D rayDir = ray.getRayDirectionNormalized();
       
          Vector3D diff = _compVect1.setValues(ray.getRayStartPoint()).subtractLocal(this.getCenter());
          float a = diff.dot(diff) - (getRadius()*getRadius());
          float a1, discr, root;
         
          if (a <= 0.0) {
              // inside sphere
            a1 = rayDir.dot(diff);
           
              discr = (a1 * a1) - a;
              root = ToolsMath.sqrt(discr);
              float[] distances = new float[] { root - a1 };
             
              Vector3D hitVect = new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint()) ;
             
//              Vector3D[] points = new Vector3D[] {
//                hitVect
//              };
             
//              IntersectionRecord record = new IntersectionRecord(distances, points);
//              return record;
              return hitVect;
          }
         
         
          a1 = rayDir.dot(diff);
          if (a1 >= 0.0) {
//              return new IntersectionRecord();
            return null;
          }
         
         
          discr = a1*a1 - a;
          if (discr < 0.0){
//              return new IntersectionRecord();
            return null;
          }else if (discr >= ToolsMath.ZERO_TOLERANCE) {
              root = ToolsMath.sqrt(discr);
              float[] distances = new float[] { -a1 - root, -a1 + root };
             
              Vector3D[] points = new Vector3D[] {
                      new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint()),
                      new Vector3D(rayDir).scaleLocal(distances[1]).addLocal(ray.getRayStartPoint())
                      };
//              IntersectionRecord record = new IntersectionRecord(distances, points);
//              return record;
              //FIXME WELCHEN PUNKT NEHMEN?
              return points[0];
          } else {
              float[] distances = new float[] { -a1 };
              Vector3D[] points = new Vector3D[] {
                      new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint())};
//              IntersectionRecord record = new IntersectionRecord(distances, points);
//              return record;
              return points[0];
          }
      }
     
     
     
//      /*
//       * (non-Javadoc)
//       *
//       * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray)
//       */
//      public IntersectionRecord intersectsWhere(Ray ray) {
//        Vector3D rayDir = ray.getDirection();
//       
//          Vector3D diff = _compVect1.setValues(ray.getRayStartPoint()).subtractLocal(this.getCenter());
//          float a = diff.dot(diff) - (getRadius()*getRadius());
//          float a1, discr, root;
//         
//          if (a <= 0.0) {
//              // inside sphere
//            a1 = rayDir.dot(diff);
//           
//              discr = (a1 * a1) - a;
//              root = FastMath.sqrt(discr);
//              float[] distances = new float[] { root - a1 };
//             
//              Vector3D[] points = new Vector3D[] {
//                  new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint())
//              };
//             
//              IntersectionRecord record = new IntersectionRecord(distances, points);
//              return record;
//          }
//         
//         
//          a1 = rayDir.dot(diff);
//          if (a1 >= 0.0) {
//              return new IntersectionRecord();
//          }
//         
//         
//          discr = a1*a1 - a;
//          if (discr < 0.0){
//              return new IntersectionRecord();
//          }else if (discr >= FastMath.ZERO_TOLERANCE) {
//              root = FastMath.sqrt(discr);
//              float[] distances = new float[] { -a1 - root, -a1 + root };
//              Vector3D[] points = new Vector3D[] {
//                      new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint()),
//                      new Vector3D(rayDir).scaleLocal(distances[1]).addLocal(ray.getRayStartPoint())
//                      };
//              IntersectionRecord record = new IntersectionRecord(distances, points);
//              return record;
//          } else {
//              float[] distances = new float[] { -a1 };
//              Vector3D[] points = new Vector3D[] {
//                      new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint())};
//              IntersectionRecord record = new IntersectionRecord(distances, points);
//              return record;
//          }
//      }

      //@Override
      public boolean containsPointLocal(Vector3D point) {
        return   Vector3D.distanceSquared(getCenter(), point)
            <
            (getRadius() * getRadius());
//          return getCenter().distanceSquared(point) < (getRadius() * getRadius());
      }

      public float distanceToEdge(Vector3D point) {
          return Vector3D.distance(center, point) - radius;
      }
     

      public float getVolume() {
          return 4 * ToolsMath.ONE_THIRD * ToolsMath.PI * radius * radius * radius;
      }




    //@Override
    public Vector3D getCenterPointLocal() {
      return center.getCopy();
    }



    //@Override
    public Vector3D getCenterPointGlobal() {
      if (centerWorldDirty){
        Vector3D tmp = this.getCenterPointLocal();
        tmp.transform(this.peerComponent.getGlobalMatrix());
        this.centerPointWorld = tmp;
        this.centerWorldDirty = false;
        return this.centerPointWorld;
      }else{
        return this.centerPointWorld;
      }
//      Vector3D tmp = center.getCopy();
//      tmp.transform(this.peerComponent.getAbsoluteLocalToWorldMatrix());
//      return tmp;
    }




    //@Override
    public float getHeightXY(TransformSpace transformSpace) {
      switch (transformSpace) {
      case LOCAL:
        return this.getHeightXYVectLocal().length();
      case RELATIVE_TO_PARENT:{
        Vector3D p = this.getHeightXYVectLocal();
        Matrix m = new Matrix(this.peerComponent.getLocalMatrix());
        m.removeTranslationFromMatrix();
        p.transform(m);
        return p.length();
      }
      case GLOBAL:{
        Vector3D p = this.getHeightXYVectLocal();
        Matrix m = new Matrix(this.peerComponent.getGlobalMatrix());
        m.removeTranslationFromMatrix();
        p.transform(m);
        return p.length();
      }
      default:
        return -1;
      }
    }




    //@Override
    public Vector3D getHeightXYVectLocal() {
      return new Vector3D(0, this.getRadius()*2,0);
    }




    //@Override
    public Vector3D[] getVectorsLocal() {
      return new Vector3D[]{this.center.getCopy()};
    }




    //@Override
    public Vector3D[] getVectorsGlobal() {
      if (this.worldVecsDirty){
        Vector3D[] vecs = Vector3D.getDeepVertexArrayCopy(this.getVectorsLocal());
        Vector3D.transFormArrayLocal(this.peerComponent.getGlobalMatrix(), vecs);
        this.worldVecs = vecs;
        this.worldVecsDirty = false;
        return this.worldVecs;
      }else{
        return this.worldVecs;
      }
     
//      Vector3D tmp = center.getCopy();
//      tmp.transform(this.peerComponent.getAbsoluteLocalToWorldMatrix());
//      return new Vector3D[]{tmp};
    }




    //@Override
    public float getWidthXY(TransformSpace transformSpace) {
      switch (transformSpace) {
      case LOCAL:
        return this.getWidthXYVectLocal().length();
      case RELATIVE_TO_PARENT:{
        Vector3D p = this.getWidthXYVectLocal();
        Matrix m = new Matrix(this.peerComponent.getLocalMatrix());
        m.removeTranslationFromMatrix();
        p.transform(m);
        return p.length();
      }
      case GLOBAL:{
        Vector3D p = this.getWidthXYVectLocal();
        Matrix m = new Matrix(this.peerComponent.getGlobalMatrix());
        m.removeTranslationFromMatrix();
        p.transform(m);
        return p.length();
      }
      default:
        return -1;
      }
    }



    //@Override
    public Vector3D getWidthXYVectLocal() {
      return new Vector3D(this.getRadius()*2, 0,0);
    }

    //@Override
    public boolean isContainedInFrustum(IFrustum frustum) {
      int test = frustum.isSphereInFrustum(this.getCenterPointGlobal(), this.getRadiusWorld());
      if (test == IFrustum.OUTSIDE
      ){
        return false;
      }else{
        return true;
      }
    }
   
   
    //TODO cache centerPointWorld und radiusWorld!
   
    //To avoid obj creation for each frustum test
    private Vector3D tmpVec = new Vector3D();
    private Matrix tmpMatrix = new Matrix();
   
    private float getRadiusWorld(){
      if (this.radiusWorldDirty){
        tmpVec.setXYZ(this.getRadius(), 0,0);
        tmpMatrix.set(this.peerComponent.getGlobalMatrix());
        tmpMatrix.removeTranslationFromMatrix();
        tmpVec.transform(tmpMatrix);
        this.radiusWorld = tmpVec.length();
        this.radiusWorldDirty = false;
        return this.radiusWorld;
      }else{
        return this.radiusWorld;
      }
//      tmpVec.setXYZ(this.getRadius(), 0,0);
////      Matrix m = new Matrix(this.peerComponent.getAbsoluteLocalToWorldMatrix());
//      tmpMatrix.set(this.peerComponent.getAbsoluteLocalToWorldMatrix());
//      tmpMatrix.removeTranslationFromMatrix();
//      tmpVec.transform(tmpMatrix);
//      return tmpVec.length();
    }
   
  }
TOP

Related Classes of org.mt4j.components.bounds.BoundingSphere

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.