Sign in to follow this  
pulpfist

OpenGL global matrices

Recommended Posts

pulpfist    528
I have made a Vector3 object to support jyk's namespace
class Vector3
{
    union {
        float v[3];
        struct { float x, y, z; } p;
    };

public:

    Vector3() { p.x = p.y = p.z = 0; }
    Vector3(const Vector3 &rhs) { p.x = rhs.p.x; p.y = rhs.p.y; p.z = rhs.p.z;}
    Vector3(float new_x, float new_y, float new_z) { Set(new_x, new_y, new_z); }
    virtual ~Vector3() {}

    void Set(float new_x, float new_y, float new_z) { p.x = new_x; p.y = new_y; p.z = new_z; }
    const float& operator[] (int offset) const { return v[offset]; }
    Vector3 operator * (float rhs) { return Vector3(p.x * rhs, p.y * rhs, p.z * rhs); }
    friend Vector3 operator * (float lhs, const Vector3 &rhs);
    Vector3 operator + (const Vector3 &rhs) { return Vector3(p.x + rhs.p.x, p.y + rhs.p.y, p.z + rhs.p.z); }
    Vector3 operator - (const Vector3 &rhs) const { return Vector3(p.x - rhs.p.x, p.y - rhs.p.y, p.z - rhs.p.z); }
    void operator += (const Vector3 &rhs) { *this = *this + rhs; }
    Vector3& operator = (const Vector3 &rhs) {
        if(this == &rhs)
            return *this;
        p.x = rhs.p.x;
        p.y = rhs.p.y;
        p.z = rhs.p.z;
        return *this;
    }
    Vector3 Cross(const Vector3 &rhs) const {
        return Vector3(p.y * rhs.p.z - rhs.p.y * p.z, p.z * rhs.p.x - rhs.p.z * p.x, p.x * rhs.p.y - rhs.p.x * p.y);
    }
    float Dot(const Vector3 &rhs) const {
        return (p.x * rhs.p.x) + (p.y * rhs.p.y) + (p.z * rhs.p.z);
    }
    void NormalizeSelf() {
        double t = std::sqrt(p.x * p.x + p.y * p.y + p.z * p.z);
        p.x /= t;
        p.y /= t;
        p.z /= t;
    }
};

// implementation of the friend:
Vector3 operator * (float lhs, const Vector3 &rhs)
{
    return Vector3(lhs * rhs.p.x, lhs * rhs.p.y, lhs * rhs.p.z);
}




My initialization code looks like this
...
    oglManager glm;
    Vector3 cam_pos, cam_target, cam_up;
    MSG msg;
    BOOL bQuit = FALSE;

    glm.move_window(0, 0);
    glm.size_window(640, 640);
    glm.init(hInstance, hPrevInstance, lpCmdLine, iCmdShow, msg_pump, render_screen);

    cam_pos.Set(0.0f, 0.0f, 0.0f);
    cam_target.Set(0.0f, 0.0f, 1.0f);
    cam_up.Set(0.0f, 1.0f, 0.0f);

    glObj.SetMode(OpenGLObject::MODE_6DOF);
    //glObj.SetPos(cam_pos);
    glObj.LookAt(cam_pos, cam_target, cam_up);

    while(!bQuit) {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
    ...






glm is an instance of my openGL manager object, and glObj is an instance of jyk's OpenGLObject. the render function...
int render_screen()
{
    //static float theta = 0.0f;

    glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
    glClear (GL_COLOR_BUFFER_BIT);

    glPushMatrix();
    //glRotatef(theta, 0.0f, 0.0f, 1.0f);
    glBegin(GL_TRIANGLES);
    glColor3f(1.0f, 0.0f, 0.0f);   glVertex2f (0.0f, 1.0f);
    glColor3f(0.0f, 1.0f, 0.0f);   glVertex2f (0.87f, -0.5f);
    glColor3f(0.0f, 0.0f, 1.0f);   glVertex2f (-0.87f, -0.5f);
    glEnd();
    glPopMatrix();

    //theta += 1.0f;

    glObj.ApplyRoll(3);
    glObj.SetOpenGLModelMatrix(); // Should this be called only once, in the init section?
    glObj.SetOpenGLViewMatrix();  // How about this?

    Sleep(1);

    return 0;
}






My guess is that my camera need to set GL's global view matrix for every render, but what is the model matrix good for? and do I need to set this for each render? jyk's namespace also introduce the
void MultOpenGLModelMatrix();
void MultOpenGLViewMatrix();
Where and why does these come in to play? PS. Some of the code (typically the Sleep(1)) is just some remains from the default Code::Blocks opengl project that I am extending. (I have no idea why someone would sleep for 1 ms. at the end of a render routine, could it be an attempt to yield the CPU?) EDIT: Any tips on how to make the Vector3 object more efficient would be appreciated aswell, like if any of the operators REALY need to be outlined? Thanks [Edited by - pulpfist on December 5, 2005 11:52:30 AM]

Share this post


Link to post
Share on other sites
jyk    2094
Hi,

I'll take a look at this later today or this evening - I can probably give you some tips on how to use the OpenGLObject class.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Just so you know, the plural of matrix is matrices, not matrixes ;-)

Share this post


Link to post
Share on other sites
pulpfist    528
Quote:

ust so you know, the plural of matrix is matrices, not matrixes ;-)

Could have fooled me ^^

Quote:

I can probably give you some tips on how to use the OpenGLObject class.


Thats great

As you can see Im a bit cunfused when it comes to movement :D
Any tips or good links to info would be great.

So far, the rotation works smooth, side/up slide works, but not smooth (moving way too far between each render), and forward/backward movement has a similar side effect aswell

Share this post


Link to post
Share on other sites
haegarr    7372
You've asked for some optimization for Vector3 class, and actually I have some suggestions:

Using initialization lists should be preferred to assigning the member fields later. So it is better to use

class Vector3
{
Vector3() : p.x(0), p.y(0), p.z(0) {}
Vector3(const Vector3 &rhs) : p.x(rhs.p.x), p.y(rhs.p.y), p.z(rhs.p.z) {}
// ...


(Unfortunately I don't remember where I've read this.)

In the following assignment I personally would drop the internal if clause. I assume it will happen very seldom that "rhs" and "this" is the same, so that most time the if clause causes (little) costs only.

Vector3& operator = (const Vector3 &rhs) {
if(this == &rhs)
return *this;
p.x = rhs.p.x;
p.y = rhs.p.y;
p.z = rhs.p.z;
return *this;
}



Using divisions is still costly. It would be better to replace them by multiplications if applying it often.

void NormalizeSelf() {
double t = 1.0f/std::sqrt(p.x * p.x + p.y * p.y + p.z * p.z);
p.x *= t;
p.y *= t;
p.z *= t;
}


However, maybe you have an inverse sqrt function at hand? I personally use an approximation to sqrt that computes 1/sqrt in fact (it simply drops the last step of the standard sqrt approximation routine). So you are able to drop 2 divisions from the routine above.

The remaining seems me ok.

Share this post


Link to post
Share on other sites
haegarr    7372
Additionally, I could give some general hints to the question about matrices. I don't know jyk's class in detail, but the explanations should hold in general. Hopefully I don't tell too much in advance of jyk's announced explanation :-)


You may have to deal with 4 usages of matrices:

(1) The matrix for projection, i.e. mainly the mapping of the 3D world to the 2D display by handling the "depth". This matrix describes the well known perspective or orthogonal projection (other are possible but seldomly used). OpenGL provides this matrix by its matrix mode GL_PROJECTION.

(2) The view matrix defines which part of the scene is displayed: where in the world the camera is placed, where does it look at. It could be understood to transform the entire world so that the camera's location becomes the origin, and the camera's orientation the basis. It is in fact the inverse of the model matrix (see below) of the camera if the camera is implemented as an object. If you look into the GetModelMatrix and GetViewMatrix routines you'll see this interrelation.

In OpenGL there is no view matrix support on its own, but in combination with the model matrix in the so-called MODELVIEW matrix mode. This is often a cause of confusion. It requires (in conjunction with OpenGL's matrix definitions) to supply the view matrix to OpenGL _before_ supplying any model matrix.

(3) A model matrix defines how a model (e.g. a mesh) is located and oriented (and perhaps also scaled) w.r.t. a reference co-ordinate system. Often the reference system is "the world", but need not. E.g. you may use a scene graph to model spatial dependencies, so you may have 4 wheels, each one with an own model matrix w.r.t. the car, what itself has a model matrix w.r.t. the world. So this kind of matrix is also called "local transformation matrix" or similar. Due to some reasons, the particular vertices of a model are usually not given w.r.t. the world but the local matrix of the model. So the rendering needs to know the current model matrix.

(4) A texture matrix. That is surely out of scope here.

So in general you set-up OpenGL's projection matrix once at start-up, its view matrix once per rendering the scene, but typically will provide model matrices as many times as models are available for rendering.


Coming to the MultOpenGLXyzMatrix() routines: Looking back to the explanation of the model matrices above, you see that matrices may need to be concatenated to describe an overall transformation (e.g. you may need to multiply the model matrix of the left front wheel by those of the car to yield in the transformation of the wheel w.r.t. the world). But be aware that matrix multiplications are not commutative, so that the order of invoking the Mult routines plays a role.

[Edited by - haegarr on December 5, 2005 9:05:54 AM]

Share this post


Link to post
Share on other sites
pulpfist    528
Thanks haegarr

That was enlightening
Exactly what I was confused about

By the way, I think I had some compiler errors when I used an initializer list, but Ill try it again. It could be my compiler MingW beeing picky

Share this post


Link to post
Share on other sites
JohnBolton    1372
Quote:
Original post by Anonymous Poster
Just so you know, the plural of matrix is matrices, not matrixes ;-)

Oh? Check your sources.
Quote:
from Merriam-Webster
Inflected Form(s): plural ma-tri-ces /'mA-tr&-"sEz, 'ma-/; or ma-trix-es /'mA-trik-s&z/
...and while we are on the subject:
Quote:
from Merriam-Webster
Inflected Form(s): plural ver-ti-ces /'v&r-t&-"sEz/; also ver-tex-es
Inflected Form(s): plural in-dex-es or in-di-ces /-d&-"sEz/

Share this post


Link to post
Share on other sites
jyk    2094
Cool, didn't know that about the alternate plurals :)

@pulpfist: I think most of the basic concepts were covered in the previous posts, but in summary:

1. Unless you're incorporating zoom or doing something else unusual, the projection matrix is commonly set at the beginning of the simulation and left alone. The OpenGLObject class doesn't deal with the projection matrix in any way.

2. The view matrix is typically set once at the beginning of each frame. In my class this is done through SetOpenGLViewMatrix(). The function loads the matrix straight into OpenGL, so you don't have to call glLoadIdentity() first.

3. After that you can render anything you like by using MultOpenGLModelMatrix() to concatenate a model transform with the current matrix. If you want to render more than one object, or render a hierarchy of objects, it will probably be easiest to use glPushMatrix() and glPopMatrix().

4. Remember that in OpenGL, the view and model matrices are combined; the difference between a view matrix and a model matrix in OpenGL is really only conceptual.
Quote:
As you can see Im a bit cunfused when it comes to movement :D
Any tips or good links to info would be great.

So far, the rotation works smooth, side/up slide works, but not smooth (moving way too far between each render), and forward/backward movement has a similar side effect aswell.
Moving too far between frames sounds like a problem outside of the OpenGLObject code. If you can describe the behavior in more detail, and/or perhaps post the code where you're moving the object, I'll certainly try to spot the problem if I can.

Share this post


Link to post
Share on other sites
pulpfist    528
Thanks
I have a better grasp of how thing is supposed to work, but doing it in code is still a bit confusing.

Here is what I got so far

Init code:

void InitOpenGL (void)
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);

// Set up initial position/orientation using LookAt
cam_pos.Set(0.0f, 0.0f, 0.0f);
cam_target.Set(0.0f, 0.0f, 3.0f);
cam_up.Set(0.0f, 1.0f, 0.0f);

// glo = OpenGLObject instance
glo.SetMode(OpenGLObject::MODE_6DOF);
glo.LookAt(cam_pos, cam_target, cam_up);

// Feed camera position/orientation into the modelview matrix
glo.SetOpenGLViewMatrix();
glo.SetOpenGLModelMatrix();
}

and here is the render:

void RenderFrame (void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glo.SetOpenGLViewMatrix();

glo.ApplyPitch(rot_pitch);
glo.MoveAlongForwardAxis(move_frwd);

glo.SetOpenGLModelMatrix(); // this makes things happen
//glo.MultOpenGLModelMatrix(); // this makes everything disappear

// draw rectangle
glTranslatef(0.0f, 0.0f, -8.0f);
//glRotatef(angle, 0.0f, 0.0f, 1.0f);
DrawTriangle();
}

DrawRectangle just draws a simple rectangle.

When the rendering starts, I am positioned at 0,0,0 and have the rectangle right in front of me.
If I press the up-key, the rectangle start to rotate around me, which is good, but if I move myself(camera) away from 0,0,0, first, and then start to rotate, the rectange still rotate around 0,0,0, and not around me.
How do I make everything rotate around me and not around 0,0,0?

(Strictly speaking, I want to be the one rotating, but if I understand correct, its more common to have the world, and everything in it, rotate instead)

Im I supposed to reset(identity) all the matrices before every object I render?
The way its set up now, I never use any kind of Identity functions except at program init.

By the way, the projection matrix is set equal to the identity matrix at startup, is this correct?

Share this post


Link to post
Share on other sites
haegarr    7372
No, it is not common to rotate the world around you. If you rotate the camera, than it is the same effect w.r.t. to projected view as you inverse rotate the world; if you translate the camera, then it is the same effect as inverse translating the world. That is done automatically due to setting the corresponding view matrix.

If you want the quad rotate around the camera, regardles of where the camera is located, then you have to apply the same translations of the camera to the quad (respectively its mesh). Those translation has to be _applied_ after the rotation. Say, you have to rotate the quad first into the correct orientation, and then translate it. (You have to take into account that OpenGL applies transformations in the opposite order you supply them.)

However, if you do so, and you have no background to which could orientate yourself, you will not see a difference, of course, since the net translation between the camera and the quad will become zero (the rotations are the same).

EDIT:

Quote:
Original post by pulpfist
Im I supposed to reset(identity) all the matrices before every object I render?

If you set the object transformations to identity then you loose their current position and orientation (as long as you don't have this saved elsewhere). It is a question whether you want to _set an absolute_ transformation, or else to _concatenate a relative_ transformation.

E.g. OpenGLObject::MoveAlongSideAxis does an _addition_ of the argument to the current value, so it does in fact _concatenate a relative_ transformation. If, on the other hand, you've set the original value to 0 (notice that 0 is the identity value for addition), you get an absolute positioning instead (since for addition it is the same as adding the offset to zero).


The problem with all this stuff is that if one understands the _principles_ one has many related problems solved. But understanding the principles is not that easy. Also my english is sometimes not sufficient to express my thoughts, sorry.

[Edited by - haegarr on December 6, 2005 8:53:48 AM]

Share this post


Link to post
Share on other sites
pulpfist    528
I see, but what I am trying to do is simply to be able to move around much like a new born baby. The triangle is there only to describe my progress, so I want it to stay still...

Walking south-east cant be that much harder than walking south!

There is something fundamental here I dont understand, so I wont make more fuzz about this for now.

Thanks

EDIT:
I just read your edit, and I think you are right.
I think I have to learn something harder than this to be able to see how easy it is =)

Share this post


Link to post
Share on other sites
haegarr    7372
Quote:
Original post by pulpfist
Walking south-east cant be that much harder than walking south!

Right you are. If your class only provides to walk south or east (and not both simultaneously), you could simply do a south step followed by an east step. Directly following translations are concatenated as if they were done in a single step.

Share this post


Link to post
Share on other sites
haegarr    7372
Problems may occur due to the order in which particular transformations are applied, and whether they replace a part of the net transformation or else are multiplied into. There are plenty of possibilities.

I suggest you the following if you want to learn about that stuff: Get a piece of paper and a pencil, and investigate how transformation are working:
Look at the particular transformation matrices for:
(a) translation
(b) rotation (best when done for each principal axis separately)
Ignore scaling for now. Look at which parts do vary when the translation is done once in +x diection and the other time in -z direction.

Then look what happens if you multiply a rotation matrix by a translation matrix, and how that is different from multiplying a translation matrix by a rotation matrix (what parts are effected).

If you do so, you'll get some "aha" experiences, and suddenly you will notice the principle. But please don't understand this not to ask any more.

Share this post


Link to post
Share on other sites
haegarr    7372
To give an example: OpenGL uses column vectors and RHS, and so I will for clarity.

E.g. two translation matrices are defined by

[ 1 0 0 x1 ]
T1 := [ 0 1 0 y1 ]
[ 0 0 1 z1 ]
[ 0 0 0 1 ]

[ 1 0 0 x2 ]
T2 := [ 0 1 0 y2 ]
[ 0 0 1 z2 ]
[ 0 0 0 1 ]



If you compute the products

[ 1 0 0 x1 ] [ 1 0 0 x2 ] [ 1 0 0 x1+x2 ]
T1 * T2 = [ 0 1 0 y1 ] * [ 0 1 0 y2 ] = [ 0 1 0 y1+y2 ]
[ 0 0 1 z1 ] [ 0 0 1 z2 ] [ 0 0 1 z1+z2 ]
[ 0 0 0 1 ] [ 0 0 0 1 ] [ 0 0 0 1 ]

[ 1 0 0 x2 ] [ 1 0 0 x1 ] [ 1 0 0 x1+x2 ]
T2 * T1 = [ 0 1 0 y2 ] * [ 0 1 0 y1 ] = [ 0 1 0 y1+y2 ]
[ 0 0 1 z2 ] [ 0 0 1 z1 ] [ 0 0 1 z1+z2 ]
[ 0 0 0 1 ] [ 0 0 0 1 ] [ 0 0 0 1 ]



Aha 1: 2 translation applied in sequence have the same effect as one translation with the sum of the both particular translation offsets. You see that because the resulting matrix has absolute the same structure as a single translation matrix, but the values are the sums of the both arguments.

Aha 2: It makes no difference whether you do first T1 and then T2 or vice-versa, since the results are the same.

Doing such stuff with other matrices, say rotation around the same, later around different axes, and finally mixing translations and rotations, you'll get a feeling of how it works.

[Edited by - haegarr on December 6, 2005 11:01:38 AM]

Share this post


Link to post
Share on other sites
jyk    2094
Yeah, I think the problem here is that although the OpenGLObject class takes care of a lot of the work for you, it still requires a basic knowledge of how transformations work to use correctly. And don't take that the wrong away - these concepts may be basic, but they're actually not that easy! Understanding how transformations combine and what order to apply them in can be difficult to get one's head around. Anyway, I'll just provide a few more observations to keep moving you in the right direction.

1. First, note that the 'set' functions, SetOpenGLViewMatrix() and SetOpenGLModelMatrix(), overwrite whatever matrix is already loaded by loading a new one from scratch. For example these two lines in the init function:
glo.SetOpenGLViewMatrix();
glo.SetOpenGLModelMatrix();
Don't really do anything. Similarly, with these lines:
glo.SetOpenGLViewMatrix();
glo.ApplyPitch(rot_pitch);
glo.MoveAlongForwardAxis(move_frwd);
glo.SetOpenGLModelMatrix();
The first line is probably not having any effect, since you're calling a 'set' function soon after (before doing any rendering). So it's only the last line that is having any effect.

2. Actually, now that I look at it more carefully, you might be able to get close to the desired behavior by doing nothing more than commenting out the line:
glo.SetOpenGLModelMatrix();    // this makes things happen
So I would try that and see what happens.

Share this post


Link to post
Share on other sites
pulpfist    528
haegarr:

That does clear some things up

Looking back, I realy have many problems.
The fact that I dont know what is going on "under the hood" of opengl is one.
I will try to catch up on this myself.
All the tutorials I find online ends with "Next chapter were gonna move our object around in our newly created 3d world..." but the next chapter is never there.

I do believe I could come up with some way to move, but I wouldnt know it as long as there is something wrong with the rendering order, or the projection for that matter.

I understand very well what you have said so far.
I should probably not ask more about these things until I have done some research.

jyk:

Quote:

So I would try that and see what happens.


Yes, hagarr said something that made me use the Mult* function, but then the rectangle never showed up.
Since I already realized that I had other problems, like "who rotates around who", I gave up trying that.
The only success I had was when I tried to use the LookAt function in the render. By updating the cam_target vector's components separatly, using key presses, the rotation looked promising. At that point I thought the MoveAlongForwardAxis would do the rest.

Anyway, I have a lot of information now thanks to you guys, so Ill go investigate some on my own.

Thanks again

[Edited by - pulpfist on December 6, 2005 11:36:49 AM]

Share this post


Link to post
Share on other sites
jyk    2094
I'd just like to add one more thing. Doing some research is probably the right idea, but if the following suggestion were to fix the problem, you'd at least have a working example as reference. So, I think you may have misunderstood what I intended to be commented out. Here's what I had in mind:
void RenderFrame (void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glo.SetOpenGLViewMatrix();

glo.ApplyPitch(rot_pitch);
glo.MoveAlongForwardAxis(move_frwd);

//glo.SetOpenGLModelMatrix(); // this makes things happen
//glo.MultOpenGLModelMatrix(); // this makes everything disappear

// draw rectangle
glTranslatef(0.0f, 0.0f, -8.0f);
//glRotatef(angle, 0.0f, 0.0f, 1.0f);
DrawTriangle();
}
If you're up for trying just one more thing, I'd give the above version a go.

Share this post


Link to post
Share on other sites
pulpfist    528
Sure ill try it... Im not giving up on this *?%!§* issue, after chasing it for years ^^

...

My goodness.

The rectangle turned out to be behind me after doing that, and now both pitch and movement works.

:D

I realy appreciate your help guys

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this