The one thing that has always hindered me from doing 3D programming is failing to understand how math works. I can go along with math fine in programming flow using methods and functions, then its all clear and logical to me, but in mathematical notation, I just can't make heads or tails from it.
I have been reading websites, a watching videos of institutes trying to explain this, but they all use mathematical notation and I simply get lost in it, my mind won't translate it to something understandable. I might have a defect there.
Also, Just using someone's code isn't my interest, I want to understand the mechanics behind it, the logic. I'd be happy to use someone else's code, but I really want to understand how it works.
The question
Can you explain to me in simple terms without mathematical notation, just programming notation/functions/psuedocode, how to implement a matrix transform along all 3 axes?
Ideally what I want is the material/understanding to write a method/object where I can define the angles of 3 axes similar to glRotate to rotate the collection of quads/triangles I have. (I am trying to program a 3D rotation of a cube shapes without having access to OpenGL functions to do it for me because this is done in one draw call every time something changes in the display list.)
What have I done?
I have attempted at making a 90 degrees transform function to get the hang of the math but failed utterly in making a proper matrix which in theory should have been the simplest to do. You can see my failed attempt in all its glory on http://jsfiddle.net/bLfg0tj8/5/
Vec3 = function(x,y,z) {
this.x = x;
this.y = y;
this.z = z;
}
Matrix = function Matrix() {
this.matrixPoints = new Array();
this.rotationPoint = new Vec3(0,0,0);
this.rotationAngle = 90;
}
Matrix.prototype.addVector = function(vector) {
this.matrixPoints.push(vector);
}
Matrix.prototype.setRotationPoint = function(vector) {
this.rotationPoint = vector;
}
Matrix.prototype.setRotationAngle = function(angle) {
this.rotationAngle = angle;
}
Matrix.prototype.populate = function() {
translateToOrigin = [[1,0,0-this.rotationPoint.x],
[0,1,0-this.rotationPoint.y],
[0,0,0-this.rotationPoint.z]];
rotationMatrix = [[0,-1,0],
[0,1,0],
[0,0,1]];
translateEnd = [[1,0,this.rotationPoint.x],
[0,1,this.rotationPoint.y],
[0,0,this.rotationPoint.z]];
currentColumn = 0;
currentRow = 0;
this.combomatrix = this.mergeMatrices(this.mergeMatrices(translateEnd,rotationMatrix),
translateToOrigin);
}
Matrix.prototype.transform = function() {
newmatrix = new Array();
for(c = 0;c<this.matrixPoints.length;c++) {
newmatrix.push(this.applyToVertex(this.matrixPoints[c]));
}
return newmatrix;
}
Matrix.prototype.applyToVertex = function(vertex) {
ret = new Vec3(vertex.x,vertex.y,vertex.z);
ret.x = ret.x + this.combomatrix[0][0] * vertex.x +
this.combomatrix[0][1] * vertex.y +
this.combomatrix[0][2] * vertex.z;
ret.y = ret.y + this.combomatrix[1][0] * vertex.x +
this.combomatrix[1][1] * vertex.y +
this.combomatrix[1][2] * vertex.z;
ret.z = ret.z + this.combomatrix[2][0] * vertex.x +
this.combomatrix[2][1] * vertex.y +
this.combomatrix[2][2] * vertex.z;
return ret;
}
Matrix.prototype.mergeMatrices = function(lastStep, oneInFront) {
step1 = [[0,0,0],[0,0,0],[0,0,0]];
step1[0][0] = lastStep[0][0] * oneInFront[0][0] +
lastStep[0][1] * oneInFront[1][0] +
lastStep[0][2] * oneInFront[2][0];
step1[0][1] = lastStep[0][0] * oneInFront[0][1] +
lastStep[0][1] * oneInFront[1][1] +
lastStep[0][2] * oneInFront[2][1];
step1[0][2] = lastStep[0][0] * oneInFront[0][2] +
lastStep[0][1] * oneInFront[1][2] +
lastStep[0][2] * oneInFront[2][2];
//============================================================
step1[1][0] = lastStep[1][0] * oneInFront[0][0] +
lastStep[1][1] * oneInFront[1][0] +
lastStep[1][2] * oneInFront[2][0];
step1[1][1] = lastStep[1][0] * oneInFront[0][1] +
lastStep[1][1] * oneInFront[1][1] +
lastStep[1][2] * oneInFront[2][1];
step1[1][2] = lastStep[1][0] * oneInFront[0][2] +
lastStep[1][1] * oneInFront[1][2] +
lastStep[1][2] * oneInFront[2][2];
//============================================================
step1[2][0] = lastStep[2][0] * oneInFront[0][0] +
lastStep[2][1] * oneInFront[1][0] +
lastStep[2][2] * oneInFront[2][0];
step1[2][1] = lastStep[2][0] * oneInFront[0][1] +
lastStep[2][1] * oneInFront[1][1] +
lastStep[2][2] * oneInFront[2][1];
step1[2][2] = lastStep[2][0] * oneInFront[0][2] +
lastStep[2][1] * oneInFront[1][2] +
lastStep[2][2] * oneInFront[2][2];
return step1;
}
Matrix.prototype.getCurrentMatrix = function() {
return this.matrixPoints;
}
myvectors = [new Vec3(50,50,0), new Vec3(20,80,0), new Vec3(80, 80, 0)];
function drawVectors(vectors,color) {
for(c=0;c<vectors.length;c++) {
document.getElementById("whoa").innerHTML += '<div style="color:'+color+';position:absolute;left:'+vectors[c].x+'px; top:'+vectors[c].y+'px;z-index:'+vectors[c].z+';">('+c+').</div>';
}
}
matrix = new Matrix();
for(c=0;c<myvectors.length;c++) {
matrix.addVector(myvectors[c]);
}
matrix.setRotationPoint(new Vec3(50,70,0));
matrix.populate();
somematrix = matrix.transform();
drawVectors(matrix.getCurrentMatrix(),"lime"); // draw current matrix that was hand coded
drawVectors([matrix.rotationPoint],'white'); // draw rotation point
drawVectors(somematrix,"red"); // transformed matrix... somehow two points merge
<div id="whoa" style="position:relative;top:50px;left:150px;background-color:green;color:red;width:400px;height:300px;">
</div>
The green text is the original triangle, the white point the center point, the red points the failed transformation(I think, because it isn't aligned around the center point). The tutorial I was in thought me how to combine matrices into a combined matrix, but I guess I screwed up somewhere.
As I said, it's really really hard for me to understand mathematical notation and speak. And not helping is that most teachers skip parts of the explanation. Took me 2 hours alone to understand when multiplying matrices you need to add each step together instead of just keep on multiplying. Yay for explanations.
A practical example what I work with/want to work with
For example I have a cube, loaded from a wavefront obj file located in the world at
x = 50
y = 100
z = 200
The cube is drawn using quads and some uv mapping. No problems here. It renders beautifully with all the textures showing correctly.
These are the location coordinates for each "face" of the cube which is drawn using a quad.
// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0
So this works all great. But what if I want this cube rotated 90 degrees along the x axis and 45 degrees around the z axis? I cannot use glRotate because at the moment I pass the data to the tesselator object I cannot do any matrix transforms to it via the opengl functions because it's just taking in the data, not actually rendering it per se.
The way the data is stored is as following:
WaveFrontObject()
|
|-> Groups(String groupname)
|
|-> Faces()
|
|-> Vertex(float x, float y, float z)[]
|-> Float UVmap[] corresponding to each vertex
|-> drawFace() // Draws the face as a quad or triangle
So each of the above coordinates I gave is stored as a face of the wavefront object in the group "cube".
When the cube is added to the tesselator it is translated to the right coordinates in the world and it renders normal.
It always renders the same however. If I would want it to render at an angle I would have to make a seperate wavefront object at this moment to be able to do that. In my opnion that is madness to do when it can be solved with some math.
Needed in the answer
- Explanation step by step how to build a translation matrix and an attempt to explain the math to me.
Explanation how to apply the translation matrix to the quads/triangles in the faces whist they keep oriented around the center of their location
x = 50.5 y = 100.5 z = 200.5
Some example/pseudo code to go along with the explanation.
The used programming language used to explain isn't really relevant as long as its in the C family
Please try to stay away from mathematical notation/speak. I don't know what alpha beta, thetha is, I do know what x axis, y axis and z axis is. I do know what angles are, but I do not know the names mathematicians find for it.
If you wish to use math names, please explain to me what they are in the 3D world/code and how they are formed/calculated.
I simply want to make a method/object along the lines of
Matrix.transformVertices(vertices[], 90deg x, 45 deg y, 0 deg z);
glm
you can find it here: glm.g-truc.net/0.9.9/index.html and a very good video series that pertains to many different fields of math that may be useful found here: youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw/…. Look for the linear algebra series! – Sneer