# Matrix math in Java

## Recommended Posts

jbviklund    100
Hey!

I'm trying to do some matrix transformations & projections in Java for a applet that is going to animate some lines in 3D.
Does this seem ok? I would also like to get some tips about how to do the perspective projection.

public class matrix3D{    private double[][] matrix;    public matrix3D(){        matrix = new double[4][4];        matrix = setIdentity();    }    private double[][] setIdentity(){        double[][] temp = {{1.0, 0.0, 0.0, 0.0},                           {0.0, 1.0, 0.0, 0.0},                           {0.0, 0.0, 1.0, 0.0},                           {0.0, 0.0, 0.0, 1.0}};                return temp;    }    public void resetMatrix(){        matrix = setIdentity();    }    public void setXRotation(double x){        double cos = Math.cos(x);        double sin = Math.sin(x);        double[][] temp = {{1.0,  0.0,  0.0,  0.0},                           {0.0,  cos, -sin,  0.0},                           {0.0,  sin,  cos,  0.0},                           {0.0,  0.0,  0.0,  1.0}};        matrix = multiply(matrix, temp);    }    public void setYRotation(double y){        double cos = Math.cos(y);        double sin = Math.sin(y);        double[][] temp = {{cos,  0.0,  sin,  0.0},                           {0.0,  1.0,  0.0,  0.0},                           {-sin, 0.0,  cos,  0.0},                           {0.0,  0.0,  0.0,  1.0}};        matrix = multiply(matrix, temp);    }    public void setZRotation(double z){        double cos = Math.cos(z);        double sin = Math.sin(z);        double[][] temp = {{cos, -sin,  0.0,  0.0},                           {sin,  cos,  0.0,  0.0},                           {0.0,  0.0,  1.0,  0.0},                           {0.0,  0.0,  0.0,  1.0}};        matrix = multiply(matrix, temp);    }        public void setTranslation(double x, double y, double z){        double[][] temp = {{1.0,  0.0,  0.0,  x  },                           {0.0,  1.0,  0.0,  y  },                           {0.0,  0.0,  1.0,  z  },                           {0.0,  0.0,  0.0,  1.0}};        matrix = multiply(matrix, temp);    }    public void  setScaling(double x, double y, double z){        double[][] temp = {{x,    0.0,  0.0,  0.0},                           {0.0,  y,    0.0,  0.0},                           {0.0,  0.0,  z,    0.0},                           {0.0,  0.0,  0.0,  1.0}};        matrix = multiply(matrix, temp);    }    private double[][] multiply(double[][] a, double[][] b){        double[][] result = new double[4][4];        result = setIdentity();        for(int i = 0; i < 4; i++)            for(int j = 0; j < 4; j++)                for(int k = 0; k < 4; k++)                    result[i][j] += a[i][k] * b[k][j];        return result;    }    public matrix3D multiply(matrix3D a, matrix3D b){        matrix3D result = new matrix3D();        for(int i = 0; i < 4; i++)            for(int j = 0; j < 4; j++)                for(int k = 0; k < 4; k++)                    result.matrix[i][j] += a.matrix[i][k] * b.matrix[k][j];                return result;    }    public vertex transformVertex(vertex v){        vertex result = new vertex();        result.setX(v.getX() * matrix[0][0] +                    v.getY() * matrix[0][1] +                    v.getZ() * matrix[0][2] +                    v.getW() * matrix[0][3]);        result.setY(v.getX() * matrix[1][0] +                    v.getY() * matrix[1][1] +                    v.getZ() * matrix[1][2] +                    v.getW() * matrix[1][3]);        result.setZ(v.getX() * matrix[2][0] +                    v.getY() * matrix[2][1] +                    v.getZ() * matrix[2][2] +                    v.getW() * matrix[2][3]);                result.setW(v.getX() * matrix[3][0] +                    v.getY() * matrix[3][1] +                    v.getZ() * matrix[3][2] +                    v.getW() * matrix[3][3]);                return result;    }}

##### Share on other sites
grhodes_at_work    1385
I didn't read carefully for errors, but it basically looks okay.

##### Share on other sites
jbviklund    100
Ok thanks! It's a bit hard to test the transformations without the perspective projection :)

##### Share on other sites
rip-off    10976
Write some unit tests.

##### Share on other sites
jbviklund    100
Fixed an error in the multiplication and then I made a function that prints the matrix:
No transformations:1,00        0,00        0,00        0,000,00        1,00        0,00        0,000,00        0,00        1,00        0,000,00        0,00        0,00        1,00X rotation (0.78 rad):1,00        0,00        0,00        0,000,00        0,71        -0,70       0,000,00        0,70        0,71        0,000,00        0,00        0,00        1,00Y rotation (0.78 rad):0,71        0,00        0,70        0,000,00        1,00        0,00        0,00-0,70       0,00        0,71        0,000,00        0,00        0,00        1,00Z rotation (0.78 rad):0,71        -0,70       0,00        0,000,70        0,71        0,00        0,000,00        0,00        1,00        0,000,00        0,00        0,00        1,00Translation (10.0, 0, 0):1,00        0,00        0,00        10,000,00        1,00        0,00        0,000,00        0,00        1,00        0,000,00        0,00        0,00        1,00Scaling (2.0, 1.0, 1.0):2,00        0,00        0,00        0,000,00        1,00        0,00        0,000,00        0,00        1,00        0,000,00        0,00        0,00        1,00X rotation + translation:1,00        0,00        0,00        10,000,00        0,71        -0,70       0,000,00        0,70        0,71        0,000,00        0,00        0,00        1,00Y rotation + translation:0,71        0,00        0,70        7,110,00        1,00        0,00        0,00-0,70       0,00        0,71        -7,030,00        0,00        0,00        1,00Z rotation + translation:0,71        -0,70       0,00        7,110,70        0,71        0,00        7,030,00        0,00        1,00        0,000,00        0,00        0,00        1,00X + Y + Z rotation:0,51        -0,50       0,70        0,000,85        0,16        -0,50       0,000,14        0,85        0,51        0,000,00        0,00        0,00        1,00X + Y + Z rotation + translation:0,51        -0,50        0,70       5,050,85        0,16        -0,50       8,520,14        0,85        0,51        1,390,00        0,00        0,00        1,00X + Y + Z rotation + translation + scaling:1,01        -0,50       0,70        5,051,70        0,16        -0,50       8,520,28        0,85        0,51        1,390,00        0,00        0,00        1,00

It seems more resonable now!

##### Share on other sites
jbviklund    100
I'm totally stuck on the perspective projection!
Take this for example:
The "canonical space mapping for perspective projections" at the bottom of the page.

http://www.cs.unc.edu/~mcmillan/comp136/Lecture16/projection.html

What is right, left bottom & top?

##### Share on other sites
JoeCooper    350
I did this a little while ago but don't have the source handy. I'll try to explain by my memory's fuzzy. Here goes.

Observe this here:

http://www.codeguru.com/dbfiles/get_image.php?id=10123&lbl=3DPROJ22_GIF&ds=20061023

You're defining that square floating in front of the viewer.

You can conjure up the figures in question from the aspect ratio.

For example, a 640x480 screen has an aspect ratio of 4:3.

We might pull a near plane distance figure out of our rear, like 1, and then base the rest on 1 multiplied with our aspect ratio.

Right might be 4/3, left might be -4/3, top and bottom are 1 and -1, respectively.

I'm gonna go dig up that code and then come back and post.

##### Share on other sites
JoeCooper    350
Here it is!

In my program I had condensed those figures into just width & height. Like so:

public static Matrix getProjectionMatrix(double focus, double near, double width, double height){	double matrix[][] = {			{ (2 * near) / width , 0, 0, 0 },			{ 0, (2 * near) / height, 0, 0 },			{ 0, 0, focus / (focus - near), -(focus / (focus - near))},			{ 0, 0, 1, 0 }	};			return new Matrix(4, 4, matrix);}

I called this like so:

Matrix projection = Main.getProjectionMatrix(50, 3, 1, 0.75);

The 1 & 0.75 figure comes from the aspect ratio of 4:3.

For every vertex, which contains 4 elemenents (x,y,z,w), we multiply it by the 4x4 matrix to yield a vector4 result, then divide the x and the y each by w.

(In my code I also divide z and w by w, and I can't think of why at the moment.)

Taking the x and y yields a point we can use to rasterize.

I don't see anything in here about multiplying the result by the pixel size of the viewport, so disregard that.

I don't know where the figures 3 and 50 came from, I presume I pulled them from my rear and decided they looked about right. Tweaking them will adjust how zoomed things look. You should be able to conjure up such figures from a given viewing angle and viewport size. I think.

##### Share on other sites
jbviklund    100
Starting to look better but still strange!
It's supposed to be a cube! :P

public void setProjection(int width, int height, double zNear, double zFar){    fov = Math.toRadians(fov);    double right = (width / height);    double left = -(width / height);    double top = 1.0;    double bottom = -1.0;    double a_a = (2.0 * zNear) / (right - left);    double a_c = -(right + left) / (right - left);    double a_d = width / 2.0;    double b_c = -(bottom + top) / (bottom - top);    double b_b = (2.0 * zNear) / (bottom - top);    double b_d = height / 2.0;    double c_c = (zFar + zNear) / (zFar - zNear);    double d_c = -(2.0 * zFar * zNear) / (zFar - zNear);    double[][] temp = {{a_a,  0.0,  a_c,  a_d},                       {0.0,  b_b,  b_c,  b_d},                       {0.0,  0.0,  c_c,  d_c},                       {0.0,  0.0,  1.0,  0.0}};    matrix = multiply(matrix, temp);}

##### Share on other sites
JoeCooper    350
lol, I have no idea. My cube correctly shows up a cube, but your projection matrix looks like it might be a tad different from mine.

##### Share on other sites
jbviklund    100
Without projection matrix:

With projection matrix:

Latest code:
    public void setProjection(int width, int height, double zNear, double zFar){        double right = (width / height);        double left = -(width / height);        double top = 1.0;        double bottom = -1.0;        double a_a = (2.0 * zNear) / (right - left);        double a_c = -((right + left) / (right - left));        double b_b = (2.0 * zNear) / (top - bottom);        double b_c = -((top + bottom) / (top - bottom));        double c_c = zFar / (zFar - zNear);        double c_d = -((zNear * zFar) / (zFar - zNear));        double d_c = 1.0;        double[][] temp = {{a_a,  0.0,  a_c,  0.0},                           {0.0,  b_b,  b_c,  0.0},                           {0.0,  0.0,  c_c,  c_d},                           {0.0,  0.0,  d_c,  0.0}};        matrix = multiply(matrix, temp);    }

I think i need some beer! :P

##### Share on other sites
JoeCooper    350
Do you remember to do the division by w bit at the end?

##### Share on other sites
jbviklund    100
When? after transforming the vertices, W is 1.0!?

##### Share on other sites
JoeCooper    350
I do this:

Matrix flattenedMatrix = projection.multiplyByMatrix(transform);					for(int i = 0; i < vertices.length; i++){	double[] result = flattenedMatrix.multiplyByVector(vertices[i]);	result[0] /= result[3]; //x = x / w	result[1] /= result[3]; //y = y / w	points.add(pointFromVertex(result));}

If w is 1f, than something borked earlier in the chain.

I was wrong earlier, I actually do multiple the result. The pointFromVertex method is defined as:

public static Point pointFromVertex(double[] vertex){	Point p = new Point();	p.translate((int) Math.round(vertex[0] * 400) + 200, (int) Math.round(vertex[1] * 300) + 150);	return p;}

This code is assuming a 400x300 viewport. Of course you should never hardcode such values. I don't know what was going through my head, but that's that.

No idea why I use translate to set the point, either. Don't do that, that's redonkulous. You might trawl through the code I linked to earlier. It's exactly what you're doing, and pretty much nothing else.

Here it is in action. That's a jar, don't let Windows save it as a zip.

##### Share on other sites
JoeCooper    350
Well? Any progress?

##### Share on other sites
jbviklund    100
I had to make a little visit to the hospital :P Has not been able to try anyting more yet..