/**
   * <code>rotateUpTo</code> is a utility function that alters the local rotation to point the Y
   * axis in the direction given by newUp.
   *
   * @param newUp the up vector to use - assumed to be a unit vector.
   */
  public void rotateUpTo(Vector3f newUp) {
    TempVars vars = TempVars.get();

    Vector3f compVecA = vars.vect1;
    Quaternion q = vars.quat1;

    // First figure out the current up vector.
    Vector3f upY = compVecA.set(Vector3f.UNIT_Y);
    Quaternion rot = localTransform.getRotation();
    rot.multLocal(upY);

    // get angle between vectors
    float angle = upY.angleBetween(newUp);

    // figure out rotation axis by taking cross product
    Vector3f rotAxis = upY.crossLocal(newUp).normalizeLocal();

    // Build a rotation quat and apply current local rotation.
    q.fromAngleNormalAxis(angle, rotAxis);
    q.mult(rot, rot);

    vars.release();

    setTransformRefresh();
  }
 /**
  * (Internal use only) Forces a refresh of the given types of data.
  *
  * @param transforms Refresh world transform based on parents'
  * @param bounds Refresh bounding volume data based on child nodes
  * @param lights Refresh light list based on parents'
  */
 public void forceRefresh(boolean transforms, boolean bounds, boolean lights) {
   if (transforms) {
     setTransformRefresh();
   }
   if (bounds) {
     setBoundRefresh();
   }
   if (lights) {
     setLightListRefresh();
   }
 }
  /**
   * @return A clone of this Spatial, the scene graph in its entirety is cloned and can be altered
   *     independently of the original scene graph.
   *     <p>Note that meshes of geometries are not cloned explicitly, they are shared if static, or
   *     specially cloned if animated.
   *     <p>All controls will be cloned using the Control.cloneForSpatial method on the clone.
   * @see Mesh#cloneForAnim()
   */
  public Spatial clone(boolean cloneMaterial) {
    try {
      Spatial clone = (Spatial) super.clone();
      if (worldBound != null) {
        clone.worldBound = worldBound.clone();
      }
      clone.worldLights = worldLights.clone();
      clone.localLights = localLights.clone();

      // Set the new owner of the light lists
      clone.localLights.setOwner(clone);
      clone.worldLights.setOwner(clone);

      // No need to force cloned to update.
      // This node already has the refresh flags
      // set below so it will have to update anyway.
      clone.worldTransform = worldTransform.clone();
      clone.localTransform = localTransform.clone();

      if (clone instanceof Node) {
        Node node = (Node) this;
        Node nodeClone = (Node) clone;
        nodeClone.children = new SafeArrayList<Spatial>(Spatial.class);
        for (Spatial child : node.children) {
          Spatial childClone = child.clone(cloneMaterial);
          childClone.parent = nodeClone;
          nodeClone.children.add(childClone);
        }
      }

      clone.parent = null;
      clone.setBoundRefresh();
      clone.setTransformRefresh();
      clone.setLightListRefresh();

      clone.controls = new SafeArrayList<Control>(Control.class);
      for (int i = 0; i < controls.size(); i++) {
        Control newControl = controls.get(i).cloneForSpatial(clone);
        newControl.setSpatial(clone);
        clone.controls.add(newControl);
      }

      if (userData != null) {
        clone.userData = (HashMap<String, Savable>) userData.clone();
      }

      return clone;
    } catch (CloneNotSupportedException ex) {
      throw new AssertionError();
    }
  }
  /**
   * <code>lookAt</code> is a convenience method for auto-setting the local rotation based on a
   * position in world space and an up vector. It computes the rotation to transform the z-axis to
   * point onto 'position' and the y-axis to 'up'. Unlike {@link
   * Quaternion#lookAt(com.jme3.math.Vector3f, com.jme3.math.Vector3f) } this method takes a world
   * position to look at and not a relative direction.
   *
   * <p>Note : 28/01/2013 this method has been fixed as it was not taking into account the parent
   * rotation. This was resulting in improper rotation when the spatial had rotated parent nodes.
   * This method is intended to work in world space, so no matter what parent graph the spatial has,
   * it will look at the given position in world space.
   *
   * @param position where to look at in terms of world coordinates
   * @param upVector a vector indicating the (local) up direction. (typically {0, 1, 0} in jME.)
   */
  public void lookAt(Vector3f position, Vector3f upVector) {
    Vector3f worldTranslation = getWorldTranslation();

    TempVars vars = TempVars.get();

    Vector3f compVecA = vars.vect4;

    compVecA.set(position).subtractLocal(worldTranslation);
    getLocalRotation().lookAt(compVecA, upVector);

    if (getParent() != null) {
      Quaternion rot = vars.quat1;
      rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation());
      rot.normalizeLocal();
      setLocalRotation(rot);
    }
    vars.release();
    setTransformRefresh();
  }
  /**
   * Rotates the spatial by the given rotation.
   *
   * @return The spatial on which this method is called, e.g <code>this</code>.
   */
  public Spatial rotate(Quaternion rot) {
    this.localTransform.getRotation().multLocal(rot);
    setTransformRefresh();

    return this;
  }
  /**
   * Scales the spatial by the given scale vector.
   *
   * @return The spatial on which this method is called, e.g <code>this</code>.
   */
  public Spatial scale(float x, float y, float z) {
    this.localTransform.getScale().multLocal(x, y, z);
    setTransformRefresh();

    return this;
  }
  /**
   * Translates the spatial by the given translation vector.
   *
   * @return The spatial on which this method is called, e.g <code>this</code>.
   */
  public Spatial move(Vector3f offset) {
    this.localTransform.getTranslation().addLocal(offset);
    setTransformRefresh();

    return this;
  }
  /**
   * Translates the spatial by the given translation vector.
   *
   * @return The spatial on which this method is called, e.g <code>this</code>.
   */
  public Spatial move(float x, float y, float z) {
    this.localTransform.getTranslation().addLocal(x, y, z);
    setTransformRefresh();

    return this;
  }
 /** <code>setLocalTransform</code> sets the local transform of this spatial. */
 public void setLocalTransform(Transform t) {
   this.localTransform.set(t);
   setTransformRefresh();
 }
 /** <code>setLocalTranslation</code> sets the local translation of this spatial. */
 public void setLocalTranslation(float x, float y, float z) {
   this.localTransform.setTranslation(x, y, z);
   setTransformRefresh();
 }
 /**
  * <code>setLocalTranslation</code> sets the local translation of this spatial.
  *
  * @param localTranslation the local translation of this spatial.
  */
 public void setLocalTranslation(Vector3f localTranslation) {
   this.localTransform.setTranslation(localTranslation);
   setTransformRefresh();
 }
 /**
  * <code>setLocalScale</code> sets the local scale of this node.
  *
  * @param localScale the new local scale.
  */
 public void setLocalScale(Vector3f localScale) {
   localTransform.setScale(localScale);
   setTransformRefresh();
 }
 /** <code>setLocalScale</code> sets the local scale of this node. */
 public void setLocalScale(float x, float y, float z) {
   localTransform.setScale(x, y, z);
   setTransformRefresh();
 }
 /**
  * <code>setLocalScale</code> sets the local scale of this node.
  *
  * @param localScale the new local scale, applied to x, y and z
  */
 public void setLocalScale(float localScale) {
   localTransform.setScale(localScale);
   setTransformRefresh();
 }
 /**
  * <code>setLocalRotation</code> sets the local rotation of this node.
  *
  * @param quaternion the new local rotation.
  */
 public void setLocalRotation(Quaternion quaternion) {
   localTransform.setRotation(quaternion);
   setTransformRefresh();
 }
 /**
  * <code>setLocalRotation</code> sets the local rotation of this node by using a {@link Matrix3f}.
  *
  * @param rotation the new local rotation.
  */
 public void setLocalRotation(Matrix3f rotation) {
   localTransform.getRotation().fromRotationMatrix(rotation);
   setTransformRefresh();
 }