Generating geometry part 2 - Going 3d
Note: In every code and snippet I use the following self-made math library for vectors and matrices. If you want to use it, then set the following link as resource in your pen. If you use it, then please credit me
Link: http://yourjavascript.com/01666321507/math-min.js
Link to part 1: Link Link to part 3: Link
Now that you know how you can create 2 dimensional object, let's learn how to create 3 dimensional ones.
The 3rd dimension's "Hello world!" - The cube
A cube consists of 8 vertices, 12 edges and 6 faces. Each of these faces needs to be constructed from 2-2 triangles, this means we need to create 12 triangles, and that means 36 indices. Let's look at a wireframe cube.
The 8 corners are marked with A, B, C, D, E, F, G and H respectively.
Let's create the vertices for this cube:
var width = 100;
var height = 100;
var depth = 100;
var vertices = [
// First layer
vertices.push(new Vector3(0, 0, 0)), // A
vertices.push(new Vector3(width, 0, 0)), // B
vertices.push(new Vector3(width, 0, depth)), // C
vertices.push(new Vector3(0, 0, depth)), // D
// Second layer
vertices.push(new Vector3(0, height, 0)), // E
vertices.push(new Vector3(width, height, 0)), // F
vertices.push(new Vector3(width, height, depth)), // G
vertices.push(new Vector3(0, height, depth)) // H
];
Now that every vertex is in place, we need to create the indices. The faces of the cube are:
ADCB
HEFG
EABF
FBCG
GCDH
HDAE
The vertices' order do matter, because most rendering APIs (OpenGL, DirectX) are sensitive to the it. They determine the front and back of a face by the order of the vertices.
Now we can create the indices for every face separately as if it were a simple plane and not part of a cube. For the ADCB
face it would be ADBBDC
, so if we change every letter to numbers based on the
A: 0, B: 1, C: 2, ...
list, then we get 0, 1, 3, 3, 1, 2
. This is the same pattern we used to create a plane.
If you create the indices for every side, then you will get
var indices = [
0, 3, 1, 1, 3, 2,
7, 4, 6, 6, 4, 5,
4, 0, 5, 5, 0, 1,
5, 1, 6, 6, 1, 2,
6, 2, 7, 7, 2, 3,
7, 3, 4, 4, 3, 0
];
And you have a cube. Now let's render it (I render only the wireframe of it, so you can see it more clearly):
I can see right trough you - A tube
Creating a tube is slightly harder than creating a cube, but it involves less hard coding the values.
To define a tube model, you need to have 4 values: A radius, a height, the amount segments the perimeter consists of and the amount of segments it consists of vertically.
If this is not clear to you, then look at the following image:
.
So, how de we create a tube? We can easily create the vertices by using trigonometry and creating a circle out of vertices in the place of each height segment. In code, this would look like this:
var vertices = [];
var height = 200;
var radius = 30;
var heightSegments = 50;
var perimeterSegments = 20;
for (var y = 0; y < heightSegments; y++) {
for (var a = 0; a < perimeterSegments; a++) {
// Angle of the current vertex
var angle = 2 * Math.PI / perimeterSegments * a;
// Position of vertex
var x = Math.cos(angle) * radius;
var y = height / heightSegments * y;
var z = Math.sin(angle) * radius;
vertices.push(new Vector3(x, y, z));
}
}
Generating the indices for these vertices is the same as generating the indices for a plane. The only difference is that you need to connect the last row of vertices with the first one. To connect vertices with indices you need to use their ids. To get their ids based on their position you need to use the
id = x + y * width
formula.
There are a total of (perimeterSegments - 1) * (heightSegments - 1) quads, thus we only need to loop trough those and not every vertex.
In code this looks like the following:
// Create the quads between the vertices
for (var x = 0; x < perimeterSegments - 1; x++) {
for (var y = 0; y < heightSegments - 1; y++) {
indices.push(
x + y * perimeterSegments, // vertex at (x; y)
x + (y + 1) * perimeterSegments, // vertex at (x; y + 1)
x + 1 + y * perimeterSegments, // vertex at (x + 1; y)
x + 1 + y * perimeterSegments, // vertex at (x + 1; y)
x + (y + 1) * perimeterSegments, // vertex at (x; y + 1)
x + 1 + (y + 1) * perimeterSegments // vertex at (x + 1; y + 1)
);
}
}
// Connect the last and first row
for (var y = 0; y < heightSegments - 1; y++) {
var x = widthSegments - 1;
indices.push(
x + y * perimeterSegments, // vertex at (x; y)
x + (y + 1) * perimeterSegments, // vertex at (x; y + 1)
y * perimeterSegments, // vertex at (0; y)
y * perimeterSegments, // vertex at (0; y)
x + (y + 1) * perimeterSegments, // vertex at (x; y + 1)
(y + 1) * perimeterSegments // vertex at (0; y + 1)
);
}
If you render it you get the following (I use wireframe rendering here too and I also made it horizontal so you can see trough it):
This concludes this tutorial. The next part will be about spheres.