Need help with quanterion (cube rotate)

Hello.
I need help with quanterion. I have spent on this all saturday and couple hour today and still I don’t know how to do it properly (I feel so dumb).
I want to rotate cube (around on own x,y or z axis) in fact on the beginning only two wall front and right.
//front wall
-0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
//right wall
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.5f, -0.5f, -1.0f,
0.5f, 0.5f, -1.0f

So if I want to make a rotation on Z axis (roll) I must to do something with 8 vectors and I did something like this


private final static float DEGTORAD = (float)(Math.PI/180);
private static float rollAngle = 4;
private static Quaternion roll = new Quaternion();
private Quaternion change;
private float[] array = new float[]{
    		-0.5f,  0.5f,  0.0f,  
    		0.5f,  0.5f,  0.0f,  
    		0.5f, -0.5f,  0.0f, 
    		-0.5f, -0.5f,  0.0f,
    		
    		0.5f,  0.5f,  0.0f,  
    		0.5f, -0.5f,  0.0f,  
    		0.5f, -0.5f, -1.0f,  
    		0.5f,  0.5f, -1.0f};
private FloatBuffer vertexData;

private void update(){
		roll();
		for(int i = 0;i<array.length;i+=3) {
			change = new Quaternion(array[i], array[i+1], array[i+2], 0.0f);
			Quaternion.mul(roll, change, change);
			array[i] = change.x;
			array[i+1] = change.y;
			array[i+2] = change.z;
		}
		 vertexData.put(array);
	     vertexData.flip();
	}

public static void roll() {
	    roll.setFromAxisAngle(new Vector4f(0, 0, 1, rollAngle * DEGTORAD));
	    roll.normalise();
	}

public void gameLoop(){
		...
		while(running) {
			update();
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glLoadIdentity();//Matrix reseting.
			GL11.glTranslatef(0.0f, 0.0f, -6.0f); // Move Right And Into The Screen
			GL11.glRotatef(80, 0.5f, 0.5f, -0.5f); // Rotate The Cube On X, Y & Z
			
			glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_COLOR_ARRAY);
			
            glVertexPointer(vertexSize, 0, vertexData);
            glColorPointer(colorSize, 0, colorData);
            
            glDrawArrays(GL_QUADS, 0, amountOfVertices);
            
            glDisableClientState(GL_COLOR_ARRAY);
            glDisableClientState(GL_VERTEX_ARRAY);
			
			Display.sync(60);
			Display.update();
		}
		glDisableClientState(GL_COLOR_ARRAY);
		glDisableClientState(GL_VERTEX_ARRAY);
		
		Display.destroy();
	}

and I must admit that the front wall rotate properly also right but unfrtunateli something bad happening with right wall namely
is getting smaller and smaller. The same happens with front wall if I change rotation e.g.
roll.setFromAxisAngle(new Vector4f(1, 0, 0, rollAngle * DEGTORAD));

http://img859.imageshack.us/img859/2908/picqq.png

What’s wrong how to do this properly?

I think i should create this topic in ‘LWJGL Development’ it’s more appropriate place. Can someone move it.

It looks like you are creating wrong quaternion in roll().

That’s why I ask. I really don’t know how it should be.

A rotation about some axis ‘V’, where V is a normalized vector about an angle “a” in quaternion space is: sin(a/2)V + cos(a/2).

I’d assume that the Quaternion class that you’re using has a method to directly set and/or create one directly from an axis angle pair.

I’m using
org.lwjgl.util.vector.Quaternion

Humm…okay, something like this:


// x,y,z must be a unit vector & 'a' is the angle.
public static Quaternion setAxisAngle(Quaternion q, float x, float y, float z, float a)
{
  a *= 0.5f;

  float sinA = (float)Math.sin(a);
  float cosA = (float)Math.cos(a);

  x *= sinA;
  y *= sinA;
  z *= sinA;

  q.set(x,y,z,cosA);

  return q;
}

Try this.


         change = new Quaternion(array[i], array[i+1], array[i+2], 1.0f);
         Quaternion.mul(roll, change, change);
         array[i] = change.x / change.w;
         array[i+1] = change.y / change.w;
         array[i+2] = change.z / change.w;

(http://en.wikipedia.org/wiki/Homogeneous_coordinates)

This method doing the same what
roll.setFromAxisAngle(new Vector4f(0, 0, 1, rollAngle * DEGTORAD));
and result is the same.

and this in general does terrible things which I will not describe :wink:

If you want to use quaternion Q to rotate point (vertex) P with respect to translated origin G use this formula:

P’ = Q(P-G)Q’ + G

where P’ is the transformed point, and Q’ is the quaternion inverse.

That’s nice algebraically, but it significantly reduces. But you hit on a good point, a rotation isn’t being performed.

Here is some code. Hope it will work with minor changes.


...

   private void update(){
      roll();
      for(int i = 0;i<array.length;i+=3) {
         change = new Vector3f(array[i], array[i+1], array[i+2]);
         MathUtils.transform(roll, change, change);
         array[i] = change.x;
         array[i+1] = change.y;
         array[i+2] = change.z;
      }
       vertexData.put(array);
       vertexData.flip();
   }

...

public class MathUtils {
   public static Vector3f transform( Quaternion q, Vector3f vector, Vector3f result )
    {
        Quaternion inv = new Quaternion(-q.getX(), -q.getY(), -q.getZ(), q.getW() );
        inv.normalize();
        
        Quaternion tmp = new Quaternion();
        
        mul(q, vector, tmp );
        tmp.mul( inv );
        
        result.set( tmp.getX(), tmp.getY(), tmp.getZ() );
        
        return ( result );
    }

    public static Quaternion mul(Quaternion q, Vector3f t, Quaternion out )
    {
        out.set( ( q.getW() * t.getX() ) + ( q.getY() * t.getZ() ) - ( q.getZ() * t.getY() ),
                    ( q.getW() * t.getY() ) + ( q.getZ() * t.getX() ) - ( q.getX() * t.getZ() ),
                    ( q.getW() * t.getZ() ) + ( q.getX() * t.getY() ) - ( q.getY() * t.getX() ),
                   -( q.getX() * t.getX() ) - ( q.getY() * t.getY() ) - ( q.getZ() * t.getZ() )
               );
        
        return ( out );
    }
  }

Oh gosh!!! Stranger and Roquen your code works the same I mean PROPERLY ;D.
The mistake was in this part of code


array[i] = change.x;
array[i+1] = change.y;
array[i+2] = change.z;

during rotation around axis Z I shouldn’t do this

array[i+2] = change.z;

around axis Y I shouldn’t do this

array[i+1] = change.y;

and

array[i] = change.x;

during rotation around axis X.

I really appreciate your help.
Thanks all of you!

That great. Now some comments. First you’re modifing your source data by incrementally rotating it. You don’t want to do this as float point operations introduce errors. You want to make a modified copy, always using the ‘source’ data as input. So you need two quaternions, one for the current rotation and a second (what you already have) to update the first (by a multiply). The second thing is that directly rotating a vector by quaternion should only be used when the number of vectors is very small (like a couple), otherwise convert to a matrix and rotate with it. And since the target data is to be used solely by the graphics card, you don’t need to rotate the vectors yourself. Feed the matrix to OpenGL and let it do the work. If your library doesn’t have a conversion, see this thread: http://www.java-gaming.org/topics/skeletal-animation/25483/view.html

@Stranger: You don’t want to directly transcribe the algebraic equation into code. The formula contains a fair amount of cancellation (by design), multiplies by zero & unneeded normalization which is a pretty undesirable thing in float point computation. See here for these operations removed: http://www.java-gaming.org/topics/quaternion-vector-multiplication/25517/view.html

@Roquen: I had in mind general case, just to get it working. Of course, the code can be optimized (if any).