user-controlled quad rotation

Started by
18 comments, last by Douglas 20 years, 7 months ago
I began to code a tiny application which purpose it is to let the user control in every moment of the execution about which axes the quad is to be rotated. For example, if key ''z'' is pressed, the quad is to be rotated about its z-axis. However, I tried to code this functionality in several ways and had several bugs each time and don''t want to explain the whole story of failure up to now. Hence, I''d be rather thankful if anyone could give me a snippet of code which successfully does what I am trying to realize.
CU Douglas
Advertisement
If you don''t want to explain the story, it''s pretty hard for us to help you. Basically how you can do it is to create an x, y and z variable that you increment each time you press one of the keys. Then in the renderloop have something like:

glIdentity();
glRotatef(x, 1.0f, 0.0f, 0.0f);
glRotatef(y, 0.0f, 1.0f, 0.0f);
glRotatef(z, 0.0f, 0.0f, 1.0f);
DrawQuad();

Sander Maréchal
[Lone Wolves Game Development][RoboBlast][Articles][GD Emporium][Webdesign][E-mail]

<hr />
Sander Marechal<small>[Lone Wolves][Hearts for GNOME][E-mail][Forum FAQ]</small>

Ok, here is the full story (as far as I can remember it).
I started with something like NeHe''s rotation tutorial:


glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity(); // Reset The View
glTranslatef(-1.5f,0.0f,-6.0f); // Move Into The Screen And Left

glRotatef(rtri,0.0f,1.0f,0.0f); // Rotate The Triangle On The Y axis ( NEW )

glBegin(GL_TRIANGLES); // Start Drawing A Triangle
glVertex3f( 0.0f, 1.0f, 0.0f); // First Point Of The Triangle
glVertex3f(-1.0f,-1.0f, 0.0f); // Second Point Of The Triangle
glVertex3f( 1.0f,-1.0f, 0.0f); // Third Point Of The Triangle
glEnd();




But since I wanted the rotation to be user-controlled, I made the elements
of the rotation vector in glRotatef variable:

glRotatef(rtri, vector.x, vector.y, vector.z);

where their value were either 1, -1 or 0 depending on the gamepad input.
Starting the changed code puzzled me since the triangle (if not rotated)
moved a long way into the screen and came back to its initial position
(like a spinning jojo if you''re watching the ground).
But I figured out that this was a mathematical / logical problem:
If no button is pressed then all elements of the vector defining the axis
to rotate the object about are zero and the axis is just a point which leaves
infinity possibilities to rotate.




Therefore, I distinguished between a standard- and a non-standard-scenaria:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity(); // Reset The View
glTranslatef(-1.5f,0.0f,-6.0f); // Move Into The Screen And Left

if(vector.x != 0 || vector.y != 0 || vector.z != 0)
{
++rtri;
glRotatef(rtri, vector.x, vector.y, vector.z);
}
else
{
// no rtri increase because no rotation is requested
}

glBegin(GL_TRIANGLES); // Start Drawing A Triangle
glVertex3f( 0.0f, 1.0f, 0.0f); // First Point Of The Triangle
glVertex3f(-1.0f,-1.0f, 0.0f); // Second Point Of The Triangle
glVertex3f( 1.0f,-1.0f, 0.0f); // Third Point Of The Triangle
glEnd();

This code fixes the "bouncing into the depth" bug described above.
The rotation is performed absolutely properly as long as the button
state doesn''t change. That means that if you press a button which wasn''t
pressed directly before or you release a button which was pressed before,
then the triangles abruptly jumps into another position (I mean angular
and not (x,y,z)). If all buttons are released, the triangle suddenly has
the position it had at the execution start.




In order to fix it, I did something like this:

else
{
// no rtri increase because no rotation is requested
glRotatef(rtri, oldVector.x, oldVector.y, oldVector.z)
}

The results:
If all buttons are released, then the triangle doensn''t jump back to its initial
position but nevertheless a jump occurs every time the button state is changed.
I got the idea, that it is caused by a twofold logical error:
1. The "steadily increasing the angle [by one degree]" only works if you rotate in
one direction all the time but not if the direction changed
2. The rotation as done above doesn''t consider the position in which the triangle
is at the moment
That''s way I replaced the rtri with a "dynamic" value, the length of the "totalRotationVector"
(where the totalRotationVector is the sum of all rotation vectors) and the vector itself as well:

glRotatef(totalVector.length, totalVector.x, totalvector.y, totalvector.z);

But this is scrap, too.
I''m tired of writing and hope you see my problem.
CU Douglas
It looks like your problems are caused by the way you feed the rotations to OpenGL. You try to use only one glRotate() function. You might want to try using 3 functions in a row (see my previous post). The glRotate function has the first variable as a rotation in degrees and the onther 3 vaiables as a vector that describes the axis of rotation. This vector should be normalized (hence your code messes up when they''re all set to 0.0f or mutliple one''s are 1.0f).

If you really want to use only one call to glRotatef() (because you want to avoid gimbal lock or something similar) you should search for axis-angle rotations and/or quaternions. Take a look at the Math & Physics FAQ which linkes to the Matrix and quaternion FAQ. More info about quaternions can also be found at Darwin 3D''s quaternion articles (1998).

Sander Maréchal
[Lone Wolves Game Development][RoboBlast][Articles][GD Emporium][Webdesign][E-mail]

<hr />
Sander Marechal<small>[Lone Wolves][Hearts for GNOME][E-mail][Forum FAQ]</small>

a) What is a "gimbal lock"?
b) What do you think will happen if you (try) to normalize a zero vector? It will lead to an undefined result, won't it?
c) After reading your last post I reconsidered my code and wrote this. The triangle performs no more unwanted jumps but it is somewhat unexact: The axes the triangle rotates about are no identical with the axes it should rotate about:



// Clear the color and depth buffers.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// reset position to origin of coordinate system

glLoadIdentity();

// move 5 units into the screen in order to have a proper
// viewing distance to the object

glTranslatef(posVector.x, posVector.y, posVector.z);

//--------------------
// move back and forth
//--------------------

if(SDL_JoystickGetButton(joystick, 0))
posVector.z += 0.03f;
if(SDL_JoystickGetButton(joystick, 1 ))
posVector.z -= 0.03f;

//----------------------------------
// move horizontally and vertically
//----------------------------------

if(SDL_JoystickGetAxis(joystick, 0) < -30000)
posVector.x -= 0.03f;
if(SDL_JoystickGetAxis(joystick, 0) > 30000)
posVector.x += 0.03f;
if(SDL_JoystickGetAxis(joystick, 1) > 30000)
posVector.y -= 0.03f;
if(SDL_JoystickGetAxis(joystick, 1) < -30000)
posVector.y += 0.03f;

//---------------------------------
// limit execution speed to 100 fps
//---------------------------------

SDL_Delay(10);

//---------------------
// rotate around x axis
//---------------------

if(SDL_JoystickGetButton(joystick, 6))
++angleVector.x;
else if(SDL_JoystickGetButton(joystick, 7))
--angleVector.x;

//---------------------
// rotate around y axis
//---------------------

if(SDL_JoystickGetButton(joystick, 8))
++angleVector.y;
else if(SDL_JoystickGetButton(joystick, 9))
--angleVector.y;

//---------------------
// rotate around z axis
//---------------------

if(SDL_JoystickGetButton(joystick, 10))
++angleVector.z;
else if(SDL_JoystickGetButton(joystick, 11))
--angleVector.z;

//--------------------
// core
//--------------------

glRotatef(angleVector.x, 1, 0, 0);
glRotatef(angleVector.y, 0, 1, 0);
glRotatef(angleVector.z, 0, 0, 1);

drawTriangle();

[edited by - Douglas on September 1, 2003 1:14:53 PM]

[edited by - Douglas on September 1, 2003 2:10:29 PM]

[edited by - Douglas on September 1, 2003 2:11:57 PM]

[edited by - Douglas on September 1, 2003 2:12:35 PM]
CU Douglas
A) Gimbal lock can occur if you rotate more than 90 degrees over multiple axis. In essence, the axes get screwed up and the computer can''t tell top from bottom anymore. Gimbal lock accidentally ''locks'' one of your rotation axis because it coïncides with another axis (say: X-axis falls on top of Y-axis). The result: You loose one axis of rotation (rotating over X and Y gives the same result since it''s the same axis now!). The best way around gimbal lock is using matrices (whickh can be slow and unstable) or quaternions (which are 4 dimensional rotations).

B) The results are eighter undefined (floating point rounding errors) or you crash your app as you try to divide by zero.

C) That''s probabely caused because you rotate and translate in the wrong order. Change your DrawTriangle routine so that it draws a triangle around the origin. Your orininal one looks reasonable enough:
glBegin(GL_TRIANGLES); // Start Drawing A TriangleglVertex3f( 0.0f, 1.0f, 0.0f); // First Point Of The TriangleglVertex3f(-1.0f,-1.0f, 0.0f); // Second Point Of The TriangleglVertex3f( 1.0f,-1.0f, 0.0f); // Third Point Of The TriangleglEnd();  

Next, change your drawing routine. First translate, then rotate. So:
glTranslatef(posVector.x, posVector.y, posVector.z);glRotatef(angleVector.x, 1, 0, 0);glRotatef(angleVector.y, 0, 1, 0);glRotatef(angleVector.z, 0, 0, 1);DrawTriangle(); 

That should fix it.



Sander Maréchal
[Lone Wolves Game Development][RoboBlast][Articles][GD Emporium][Webdesign][E-mail]

<hr />
Sander Marechal<small>[Lone Wolves][Hearts for GNOME][E-mail][Forum FAQ]</small>

I did what you suggested me, but without success.
Here's the whole function (and please don't tell me again that I should move glTranslatef since it really does come first in my code):


void drawScreen(void)
{
static skpVector3D posVector = { 0, 0, -5};
static skpVector3D angleVector = { 0, 0, 0 };

// draw backsides as well

glDisable(GL_CULL_FACE);

// We don't want to modify the projection matrix.

glMatrixMode(GL_MODELVIEW);

// prepare joystick input

SDL_Joystick *joystick;
joystick = SDL_JoystickOpen(0);

// Clear the color and depth buffers.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// reset position to origin of coordinate system

glLoadIdentity();

// move 5 units into the screen in order to have a proper
// viewing distance to the object

glTranslatef(posVector.x, posVector.y, posVector.z);

// keep joystick data up to date

SDL_JoystickUpdate();

//---------------------
// rotate around x axis
//---------------------

if(SDL_JoystickGetButton(joystick, 6))
++angleVector.x;
else if(SDL_JoystickGetButton(joystick, 7))
--angleVector.x;

// ... same with y- and z-axis

glRotatef(angleVector.x, 1, 0, 0);
glRotatef(angleVector.y, 0, 1, 0);
glRotatef(angleVector.z, 0, 0, 1);

glBegin(GL_TRIANGLES); // Start Drawing A Triangle
glVertex3f( 0.0f, 1.0f, 0.0f); // First Point Of The Triangle
glVertex3f(-1.0f,-1.0f, 0.0f); // Second Point Of The Triangle
glVertex3f( 1.0f,-1.0f, 0.0f); // Third Point Of The Triangle
glEnd();

//-------------
// swap buffers
//-------------

SDL_GL_SwapBuffers();
}

[edited by - Douglas on September 1, 2003 2:46:48 PM]

[edited by - Douglas on September 1, 2003 2:49:42 PM]

[edited by - Douglas on September 1, 2003 2:53:10 PM]
CU Douglas
Well, I can''t spot any errors in your code. What is it that''s not working? I take it that it is rotating around the correct point (the middle of the triangles) but that the rotations are going in another direction that you expect them to go (Can be a sign of gimal lock!). If that is the case, try to reverse the order of the three rotations (first rotate around z, then y and finally x). That will make it more naturally. Enough for simple games (platformers, shooters).

If you really want more complicated rotations (say, for a simple 3D space shooter, in which you can make loopings and stuff) then you really need quaternions or matrices. I suggest you take a look at the OpenGL tutorials at Game Tutorials. Yhey have a pretty straightforward quaternion tut (OpenGL tut''s page 3 or 4 if I remember correctly).



Sander Maréchal
[Lone Wolves Game Development][RoboBlast][Articles][GD Emporium][Webdesign][E-mail]

<hr />
Sander Marechal<small>[Lone Wolves][Hearts for GNOME][E-mail][Forum FAQ]</small>

I don't want quaternions or spaceships. Everything I want right now is a correctly rotating nasty triangle. Of course I tried your last tip to change to order of the three glRotatef statement but without success.
And you're right: It's rotating around the right point but it nevertheless looks strange, but I'm unable to describe it.

[edited by - Douglas on September 1, 2003 3:27:57 PM]
CU Douglas
Well, there''s one last thing you can so. Wrap the project in a zip file and post ia link. If it''s the rotation order or gimbal lock it''ll be easily spotted by others. Also, a spaceship is no different from your triangle. If you rotate it around too much you can get gimbal lock. And there are really only two ways out of that: Quaternions and Matrices.

Sander Maréchal
[Lone Wolves Game Development][RoboBlast][Articles][GD Emporium][Webdesign][E-mail]

<hr />
Sander Marechal<small>[Lone Wolves][Hearts for GNOME][E-mail][Forum FAQ]</small>

This topic is closed to new replies.

Advertisement