PDA

View Full Version : Newbie question:Translation and vertex coordinates

Ghazghkull
02-06-2009, 06:24 AM
Okay, so this is my second time trying graphical programming. The last time was in directx. Since my move to linux, it makes sense for me to now try opengl.

Anyway, I have a problem. What I basically want to do before I go any further is translate, for example, a quad. But I want to translate it after a rotation, but translate it so that it moves along a forward facing line. So if untranslated, say, it would move upward. Then if I rotated it 45 degrees, it would translate along that the same line, rotated 45 degrees. Basically so I have a front end that the quad/triangle moves towards. Imagine asteriods.

I am half-way through taking A-level further-mathematics, so I understand matrices and matrix transformations well (dot and cross product, triple scalar product, eigenvectors, equations, intersections and angles of lines and planes etc).

As a result I would think to approach this by finding the direction of the line I want to move it along (find the difference of two vertices along the line I want to move it along) and multiplying this to move it. However, I don't know how to access the coordinates of the vertices after translation.

I am starting out from the code::blocks auto-generated project code:

/* A simple program to show how to set up an X window for OpenGL rendering.
* X86 compilation: gcc -o -L/usr/X11/lib main main.c -lGL -lX11
* X64 compilation: gcc -o -L/usr/X11/lib64 main main.c -lGL -lX11
*/
#include <stdio.h>
#include <stdlib.h>

#include <GL/glx.h> /* this includes the necessary X headers */
#include <GL/gl.h>

#include <X11/X.h> /* X11 constant (e.g. TrueColor) */
#include <X11/keysym.h>

static int snglBuf[] = {GLX_RGBA, GLX_DEPTH_SIZE, 16, None};
static int dblBuf[] = {GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None};

Display *dpy;
Window win;
GLfloat xAngle = 42.0, yAngle = 82.0, zAngle = 112.0;
GLboolean doubleBuffer = GL_TRUE;

void fatalError(char *message)
{
fprintf(stderr, "main: %s\n", message);
exit(1);
}

void redraw(void)
{
static GLboolean displayListInited = GL_FALSE;

if (displayListInited)
{
/* if display list already exists, just execute it */
glCallList(1);
}
else
{
/* otherwise compile and execute to create the display list */
glNewList(1, GL_COMPILE_AND_EXECUTE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

/* front face */
glColor3f(0.0, 0.7, 0.1); /* green */
glVertex3f(-1.0, 1.0, 1.0);
glVertex3f(1.0, 1.0, 1.0);
glVertex3f(1.0, -1.0, 1.0);
glVertex3f(-1.0, -1.0, 1.0);

/* back face */
glColor3f(0.9, 1.0, 0.0); /* yellow */
glVertex3f(-1.0, 1.0, -1.0);
glVertex3f(1.0, 1.0, -1.0);
glVertex3f(1.0, -1.0, -1.0);
glVertex3f(-1.0, -1.0, -1.0);

/* top side face */
glColor3f(0.2, 0.2, 1.0); /* blue */
glVertex3f(-1.0, 1.0, 1.0);
glVertex3f(1.0, 1.0, 1.0);
glVertex3f(1.0, 1.0, -1.0);
glVertex3f(-1.0, 1.0, -1.0);

/* bottom side face */
glColor3f(0.7, 0.0, 0.1); /* red */
glVertex3f(-1.0, -1.0, 1.0);
glVertex3f(1.0, -1.0, 1.0);
glVertex3f(1.0, -1.0, -1.0);
glVertex3f(-1.0, -1.0, -1.0);
glEnd();
glEndList();
displayListInited = GL_TRUE;
}
if (doubleBuffer)
glXSwapBuffers(dpy, win);/* buffer swap does implicit glFlush */
else
glFlush(); /* explicit flush for single buffered case */
}

int main(int argc, char **argv)
{
XVisualInfo *vi;
Colormap cmap;
XSetWindowAttributes swa;
GLXContext cx;
XEvent event;
GLboolean needRedraw = GL_FALSE, recalcModelView = GL_TRUE;
int dummy;

/*** (1) open a connection to the X server ***/

dpy = XOpenDisplay(NULL);
if (dpy == NULL)
fatalError("could not open display");

/*** (2) make sure OpenGL's GLX extension supported ***/

if(!glXQueryExtension(dpy, &amp;dummy, &amp;dummy))
fatalError("X server has no OpenGL GLX extension");

/*** (3) find an appropriate visual ***/

/* find an OpenGL-capable RGB visual with depth buffer */
vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf);
if (vi == NULL)
{
vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf);
if (vi == NULL) fatalError("no RGB visual with depth buffer");
doubleBuffer = GL_FALSE;
}
if(vi->class != TrueColor)
fatalError("TrueColor visual required for this program");

/*** (4) create an OpenGL rendering context ***/

/* create an OpenGL rendering context */
cx = glXCreateContext(dpy, vi, /* no shared dlists */ None,
/* direct rendering if possible */ GL_TRUE);
if (cx == NULL)
fatalError("could not create rendering context");

/*** (5) create an X window with the selected visual ***/

/* create an X colormap since probably not using default visual */
cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone);
swa.colormap = cmap;
swa.border_pixel = 0;
win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0,
300, 300, 0, vi->depth, InputOutput, vi->visual,
CWBorderPixel | CWColormap | CWEventMask, &amp;swa);
XSetStandardProperties(dpy, win, "main", "main", None,
argv, argc, NULL);

/*** (6) bind the rendering context to the window ***/

glXMakeCurrent(dpy, win, cx);

/*** (7) request the X window to be displayed on the screen ***/

XMapWindow(dpy, win);

/*** (8) configure the OpenGL context for rendering ***/

glEnable(GL_DEPTH_TEST); /* enable depth buffering */
glDepthFunc(GL_LESS); /* pedantic, GL_LESS is the default */
glClearDepth(1.0); /* pedantic, 1.0 is the default */

/* frame buffer clears should be to black */
glClearColor(0.0, 0.0, 0.0, 0.0);

/* set up projection transform */
glMatrixMode(GL_PROJECTION);
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10.0);
/* establish initial viewport */
/* pedantic, full window size is default viewport */
glViewport(0, 0, 300, 300);

printf( "Press left mouse button to rotate around X axis\n" );
printf( "Press middle mouse button to rotate around Y axis\n" );
printf( "Press right mouse button to rotate around Z axis\n" );
printf( "Press ESC to quit the application\n" );

/*** (9) dispatch X events ***/

while (1)
{
do
{
XNextEvent(dpy, &amp;event);
switch (event.type)
{
case KeyPress:
{
KeySym keysym;
XKeyEvent *kevent;
char buffer[1];
/* It is necessary to convert the keycode to a
* keysym before checking if it is an escape */
kevent = (XKeyEvent *) &amp;event;
if ( (XLookupString((XKeyEvent *)&amp;event,buffer,1,&amp;keysym,NULL) == 1)
&amp;&amp; (keysym == (KeySym)XK_Escape) )
exit(0);
break;
}
case ButtonPress:
recalcModelView = GL_TRUE;
switch (event.xbutton.button)
{
case 1: xAngle += 10.0;
break;
case 2: yAngle += 10.0;
break;
case 3: zAngle += 10.0;
break;
}
break;
case ConfigureNotify:
glViewport(0, 0, event.xconfigure.width,
event.xconfigure.height);
/* fall through... */
case Expose:
needRedraw = GL_TRUE;
break;
}
} while(XPending(dpy)); /* loop to compress events */

if (recalcModelView)
{
glMatrixMode(GL_MODELVIEW);

/* reset modelview matrix to the identity matrix */

/* move the camera back three units */
glTranslatef(0.0, 0.0, -3.0);

/* rotate by X, Y, and Z angles */
glRotatef(xAngle, 0.1, 0.0, 0.0);
glRotatef(yAngle, 0.0, 0.1, 0.0);
glRotatef(zAngle, 0.0, 0.0, 1.0);

recalcModelView = GL_FALSE;
needRedraw = GL_TRUE;
}
if (needRedraw)
{
redraw();
needRedraw = GL_FALSE;
}
}

return 0;
}

Am I approaching this the right way? If so, how do I access the vertex coordinates? If not, how would I go about this? I can't seem to find information about this on the net anywhere, websites just list how to make basic transformations.

Ghaz.

_NK47
02-06-2009, 06:35 AM
"Am I approaching this the right way? If so, how do I access the vertex coordinates? If not, how would I go about this? I can't seem to find information about this on the net anywhere, websites just list how to make basic transformations."

you can't get the transformed vertices with standard OpenGL transformation. you should have your own matrix class for this written in your language which supports all kinds of matrix operations (transform, transpose, inverse, rotate, scale, translate, ...). then if you multiply by your matrix on CPU you can read the value out and work with it. besides, all glScale, glRotate calls are deprectated afaik. there are Plenty of matrix classes outthere just taking one from an open-source 3d engine (take care of row-major/column-major).

Ghazghkull
02-06-2009, 07:36 AM
So basically, don't use the built in transformations, but create my own?

-Ekh-
02-06-2009, 07:44 AM
Why do you want to have access to the vertices coordinates ? You can, of course, but if your model (say your quad) does not have to change (meaning it remains the same quad), but only translate, rotate, you can do it without modifying vertices.

you can do something like that:

//perspective/modelview init
glPushMatrix();
glTranslatef (where.x,where.y,where.z);
glPushMatrix();
glRotatef (angle,axis.x,axis.y,axis.z);
DrawModel();
glPopMatrix();
glPopMatrix();
...

Ghazghkull
02-06-2009, 08:15 AM
Ah, I See. I have seen something about push and pop before, but I think I misinterpreted its use. I think I understand it now.

I will look into use pop and push, as well as the making matrix classes.

Thanks for the help!

EDIT: I tried the pop and push method, but it yielded the same results as before.. If I rotate it, and then translate it, it still translates in a way as if it had not been rotated.

Am I doing something wrong?

Ghazghkull
02-06-2009, 09:33 AM
So am I doing something wrong? Should this work? Or am I going to have to make my own matrix transformation classes?

_NK47
02-06-2009, 09:40 AM
don't do your own, take some existing classes.

Ghazghkull
02-06-2009, 09:48 AM
Where would I be able to find such classes? Maybe someone knows of a good one?

Ghazghkull
02-06-2009, 10:27 AM
Also, once I have done my own transformation, how would I then insert these for the object I am transforming?

Sorry to be such a pain in the neck, but I think once I have this down I will be set for a while.

_NK47
02-06-2009, 10:46 AM
ANY open-source 3D engine would do, google for them.

Ghazghkull
02-06-2009, 10:55 AM
Okay, thanks.

MaxH
02-06-2009, 12:07 PM
You are making this problem out to be more difficult than it really is.
The pseudo-code required would be something like:

PushMatrix

Rotate
Translate
Draw_Rectangle

PopMatrix

Try switching the order of Rotate and Translate to see what affect it has.

Ghazghkull
02-06-2009, 01:48 PM
You are making this problem out to be more difficult than it really is.
The pseudo-code required would be something like:

PushMatrix

Rotate
Translate
Draw_Rectangle

PopMatrix

Try switching the order of Rotate and Translate to see what affect it has.

This works :D, although now the cube rotates around the origin of where it was... I think this will be the last obstacle in this whole translation thing.

Ghazghkull
02-06-2009, 02:44 PM
I can see why it would rotate like that, I just need a way around it.

MaxH
02-06-2009, 04:05 PM
I can see why it would rotate like that, I just need a way around it.

PushMatrix

Rotate (30, 0,1,0) (or something similar)
Translate
Rotate (-30, 0,1,0)
Draw_Rectangle

PopMatrix

Ghazghkull
02-06-2009, 06:50 PM
Hmm, that does not seem to work... it just moves around in an odd way.

MaxH
02-06-2009, 07:47 PM
You should not be using the values I have in my Rotate commands. You should be using the values appropriate to your application. I put 0,1,0 just as an example. You said this 'almost' worked when you used just one Rotate command, right? Use that same Rotate command (that almost worked) but with negative the rotation angle in the second call to glRotate. That should work IF I understand what you are trying to do.

Ghazghkull
02-07-2009, 02:57 AM
You should not be using the values I have in my Rotate commands. You should be using the values appropriate to your application. I put 0,1,0 just as an example. You said this 'almost' worked when you used just one Rotate command, right? Use that same Rotate command (that almost worked) but with negative the rotation angle in the second call to glRotate. That should work IF I understand what you are trying to do.

Honestly,

glPushMatrix();

glRotatef(yAngle, 0.0, 1, 0.0);

glTranslatef(amount, 0, 0);

glRotatef(-yAngle, 0.0, 1, 0.0);

recalcModelView = GL_FALSE;
needRedraw = GL_TRUE;
}
if (needRedraw)
{
redraw();
needRedraw = GL_FALSE;
}
}
glPopMatrix();

does not work ;/

Ghazghkull
02-07-2009, 03:38 AM
However, I can understand things I have read elsewhere

Translate
Rotate
-Translate

The idea is to get your axis of rotation to the centre of the object, rotate, then move it back again.

However, this moves in an odd way too.

Ghazghkull
02-07-2009, 04:17 AM
Hmm, I am slowly thinking my way through this one. What I need to do is move the object back to the origin, rotate it, then move it back. I just need to think of a way to implement this.

Ghazghkull
02-07-2009, 06:01 AM
Hmm, It might not be that simple, as obviously when I translate it after I return it to the origin it isn't going to return to where it was.

MaxH
02-07-2009, 12:33 PM
Hmm, that does not seem to work... it just moves around in an odd way. My example of unrotating the cube before rotating may have been oversimplified in one respect. In my example, only one rotation around the Y axis is used. If you are using rotations around X, Y, and Z, then in addition to rotating the cube by the -angles, you must reverse of the order of rotations. I have coded this up and tested it. It seems to be working for me if I understand what you really want. The lines of code of interest to you would be ...

Example of Code (http://pastebin.com/m27cbe944)

Ghazghkull
02-07-2009, 01:23 PM
Hmm, that does not seem to work... it just moves around in an odd way. My example of unrotating the cube before rotating may have been oversimplified in one respect. In my example, only one rotation around the Y axis is used. If you are using rotations around X, Y, and Z, then in addition to rotating the cube by the -angles, you must reverse of the order of rotations. I have coded this up and tested it. It seems to be working for me if I understand what you really want. The lines of code of interest to you would be ...

Example of Code (http://pastebin.com/m27cbe944)

This refuses to work as well. Again this may be my mistake, I will play around. Let me clarify what I want to do. My problem is that after I translate the cube, the rotation is around the origin on the screen. Obviously If I translate first and then rotate, this problem does not occur. But this will not result in the desired movement. I want it so that upon a keypress, the cube will move towards, say, the blue face. If then I rotate the cube (around its own axes), I want it to continue to move towards the blue face.
I have read elsewhere (and I understand why this is being done) people move the object back to the origin, rotate it, and then translate it back to where it was; but I am having trouble implementing this.

MaxH
02-07-2009, 01:53 PM
So your cube is not moving in a straight line. You want to be able to steer it, i.e. interactively change its direction of motion. To do that I would use glRotates to orient the cube and glTranslate to move it. This gets more involved cause you have to calculate the translation vector by doing your own rotation operations (short, matrix multiplication routines).

Ghazghkull
02-08-2009, 03:32 AM
So your cube is not moving in a straight line. You want to be able to steer it, i.e. interactively change its direction of motion. To do that I would use glRotates to orient the cube and glTranslate to move it. This gets more involved cause you have to calculate the translation vector by doing your own rotation operations (short, matrix multiplication routines).

Yes, that is basically what I want. Well I know that if I rotate it and then translate it, I will later have problems rotating it, as it will rotate around the origin. If this were a further pure mathematics question I would simply calculate the needed matrix, but it seems I don't have direct access to the matrix, so maybe you could eleborate a bit further?

btw thanks for all your help :)

CodeFoo
07-28-2010, 11:59 AM
Hi Ghaz,

Let me first throughout a quick disclaimer that I myself am quite the openGL newbie and by no means of polished code or 100% correct ideas. This being said, I HAVE recently done some things similar to what you are attempting with decent success and particularly this is how I would attack your problem:

First, I return attention to Ekh's post way back when, who has the correct foundation for what you need to do:

//perspective/modelview init
glPushMatrix();
glTranslatef (where.x,where.y,where.z);
glPushMatrix();
glRotatef (angle,axis.x,axis.y,axis.z);
DrawModel();
glPopMatrix();
glPopMatrix();
...

This is the most intuitive way, for me at least to use the matrix translations as what this is doing in English is

a) glTranslatef(...): Translates the origin for our quad to such a new position (moves it linearly about the world frame)

b) glRotatef(...): Rotates the object by such an 'angle' around this NEW origin

c) draw(): calls on any rendering function you yourself defined (in this case, glBegin(GL_QUADS)...). NOTE: that this entire object is draw about the NEW origin location in the NEW rotation from a) and b)

d) glPopMatrix()x2: Inverse to the glPushMatrix()'s preceding a) and b). Effectively undoes the translation and rotation for all future rendering (useful if say, you wanted to have a second cube whose position/orientation were independent from the first...)

NEWBIE PITFALL: quick note about push/pop (cause I was that guy that spent a day figuring this out the hard way...)

You should be VERY careful to perform the inverse pop for each and every push that you write, ESPECIALLY if you are looping through that piece of code (odds are you are if you get to it from glutIdleFunct(...) or some such thing). The reason being is that you will be one level higher on your stack than you meant to be for each pop that you miss. This error propagates in many amusing and head-scratching ways if you don't know to look for it, effectively moving your scene infinitely if translations are involved.

So, where was I? Back to your issue at hand. Assuming you understand the above well enough to implement such a foundation, your problem in my eyes is NOT that you need more matrices as this gets to be a heartache and a half (at least to me), but rather that you need some sort of calculated variable for the parameters of your single glTranslatef(). While it sounds complicated, I humbly believe that this method will be better than any additional matrices in the end as it allows you more direct control over your program rather than calling on obscure matrix transform functions more than necessary.

Simply put, you want to advance your cube's position some delta-position in the x,y, and z every time you move it 'forward' as defined by your current orientation. I assume turning it is no big deal, as you can just intuitively alter the value of glRotatef() directly, however this angle shall be invaluable for calculating your LINEAR translation as well.

All you have to do is a bit of highschool trig so long as you know the initial 'front' of your cube and of course the current angle for glRotatef(). I will for simplicity assume that th front is facing the +y axis, and that your rotation is of the form glRotatef(angle,0,0,1), as in it will rotate counter-clockwise as you increment 'angle'. I will also assume that you desire to move the cube forward some distance, D. You merely need a

dx = -D*sin(angle*PI/180);

and

dy = D*cos(angle*PI/180);

ASIDE: note the need to convert the angle value as trig functions in openGL read RADIANS but our mind thinks in DEGREES generally, which I assume is true for your program and the initial units of 'angle'. Feel free to make this ratio a cleaner constant for your program to use. I put in the actual value for clarity. Also note that dx is calculated as negative direction by virtue of large values for 'angle' turning the object COUNTER-clockwise (there is a chance I am wrong about this...).

So, with this dx and dy you need only two more lines:

x_translate += dx;

and

y_translate += dy;

Before you call upon your matrix translations where x_translate and y_translate are your x and y values for glTranslatef(), respectively. Note the beauty that at the end of the day these two values represent your TOTAL overall translation from your initial origin, which is exactly how glTranslate() operates.

Done.

Amusing anecdote:

Though I am painfully aware of my already exorbitantly long post, I cannot help sharing a (wrong) idea that occurred to me for this problem as I was typing:

To keep the whole program utilizing JUST matrix transforms and no math, one could reasonably write a recursive method of a form:

...

void cubeMove(int i){
pushMatrix();
rotate(angle[i]);
pushMatrix();
translate(distance[i]);
if(i==0) //draw once you've traversed the WHOLE path...
drawStuff();
else //many more matrices, yay!
cubeMove(i-1);
popMatrix();
popMatrix();
}//worst method ever.

where basically, your distance and angle arrays become one larger every time the user changes angle (and/or?) distance, or quite possibly each time the program loops period, and at this point the cubeMove() is called upon with an index one higher, to infinity...hmmm. However HORRIBLE of an idea this is, it is interesting to note that in this form we have chicken-and-egg situation where we can pretty safely exchange the hierarchical of translate and rotate as we have an infinite number of each to work with.

So ya, this is the sort of thing that comes to mind for me when i think of working with any number of matrices greater than two for a given object. Though I am sure such previously proposed scenarios cannot be worse than infinite matrices...:).