[commited]lookAt for children of a node

Spatial's lookAt works quite nice as long as it is not a child of a node that has a worldrotation != Quat(0,0,0,1)



Let's say I have a node with rotation r1 and a childnode c1 that I want to lookAt(Vec3(1,1,1))



The result is that the localrotation of c1 is pointing to the Vec3(1,1,1) but to c1's localrotation the localrotation of the parent (r1) is added as well. So in the end c1 is not pointing to Vec3(1,1,1) at all. (known behaviour)



Here an example to show what I mean:



Three Boxes: b1,b2,b3.  b1 parent of b2, b2 parent of b3. All boxes set to lookAt(Vec3(1,0,1)):


import com.jme.app.SimpleGame;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Arrow;
import com.jme.scene.shape.Box;

public class Test extends SimpleGame{

   Node n,n2;
   
   @Override
   protected void simpleInitGame() {

      // first node with one box
      Node node1 = new Node();
      Box a = new Box("b1",new Vector3f(0,0,0),new Vector3f(3,1,1));
      node1.attachChild(a);
      
      // second node with one box as well (same size)
      Node node2 = new Node();
      Box a2 = new Box("b1",new Vector3f(0,0,0),new Vector3f(3,1,1));
      a2.getLocalTranslation().addLocal(0,1.5f,0);
      a2.updateGeometricState(0,true);
      node2.attachChild(a2);
      
      Node node3 = new Node();
      Box a3 = new Box("b1",new Vector3f(0,0,0),new Vector3f(3,1,1));
      a3.getLocalTranslation().addLocal(0,3f,0);
      a3.updateGeometricState(0,true);
      node3.attachChild(a3);      
      
      node2.attachChild(node3);
      node1.attachChild(node2);

      rootNode.attachChild(node1);
      
//      node1.lookAt(new Vector3f(1,0,1), Vector3f.UNIT_Y,true);
      node1.lookAt(new Vector3f(1,0,1), Vector3f.UNIT_Y);
      node1.updateGeometricState(0, true);
//      node2.lookAt(new Vector3f(1,0,1), Vector3f.UNIT_Y,true);
      node2.lookAt(new Vector3f(1,0,1), Vector3f.UNIT_Y);
      node2.updateGeometricState(0, true);
//      node3.lookAt(new Vector3f(1,0,1), Vector3f.UNIT_Y,true);
      node3.lookAt(new Vector3f(1,0,1), Vector3f.UNIT_Y);
      node3.updateGeometricState(0, true);
   }


   public static void main(String[] args)
   {
      new Test().start();
   }
   
}



You see all 3 boxes point to different directions.

Actually I'm not sure if there is a possibility to fix it. I didn't find any. So here is a way. (sry I have no svn in my work's eclipse so I have no patch right now... )Just replace the whole lookAt-section of spatial by this(the old behavior is still working):

[EDIT: Patch in next post]

In the test are 3 commented lines. uncomment them (and comment the corrsponding lines). Now all three
boxes are pointing in the same direction.

Although it seems to work I have to say that I'm no master of maths so there might be still something wrong.

Index: src/com/jme/scene/Spatial.java
===================================================================
--- src/com/jme/scene/Spatial.java   (revision 4770)
+++ src/com/jme/scene/Spatial.java   (working copy)
@@ -498,9 +498,37 @@
      *            a vector indicating the (local) up direction. (typically {0,
      *            1, 0} in jME.)
      */
-    public void lookAt(Vector3f position, Vector3f upVector) {
+    public void lookAt(Vector3f position, Vector3f upVector)
+    {
+       lookAt(position,upVector,false);
+    }
+   
+   // with setting the 3rd parameter (takeParentInAccount) to true the localrotation
+   // is set so that the spatials worldrotation (and not the localroation) is looking to the
+   // given position
+    /**
+     * <code>lookAt</code> is a convenience method for auto-setting the local
+     * rotation based on a position 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} this method takes a world position to
+     * look at not a relative direction.
+     *
+     * @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.)
+     * @param takeParentInAccount
+     *             if true : the localrotation is fixed so that this spatial's worldrotation
+     *                      is pointing to the position
+     *            if false: the localrotation is pointing to the position (worldrotation might be
+     *                      not pointing to it
+     */
+    public void lookAt(Vector3f position, Vector3f upVector, boolean takeParentInAccount) {
         compVecA.set(position).subtractLocal(getWorldTranslation());
         getLocalRotation().lookAt(compVecA, upVector);
+      if (takeParentInAccount && parent!=null)
+         getLocalRotation().multLocal(parent.getWorldRotation().inverse());       
     }
 
     /**
@@ -1685,6 +1713,7 @@
 
         return renderStateList != null ? renderStateList[type.ordinal()] : null;
     }
+   
 
     /**
      * Clears a given render state index by setting it to null.



Hmm,...there seems to be a rounding problem. Using this gets as result (where the quaternions should be the same):

[x=-0.27984813 y=0.36470518 z=0.11591689 w=0.88047624]
[x=-0.27984816 y=0.36470518 z=0.11591692 w=0.88047636]

Hmm,...too bad as I don't know how to write the unit-test now...!? There might a almostEqual(float)-method in quaternion/Vector3f where you can get rid out rounding problems...What do you think?