A Simple Quaternion Question?

I know that much has been asked and answered about the use of Quaternions, however I haven't found what I'm looking for.



Essentially, I would like to use a SpatialTransformer to smoothly turn an attacker toward a defender, and likewise the defender toward the attacker – so that they can clash face to face.  It sounds simple enough, but… while I am able to get the basic behavior that I want, the rotations appear to be approximations of what I'm actually expecting, sometimes the models are over rotated and sometimes under rotated and at other times they appear to be correct.



I set up the SpatialTransformer in the usual way and then feed it two rotations, the first being the model's original rotation and the second being a Quaternion built with lookAt using the target model's translation.  I have tried both world and local (to the model that will be rotating) translations, but both appear to give the same result.



The code to grab the Quaternions basically looks like this:



Vector3f destLoc = new Vector3f();
Quaternion originRot = new Quaternion(baseNode.getLocalRotation);
// using world to local
Vector3f destLoc = new Vector3f();
baseNode.worldToLocal(targetNode.getWorldTranslation(), destLoc);
Quaternion destRot = new Quaternion();
destRot.lookAt(new Vector3f(destLoc.x, 0, destLoc.z), new Vector3f(0,1,0));

// feed originRot and destRot to the SpatialTransformer...



Any ideas what I'm doing wrong?  Is there a step to this that I'm missing?





The original rotation should get the world rotation, I believe. Because what you want is for it to look at the object in world coordinates. I don't know if look at takes world coordinates into account, but you could check that out.

In trying to apply the "everything but the kitchen sink" formula :wink: I tried this.  But I tried it again for kicks – to no avail – along with a few other things.  Now if I only knew what the kitchen sink of the problem was.  (Maybe in understanding the kitchen of how Quaternions work?  Probably.)  Anyway, thanks for the suggestion… I'm going to back-burner this and open it in a lab sometime so that I can look at the problem more microscopically.  I must move along… this was only a side-quest to the tasks to accomplish this weekend.  Using Spatial's lookAt and some fancy camera tricks will mask the problem for now.  Of course any other suggestions would be very welcome, so that when I open the lab I have a few more items on the list to explore.

What about posting some test case for me to have a look at it?  :wink: (I am lazy, sorry)

I plan to do that if I can't figure it out in a lab… however, it seems that you could put one together in probably a half an hour by using simple game and a couple of boxes – box A is the target to look at via a Quaternion and B is the one that will look at it.  Iniitialize B to a random rotation with a stable Y axes to simulate a player model "standing upright" and then when say the Space Bar is pressed rotate it to look at A via Quaternion lookAt and a SpatialTransformer to control the rotation.  The next step is to put boxes C, D, and E on the field and use the keypad to define which one to look at.  Right now, I'm simply too far into something else in the KISS CE – of which I will post a video of later in my showcase thread :wink:

Thanks, basixs.  I'll check it out!

Glad I could help :slight_smile:



(mouth is 'watering' in anticipation to try out KISS CE ;))

Basixs – this works (minus your extra int in the Cylinder constructor)!  However, its not quite what I'm after… what I'm trying to do is feed a SpatialTransformer the first node's original rotation ~and~ the final rotation (which will point to the second node), so that the ST can smoothly rotate the first node from original to final rotations.  This cannot be done by directly setting the rotation with a Quaternion.lookAt since that will change the node's rotation when the next frame is processed… however your code gave me an idea of how to grab the Quaternion.lookAt rotation (which is void return) without affecting the node being operated upon: copy and modify the Quaternion.lookAt code to provide a return!  So, while time is short here, I think that the following will work… and which I'll have to see about tomorrow evening:



  private void lookAt( final Node nodeOne, final Vector3f location ) {
        final Vector3f direction = nodeOne.getLocalTranslation().subtract( location ).negateLocal().normalizeLocal();

        Quaternion qOriginal = new Quaternion(nodeOne.getLocalRotation());
        Quaternion qNew = new Quaternion(quatLookAt(direction, worldUp));
       
        System.out.println(nodeOne.getName() + " [Original]: " + qOriginal);
        System.out.println(nodeOne.getName() + " [     New]: " + qNew);
       
        // feed qOriginal and qNew to SpatialTransformer

        // nodeOne.getLocalRotation().lookAt( direction, worldUp );
    }

    private Quaternion quatLookAt(Vector3f direction, Vector3f up ) {
       
       Vector3f tmpZaxis = new Vector3f();
       Vector3f tmpXaxis = new Vector3f();
       Vector3f tmpYaxis = new Vector3f();
       Quaternion quat = new Quaternion();
       
       
        tmpZaxis.set( direction ).normalizeLocal();
        tmpXaxis.set( up ).crossLocal( direction ).normalizeLocal();
        tmpYaxis.set( direction ).crossLocal( tmpXaxis ).normalizeLocal();
        quat.fromAxes( tmpXaxis, tmpYaxis, tmpZaxis );
       
        return(quat);
       
    }



and if this works, its onto locking the Y axis so that the models don't unnaturally tilt during rotation... something tells me that this isn't as easy as just setting all Y's to zero when building the Quaternions...

(Beyond this, about the KISS CE: I hope to get a WebStart package together in the next month or so for everyone to check out first hand ; - )

new Cylinder( "cylinder", 12, 12, 0, 2, 5, true, false );

(axisSamples, radialSamples, radius, radius2, height, closed, inverted);



(the first radius at 0 makes it a cone ;))





since the lookAt takes a direction, you can just manipulate the y direction then (set it to 0, the re-normalize the vector) :wink:




copy and modify the Quaternion.lookAt code to provide a return!

You could send in a 'calc quaternion' (to the original lookAt method) then use that as your final rotation (this would avoid un-necessary object creation)...


    private void lookAt( final Quaternion endRotation, final Vector3f location,  final Vector3f lookAtLocation ) {
        final Vector3f direction = location.subtract( lookAtLocation ).negateLocal().normalizeLocal();
        endRotation.lookAt( direction, worldUp );
    }


(Directly in your actor class might be a good spot ;))
basixs said:

new Cylinder( "cylinder", 12, 12, 0, 2, 5, true, false );
(axisSamples, radialSamples, radius, radius2, height, closed, inverted);

(the first radius at 0 makes it a cone ;))


Hmm... eclipse was noting that Cylinder didn't have such a constructor (jME 2.0).  I didn't look too deeply into the issue.


since the lookAt takes a direction, you can just manipulate the y direction then (set it to 0, the re-normalize the vector) ;)


Noted.


copy and modify the Quaternion.lookAt code to provide a return!

You could send in a 'calc quaternion' (to the original lookAt method) then use that as your final rotation (this would avoid un-necessary object creation)...


    private void lookAt( final Quaternion endRotation, final Vector3f location,  final Vector3f lookAtLocation ) {
        final Vector3f direction = location.subtract( lookAtLocation ).negateLocal().normalizeLocal();
        endRotation.lookAt( direction, worldUp );
    }


(Directly in your actor class might be a good spot ;))


This is probably better -- I was not thinking in optimal mode last night but in "let's do this by hook or by crook" mode. 

"let's do this by hook or by crook" mode.

LOL

Now… this works as expected! 



I think that my whole initial problem was corrected by this line/math:



final Vector3f direction = location.subtract( lookAtLocation ).negateLocal().normalizeLocal();



And, using something like:


direction.setY(0);
direction.normalizeLocal();



prevents the model from rotating on the Y, thus keeping the final rotation bound to the X and Z axes only.

basixs -- great thanks for your help!  : - )

glad to help :slight_smile: