Thanks for the great tutorial series, I’ve really enjoyed it so far (I’ve just implemented .obj loading). I noticed that there’s one simple, yet crucial optimization you seem to have overlooked, and I wanted to bring it to your attention.

The Render() method quickly becomes a major bottleneck when rendering a model that has more than just a couple of vertices. The problem is the calling of Project() once for every vertex of every face. The result is that every vertex in the model is projected _multiple times_. And Project() – mostly because of the TransformCoordinate() it uses – is a costly method, so minimizing the amount of calls to it helps the performance.

Here, I made some calculations with the suzanne.obj model:

Vertices in the model: 507

Faces in the model: 968, each consisting of 3 vertices

Projections per frame:

Number of faces * 3

= 968 * 3

= 2904

Projections per vertex per frame:

Projections per frame / vertices in model

= 2904 / 507

= ~5.7

So, on average, a single vertex is projected 5.7 times per frame. So, the program is working as if it was rendering a model 5.7 times as complex (vertex-wise) as suzanne.obj.

Now, how many projections per second would be needed for a nice performance of 60 frames per second?

Unoptimized:

Number of projections per frame * 60

= 2904 * 60

= 174240

Optimized:

Number of vertices * 60

= 507 * 60

= 30420

The optimization? Simple, just add an array of Vector2 to mesh and make it hold as many elements as the vertex array. Then, before rendering a mesh, reset the array. Before projecting a vertex, check if the matching index in the Vector2 array is set and if so, use that value and skip the projection. Otherwise do the projection and save the result in the array with an index matching that of the vertex in question. Now every vertex is projected only once per frame.

For my implementation of the engine this optimization alone doubled the performance for the suzanne.obj model. From 20 fps to 40. Of course, the optimization comes at the prize of memory, and it’s up to the developer to decide if it’s worth it or not.

———-

I’ve been implementing the engine on this rather old game creation software called Game Editor. It’s a 2D engine without support for using external libraries. So I had to implement all the matrix operations by myself (found great help from .NET’s reference source, though). I still don’t quite understand the way the 3D math works, but I’ve become a little more familiar with matrices and 3D transformations. The project can be found here: https://github.com/lclMetal/software3D

Again, thanks for the tutorial!

Lassi

]]>Device.prototype.drawLine = function (point0, point1, color, shader) {

var x0;

var y0;

var x1;

var y1;

if (point0.x <= point1.x) {

x0 = point0.x;

y0 = point0.y;

x1 = point1.x;

y1 = point1.y;

}

else {

x0 = point1.x;

y0 = point1.y;

x1 = point0.x;

y1 = point0.y;

}

var xDist = x1 – x0;

var yDist = y1 – y0;

var slope = (y1 – y0) / (x1 – x0);

var cx = x0;

var cy = y0;

var i;

var point;

// Exit loop if dist between the next pixel and the vertex is less than 2 (line is done rendering)

if (Math.abs(xDist) < 2 && Math.abs(yDist) = -1 && slope <= 1) {

for (i = x0; i < x1; i++) {

cx = i;

cy += slope;

var gradient = (cy – y0) / (y1 – y0);

var z = this.interpolate(point0.z, point1.z, gradient);

point = new BABYLON.Vector3(cx, Math.round(cy), z – wireOffset); //wireOffset is a separate variable I used to combat z- fighting, the best compromise was around 0.0000035

this.drawPoint(point, color);

}

}

// If slope is more vertical, then render based on dx/dy

else {

if (y0 < y1) {

for (i = y0; i < y1; i++) {

cy = i;

cx += 1 / slope;

var gradient = (cy – y0) / (y1 – y0);

var z = this.interpolate(point0.z, point1.z, gradient);

point = new BABYLON.Vector3(Math.round(cx), cy, z – wireOffset);

this.drawPoint(point, color);

}

}

else if (y1 < y0) {

cx = x1;

for (i = y1; i < y0; i++) {

cy = i;

cx += 1 / slope;

var gradient = (cy – y1) / (y0 – y1);

var z = this.interpolate(point0.z, point1.z, gradient);

point = new BABYLON.Vector3(Math.round(cx), cy, z – wireOffset);

this.drawPoint(point, color);

}

}

}

};

So thank you.

]]>Ps: Please update the links, some of them are not working!

]]>