When dealing with this subject of 3D rotation, basically, there are these steps to keep in mind;
1. Make three (3x3) matrices, one for each axis, X, Y, and Z, and another one for the concatenation result. It is also a good idea to establish the angle variables for each matrix, too.
2. Make a function that calculates the operations in the matrices (basic trigonometry stuff).
3. Make a function that concatenates the three matrices into one.
4. Make a function that computes where the vertices of the shape are going to end up, applying the final, concatenated matrix.
5. Make a function that draws the shape to the screen.
Steps one and three have already been covered, in part, in my previous post. Step five has also previously been brushed upon in this other previous post. They will have their applicable review here, however.
I make a globally declared float matrix type, 3x3, this way;
typedef matrix[3][3];
...and define my matrices in main() this way;
// the matrices...
matrix X_Matrix, Y_Matrix, Z_Matrix, Final_Matrix;
// the angles for each of the three axis matrices...
float x_angle = 0.0f;
float y_angle = 0.0f;
float z_angle = 0.0f;
float deg_to_rad(float deg)
{
return deg * ((float)M_PI / 180.0f);
}
...and that is the end of Step One!
void Matrix_Handler(matrix X_Mat, matrix Y_Mat, matrix Z_Mat, float x_ang, float y_ang, float z_ang)
{
// X Axis Matrix;
X_Mat[0][0] = 1.0f;
X_Mat[0][1] = 0.0f;
X_Mat[0][2] = 0.0f;
X_Mat[1][0] = 0.0f;
X_Mat[1][1] = cosf(x_ang);
X_Mat[1][2] = sinf(x_ang);
X_Mat[2][0] = 0.0f;
X_Mat[2][1] = -sinf(x_ang);
X_Mat[2][2] = cosf(x_ang);
// Y Axis Matrix
Y_Mat[0][0] = cosf(y_ang);
Y_Mat[0][1] = 0.0f;
Y_Mat[0][2] = -sinf(y_ang);
Y_Mat[1][0] = 0.0f;
Y_Mat[1][1] = 1.0f;
Y_Mat[1][2] = 0.0f;
Y_Mat[2][0] = sinf(y_ang);
Y_Mat[2][1] = 0.0f;
Y_Mat[2][2] = cosf(y_ang);
// Z Axis Matrix
Z_Mat[0][0] = cosf(z_ang);
Z_Mat[0][1] = sinf(z_ang);
Z_Mat[0][2] = 0.0f;
Z_Mat[1][0] = -sinf(z_ang);
Z_Mat[1][1] = cosf(z_ang);
Z_Mat[1][2] = 0;
Z_Mat[2][0] = 0.0f;
Z_Mat[2][1] = 0.0f;
Z_Mat[2][2] = 1.0f;
}
void Matrix_Handler(matrix X_Mat, matrix Y_Mat, matrix Z_Mat, float x_ang, float y_ang, float z_ang)
{
// X Axis Matrix;
X_Mat[1][1] = cosf(x_ang);
X_Mat[1][2] = sinf(x_ang);
X_Mat[2][1] = -sinf(x_ang);
X_Mat[2][2] = cosf(x_ang);
// Y Axis Matrix
Y_Mat[0][0] = cosf(y_ang);
Y_Mat[0][2] = -sinf(y_ang);
Y_Mat[2][0] = sinf(y_ang);
Y_Mat[2][2] = cosf(y_ang);
// Z Axis Matrix
Z_Mat[0][0] = cosf(z_ang);
Z_Mat[0][1] = sinf(z_ang);
Z_Mat[1][0] = -sinf(z_ang);
Z_Mat[1][1] = cosf(z_ang);
}
Step Three is the multiplication of these matrices. Do I need to go into this again, seeing as it is already covered? Here's the functions, anyway...
void mult_matrix(matrix Fst_Mat, matrix Sec_Mat, matrix Result_Mat)
{
float temp = 0.0f;
int a, b, c;
for(a = 0; a < 3; a++)
{
for(b = 0; b < 3; b++)
{
for(c = 0; c < 3; c++)
{
temp += Fst_Mat[b][c] * Sec_Mat[c][a];
}
Result_Mat[b][a] = temp;
temp = 0.0f;
}
}
}
// *********************************
void Concatenate_Matrices(matrix First_Mat, matrix Second_Mat, matrix Third_Mat, matrix Fin_Mat)
{
matrix Concat_Mat;
mult_matrix(First_Mat, Second_Mat, Concat_Mat);
mult_matrix(Concat_Mat, Third_Mat, Fin_Mat);
}
// a structure to hold the coordinates of the vertex...
struct verts
{
float vx;
float vy;
float vz;
};
// *********************************
// a structure for the order of the drawing of the lines...
struct lines
{
int start;
int end;
};
//.....
// set the positions of the vertices (this is called from main(), with an array of type verts)...
void set_shape_verts(verts *_cube_verts)
{
_cube_verts[0].vx = -100.0f;
_cube_verts[0].vy = -100.0f;
_cube_verts[0].vz = 100.0f;
_cube_verts[1].vx = -100.0f;
_cube_verts[1].vy = -100.0f;
_cube_verts[1].vz = -100.0f;
_cube_verts[2].vx = 100.0f;
_cube_verts[2].vy = -100.0f;
_cube_verts[2].vz = -100.0f;
_cube_verts[3].vx = 100.0f;
_cube_verts[3].vy = -100.0f;
_cube_verts[3].vz = 100.0f;
_cube_verts[4].vx = -100.0f;
_cube_verts[4].vy = 100.0f;
_cube_verts[4].vz = 100.0f;
_cube_verts[5].vx = 100.0f;
_cube_verts[5].vy = 100.0f;
_cube_verts[5].vz = 100.0f;
_cube_verts[6].vx = 100.0f;
_cube_verts[6].vy = 100.0f;
_cube_verts[6].vz = -100.0f;
_cube_verts[7].vx = -100.0f;
_cube_verts[7].vy = 100.0f;
_cube_verts[7].vz = -100.0f;
}
// *********************************
// set which vertices are going to be connected by lines (this is called from main(), with an array of type lines)...
void set_shape_lines(lines *_cube_lines)
{
_cube_lines[0].start = 0;
_cube_lines[0].end = 1;
_cube_lines[1].start = 1;
_cube_lines[1].end = 2;
_cube_lines[2].start = 2;
_cube_lines[2].end = 3;
_cube_lines[3].start = 3;
_cube_lines[3].end = 0;
_cube_lines[4].start = 4;
_cube_lines[4].end = 5;
_cube_lines[5].start = 5;
_cube_lines[5].end = 6;
_cube_lines[6].start = 6;
_cube_lines[6].end = 7;
_cube_lines[7].start = 7;
_cube_lines[7].end = 4;
_cube_lines[8].start = 0;
_cube_lines[8].end = 4;
_cube_lines[9].start = 1;
_cube_lines[9].end = 7;
_cube_lines[10].start = 2;
_cube_lines[10].end = 6;
_cube_lines[11].start = 3;
_cube_lines[11].end = 5;
}
...and here is the function that sets the "rotated" position of those vertices.
void Calc_Screen_Verts(matrix Fin_Mat, ver ts *c_verts, verts *s_verts)
{
float new_x;
float new_y;
float new_z;
float x_center = (float)SCREEN_X / 2.0f;
float y_center = (float)SCREEN_Y / 2.0f;
int i;
for(i = 0; i < CUBE_VERTS; i++)
{
new_x = (Fin_Mat[0][0] * c_verts[i].vx) + (Fin_Mat[0][1] * c_verts[i].vy) + (Fin_Mat[0][2] * c_verts[i].vz);
new_y = (Fin_Mat[1][0] * c_verts[i].vx) + (Fin_Mat[1][1] * c_verts[i].vy) + (Fin_Mat[1][2] * c_verts[i].vz);
new_z = (Fin_Mat[2][0] * c_verts[i].vx) + (Fin_Mat[2][1] * c_verts[i].vy) + (Fin_Mat[2][2] * c_verts[i].vz);
s_verts[i].vx = new_x + x_center;
s_verts[i].vy = new_y + y_center;
s_verts[i].vz = new_z;
}
}
Finally, Step Five uses a technique I already covered (again, here) to draw lines on the screen between the vertices held in the lines type structure array. Here it is...
void Draw_Shape(verts *s_verts, lines *c_lines)
{
int i;
for(i = 0; i < CUBE_LINES; i++)
{
al_draw_line(s_verts[c_lines[i].start].vx, s_verts[c_lines[i].start].vy,
s_verts[c_lines[i].end].vx, s_verts[c_lines[i].end].vy,
al_map_rgb(0,255,50), 0);
}
}
#define _USE_MATH_DEFINES
#include "allegro5/allegro.h"
#include "allegro5/allegro_primitives.h"
#include "math.h"
/* `pkg-config --libs allegro-5.1 allegro_primitives-5.1` */
const int SCREEN_X = 800;
const int SCREEN_Y = 600;
const float FPS = 60.0f;
const int CUBE_LINES = 12;
const int CUBE_VERTS = 8;
// *********************************
typedef float matrix[3][3];
// *********************************
struct verts
{
float vx;
float vy;
float vz;
};
// *********************************
struct lines
{
int start;
int end;
};
// *********************************
float deg_to_rad(float deg)
{
return deg * ((float)M_PI / 180.0f);
}
// *********************************
void check_angles(float &x_ang, float &y_ang, float &z_ang)
{
if(x_ang >= 180.0f)
x_ang -= 360.0f;
else if(x_ang < -180.0f)
x_ang += 360.f;
if(y_ang >= 180.0f)
y_ang -= 360.0f;
else if(y_ang < -180.0f)
y_ang += 360.f;
if(z_ang >= 180.0f)
z_ang -= 360.0f;
else if(z_ang < -180.0f)
z_ang += 360.f;
}
// *********************************
void set_shape_verts(verts *_cube_verts)
{
_cube_verts[0].vx = -100.0f;
_cube_verts[0].vy = -100.0f;
_cube_verts[0].vz = 100.0f;
_cube_verts[1].vx = -100.0f;
_cube_verts[1].vy = -100.0f;
_cube_verts[1].vz = -100.0f;
_cube_verts[2].vx = 100.0f;
_cube_verts[2].vy = -100.0f;
_cube_verts[2].vz = -100.0f;
_cube_verts[3].vx = 100.0f;
_cube_verts[3].vy = -100.0f;
_cube_verts[3].vz = 100.0f;
_cube_verts[4].vx = -100.0f;
_cube_verts[4].vy = 100.0f;
_cube_verts[4].vz = 100.0f;
_cube_verts[5].vx = 100.0f;
_cube_verts[5].vy = 100.0f;
_cube_verts[5].vz = 100.0f;
_cube_verts[6].vx = 100.0f;
_cube_verts[6].vy = 100.0f;
_cube_verts[6].vz = -100.0f;
_cube_verts[7].vx = -100.0f;
_cube_verts[7].vy = 100.0f;
_cube_verts[7].vz = -100.0f;
}
// *********************************
void set_shape_lines(lines *_cube_lines)
{
_cube_lines[0].start = 0;
_cube_lines[0].end = 1;
_cube_lines[1].start = 1;
_cube_lines[1].end = 2;
_cube_lines[2].start = 2;
_cube_lines[2].end = 3;
_cube_lines[3].start = 3;
_cube_lines[3].end = 0;
_cube_lines[4].start = 4;
_cube_lines[4].end = 5;
_cube_lines[5].start = 5;
_cube_lines[5].end = 6;
_cube_lines[6].start = 6;
_cube_lines[6].end = 7;
_cube_lines[7].start = 7;
_cube_lines[7].end = 4;
_cube_lines[8].start = 0;
_cube_lines[8].end = 4;
_cube_lines[9].start = 1;
_cube_lines[9].end = 7;
_cube_lines[10].start = 2;
_cube_lines[10].end = 6;
_cube_lines[11].start = 3;
_cube_lines[11].end = 5;
}
// *********************************
void Matrix_Handler(matrix X_Mat, matrix Y_Mat, matrix Z_Mat, float x_ang, float y_ang, float z_ang)
{
// X Axis Matrix;
X_Mat[0][0] = 1.0f;
X_Mat[0][1] = 0.0f;
X_Mat[0][2] = 0.0f;
X_Mat[1][0] = 0.0f;
X_Mat[1][1] = cosf(x_ang);
X_Mat[1][2] = sinf(x_ang);
X_Mat[2][0] = 0.0f;
X_Mat[2][1] = -sinf(x_ang);
X_Mat[2][2] = cosf(x_ang);
// Y Axis Matrix
Y_Mat[0][0] = cosf(y_ang);
Y_Mat[0][1] = 0.0f;
Y_Mat[0][2] = -sinf(y_ang);
Y_Mat[1][0] = 0.0f;
Y_Mat[1][1] = 1.0f;
Y_Mat[1][2] = 0.0f;
Y_Mat[2][0] = sinf(y_ang);
Y_Mat[2][1] = 0.0f;
Y_Mat[2][2] = cosf(y_ang);
// Z Axis Matrix
Z_Mat[0][0] = cosf(z_ang);
Z_Mat[0][1] = sinf(z_ang);
Z_Mat[0][2] = 0.0f;
Z_Mat[1][0] = -sinf(z_ang);
Z_Mat[1][1] = cosf(z_ang);
Z_Mat[1][2] = 0;
Z_Mat[2][0] = 0.0f;
Z_Mat[2][1] = 0.0f;
Z_Mat[2][2] = 1.0f;
}
// *********************************
void mult_matrix(matrix Fst_Mat, matrix Sec_Mat, matrix Result_Mat)
{
float temp = 0.0f;
int a, b, c;
for(a = 0; a < 3; a++)
{
for(b = 0; b < 3; b++)
{
for(c = 0; c < 3; c++)
{
temp += Fst_Mat[b][c] * Sec_Mat[c][a];
}
Result_Mat[b][a] = temp;
temp = 0.0f;
}
}
}
// *********************************
void Concatenate_Matrices(matrix First_Mat, matrix Second_Mat, matrix Third_Mat, matrix Fin_Mat)
{
matrix Concat_Mat;
mult_matrix(First_Mat, Second_Mat, Concat_Mat);
mult_matrix(Concat_Mat, Third_Mat, Fin_Mat);
}
// *********************************
void Calc_Screen_Verts(matrix Fin_Mat, verts *c_verts, verts *s_verts)
{
float new_x;
float new_y;
float new_z;
float x_center = (float)SCREEN_X / 2.0f;
float y_center = (float)SCREEN_Y / 2.0f;
int i;
for(i = 0; i < CUBE_VERTS; i++)
{
new_x = (Fin_Mat[0][0] * c_verts[i].vx) + (Fin_Mat[0][1] * c_verts[i].vy) + (Fin_Mat[0][2] * c_verts[i].vz);
new_y = (Fin_Mat[1][0] * c_verts[i].vx) + (Fin_Mat[1][1] * c_verts[i].vy) + (Fin_Mat[1][2] * c_verts[i].vz);
new_z = (Fin_Mat[2][0] * c_verts[i].vx) + (Fin_Mat[2][1] * c_verts[i].vy) + (Fin_Mat[2][2] * c_verts[i].vz);
s_verts[i].vx = new_x + x_center;
s_verts[i].vy = new_y + y_center;
s_verts[i].vz = new_z;
}
}
// *********************************
void Draw_Shape(verts *s_verts, lines *c_lines)
{
int i;
for(i = 0; i < CUBE_LINES; i++)
{
al_draw_line(s_verts[c_lines[i].start].vx, s_verts[c_lines[i].start].vy,
s_verts[c_lines[i].end].vx, s_verts[c_lines[i].end].vy,
al_map_rgb(0,255,50), 0);
}
}
// *********************************
int main(int argc, char *argv[])
{
bool loop = true;
bool draw = false;
float x_angle = 0.0f;
float y_angle = 0.0f;
float z_angle = 0.0f;
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
ALLEGRO_TIMER *timer = NULL;
matrix X_Matrix, Y_Matrix, Z_Matrix, Final_Matrix;
verts cube_verts[CUBE_VERTS];
set_shape_verts(cube_verts);
verts screen_verts[CUBE_VERTS];
lines cube_lines[CUBE_LINES];
set_shape_lines(cube_lines);
al_init();
al_init_primitives_addon();
al_set_new_display_option(ALLEGRO_VSYNC, 1, ALLEGRO_SUGGEST);
display = al_create_display(SCREEN_X, SCREEN_Y);
event_queue = al_create_event_queue();
timer = al_create_timer(1.0f / FPS);
al_register_event_source(event_queue, al_get_display_event_source(display));
al_register_event_source(event_queue, al_get_timer_event_source(timer));
al_start_timer(timer);
while(loop)
{
ALLEGRO_EVENT event;
al_wait_for_event(event_queue, &event);
switch(event.type)
{
case ALLEGRO_EVENT_DISPLAY_CLOSE:
loop = false;
break;
case ALLEGRO_EVENT_TIMER:
draw = true;
break;
default:
break;
}
if(draw && al_event_queue_is_empty(event_queue))
{
draw = false;
al_clear_to_color(al_map_rgb(0,0,25));
y_angle += 0.50f;
x_angle += 0.25f;
check_angles(x_angle, y_angle, z_angle);
Matrix_Handler(X_Matrix, Y_Matrix, Z_Matrix, deg_to_rad(x_angle), deg_to_rad(y_angle), deg_to_rad(z_angle));
Concatenate_Matrices(X_Matrix, Y_Matrix, Z_Matrix, Final_Matrix);
Calc_Screen_Verts(Final_Matrix, cube_verts, screen_verts);
Draw_Shape(screen_verts, cube_lines);
al_flip_display();
}
}
al_stop_timer(timer);
al_flush_event_queue(event_queue);
al_destroy_event_queue(event_queue);
al_destroy_timer(timer);
al_destroy_display(display);
return 0;
}
...and a screen shot of what you should get...
That's it.