Position and Rotate using GL matrix

BlitzMax Forums/MiniB3D Module/Position and Rotate using GL matrix

Yasha(Posted 2012) [#1]
Hi,

I have a library to manipulate 3D objects, and an app receiving object data that uses iminiB3D. The library returns the object's position and rotation relative to the camera as a "GL modelview matrix" (float[16]), and I've been trying (and failing!) to work out how to use this information to position and rotate my scene model within iminiB3D correctly.

As far as I can tell, miniB3D doesn't actually have a function to simply let me apply a matrix to a model, since it uses some mixture of Eulers for local position and a matrix for global position...? I assume it's also doing something strange to replicate DirectX's coordinate system in order to match up better with B3D (I don't know if this would make any great difference).

Anyway, what I have so far managed is to read the position (easy):

trPosition.x = -mat[13];
trPosition.y = -mat[12];
trPosition.z = mat[14];


These seem to be correct. Of note being that the order isn't what I initially expected, so I guess this is where the cordinates start to get odd.

I got as far as trying to copy the relevant code from EntityPitch etc. to try to read rotation values from the matrix:

static float MatrixPitch(float * mat) {
    float ang = atan2deg(mat[6], sqrt(mat[2] * mat[2] + mat[10] * mat[10]));
    if (ang <= 0.0001 && ang >= -0.0001) { ang = 0; }
    return -ang;
}

static float MatrixYaw(float * mat) {
    float a = mat[2], b = mat[10];
    if(a <= 0.0001 && a >= -0.0001) { a = 0; }
    if(b <= 0.0001 && b >= -0.0001) { b = 0; }
    return atan2deg(a, b);
}

static float MatrixRoll(float * mat) {
    float a = mat[4], b = mat[5];
    if(a <= 0.0001 && a >= -0.0001) { a = 0; }
    if(b <= 0.0001 && b >= -0.0001) { b = 0; }
    return atan2deg(a, b);
}


...tried with the matrix aligned both ways (although I don't really know what that means). This seemed better... but neither was right.

After some swapping of axes, negatives, and adding 90 to the pitch, I eventually got to a point where it looked like it might be that the Euler rotations were being applied in a funny order? e.g. Pitch and yaw being nonzero result in some funny rolling movement that looked like it might be caused by doing them the wrong way around. This seems to be consistent, anyway: if I can force one axis (usually pitch) to move in an apparently sensible fashion, the other two spin around in a weird combination of ways that I haven't been able to fix by simply swapping them around.

Yeah, I don't know what I'm doing at all (and feel like an idiot because most people seem to have no trouble with this).

The short answer would probably be if there's an easy way to simply take that original matrix and apply it to the model without going through this business with the Euler angles at all?

Last edited 2012


Kryzon(Posted 2012) [#2]
Hi Yasha.
Let's try the simpler stuff first. I'm not keen on C++ so take it with a grain:
void OverwriteFloat(Float mat[]){
	grid[0][0]=mat[0];
	grid[1][0]=mat[1];
	grid[2][0]=mat[2];
	grid[3][0]=mat[3];
	grid[0][1]=mat[4];
	grid[1][1]=mat[5];
	grid[2][1]=mat[6];
	grid[3][1]=mat[7];
	grid[0][2]=mat[8];
	grid[1][2]=mat[9];
	grid[2][2]=mat[10];
	grid[3][2]=mat[11];
	grid[0][3]=mat[12];
	grid[1][3]=mat[13];
	grid[2][3]=mat[14];
	grid[3][3]=mat[15];
}

See if this works. This is to be added to imB3D's 'matrix.h'.

If it does work, see if you can extend this by mapping each of grid's elements to a float pointer. This way, any changes the engine makes to that float array is applied to your entity's matrix automagically (I'm sure you can do this with C++, Klepto2 did it with BMax in his Newton wrapper).


Yasha(Posted 2012) [#3]
Thanks for looking. Unfortunately that didn't really help... it just made the model disappear (besides, I know from the position elements that the remote matrix doesn't match the miniB3D matrix 1:1 anyway).

However, I did discover something... I have apparently managed to hit upon the right arrangement of functions that each individual axis is being assigned to the model correctly:

trRotation.y = MatrixPitch(mat);
trRotation.p = -MatrixYaw(mat);
trRotation.r = MatrixRoll(mat);
trRotation.p += 90;


I thought it was perhaps an ordering issue, so I tried applying each axis to the model individually (mdl->RotateEntity(trRotation.p, 0, 0); etc.) - not in sequence, but viewing them individually. And it seems that the values for Yaw and Roll are correct! I think the pitch value is also correct but I'm not 100%... it is correct when facing the model but goes funny when the source is yaw-ed.

So... does this mean the order in which I apply the rotations to the entity will affect things? Previously I'd just been plugging all three values into a single RotateEntity call. Perhaps I can apply one and then add a couple of TurnEntity calls? This seems... completely awful, but I get the impression this may be an expected consequence of dealing with Euler rotations...?

(I wish I knew what I was doing. I will definitely need to spend some time learning about 3D once I have this aligned.)


ima747(Posted 2012) [#4]
Rotation order is important. if you're dealing with offset angles rather than fixed orientation. Example:
1) Pitch something 90deg (so it faces down) now roll it 90 deg, it's still facing down but is now twisted.
2) Same thing, but reverse the order, roll it 90 deg, so it's on it's side, now pitch 90 deg and it's facing to the side not down...
3) think about what yaw would do at the beginning middle or end of the above examples... even further out of whack.

I suspect your input is ordered. Mixing data of 2 types is tricky no matter what you're doing but trying to stick matrix's together without knowing their structures or the systems is extra tricky :0)

Last edited 2012


Kryzon(Posted 2012) [#5]
With Euler angles the order of axes does affect the end result. When you call Rotate\Turn Entity, the function takes care of ordering the transformations consistently.

The problem is that inside GL the Z axis is flipped, so if you're trying to apply a value from a GL matrix using the minib3d framework you'd need to flip it before sending to a function\method, or use it raw if you manually assign the values to the proper fields.

I still think that that matrix mapping is the way to go. This is how physics engines work, because this allows them to process and return a matrix that the client's game engine can apply to their meshes in whatever way they see fit.
If the order of the elements isn't correct, see if you can debug the matrix that library outputs, and find the correct mappings. It might be that old column-major vs row-major difference.


Yasha(Posted 2012) [#6]
Yay, I found a solution, thanks to both of those suggestions (confirming the order issue and manipulating the matrix):

- the input is ordered RYP rather than miniB3D's YPR
- manipulating the matrix is easier than manipulating the entity with the B3D commands
- ...and my roll was still inverted. Whoops.

So still extracting Euler angles from the original matrix, instead of rotating the entity I rotated the entity's matrix using the RotatePitch, RotateYaw, RotateRoll commands helpfully provided in matrix.h, and I could just reorder them until I found the right combination (on the sixth and last try no less!).

Thank you both!


AdamRedwoods(Posted 2012) [#7]
Opengl is column-major ordered. Different libraries use different axis conventions. Left-handed vs. right-handed conventions. It makes life difficult.

GL_MODELVIEW matrix is not the same as world matrix, so you can't use the matrix one-to-one. miniB3D uses the world matrix. So therefore, you need to remove the view matrix (camera matrix) out of the modelview matrix.

To do this, *i think* you need the camera matrix, or rebuild it yourself, and multiply the inverse to the modelview matrix to get your final world matrix.

http://www.songho.ca/opengl/gl_transform.html

EDIT: ah, you solved it. So perhaps the camera was assumed stationary? Then perhaps to get the one-to-one matrix with minib3d you only need to inverse the given matrix (ie. mat.Inverse() )?

Last edited 2012


Kryzon(Posted 2012) [#8]
Wow that's right, I'd totally forgotten the model-view is not the same as world.

For the sake of clarity: mesh.WorldMatrix = mesh.ModelView * Inverse(camera.WorldMatrix)

Thanks for the clarification Adam!