About this Blog

This is my first blog. Ever.

It is simply going to be about my hobby; playing with computer programming. I do not know much about blogging, but I will use this one to learn a bit more about it.

Programming has always been a bit of a passion for me, as from those early days when I first tapped in a sample BASIC program on my old Sinclair Spectrum back in 1986. I have been through many platforms, languages and OS's since, but always carried the hobby with me. I am not particularly good at it; perfection requires a large time investment and continuous practice. I do not have the luxury of the amount of time required to keep the fire burning constantly, so the hobby has inevitably gone through periods of extreme withering. I have, however, finally settled for C++, as the title of this blog implies, and play around with it for some entertainment when ever I can.

This here will serve me as a written record of what I am up to, and hopefully be a reinforcement to my memory every now and then. That is all there is to it.

So, if you read this blog, please don't expect anything snazzy, but be you welcome just the same!
Showing posts with label matrix concatenation. Show all posts
Showing posts with label matrix concatenation. Show all posts

Saturday, 21 January 2012

3D object rotation

So, what was all that business about concatenating matrices in my previous post? Well, it is one of the vital steps in accomplishing the rotation of a 3D shape on a screen. Now that it is clear how to concatenate matrices, I am going to look at what actually goes into those matrices, and what they mean, in 3D graphics. Now, there are all sorts of discussions around the web concerning right hand and left hand coordinate systems. I am not bothered by them. Here's a link that delves a little into that subject, quite coherently. I am just going "right handed", and be done.

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;


These will be in degrees, but C++ math.h trig functions use radians. I do like making my own little function for the conversion. Here it is (again)...

float deg_to_rad(float deg)
{
return deg * ((float)M_PI / 180.0f);
}


...and that is the end of Step One!

Now, for Step Two, here are the "formats" of my three matrices, X, Y and Z, respectively...



These are the operations that are going to be carried out in each matrix, with its respective angle fed to it. Here's the function that does it...

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;
}


...a bit of overkill, for clarity. It can be seen that, if I preset the matrix "cells" that do not contain any cosf(), sinf() or -sinf() operations at the beginning, this function could easily look like;

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);
}


...as all the other cells are always going to be either a zero or a one, according to the format of the concerned matrix. But that is all aside the point. This is all there is, really, to Step Two, so I will press on.

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);
}


The next Step, Four, is the first one that actually comes into contact with vertices or the "stored", 3D object. So, to be clear, here is the object. Yes, it is the everlasting cube. Look at the complete source code at the end of this post to see how it actually fits in.

// 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;
}
}


Note that another vert structure is passed to this function (s_verts). This is to receive the output of the rotated vertices held in c_verts. I do not actually want to alter the "mother" vertices of the cube, so I pass them through this function, modify (rotate) them using the concatenated Final_Matrix, and collect the rotation result in s_verts. There is no need to pass any angles, that has already been catered for by the matrix operation done previously. We need only pass the pre-rotated matrix to the function, and with the following operation, plot the new points positions...

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);
}
}


Obviously, I have missed out a great deal about perspective calculations, or vertex sorting for depth. That was not the point of these posts. All I wanted to do was review, quite superficially, the inner working of OpenGL. In any case, here is the complete working Allegro 5.1 source code for this demo.

#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.

Friday, 20 January 2012

3x3 Matrix Multiplication

Going from 2D matrix (from my previous post) to 3D matrix manipulation is a reasonably large step, and there is no real in between step to ease the transition. It is quite a leap of faith, when it is done the very first time. However, it does make sense, if the procedure is revisited (from our college days?), worked out on paper and studied a little. First, we simply must look at matrix multiplication.

Here's a simple example,


Now a 2 x 2 matrix to matrix multiplication (concatenation of two similar matrices into one)...


...and that constitutes a review.

Before making any attempt at hand made 3D graphics, procedures that multiply matrices in a program must be established. The following is a simple console program, that as usual can be strung together in the presented order and compiled to make the working demo application.

I am going to make three matrices; two that are going to be multiplied together, and a third to hold the output of the multiplication (concatenation) operation. First, it is a good idea to define a type out of the matrix, so that multiple, compatible instances of it can be created, make a function to populate the two matrices to be multiplied together with some values, and a function to display (cout) the contents of any of the matrices. This is simple stuff that anyone with an understanding of C++ just a little greater that "Hello World" can comprehend (which more or less guarantees that I will catch on again from here)...

#include "iostream"

using namespace std;

typedef int matrix[3][3];

void set_matrix(matrix x_mat, matrix y_mat)
{
x_mat[0][0] = 9;
x_mat[0][1] = 8;
x_mat[0][2] = 7;

x_mat[1][0] = 6;
x_mat[1][1] = 5;
x_mat[1][2] = 4;

x_mat[2][0] = 3;
x_mat[2][1] = 2;
x_mat[2][2] = 1;

y_mat[0][0] = 1;
y_mat[0][1] = 2;
y_mat[0][2] = 3;

y_mat[1][0] = 4;
y_mat[1][1] = 5;
y_mat[1][2] = 6;

y_mat[2][0] = 7;
y_mat[2][1] = 8;
y_mat[2][2] = 9;
}

void show_matrix(matrix mat)
{
int i, j;

for(i = 0; i < 3; i++)
{
for(j = 0; j < 3; j++)
{
cout << mat[i][j] << " ";
}
cout << endl;
}
cout << endl;
}


Then I will make a function that, in an expanded form, permits the easy following of what is happening during the matrix multiplication. The function accepts all three matrices as parameters, multiplies two of them (rows by columns, added together, in this case mat_1 and mat_2) and puts the results into the third (output matrix, in this case fin_mat)...

void expanded_mult(matrix mat_1, matrix mat_2, matrix fin_mat)
{
fin_mat[0][0] = (mat_1[0][0] * mat_2[0][0]) +
(mat_1[0][1] * mat_2[1][0]) +
(mat_1[0][2] * mat_2[2][0]);

fin_mat[1][0] = (mat_1[1][0] * mat_2[0][0]) +
(mat_1[1][1] * mat_2[1][0]) +
(mat_1[1][2] * mat_2[2][0]);

fin_mat[2][0] = (mat_1[2][0] * mat_2[0][0]) +
(mat_1[2][1] * mat_2[1][0]) +
(mat_1[2][2] * mat_2[2][0]);


fin_mat[0][1] = (mat_1[0][0] * mat_2[0][1]) +
(mat_1[0][1] * mat_2[1][1]) +
(mat_1[0][2] * mat_2[2][1]);

fin_mat[1][1] = (mat_1[1][0] * mat_2[0][1]) +
(mat_1[1][1] * mat_2[1][1]) +
(mat_1[1][2] * mat_2[2][1]);

fin_mat[2][1] = (mat_1[2][0] * mat_2[0][1]) +
(mat_1[2][1] * mat_2[1][1]) +
(mat_1[2][2] * mat_2[2][1]);


fin_mat[0][2] = (mat_1[0][0] * mat_2[0][2]) +
(mat_1[0][1] * mat_2[1][2]) +
(mat_1[0][2] * mat_2[2][2]);

fin_mat[1][2] = (mat_1[1][0] * mat_2[0][2]) +
(mat_1[1][1] * mat_2[1][2]) +
(mat_1[1][2] * mat_2[2][2]);

fin_mat[2][2] = (mat_1[2][0] * mat_2[0][2]) +
(mat_1[2][1] * mat_2[1][2]) +
(mat_1[2][2] * mat_2[2][2]);

}


All that is left to do is create the main() loop of the program now, and we are away with a 3x3 matrix multiplication program...

int main()
{
// locally create our three matrices of type matrix...
matrix x_matrix, y_matrix, final_matrix;

// populate the two matrices to be multiplied...
set_matrix(x_matrix, y_matrix);

// display the two matrices, so we can see they have been populated correctly...
show_matrix(x_matrix);
show_matrix(y_matrix);

// send all three matrices to the multiplier function, so that final_matrix can be populated with the results...
expanded_mult(x_matrix, y_matrix, final_matrix);

// and show the results of final matrix...
show_matrix(final_matrix);

return 0;
}


Does the trick? That's the whole program. Now some modifications to "compress" the code. I want to go after that void expanded_mult(matrix mat_1, matrix mat_2, matrix fin_mat) function to create something that is both a little more generic and less lengthy. for example, note that if you are doing a 4x4 matrix multiplication, that function is going to grow into an unwieldy...

//DEMO EXAMPLE
void expanded_mult(matrix mat_1, matrix mat_2, matrix fin_mat)
{
fin_mat[0][0] = (mat_1[0][0] * mat_2[0][0]) +
(mat_1[0][1] * mat_2[1][0]) +
(mat_1[0][2] * mat_2[2][0]) +
(mat_1[0][3] * mat_2[3][0]);

fin_mat[1][0] = (mat_1[1][0] * mat_2[0][0]) +
(mat_1[1][1] * mat_2[1][0]) +
(mat_1[1][2] * mat_2[2][0]) +
(mat_1[1][3] * mat_2[3][0]);

fin_mat[2][0] = (mat_1[2][0] * mat_2[0][0]) +
(mat_1[2][1] * mat_2[1][0]) +
(mat_1[2][2] * mat_2[2][0]) +
(mat_1[2][3] * mat_2[3][0]);

fin_mat[3][0] = (mat_1[3][0] * mat_2[0][0]) +
(mat_1[3][1] * mat_2[1][0]) +
(mat_1[3][2] * mat_2[2][0]) +
(mat_1[3][3] * mat_2[3][0]);



fin_mat[0][1] = (mat_1[0][0] * mat_2[0][1]) +
(mat_1[0][1] * mat_2[1][1]) +
(mat_1[0][2] * mat_2[2][1]) +
(mat_1[0][3] * mat_2[3][1]);

fin_mat[1][1] = (mat_1[1][0] * mat_2[0][1]) +
(mat_1[1][1] * mat_2[1][1]) +
(mat_1[1][2] * mat_2[2][1]) +
(mat_1[1][3] * mat_2[3][1]);

fin_mat[2][1] = (mat_1[2][0] * mat_2[0][1]) +
(mat_1[2][1] * mat_2[1][1]) +
(mat_1[2][2] * mat_2[2][1]) +
(mat_1[2][3] * mat_2[3][1]);

fin_mat[3][1] = (mat_1[3][0] * mat_2[0][1]) +
(mat_1[3][1] * mat_2[1][1]) +
(mat_1[3][2] * mat_2[2][1]) +
(mat_1[3][3] * mat_2[3][1]);



fin_mat[0][2] = (mat_1[0][0] * mat_2[0][2]) +
(mat_1[0][1] * mat_2[1][2]) +
(mat_1[0][2] * mat_2[2][2]) +
(mat_1[0][3] * mat_2[3][2]);

fin_mat[1][2] = (mat_1[1][0] * mat_2[0][2]) +
(mat_1[1][1] * mat_2[1][2]) +
(mat_1[1][2] * mat_2[2][2]) +
(mat_1[1][3] * mat_2[3][2]);

fin_mat[2][2] = (mat_1[2][0] * mat_2[0][2]) +
(mat_1[2][1] * mat_2[1][2]) +
(mat_1[2][2] * mat_2[2][2]) +
(mat_1[2][3] * mat_2[3][2]);

fin_mat[3][2] = (mat_1[3][0] * mat_2[0][2]) +
(mat_1[3][1] * mat_2[1][2]) +
(mat_1[3][2] * mat_2[2][2]) +
(mat_1[3][3] * mat_2[3][2]);



fin_mat[0][3] = (mat_1[0][0] * mat_2[0][3]) +
(mat_1[0][1] * mat_2[1][3]) +
(mat_1[0][2] * mat_2[2][3]) +
(mat_1[0][3] * mat_2[3][3]);

fin_mat[1][3] = (mat_1[1][0] * mat_2[0][3]) +
(mat_1[1][1] * mat_2[1][3]) +
(mat_1[1][2] * mat_2[2][3]) +
(mat_1[1][3] * mat_2[3][3]);

fin_mat[2][3] = (mat_1[2][0] * mat_2[0][3]) +
(mat_1[2][1] * mat_2[1][3]) +
(mat_1[2][2] * mat_2[2][3]) +
(mat_1[2][3] * mat_2[3][3]);

fin_mat[3][3] = (mat_1[3][0] * mat_2[0][3]) +
(mat_1[3][1] * mat_2[1][3]) +
(mat_1[3][2] * mat_2[2][3]) +
(mat_1[3][3] * mat_2[3][3]);

}


Phew! This function does the same thing, using for loops...

// DEMO EXAMPLE
void mult_matrix(matrix mat_1, matrix mat_2, matrix fin_mat)
{
int temp = 0;
int a, b, c;

for(a = 0; a < 3; a++)
{
for(b = 0; b < 3; b++)
{
for(c = 0; c < 3; c++)
{
temp += mat_1[b][c] * mat_2[c][a];
}
fin_mat[b][a] = temp;
temp = 0;
}
}
}


...for a 3x3 matrix, or...

// DEMO EXAMPLE
void mult_matrix(matrix mat_1, matrix mat_2, matrix fin_mat)
{
int temp = 0;
int a, b, c;

for(a = 0; a < 4; a++)
{
for(b = 0; b < 4; b++)
{
for(c = 0; c < 4; c++)
{
temp += mat_1[b][c] * mat_2[c][a];
}
fin_mat[b][a] = temp;
temp = 0;
}
}
}


...for a 4x4 matrix. Put this function in (the 3x3 version) instead of the expanded_mult() function, and modify main() to look like this...

int main()
{
matrix x_matrix, y_matrix, final_matrix;

set_matrix(x_matrix, y_matrix);
show_matrix(x_matrix);
show_matrix(y_matrix);

mult_matrix(x_matrix, y_matrix, final_matrix);

show_matrix(final_matrix);

return 0;
}


...and you're away.

To wrap up, here's a complete listing of how I am handling 4x4 (or any, symmetrical matrix, depending on the value of SYM_MAT).

#include "iostream"

using namespace std;

const int SYM_MAT = 4;

typedef int matrix[SYM_MAT][SYM_MAT];


void set_matrix(matrix x_mat, matrix y_mat)
{
x_mat[0][0] = 16;
x_mat[0][1] = 15;
x_mat[0][2] = 14;
x_mat[0][3] = 13;

x_mat[1][0] = 12;
x_mat[1][1] = 11;
x_mat[1][2] = 10;
x_mat[1][3] = 9;

x_mat[2][0] = 8;
x_mat[2][1] = 7;
x_mat[2][2] = 6;
x_mat[2][3] = 5;

x_mat[3][0] = 4;
x_mat[3][1] = 3;
x_mat[3][2] = 2;
x_mat[3][3] = 1;


y_mat[0][0] = 1;
y_mat[0][1] = 2;
y_mat[0][2] = 3;
y_mat[0][3] = 4;

y_mat[1][0] = 5;
y_mat[1][1] = 6;
y_mat[1][2] = 7;
y_mat[1][3] = 8;

y_mat[2][0] = 9;
y_mat[2][1] = 10;
y_mat[2][2] = 11;
y_mat[2][3] = 12;

y_mat[3][0] = 13;
y_mat[3][1] = 14;
y_mat[3][2] = 15;
y_mat[3][3] = 16;
}


void show_matrix(matrix mat)
{
int i, j;

for(i = 0; i < SYM_MAT; i++)
{
for(j = 0; j < SYM_MAT; j++)
{
cout << mat[i][j] << " ";
}
cout << endl;
}
cout << endl;
}


void mult_matrix(matrix mat_1, matrix mat_2, matrix fin_mat)
{
int temp = 0;
int a, b, c;

for(a = 0; a < SYM_MAT; a++)
{
for(b = 0; b < SYM_MAT; b++)
{
for(c = 0; c < SYM_MAT; c++)
{
temp += mat_1[b][c] * mat_2[c][a];
}
fin_mat[b][a] = temp;
temp = 0;
}
}
}


int main()
{
matrix x_matrix, y_matrix, final_matrix;

set_matrix(x_matrix, y_matrix);
show_matrix(x_matrix);
show_matrix(y_matrix);

mult_matrix(x_matrix, y_matrix, final_matrix);

show_matrix(final_matrix);

return 0;
}


That's all, for this post.