PDA

View Full Version : OpenGL 3.x Rotation Problem



Brainer
14-02-2010, 10:45 AM
Hello.:)

I've been struggling with this problem for about two weeks now and was unable to find a solution. This is my first attempt at OpenGL 3.x programming, so I may be doing something wrong.

I'm trying to make a simple spinning triangle demo with a camera the user can move and use the mouse to look around the world. Here's how the triangle is created:


procedure TMainForm.InitializeOpenGL;
var
Vert: array[0..8] of Single;
begin
{ ... }
Vert[0] := -0.3;
Vert[1] := 0.5;
Vert[2] := -1.0;
Vert[3] := -0.8;
Vert[4] := -0.5;
Vert[5] := -1.0;
Vert[6] := 0.2;
Vert[7] := -0.5;
Vert[8] := -1.0;

TriangleRot := 0.0;

glGenVertexArrays(1, @TriangleVAO);
glBindVertexArray(TriangleVAO);
glGenBuffers(1, @TriangleVBO);
glBindBuffer(GL_ARRAY_BUFFER, TriangleVBO);
glBufferData(GL_ARRAY_BUFFER, 9 * SizeOf(Single), @Vert, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, False, 0, nil);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
end;


And now here's the Render procedure which renders my scene:


var
ProjMat, Cam, ModelView: TMatrix;
begin
glViewport(0, 0, ClientWidth, ClientHeight);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

// Projection update
if (ClientHeight <= 0) then
ClientHeight := 1;
ProjMat := MatrixProjection(40.0, ClientWidth / ClientHeight, 0.1, 1000.0);
glUniformMatrix4fv(glGetUniformLocation(ShaderMana ger.GLSLPrograms['minimal'].
ProgramHandle, 'projectionMatrix'), 1, ByteBool(GL_FALSE), @ProjMat);

// Camera transformations
Cam := MatrixTransform(VectorNegate(Camera.Pos), VectorUniform(0.0),
VectorUniform(1.0));
ModelView := MatrixTransform(VectorUniform(0.0), VectorCreate(0.0, TriangleRot, 0.0), VectorUniform(1.0));
ModelView := MatrixMultiply(ModelView, Cam);
glUniformMatrix4fv(glGetUniformLocation(ShaderMana ger.GLSLPrograms['minimal'].
ProgramHandle, 'modelViewMatrix'), 1, ByteBool(GL_FALSE), @ModelView);

glBindVertexArray(TriangleVAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
end;


Here's what gets rendered: http://www.youtube.com/watch?v=o9LDivwjUx0

But instead, I'd like the triangle to spin around itself. JSoftware provided me with a vector that indicates the center of rotation of the triangle. So, using it here:


ModelView := MatrixTransform(VectorCreate(0.3, 5.0 / 3.0, 1.0),
VectorCreate(0.0, TriangleRot, 0.0), VectorUniform(1.0));
ModelView := MatrixMultiply(ModelView, Cam);

I get: http://www.youtube.com/watch?v=aQleDlo5li8

Everything is OK, but the object is not placed at (0,0,0), which is the point I want it to be put at. :(

How do I change the code so that it spins around itself and stays at (0,0,0)? ???

If you blame my matrix code, here's the whole unit: http://www.nopaste.pl/Source/m6s.txt

chronozphere
14-02-2010, 01:29 PM
By looking at the coordinates of your triangle, the center is approx. at (-0.3, 0.0, -1.0). Therefore the first video makes sense to me.

What I would do, is define the triangle with it's center at (0,0,0). You could do the following:

//top vertex
Vert[0] := 0.0;
Vert[1] := 0.5;
Vert[2] := 0.0;

//right vertex (I think LOL )
Vert[3] := -0.3;
Vert[4] := -0.5;
Vert[5] := 0.0;

//left vertex
Vert[6] := 0.3;
Vert[7] := -0.5;
Vert[8] := 0.0;


It's common to define objects with their center at (0,0,0). If you want them to appear somewhere else, you just change the model matrix. :)

Brainer
14-02-2010, 02:18 PM
Using your code and this:


Cam := MatrixTransform(VectorNegate(Camera.Pos), VectorUniform(0.0),
VectorUniform(1.0));
ModelView := MatrixTransform(VectorCreate(0.0, 0.0, 1.0), VectorCreate(0.0, TriangleRot, 0.0), VectorUniform(1.0));
ModelView := MatrixMultiply(ModelView, Cam);
glUniformMatrix4fv(glGetUniformLocation(ShaderMana ger.GLSLPrograms['minimal'].
ProgramHandle, 'modelViewMatrix'), 1, ByteBool(GL_FALSE), @ModelView);

I still have the triangle not spinning around itself. :(

What could be the matter? I even tried to use the vertices as given here (http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=02), but still when I want to place the object at (0,0,0), it rotates wrong.

JSoftware
14-02-2010, 02:34 PM
Use his coordinates and this:


Cam := MatrixTransform(VectorNegate(Camera.Pos), VectorUniform(0.0),
VectorUniform(1.0));
ModelView := MatrixTransform(VectorCreate(0.0, 0.0, 0.0), VectorCreate(0.0, TriangleRot, 0.0), VectorUniform(1.0));
ModelView := MatrixMultiply(ModelView, Cam);
glUniformMatrix4fv(glGetUniformLocation(ShaderMana ger.GLSLPrograms['minimal'].
ProgramHandle, 'modelViewMatrix'), 1, ByteBool(GL_FALSE), @ModelView);

Brainer
14-02-2010, 02:50 PM
I tried those coords:


Vert[0] := 0.0;
Vert[1] := 1.0;
Vert[2] := 0.0;
Vert[3] := -1.0;
Vert[4] := -1.0;
Vert[5] := 0.0;
Vert[6] := 1.0;
Vert[7] := -1.0;
Vert[8] := 0.0;

Matrix code:


Cam := MatrixTransform(VectorNegate(Camera.Pos), VectorUniform(0.0),
VectorUniform(1.0));
ModelView := MatrixTransform(VectorCreate(0.0, 0.0, 0.0),
VectorCreate(0.0, TriangleRot, 0.0), VectorUniform(0.5));
ModelView := MatrixMultiply(ModelView, Cam);
glUniformMatrix4fv(glGetUniformLocation(ShaderMana ger.GLSLPrograms['minimal'].
ProgramHandle, 'modelViewMatrix'), 1, ByteBool(GL_FALSE), @ModelView);

And changed the MatrixTransform function back to its original form:


{ .: MatrixTransform :. }
function MatrixTransform(const Position, Rotation, Scale: TVector): TMatrix;
var
CosRx, CosRy, CosRz: Single;
SinRx, SinRy, SinRz: Single;
begin
CosRx := Cos(Rotation.X); // Used 6x
CosRy := Cos(Rotation.Y); // Used 4x
CosRz := Cos(Rotation.Z); // Used 4x
SinRx := Sin(Rotation.X); // Used 5x
SinRy := Sin(Rotation.Y); // Used 5x
SinRz := Sin(Rotation.Z); // Used 5x
Result := MatrixIdentity;

with Result do
begin
_11 := CosRy * CosRz * Scale.X;
_12 := CosRy * SinRz * Scale.X;
_13 := -SinRy * Scale.X;

_21 := (CosRz * SinRx * SinRy * Scale.Y) - (CosRx * SinRz * Scale.Y);
_22 := (CosRx * CosRz * Scale.Y) + (SinRx * SinRy * SinRz * Scale.Y);
_23 := CosRy * SinRx * Scale.Y;

_31 := (CosRx * CosRz * SinRy * Scale.Z) + (SinRx * SinRz * Scale.Z);
_32 := (-CosRz * SinRx * Scale.Z) + (CosRx * SinRy * SinRz * Scale.Z);
_33 := CosRx * CosRy * Scale.Z;

_41 := Position.X;
_42 := Position.Y;
_43 := Position.Z;
end;
end;


And it worked! :o It even worked when I changed the coordinates to (3,0,0), so this approach seems to be working well.

Can someone explain why it does work and what I should do to avoid such mistakes in the future? :-[

chronozphere
14-02-2010, 04:50 PM
You have to understand what MatrixTransform() does. It first translates, then rotates, then translates back and finally it scales. If you use (0,0,0) as the first vector, it will only rotate and scale. If you use (1,1,1) (which is VectorUniform(1.0) ) as the scaling matrix, you will only have rotation left. However, if you only need rotation, using the MatrixTransform() routine is overkill. :)

What you want to do, is rotate about the center of something. In that case you have to determine where the center is. Then you have to move your geometry in such a way that the center lies at (0,0,0) before rotating. The code in your startpost seems to look right.

I'd suggest you to play with this for a while. Understanding matrices is quite hard (for me too). Just start off with the code in your startpost (corresponding to the first youtube movie). Make sure to keep in mind what MatrixTransform() does exactly.

Good luck.

Brainer
14-02-2010, 04:55 PM
I think the problem was with the center of the triangle I drawn before. After correcting the vertices, the code works ok. Next time, just as you suggested, I must "center" my objects at (0,0,0) and then move them around using appropiate matrix translations. :)

Anyway, I think you guys helped me work it out. Thank you very much, I'd not guess the vertices were the case of my problems. :)

EDIT
Nevertheless, getting it all done is tricky. After I'm done with a basic mouse-looking example, I'll post the code on the board. :)

Brainer
15-02-2010, 05:05 AM
Hello again. :)

I noticed that the code does not entirely do what I want it to. The thing is that the code doesn't behave like it takes the camera into account. :(

I mean the part of the code where the camera transformation is calculated. I changed it a little to include the rotation of the camera. Here's the changed part:


// Camera transformations
Cam := MatrixTransform(VectorNegate(Camera.Pos), VectorCreate(Camera.Pitch,
Camera.Yaw, 0.0), VectorUniform(1.0));
ModelView := MatrixTransform(VectorCreate(0.0, 0.0, 0.0),
VectorCreate(0.0, TriangleRot, 0.0), VectorUniform(0.5));
ModelView := MatrixMultiply(ModelView, Cam);
ModelView := MatrixMultiply(ModelView, ProjMat);

// Update the shader
glUniformMatrix4fv(glGetUniformLocation(ShaderMana ger.GLSLPrograms['minimal'].
ProgramHandle, 'mvpmatrix'), 1, ByteBool(GL_FALSE), @ModelView);

The new code of my vertex shader:


#version 150

precision highp float;

uniform mat4 mvpmatrix;

in vec3 in_Position;
in vec3 in_Color;

out vec3 ex_Color;
void main(void) {
gl_Position = mvpmatrix * vec4(in_Position, 1.0);

ex_Color = in_Color;
}


Changing the values Pitch and Yaw doesn't really rotate the camera, but the object on the contrary. :o How do I change the code? Any suggestions?

BTW, before I changed the shader code, it didn't work as well.

JSoftware
15-02-2010, 06:33 AM
I believe you are doing it in the wrong order. Remember order of matrix operations are critical

Normally in OpenGL you have:
ProjectionMatrix*ModelViewMatrix*matrix1*matrix2.. .



// Camera transformations
Cam := MatrixTransform(VectorNegate(Camera.Pos), VectorCreate(Camera.Pitch, Camera.Yaw, 0.0), VectorUniform(1.0)); //"Camera" transformation
ModelView2 := MatrixTransform(VectorCreate(0.0, 0.0, 0.0), VectorCreate(0.0, TriangleRot, 0.0), VectorUniform(0.5)); //Object transformation
ModelView := MatrixMultiply(ProjMat, MatrixMultiply(ModelView2, Modelview));

// Update the shader
glUniformMatrix4fv(glGetUniformLocation(ShaderMana ger.GLSLPrograms['minimal'].
ProgramHandle, 'mvpmatrix'), 1, ByteBool(GL_FALSE), @ModelView);

Brainer
15-02-2010, 07:59 PM
Shouldn't this:


ModelView := MatrixMultiply(ProjMat, MatrixMultiply(ModelView2, Modelview));

be


ModelView := MatrixMultiply(ProjMat, MatrixMultiply(ModelView2, Cam));

instead? ???

Brainer
16-02-2010, 06:50 PM
Sorry, but the code proposed by you doesn't not seem to work. It doesn't even display the triangle. :(

User137
16-02-2010, 08:25 PM
How about:

ModelView := MatrixMultiply(MatrixMultiply(ProjMat, ModelView2), Cam);

Brainer
16-02-2010, 08:28 PM
Nope, it's still the same. In both cases nothing's even displayed.

Brainer
18-02-2010, 06:49 AM
I noticed that there is a problem with the math I'm using to create the matrices. I noticed that when I tried to transpose the matrix using glUniform and my custom-build function. They produced different results. :o

Does anyone have any decent Pascal math library with matrix functions (I'd also like to have functions for calculating transformation matrices included)?

JSoftware
18-02-2010, 09:38 AM
I just took a look at your transformation matrix code. It seems some of the signs are wrong when you calculate the rotation matrix. I'll take a look later when I'm drunk enough to understand this matrix stuff

JSoftware
18-02-2010, 11:38 AM
This is the matrix that I calculated. It might be that it's for a righthand coordinate system though. That might be why


[ cos(ty)*cos(tz), -cos(ty)*sin(tz), sin(ty),0]
[ cos(tx)*sin(tz) + cos(tz)*sin(tx)*sin(ty), cos(tx)*cos(tz) - sin(tx)*sin(ty)*sin(tz), -cos(ty)*sin(tx),0]
[ sin(tx)*sin(tz) - cos(tx)*cos(tz)*sin(ty), cos(tz)*sin(tx) + cos(tx)*sin(ty)*sin(tz), cos(tx)*cos(ty),0]
[0,0,0,1]

Brainer
18-02-2010, 12:59 PM
Thanks. :) I did the corrections.

But it's still the same. :( I even tried changing a little the rendering code:


{ .: MatrixLookAt :. }
function MatrixLookAt(const From, At, Up: TVector): TMatrix;
var
xAxis, yAxis, zAxis: TVector;
begin
zAxis := VectorSubtract(From, At);
zAxis := VectorNormalize(zAxis);
xAxis := VectorCrossProduct(Up, zAxis);
xAxis := VectorNormalize(xAxis);
yAxis := VectorCrossProduct(zAxis, xAxis);

with Result do
begin
_11 := xAxis.X;
_12 := yAxis.X;
_13 := zAxis.X;
_14 := 0.0;

_21 := xAxis.Y;
_22 := yAxis.Y;
_23 := zAxis.Y;
_24 := 0.0;

_31 := xAxis.Z;
_32 := yAxis.Z;
_33 := zAxis.Z;
_34 := 0.0;

_41 := VectorDotProduct(VectorScale(xAxis, -1.0), At);
_42 := VectorDotProduct(VectorScale(yAxis, -1.0), At);
_43 := VectorDotProduct(VectorScale(zAxis, -1.0), At);
_44 := 1.0;
end;
end;

{ .: TMainForm.Render :. }
procedure TMainForm.Render;
var
ProjMat, Cam, ModelView, Model: TMatrix;
begin
glViewport(0, 0, ClientWidth, ClientHeight);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

// Projection update
if (ClientHeight <= 0) then
ClientHeight := 1;
ProjMat := MatrixProjection(40.0, ClientWidth / ClientHeight, 0.1, 1000.0);

// Camera transformations
Cam := MatrixLookAt(VectorNegate(Camera.Pos), VectorCreate(0.0, 0.0, -1.0),
VectorCreate(0.0, 1.0, 0.0));
Model := MatrixTransform(VectorCreate(0.0, 0.0, 0.0),
VectorCreate(0.0, TriangleRot, 0.0), VectorUniform(0.5));
ModelView := MatrixIdentity;
ModelView := MatrixMultiply(MatrixMultiply(Model, Cam), ProjMat);

// Update the shader
glUniformMatrix4fv(glGetUniformLocation(ShaderMana ger.GLSLPrograms['minimal'].
ProgramHandle, 'mvpmatrix'), 1, ByteBool(GL_FALSE), @ModelView);

// Render the triangle
glBindVertexArray(TriangleVAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
end;

The new code displays the triangle, but the translation doesn't work correctly. When I translate the triangle with an arrow key, it doesn't change its position. Once I strafe and then try to move it forward, it suddenly accelerates. Clearly it's a problem with the matrix I pass to the shader, but where is the bug hidden? The function was taken from LEAF2 engine - I hoped it was a good one.

User137
18-02-2010, 02:13 PM
The LookAt function is definitely off. Was doing my Matrix lib today based on GLScene and tested this on the way. Lib itself works perfectly when i rotate object with mouse with my functions. It's when i set camera with this lookAt it doesn't go to right place.

// This doesn't work
cam:=MatrixLookAt(Vector(0,0,-3),Vector(0,0,0),Vector(0,1,0));

// This works
cam:=NewMatrix;
TranslateMatrix(cam,Vector(0,0,-3));

Edit: I also used your matrix multiplication function so it means they are read same way :) GLScene made 1 line for each 16 array elements, 2 x for loop is more compact.

Brainer
18-02-2010, 07:56 PM
Hm, okay. :) Thanks for your feedback.

So you suggest using normal transformation functions instead of LookAt, right? But this approach doesn't work, either. The first snippet I posted contained the MatrixTransform function. It was wrong at first, but JSoftware provided us with a new matrix and I modified the code. Nevertheless, the effect is still far from what's desired.

Maybe it's something wrong with the shader? Maybe I'm looking for a bug in a wrong place?

paul_nicholls
18-02-2010, 11:52 PM
Hi Brainer,
A while ago I was fiddling around with creating software OpenGL routines (for my gp2x handheld), and I DID get something basic working.

I could emulate exactly how OpenGL did its rendering with regards to transformations.

I have a unit you could look at which includes all my OpenGL transformations, etc. but it is 1307 lines long...

Is it ok if I post it here?

Perhaps it may help you with your matrix issues...

cheers,
Paul

Brainer
19-02-2010, 12:10 AM
Sure, but you may consider using http://pastebin.com/ :)

paul_nicholls
19-02-2010, 12:29 AM
Done!

http://pastebin.com/ff5a251c

enjoy, and I hope it helps :)

BTW, it isn't a COMPLETE implementation, but it does points, lines, triangles (wireframe) only...

cheers,
Paul

Brainer
19-02-2010, 06:53 AM
Whoa, looks great. :) Thanks a bunch, Paul! :D

I'll see if I can make something out of this.

chronozphere
19-02-2010, 03:45 PM
@Paul: Your openGL emulation code looks very interesting. Great job! :)

Brainer
21-02-2010, 09:19 PM
Finally, after weeks of struggling, the code works! ;D

And here is a step-by-step guide:
1.First of all, I disposed of all my matrix code and rewrote it. Here are the functions you will need to compile the code: http://www.nopaste.pl/Source/mhc.txt
2.Use the following shaders: http://www.nopaste.pl/Source/mhd.txt
3.Your rendering code should look like this:


procedure TMainForm.RenderWorld;
var
Model, View, Proj, MVP: TBrainMatrix;
begin
// Clear the frame buffer
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

// Compute the model-view-projection matrix
Proj := Mat4CreatePerspective(60.0, ClientWidth / ClientHeight, 0.1, 1000.0);
Model := Mat4Translate(Mat4CreateRotationEuler(ModelRotatio n), Vec3Negate(ModelPosition));
View := Mat4Translate(Mat4CreateRotationEuler(CameraRotati on), Vec3Negate(CameraPosition));
View := Mat4Inverse(View);

MVP := Mat4Multiply(Model, Mat4Multiply(View, Proj));

// Update the shader
glUniformMatrix4fv(ShaderManager.ActiveProgram.Get UniformLocation('mvpmatrix'),
1, False, @MVP);

// Rendering code here...
end;


I hope it saves someone almost two weeks of cursing. :D

chronozphere
21-02-2010, 09:45 PM
Great job. :D

I wonder though why you need to invert the view matrix.

I definitly need to dust off my knowledge about linear algebra. I still have the "3D Math primer for graphics and gamedevelopment" book, which needs to be studied lol. :)

paul_nicholls
22-02-2010, 12:57 AM
Finally, after weeks of struggling, the code works! ;D

And here is a step-by-step guide:
1.First of all, I disposed of all my matrix code and rewrote it. Here are the functions you will need to compile the code: http://www.nopaste.pl/Source/mhc.txt
2.Use the following shaders: http://www.nopaste.pl/Source/mhd.txt
3.Your rendering code should look like this:


procedure TMainForm.RenderWorld;
var
Model, View, Proj, MVP: TBrainMatrix;
begin
// Clear the frame buffer
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

// Compute the model-view-projection matrix
Proj := Mat4CreatePerspective(60.0, ClientWidth / ClientHeight, 0.1, 1000.0);
Model := Mat4Translate(Mat4CreateRotationEuler(ModelRotatio n), Vec3Negate(ModelPosition));
View := Mat4Translate(Mat4CreateRotationEuler(CameraRotati on), Vec3Negate(CameraPosition));
View := Mat4Inverse(View);

MVP := Mat4Multiply(Model, Mat4Multiply(View, Proj));

// Update the shader
glUniformMatrix4fv(ShaderManager.ActiveProgram.Get UniformLocation('mvpmatrix'),
1, False, @MVP);

// Rendering code here...
end;


I hope it saves someone almost two weeks of cursing. :D



Was my code helpful to you getting this working?

I hope so LOL

cheers,
Paul

Brainer
22-02-2010, 05:57 AM
Was my code helpful to you getting this working?

Nope, Paul, but I'm glad you tried to help. :) I really appreciate that.

@chronozphere
Take a look at the quote:


Creating the view matrix
The view matrix transforms the world coordinates system into the coordinate system of the camera. In other words, after applying the view matrix transform the coordinates will be relative to the camera and not the origin of the world. If we think of the camera as one of the objects in the world we can see that the view matrix is actually an inverse transform of the camera's world matrix. By inverting the world matrix used to place the camera in the world we get the view matrix.

Full article is here: http://www.programmersheaven.com/2/world-view

User137
22-02-2010, 12:44 PM
I used http://www.gamedev.net/community/forums/topic.asp?topic_id=468539 to create LookAt function but i'm still not getting results that i need:
function LookAt(const eye, target, up: TVector): TMatrix;
var x,y,z,p: TVector;
begin
z:=invert(norm(VectorSub(target,eye)));
x:=norm(crossproduct(norm(up),z));
y:=crossproduct(z,x);
p:=vector(-dot(eye,x),-dot(eye,y),-dot(eye,z));
result:=CreateMatrix(x,y,z,p);
end;
It is short and "simple" but yet it's not working right. I have tried to visualize the matrix vectors in 2D plane but it's hella confusing... And i've tested and tested the math behind tool functions but they should all work correctly. They are used in numerous other 3D functions which behave right, including MatrixToSurface() that is another kind of LookAt formed of pos and normal on a plane, RayTriangleIntersect(), RaySphereIntersect().

This is how it's i've debugged the matrix: http://i49.tinypic.com/fbc9oj.png
Red dot is Eye vector and white is its Invert. Yellow dot is Target vector. Green represent LookAt matrix returned and line drawn is Z-rotation vector of it.

Basically.. i was able with the same functions to point the ship with my mouse and draw another smaller ship on surface of it where cursor ray first crossed, perfectly aligned with the triangle on the point, yet LookAt doesn't work.

Edit2: Getting a little closer, but its upside down and is not directly following target or eye pos :( http://i45.tinypic.com/2nly2id.png
I drew the matrix-Z vector from eye pos aswell. It's green line that should hit yellow sphere for it to be correct.

User137
22-02-2010, 04:47 PM
Doh, error was in my own CreateMatrix().. it was built wrong sided.

It was your code Brainer that worked first though 8)

I replaced
_41 := Dot(Scale(xAxis, -1.0), From);
_42 := Dot(Scale(yAxis, -1.0), From);
_43 := Dot(Scale(zAxis, -1.0), From);
with
_41 := Dot(Scale(xAxis, -1.0), At);
_42 := Dot(Scale(yAxis, -1.0), At);
_43 := Dot(Scale(zAxis, -1.0), At);
And it worked straight away.

My function optimized is now:
function LookAt(const eye, target, up: TVector): TMatrix;
var x,y,z,p: TVector;
begin
z:=norm(VectorSub(eye,target));
x:=norm(crossproduct(up,z));
y:=crossproduct(z,x);
p:=vector(-dot(eye,x),-dot(eye,y),-dot(eye,z));
result:=CreateMatrix(x,y,z,p);
end;

Brainer
23-02-2010, 06:03 AM
I have been trying to include the direction vector in my model matrix, but no success. What can be wrong with this code?


function TBrainObject.GetMatrix: TBrainMatrix;
var
M: TBrainMatrix;
V: TBrainVector;
begin
if UpdateNeeded then
begin
M := Mat4CreateRotationEuler(FRotation);
V := Mat4ApplyToVector(M, Vec3(0.0, 0.0, -1.0));
V := Vec3Normalize(V);

CachedMatrix := Mat4Scale(Mat4Translate(M,
Vec3Negate(Vec3Add(FPosition, V))), FScale);

UpdateNeeded := False;
end;
Result := CachedMatrix;
end;


What happens is that the camera doesn't include the new direction after rotation and instead moves on the same axes. Here's a video for better explanation: http://www.youtube.com/watch?v=lOL8sqR5fQ0

Do you happen to know what is wrong? ???

EDIT
Hm, I also thought of using a LookAt function. What do you think?
@User137: Can you post the code of the CreateMatrix function here?

chronozphere
23-02-2010, 06:49 AM
Can you elaborate? What does the Direction vector have to do "exactly"?

I think a lookAt function is a must have. Almost everyone uses one. :)
There are three things you need to know about your camera:
- Position
- LookAt
- UpVector (Denotes the "up" direction)
Simply pass these three to a lookAt function and you should be all good. ;)

http://pyopengl.sourceforge.net/documentation/manual/gluLookAt.3G.html

Brainer
23-02-2010, 07:03 AM
The vector specifies what direction the object should move at. By default, the vector is (0,0,-1), but after rotation it should change along with the rotation angles.

User137
23-02-2010, 10:38 AM
You can see the whole 3D unit here, didn't paste the 2D math but you can figure if needed functions out i suppose, or ask:
http://pastebin.com/gtLhUvj5

// Create a rotation matrix
function CreateMatrix(const x, y, z: TVector): TMatrix;
begin
result[0,0]:=x.x; result[0,1]:=x.y; result[0,2]:=x.z; result[0,3]:=0;
result[1,0]:=y.x; result[1,1]:=y.y; result[1,2]:=y.z; result[1,3]:=0;
result[2,0]:=z.x; result[2,1]:=z.y; result[2,2]:=z.z; result[2,3]:=0;
result[3,0]:=0; result[3,1]:=0; result[3,2]:=0; result[3,3]:=1;
end;

// Rotation matrix written with rows and columns switched
function CreateMatrix2(const x, y, z: TVector): TMatrix;
begin
result[0,0]:=x.x; result[1,0]:=x.y; result[2,0]:=x.z; result[3,0]:=0;
result[0,1]:=y.x; result[1,1]:=y.y; result[2,1]:=y.z; result[3,1]:=0;
result[0,2]:=z.x; result[1,2]:=z.y; result[2,2]:=z.z; result[3,2]:=0;
result[0,3]:=0; result[1,3]:=0; result[2,3]:=0; result[3,3]:=1;
end;

function CreateTranslateMatrix(const p: TVector): TMatrix;
begin
result:=NewMatrix;
result[3,0]:=p.x; result[3,1]:=p.y; result[3,2]:=p.z;
end;

Edit: I changed the code a bit to make everything click. LookAt function has to use CreateMatrix2 that uses opposite row direction while usual operations such as MatrixOnPlane() use CreateMatrix.

Brainer
24-02-2010, 07:37 AM
I played with your LookAt yesterday and it seems to work, but there's still a little bug I'm going to track down. Something is calculated wrong, because the object slightly rotates with the camera and when the camera angle is high enough, the triangle disappears.

Clearly, it's a problem with matrices. I suppose the function which returns the direction vector from a matrix is faulty. Here is the code:


{ .: Mat4ApplyToVector :. }
function Mat4ApplyToVector(const M: TBrainMatrix; const V: TBrainVector): TBrainVector; inline;
begin
Result.X := V.X * M.M[0, 0] + V.Y * M.M[1, 0] + V.Z * M.M[2, 0] + M.M[3, 0];
Result.Y := V.X * M.M[0, 1] + V.Y * M.M[1, 1] + V.Z * M.M[2, 1] + M.M[3, 1];
Result.Z := V.X * M.M[0, 2] + V.Y * M.M[1, 2] + V.Z * M.M[2, 2] + M.M[3, 2];
end;

{ .: Mat4GetDirectionVector :. }
function Mat4GetDirectionVector(const M: TBrainMatrix): TBrainVector; inline;
var
M1: TBrainMatrix;
E: TBrainEuler;
begin
Result := Vec3(0.0, 0.0, -1.0);

E := Mat4GetRotationEuler(M);
M1 := Mat4CreateRotationEuler(E);
Result := Mat4ApplyToVector(M1, Result);
Result := Vec3Normalize(Result);
end;


Or maybe the problem is in the way I use them? I'm not sure. Anyway, if someone can tell me whether these two functions are correct, I'll be glad. :)

I think I'm very close to solving the problem. Of course once I'm done with that, I'll post a complete source code here. :)

User137
24-02-2010, 12:11 PM
I just doublechecked the camera by looking both directions along all X,Y,Z-axis and their mid-angles and all worked fine. First with up-vector(0,1,0) and then vector(-0.2,0.93,0.2) (normalized).

Brainer
24-02-2010, 12:32 PM
Which vector did you normalize?

User137
24-02-2010, 02:03 PM
Everything should be normalized unless you absolutely know that it already is. Scaling matrix is exception to that.

I mean, i sometimes test with even vectors like norm(vector(30,50,-70)). Just to vaguely point them somewhere.

Brainer
24-02-2010, 09:35 PM
Here's a demo of the framework: http://pateman.net76.net/BrainEngineDemo.rar
Can you tell me whether the camera works correctly in your opinion?
Edit
W,S,A,D - move around
Left key, right key - rotate the camera

User137
25-02-2010, 10:57 AM
Looks perfectly normal to me, but oh the colors hurt my eyes :D

Brainer
25-02-2010, 12:02 PM
haha, that was on purpose! ;)

Three positive feedback! Waitin for more. :)

Brainer
26-02-2010, 12:13 AM
Hello again! :)

This time there seems to be a problem with scaling. I created a 3D cube and when the model matrix is scaled, it shrinks and grows back to its original dimensions! :o Here's how the model matrix is calculated:


if UpdateNeeded then
begin
CachedMatrix := Mat4Scale(Mat4Translate(Mat4CreateRotationEuler(FR otation),
Vec3Negate(FPosition)), FScale);

UpdateNeeded := False;
end;
Result := CachedMatrix;

I suppose the scaling should be placed somewhere else, but where?

Oh, and here's Mat4Scale's code:


function Mat4Scale(const M: TBrainMatrix; const V: TBrainVector): TBrainMatrix; inline;
begin
Result := M;

Result.M[0, 0] := Result.M[0, 0] + V.X;
Result.M[1, 1] := Result.M[1, 1] + V.Y;
Result.M[2, 2] := Result.M[2, 2] + V.Z;
end;


Any ideas, guys? :)

User137
26-02-2010, 03:05 AM
If you scale matrix by constant it goes like:
procedure Scale(var M: TMatrix; const s: Single);
var i: Integer;
begin
for i:=0 to 2 do begin
M[i, 0]:=M[i, 0] * s;
M[i, 1]:=M[i, 1] * s;
M[i, 2]:=M[i, 2] * s;
end;
end;

Extending that function to work with vector might be something like: (i'm just guessing here...)
procedure Scale(var M: TMatrix; const v: TVector);
var i: Integer;
begin
for i:=0 to 2 do begin
M[i, 0]:=M[i, 0] * v.x;
M[i, 1]:=M[i, 1] * v.y;
M[i, 2]:=M[i, 2] * v.z;
end;
end;

Brainer
26-02-2010, 06:56 AM
I tried it, but if the scale vector is (1,1,1), multiplication won't change the matrix.

chronozphere
26-02-2010, 07:19 AM
Exactly! Because the vector contains scaling "factors", a factor of one means "keep this the same" (a * 1 = a). :)

Because the scaling vector contains factors, your code will not work:


function Mat4Scale(const M: TBrainMatrix; const V: TBrainVector): TBrainMatrix; inline;
begin
Result := M;

Result.M[0, 0] := Result.M[0, 0] + V.X;
Result.M[1, 1] := Result.M[1, 1] + V.Y;
Result.M[2, 2] := Result.M[2, 2] + V.Z;
end;


Let's say you have an identity matrix:



1, 0, 0, 0
0, 1, 0, 0
0, 0, 1, 0
0, 0, 0, 1


And you want to make it twice as big (factor 2). Acoording to your code, this would result in:



3, 0, 0, 0
0, 3, 0, 0
0, 0, 3, 0
0, 0, 0, 1


The numbers in the diagonal part of the matrix are also used as scaling factors. As you see, it will result in 3x scaling instead of 2x.

So, first of all, treat the numbers as factors!

Secondly, concatenating two transforms is ALWAYS done by matrix multiplication. In this case we would have a scaling matrix, constructed by vector V. It would look like this:



V.x, 0, 0, 0
0, V.y, 0, 0
0, 0, V.z, 0
0, 0, 0, 1


Secondly, we have our original matrix M.
We need to multiply these to get the matrix M with the scaling applied on it.
This brings me to a more difficult issue:

Matrix multiplication is not commutative, which means that A x B will not yield the same result as B x A. We should be aware of the order in which we multiply matrices.

I still haven't figured this part out, so I better don't give any advice, other than that you have to be carefull. :)

This is what I'd do:


function Mat4Scale(const M: TBrainMatrix; const V: TBrainVector): TBrainMatrix; inline;
begin
Result := MatIdentity();
Result.M[0, 0] := V.X; //The identity matrix has only 1's diagonally, so multiplying
Result.M[1, 1] := V.Y; //V with 1 makes no sense.. we can assign it directly
Result.M[2, 2] := V.Z;

//multiply with M here, this could be
Result = MatMultiply( Result, M)
// OR !!
Result = MatMultiply( M, Result )
end;


Also, I suggest you study a few examples of matrices and see how they affect geometry:

http://msdn.microsoft.com/en-us/library/ee422511%28VS.85%29.aspx

(I know it's direct3D, but you can still use this knowledge).

Also, take a very close look at existing matrix code, instead of writing your own (I use math code written by others. ;) )

Hope this helps

Brainer
26-02-2010, 07:39 AM
Thanks for this, chronozphere, but I've already tried this approach and it didn't work either. :(

Maybe I should scale the vertices when creating an object, let's say a triangle?


// No scaling
Vertices.Add(Vec3(0.0, 1.0, 0.0));
Vertices.Add(Vec3(-1.0, -1.0, 0.0));
Vertices.Add(Vec3(1.0, -1.0, 0.0));

// Scaling on
Vertices.Add(Vec3(0.0 * FScale.X, 1.0 * FScale.Y, 0.0 * FScale.Z));
Vertices.Add(Vec3(-1.0 * FScale.X, -1.0 * FScale.Y, 0.0 * FScale.Z));
Vertices.Add(Vec3(1.0 * FScale.X, -1.0 * FScale.Y, 0.0 * FScale.Z));

But then everytime I change scaling, I'd have to re-build the object's VAO and VBOs... That is certainly not what I want.

User137
26-02-2010, 12:35 PM
It does work for me. (Oh, note that if your matrix has translation included in it, it will be used in scaling aswell. You need to multiply rotation matrix in different function without translation part)
procedure Scale(var M: TMatrix; const v: TVector); overload;
var M2: TMatrix;
begin
M2:=NewMatrix;
M2[0,0]:=v.x; M2[1,1]:=v.y; M2[2,2]:=v.z;
M:=Multiply(M,M2);
end;

// call it like this to triple size in Y direction:
Scale(m2,vector(1,3,1));

// if you use this kind of vector nothing will happen as intended
Scale(m2,vector(1,1,1));

chronozphere
26-02-2010, 05:43 PM
But then everytime I change scaling, I'd have to re-build the object's VAO and VBOs... That is certainly not what I want.


yeah.. That's why matrices are THE way to do transformations.

Can you tell me what doesn't work?

Brainer
26-02-2010, 10:17 PM
I tried these both (not at the same time, of course):


FScale := Vec3(2.0, 2.0, 2.0);
// #1
CachedMatrix := Mat4Multiply(Mat4Multiply(Mat4CreateRotationEuler( FRotation),
Mat4CreateTranslation(Vec3Negate(FPosition))), Mat4CreateScale(FScale));
// #2
CachedMatrix := Mat4Multiply(Mat4Multiply(Mat4CreateRotationEuler( FRotation),
Mat4CreateScale(FScale)), Mat4CreateTranslation(Vec3Negate(FPosition)));


EDIT
I got it working. :) It's still in tests, though. Here's the code anyway:


CachedMatrix := Mat4Translate(Mat4Multiply(Mat4CreateRotationEuler (FRotation),
Mat4CreateScale(FScale)), Vec3Negate(FPosition));

Brainer
27-02-2010, 09:39 PM
It's me again.

The problem with scalling has been solved once and for all. ;) Everything works like a charm.

Now, I want to expand the possibilities of my camera class by adding a third-person mode. The idea to create the camera matrix is simple:


CachedMatrix := LookAt(Position, Target.Position, Vec3(0.0, 1.0, 0.0));

This works great, but now, how do I implement moving and strafing? In a first-person mode, I used these:


procedure TBrainCamera.Move(const AFactor: Single);
begin
if (AFactor <> 0.0) then
begin
FDirection := Mat4GetDirectionVector(Mat4CreateRotationEuler(Rot ation));

case FCamType of
ctFree:
Position := Vec3(Position.X + (FDirection.X * -AFactor),
Position.Y + (FDirection.Y * -AFactor),
Position.Z + (FDirection.Z * -AFactor));
ctFirstPerson:
Position := Vec3(Position.X + (FDirection.X * -AFactor),
Position.Y, Position.Z + (FDirection.Z * -AFactor));
end;

UpdateNeeded := True;
UpdateInvNeeded := True;
end;
end;
procedure TBrainCamera.Strafe(const AFactor: Single);
begin
if (AFactor <> 0.0) then
begin
FDirection := Mat4GetDirectionVector(Mat4CreateRotationEuler(Rot ation));
Position := Vec3(Position.X + (FDirection.Z * -AFactor),
Position.Y, Position.Z + (-FDirection.X * -AFactor));

UpdateNeeded := True;
UpdateInvNeeded := True;
end;
end;


My first idea was to translate the target object and change the camera position accordingly to a new target's position. So the code for a third-person camera would be here:


procedure TBrainCamera.Move(const AFactor: Single);
begin
{...}
ctThirdPerson:
begin
Target.Move(AFactor);
Position := Vec3Subtract(Target.Position, Position);
end;

But this approach doesn't seem to work.

EDIT
I followed the instructions given here (http://www.gamedev.net/reference/articles/article1591.asp), but it didn't help either.

From all I know, the best thing would be to base the camera on quaternions, but that would mean re-inventing the whole design from the ground up. I don't want them and I believe it can be done on Euler angles.

Do you have any suggestions?

User137
27-02-2010, 11:49 PM
What i'd do is very quick way (strafing):
1) Read X-vector from camera matrix
2) Normalize, and multiply the vector by amount you want to move sideways
3) Translate camera by this vector to strafe

If you need to move it forward or backwards, read Z-vector instead. This will naturally work for movement like space ship also, totally free angles.

Any questions? :)

Brainer
28-02-2010, 07:44 AM
But will this work for a third-person camera? ???

As you can see, I'm looking for solutions regarding a third-person camera. I've done it for a first-person one. :)

User137
28-02-2010, 12:44 PM
Yes it will work (in worst case you would get inverted vectors in which case all you would do is negate the movement amount), however when it comes to moving and camera, moving should be based on character. You can place camera on character's eyes or behind afterwards. Character on a common FPS game that's on land doesn't need a matrix. You need only position and direction vectors from which you can easily calculate movement.

I mean, what if you want to tint camera sideways to simulate earthquake or something, then strafing based on camera matrix wouldn't go along with what character is doing, or if you look up, then he would suddenly not move forward quite the right direction...

Edit2: Ew, i think that's what you were trying to do all along...

Maybe try like this
ctThirdPerson:
begin
Target.Move(AFactor);
Position := Vec3Subtract(Target.Position, AFactor);
// Reduce position by direction character is facing to whatever vector...
end;

Brainer
05-03-2010, 11:08 PM
I tried to make the third-person mode work on quaternions. Here's what I got:


ctThirdPerson:
if Assigned(FTarget) then
begin
FTarget.Move(AFactor);

Q := QuaternionFromEuler(Rotation);
Q1 := Quaternion(0.0, 0.0, 2.0, 5.0); // desired distance from the target
Q2 := QuaternionMultiply(QuaternionConjugate(Q),
QuaternionMultiply(Q1, Q));

Offset := Vec3(Q2.X, Q2.Y, Q2.Z);
NewPos := Vec3Add(Offset, FTarget.Position);

Position := NewPos;
end;

But this code doesn't work, because the camera moves, while the target object remains stationery, even though there's a line that "commands" him to move.

Any suggestions?

EDIT
I haven't tried your code, User137 yet. First, I wanna see if it's possible to do on quaternions. The code I'm using is taken from another source, where it works.

EDIT2
Here's another code I found (in C#):


Matrix transform = Matrix.Identity;
transform.Forward = ChaseDirection;
transform.Up = Up;
transform.Right = Vector3.Cross(Up, ChaseDirection);

// Calculate desired camera properties in world space
desiredPosition = ChasePosition +
Vector3.TransformNormal(DesiredPositionOffset, transform);
lookAt = ChasePosition +
Vector3.TransformNormal(LookAtOffset, transform);

I could use it if I knew which array fields correspond to forward, up, and right vectors. I saw a nice diagram somewhere, but can't find it now. Can someone tell me where these vectors are?
EDIT3
haha, I found the picture: http://www.songho.ca/opengl/files/gl_anglestoaxes01.png

Brainer
20-03-2010, 10:41 PM
Ok, time for a BUMP. ;)

I got the third person camera working, but there's still one thing I can't work out. I mean, mouse looking in the third person mode. The code I'm using works perfectly in the first person mode, but when it's used with the latter, it produces an effect like, well, a "barrel roll"? :o Here's a picture for a better look at the situation:
http://img144.imageshack.us/img144/5592/jajco.th.png (http://img144.imageshack.us/i/jajco.png/)

And of course, the code:


procedure TBrainCamera.MouseLook(const CursorX, CursorY: Integer);
var
DeltaX, DeltaY: Single;
R: TBrainEuler;
begin
if (CursorX = CenterX) and (CursorY = CenterY) then
exit;

DeltaX := CenterX - CursorX;
DeltaY := CenterY - CursorY;

R := Rotation; // for convenience
R.Yaw := R.Yaw - DeltaX * FMouseSmooth;

if FInvert then
R.Pitch := R.Pitch - DeltaY * FMouseSmooth
else
R.Pitch := R.Pitch + DeltaY * FMouseSmooth;

if (R.Pitch < FMinAngle) then
R.Pitch := FMinAngle;
if (R.Pitch > FMaxAngle) then
R.Pitch := FMaxAngle;

Rotation := R;

if (FCamType = ctThirdPerson) and (Assigned(FTarget)) then
FTarget.Rotation := Euler(FTarget.Rotation.Pitch, R.Yaw,
FTarget.Rotation.Roll);

SetCursorPos(CenterX, CenterY);

UpdateNeeded := True;
UpdateInvNeeded := True;
end;


Does anyone have a slightest clue what is wrong there?

Brainer
26-03-2010, 01:52 PM
I solved the problem, which wasn't in the mouse looking code, but in matrix calculations. Well, the camera seems to be working well now. :) You can see the code here (http://pastebin.com/rguhhD7b).

If you encounter any bugs, please let me know.