3D rotation

R

Rukola

Guest
Hello new forum! :D

I'm using JuJu's 3d planet model for rotating a 3d cube. This works quite nicely but I'm trying to have a character walk this cube and her being able to walk over the edge of the cube on to the next plane of the cube!

To do so, I use quaternions | quaternions. I heard they're not necessary but it seems like an elegant solution.

What I can't get right however, is see on which exact plane the character is and which angle that plane is turned to. So before I know it, the character is moving into the cube or is facing away from the camera.

Last but not least, I want to do this with an AI walking the cube as well.

what to do?

This is the code for walking on each plane of the cube but when rotating this goes very wrong:
Code:
switch(plane)
    {
        case 0: x+=hsp; y+=vsp; break; //top
        case 1: y+=hsp; z-=vsp; break; //right
        case 2: x+=hsp; y+=vsp; break; //bottom
        case 3: y+=hsp; z-=vsp; break; //left
    
        case 4: z+=hsp; x+=vsp; break; //front
        case 5: z+=hsp; x-=vsp; break; //back
    }
old:
It's set up so that the camera doesn't move but Jujus code rotates every object in my room with this:
Code:
d3d_transform_add_rotation_axis( camera_quat_a, camera_quat_b, camera_quat_c, camera_quat_d );
Then my character changes variable side whenever she walks across a border to 1 of the 6 sides:
Code:
switch(side)
    {
        //0 = top
        case 1: d3d_transform_add_rotation_axis(0, 1, 0, 090); break; // right
        case 2: d3d_transform_add_rotation_axis(0, 1, 0, 180); break; // bottom
        case 3: d3d_transform_add_rotation_axis(0, 1, 0, -90); break; // left
        case 4: d3d_transform_add_rotation_axis(1, 0, 0, -90); break; // front
        case 5: d3d_transform_add_rotation_axis(1, 0, 0, 090); break; // back
    }
This totally doesn't work however because as soon as you move right, down and then left the character warps to a wrong side. And this already happens without the cube rotated..

help!

tldr: I'm trying to understand how 3d rotations work in a degree so that I can combine and manipulate them beyond the usual scope.
 
Last edited by a moderator:
Q

Quackertree

Guest
So.. you've got a cube, on which the player walks, but the player has to be able to walk "off the edge" and onto the face adjacent to that edge?

I believe this is usually done by rotating the cube, and not rotating the player. It's what most of these games do; They leave the player at (0, 0, 0) and move the world around it, especially whenever the world is really dynamic and does a lot of rotating / moving.

Anyhow, as for the rotations; The rotation axis works a little different then you think it does, if I recall correctly. As far as I'm aware, you're supposed to use quaternions instead of euler angles for the rotation axis. If you want to use euler, you have to use d3d_transform_add_rotation_x/y/z() and then translate around the rotation point.

Example:

Code:
d3d_transform_add_rotation_z(45);
d3d_transform_add_translation(x, y, z);
d3d_draw_block(-8, -8, -8, 8, 8, 8, -1, 1, 1);
d3d_transform_set_identity();
This draws a block of size 16x16x16, centered around {x, y, z}, with a rotation around the z-axis of 45 degrees. Finally, it clears the transformations by setting them back to the identity.

My suggestion would be to put the player at a static location, then move/rotate the cube below the player by using code like my example.
 

TheSnidr

Heavy metal viking dentist
GMC Elder
This is a bit more tricky than it might seem at first. If you walk right, down and then left, you'll end up back at the face you started on, but your view will be rotated 90 degrees. Storing everything in one variable (in your case, "side") will not convey enough information to keep track of how the player should move on each side, and how it should transition from one side to another.

I would solve this by moving both the player and the camera around in 3D space, and keeping the cube static. When you press up, the player should move in the up-direction of the camera. When you walk over the edge, the camera is rotated, and the player will now move according the camera's new orientation. This is what I do in an old project of mine called Planetoids. I made a simplified version of it for you here, though even that might be overkill if you just want to move on a cube. I'll make a much simpler, bare-bones example for you if you're interested.

EDIT:
I made it anyway. Here's how I would do movement on a cube:
https://dl.dropboxusercontent.com/u/41252865/GamemakerExamples/WalkOnCubeSimple.gmz
 
Last edited:
R

Rukola

Guest
This is a bit more tricky than it might seem at first. If you walk right, down and then left, you'll end up back at the face you started on, but your view will be rotated 90 degrees. Storing everything in one variable (in your case, "side") will not convey enough information to keep track of how the player should move on each side, and how it should transition from one side to another.

I would solve this by moving both the player and the camera around in 3D space, and keeping the cube static. When you press up, the player should move in the up-direction of the camera. When you walk over the edge, the camera is rotated, and the player will now move according the camera's new orientation. This is what I do in an old project of mine called Planetoids. I made a simplified version of it for you here, though even that might be overkill if you just want to move on a cube. I'll make a much simpler, bare-bones example for you if you're interested.

EDIT:
I made it anyway. Here's how I would do movement on a cube:
https://dl.dropboxusercontent.com/u/41252865/GamemakerExamples/WalkOnCubeSimple.gmz
That looks.. astonishing <3 I'll dive into the code asap! Also thanks for sharing your planetoids concept. It totally 'headshots' me but I'll make sure to learn from it.

I very much noticed that it was a bit more tricky than it seemed at first glance. This is my first real 3d coding experience and using quaternions is making my head spin.. ha

If you're curious, this is how far I got myself. Watch out though; it's a mess xD Arrow keys move the character while wasd-qe move the camera: https://dl.dropboxusercontent.com/u/36587694/CUBEGAMEv3.gmz


So.. you've got a cube, on which the player walks, but the player has to be able to walk "off the edge" and onto the face adjacent to that edge?
Thank you for your input. I am currently using quaternions and I'm doing as you say; rotating the cube. The problem however is having a character decide on which side of the cube he is and which direction he is facing.
 
Last edited by a moderator:
R

Rukola

Guest
I loved the example but even there I'm having a hard time grasping the rotation. I'll add more than 1 character in my game and in the example I couldn't :(

I think I'm close but no sigar..
.. I'm using the variable side to make sure the movement is always on par with the side of the cube. This way if I'd add zsp that would always be a jump.

What goes wrong here is that my hsp en vsp calculations are rubbish! But why?

Code:
if(keyboard_check_pressed(vk_right))
{
    hsp = sign( lengthdir_x( 1, objCam.xx*90) + lengthdir_y( 1, objCam.yy*90) + lengthdir_y( 1, objCam.zz*90) );
    vsp = sign( lengthdir_y( 1, objCam.xx*90) + lengthdir_y( 1, objCam.yy*90) + lengthdir_y( 1, objCam.zz*90) );
}

if(keyboard_check_pressed(vk_left )) {hsp=-1;}
if(keyboard_check_pressed(vk_up   )) {vsp= -1;}
if(keyboard_check_pressed(vk_down )) {vsp= 1;}

if hsp!=0 || vsp!=0
{
    switch(side)
    {
        case 0: x+=hsp; y+=vsp; break; //top
        case 1: y+=hsp; z-=vsp; break; //right
        case 2: x+=hsp; y+=vsp; break; //bottom
        case 3: y+=hsp; z-=vsp; break; //left
      
        case 4: x+=hsp; z+=vsp; break; //front
        case 5: x+=hsp; z+=vsp; break; //back
    }
  
    hsp=0;
    vsp=0;
}
 

TheSnidr

Heavy metal viking dentist
GMC Elder
Hi Snidr,
Now.. your example is my plan B and it's amazing, it doesn't fully support my idea as I need e.g. more than one character moving around. You commented that my variable 'side' didn't contain enough information for 3d rotation and I understand why. I ask you for one question only! What would the math be to correct the movement on each side of one character?

All the angles are there but I can't see through the math!

Thanks either way <3
You have to realize that a game with fully 3D gameplay also requires fully 3D math. Normal 3D games that take place on the xy-plane with z used for jumping can get away with much simpler maths, like angles and 2D vectors. You'll have to use 3D vectors and matrices to get your concept to work. You need to define which direction is "forward" and which direction is "sideways" for each face of the cube. The most intuitive way to solve this is to keep this dynamic by making the player move according how he's seen on the screen. This is what I do in the example I sent you: The camera has a 4x4 matrix that depends on the player's position and orientation (see draw event of o_camera) - and the player's movement direction depends on the camera's matrix (see step event of o_player). This is where I wonder how you plan to move two separate player characters, as there would be no such intuitive way to move a player on a different side of the cube than the one the camera is looking at.

If this is too difficult, you'll have to keep your game simple. Quackertree's suggestion is super simple. Alternatively you could move the player around on the xy-plane, and when he crosses the edge, you rotate the cube 90 degrees and move him to the other side of the room. That way you can keep the entire game "2D" while it looks like you're moving around the surface of a cube.

For the record, quaternions are not necessary in this case and are just complicating things IMO.
 
R

Rukola

Guest
You have to realize that a game with fully 3D gameplay also requires fully 3D math. Normal 3D games that take place on the xy-plane with z used for jumping can get away with much simpler maths, like angles and 2D vectors. You'll have to use 3D vectors and matrices to get your concept to work. You need to define which direction is "forward" and which direction is "sideways" for each face of the cube. The most intuitive way to solve this is to keep this dynamic by making the player move according how he's seen on the screen. This is what I do in the example I sent you: The camera has a 4x4 matrix that depends on the player's position and orientation (see draw event of o_camera) - and the player's movement direction depends on the camera's matrix (see step event of o_player). This is where I wonder how you plan to move two separate player characters, as there would be no such intuitive way to move a player on a different side of the cube than the one the camera is looking at.

If this is too difficult, you'll have to keep your game simple. Quackertree's suggestion is super simple. Alternatively you could move the player around on the xy-plane, and when he crosses the edge, you rotate the cube 90 degrees and move him to the other side of the room. That way you can keep the entire game "2D" while it looks like you're moving around the surface of a cube.

For the record, quaternions are not necessary in this case and are just complicating things IMO.
I'm afraid you're absolutely right about having to conquer 3d maths. For the multiple characters, I meant NPC / AI characters.

I've got it working so far that the character stays on top. While the cube rotates it only works in 1 direction
and that concept leaves out the AI. On the note of keeping it simple; I would but the core gameplay requires it and I'd love to get it working proper <3
 
Hey, I was thinking about your problem, and I found a pretty easy way to have a character walk around the surface of a cube.

First, here's a download link to an example:
https://app.box.com/s/zynfhq3uc9s8a9nhpwkehiskykjes06i

All right, the way it works is the character has a matrix that represents his rotation and position relative to the cube.

Whenever the character moves, he checks if his position along each axis is out of a valid range. Say, the cube had a radius of 256, he checks if his position (in each axis) is greater than 256 or less than -256.

If one of the axes is out of range, then he must rotate over to the other surface he should now be on.

First, you wrap the out of range axis by subtracting 2*(cube radius) * sign(out of range axis position).

Then, you have to find the axis which is perpendicular to both his current up direction and the axis which is out of range. This is done using a cross product. You then rotate around this perpendicular axis.

Finally, you make sure the new up axis is exactly + or - the cube radius (depending on whether the out of range axis was positive or negative).

All of this is done independent of the cube's position or orientation, so the cube can be located anywhere in your world without affecting how the character moves around it.

There are a few things I didn't include in the project, so ask if it becomes necessary. 1) translating characters cube position into world position. 2) repairing rotation matrix if floating point precision errors accumulate to unacceptable levels. (probably would take a long time for that to happen).
 
Last edited:
R

Rukola

Guest
Hey, I was thinking about your problem, and I found a pretty easy way to have a character walk around the surface of a cube.

First, here's a download link to an example:
https://app.box.com/s/zynfhq3uc9s8a9nhpwkehiskykjes06i

All right, the way it works is the character has a matrix that represents his rotation and position relative to the cube.

Whenever the character moves, he checks if his position along each axis is out of a valid range. Say, the cube had a radius of 256, he checks if his position (in each axis) is greater than 256 or less than -256.

If one of the axes is out of range, then he must rotate over to the other surface he should now be on.

First, you wrap the out of range axis by subtracting 2*(cube radius) * sign(out of range axis position).

Then, you have to find the axis which is perpendicular to both his current up direction and the axis which is out of range. This is done using a cross product. You then rotate around this perpendicular axis.

Finally, you make sure the new up axis is exactly + or - the cube radius (depending on whether the out of range axis was positive or negative).

All of this is done independent of the cube's position or orientation, so the cube can be located anywhere in your world without affecting how the character moves around it.

There are a few things I didn't include in the project, so ask if it becomes necessary. 1) translating characters cube position into world position. 2) repairing rotation matrix if floating point precision errors accumulate to unacceptable levels. (probably would take a long time for that to happen).
First off, YES.

That looks very promising. I would've replied earlier if I wasn't so swamped. I'm looking through the code and while very different than what I'm used to so far.. it totally works! I noticed the projection limitation of not being able to turn all around the z axis. I think however, your idea of multiplying the matrices together will work for quaternions as well. So I'll rotate the cube and then interpret your character code!

I'll keep you posted as soon as I have some result<3

note to future self: Use the #define multiply_quaternion by Boris.
Code:
#define multiply_quaternion
///multiply_quaternion(r0,r1,r2,r3,s0,s1,s2,s3)
/*
* multiply two quaternions r and s
* arguments are real,i,j,k,real2,i2,j2,k2
* result is returned in variables q0,q1,q2,q3
*
* note that the order of the two quaternions is very
* important. multiply_quaternion(s,r) is not the same as
* multiply_quaternion(r,s)
*/

q0=argument0*argument4-argument1*argument5-argument2*argument6-argument3*argument7;
q1=argument0*argument5+argument1*argument4+argument2*argument7-argument3*argument6;
q2=argument0*argument6+argument2*argument4+argument3*argument5-argument1*argument7;
q3=argument0*argument7+argument3*argument4+argument1*argument6-argument2*argument5;
See what axis is crossed
Code:
if (abs(m[12+i]) > my_cube.scale)
        {
            axis_out_of_range = i;
            break;
        }
First, you wrap the out of range axis by subtracting 2*(cube radius) * sign(out of range axis position).
// invert position

Then, you have to find the axis which is perpendicular to both his current up direction and the axis which is out of range. This is done using a cross product. You then rotate around this perpendicular axis.
// complex find 'adjacent side' code.

Finally, you make sure the new up axis is exactly + or - the cube radius (depending on whether the out of range axis was positive or negative).
// hmm.
Code:
m[_axis] = 256 * _s;
 
Last edited by a moderator:

Yal

🐧 *penguin noises*
GMC Elder
If the game world will always be a hexahedral prism (cube or anything you can get from a cube if you just stretch it along an axis) you could probably solve this much more simply by representing characters' positions in a 2D grid, then just map portions of that to the cube. It's a bit more complex than that since you can jump between parts of the grid when you pass an edge, but you could probably do that by having each grid cell have IDs of their north, south, east and west neighbours and use that for moving. This approach also lets you use A* pathfinding with ease.

Moving a player character around isn't as easy as 'W means north, A means west' and so on for the reasons @TheSnidr pointed out, but it's just a matter of adding the player's rotation relative to the current grid cell before looking up the correct neighbour. (E.g. if the player is facing 'south' on the cell and presses A, that means they're moving towards the west neighbour of the cell)
 
I haven't learned quarternions yet so I can't tell you how the mothod I posted could be translated into using them. However I don't know know what you're talking about not being able to rotate around the z axis. The player is free to rotate around his z axis, and I believe the cube is free to rotate in any direction without affecting the system. So can you elaborate on what you mean there?
 
R

Rukola

Guest
If the game world will always be a hexahedral prism (cube or anything you can get from a cube if you just stretch it along an axis) you could probably solve this much more simply by representing characters' positions in a 2D grid, then just map portions of that to the cube. It's a bit more complex than that since you can jump between parts of the grid when you pass an edge, but you could probably do that by having each grid cell have IDs of their north, south, east and west neighbours and use that for moving. This approach also lets you use A* pathfinding with ease.

Moving a player character around isn't as easy as 'W means north, A means west' and so on for the reasons @TheSnidr pointed out, but it's just a matter of adding the player's rotation relative to the current grid cell before looking up the correct neighbour. (E.g. if the player is facing 'south' on the cell and presses A, that means they're moving towards the west neighbour of the cell)
I've had the same idea about the grid and even the pathfinding! I was talking about it with a friend the other day and he told me something similar as you're explaining. For some reason, when I tried to convey that into something useful, I ended up with a character teleporting across seemingly random faces of the dice. After trying Flying Saucers implementation, I'll try to write your suggestion in pseudo code to see where I went wrong :D

I haven't learned quarternions yet so I can't tell you how the method I posted could be translated into using them. However I don't know know what you're talking about not being able to rotate around the z axis. The player is free to rotate around his z axis, and I believe the cube is free to rotate in any direction without affecting the system. So can you elaborate on what you mean there?
I meant the part you wrote this piece of code for
Code:
cam_orbit_up_down = clamp(cam_orbit_up_down,-89,89);
When the cube moves upwards or downwards to far it either disappears at 90 degrees and inverts the projection at 91 :confused:
 
Last edited by a moderator:

Yal

🐧 *penguin noises*
GMC Elder
I think you might need to make a diagram with all the cube faces, and number all faces (and make arrows defining the reference direction) and edges... this makes it a bit easier to wrap your head around what's happening and how the mapping should be done. The hardest part with this method is mapping the grid to the surface of the prism, once you've gotten that down properly it should be smooth sailing.
 
M

Misty

Guest
There might be an easy trick or illusion to go about this.
Instead of rotating the player, you simply rotate the drawn world around the player, this way you don't have to add a bunch of extra code to compute new gravity directions.

Alternatively, what I would do is utterly scrap the code you have and start fresh.
What I mean by this is this:
There are two options, either do it using 1. the illusion or 2. the old fashioned way.
But even if you do it the old fashioned way I would scrap all the code you have and start fresh using the following ideas:
You should stop worrying about the actual "movement" for now and just make a sample camera that acts like a free cam (a free cam is a camera that just follows the mouse_dir like a space ship...for example 313 guilty spark or spectator cam in fps games.)
Then after you make sure the free cam (or space/spec cam ) is working, do this.
Grab the normal of the plane you are on, and make the gravity increase based on the inverse of the normal. Then when the camera hits the plane, reset the gravity to zero.

This is a robust solution that wont cause any confusion later on down the road. There is no way to fake it, and it absolutely needs to use raytracing.
Do not fake the raytracing by trying to be "fancy" and hand-coding some kind of bounding box thing.
Much easier and robust just doing the raytracing.

None of this requires quaternions. BUT
The raytracing must do three things:
1. Detect the distance to the plane
2. Determine whether or not the line is WITHIN the projection of the points defining a convex shape orthogonal to the plane
3. Return the surface normal of the plane

Also, you have 2 options for the direction of the rays. Either make 6 rays in all 6 ninety degree directions, or one ray that points to the middle of the cube (experimental.)
6 rays is better because if there are 2 cubes it may become glitchy and the gravity wont be smoothly interpolated.

The point of the project, is to test the simple gravity, making sure the player is gravitated to the plane based on the normal.

But after you perfect this, you will probably need quaternions for robust movement (unless you want to be fancy and adjust the movement as you were doing earlier, using switch statements.
 
Last edited:
I
I meant the part you wrote this piece of code for
Code:
cam_orbit_up_down = clamp(cam_orbit_up_down,-89,89);
When the cube moves upwards or downwards to far it either disappears at 90 degrees and inverts the projection at 91 :confused:
Hi. This can be easily fixed. All that needs to be done is to calculate a new up vector for the camera.

Code:
cam_up_z = dcos(-cam_orbit_up_down);
var _xy = dsin(-cam_orbit_up_down);
cam_up_x = dcos(cam_orbit_sideways) * _xy;
cam_up_y = dsin(cam_orbit_sideways) * _xy;
You need to provide cam_up_x, cam_up_y, cam_up_z as the up direction when setting the projection.
 
Top