rogersgb, I have a small update to the computeNormals() function for 3DS in MaxLoader.java (originally this was in Loader3DS.java).
Since I’ve not used the “new” code as of yet I didn’t feel comfortable commiting this via subversion so I would appreciate if you could try it out and update it yourself. The “new” code seems to match the “old” code except that the Vec3 is now Vec4, and “object.verts” is now “object.vertices”
Basically I ran a profiler on the joglutils project (old one) and I noticed that computeNormals() was a huge bottleneck and so I started looking at the code and realized that it has way too much nested looping that is causing the long computation times. Initially the code loops over the objects and then each face of the object and calculates the normal for each face. It then loops over the vertices of each object and for each vertex it loops over all of the faces and tries to find the face who has a vertex that matches the current vertex and then it uses the face normal to add to the vertex normal…and then it normalizes the normal. Well, the looping over each face for every vertex is horrible because the loop never terminates early because it has to loop through all of the faces to find if any has a matching vertex. Also, it calculates a ‘shared’ counter that is used to divide the normal summation for each vertex normal but this is pretty much useless because we normalize the vertex normal anyway so dividing it by some value does nothing…it just adds an unnecessary calculation.
I rewrote the code to the following:
Loop through each object
- initialize the normal vector in each object
Loop through each object
- Loop through each face of the object
- calculate face normal
- add face normal to each of the 3 vertex normals of the face
Loop through each object
- Loop through each vertex of the object
- normalize the vertex normal
It may seem like there are three loops and this is worse but in fact this is much much faster and gives the correct results (or at least it seems to). And when I say it is much faster, on a model of 500,000 vertices the calculation was 30 times faster than before…I don’t think the model size matters but I’m just giving you an example.
Here is the code that I believe works for the “new” code (MaxLoader.java)
private void computeNormals(Model model)
{
Vec4 vVector1 = new Vec4();
Vec4 vVector2 = new Vec4();
Vec4 vPoly[] = new Vec4[3];
int numObjs = model.getNumberOfMeshes();
// Initialize the normals vectors for each vertex of each object
for (int index=0; index<numObjs; index++) {
Mesh object = model.getMesh(index);
object.normals = new Vec4[object.numOfVerts];
for (int i=0; i<object.numOfVerts; i++) {
object.normals[i] = new Vec4(0.0f, 0.0f, 0.0f);
}
}
// Calculate the normal for each face and add this normal value to each
// vertex of the face
for (int index=0; index<numObjs; index++) {
Mesh object = model.getMesh(index);
for (int i=0; i<object.numOfFaces; i++) {
vPoly[0] = new Vec4(object.vertices[object.faces[i].vertIndex[0]]);
vPoly[1] = new Vec4(object.vertices[object.faces[i].vertIndex[1]]);
vPoly[2] = new Vec4(object.vertices[object.faces[i].vertIndex[2]]);
vVector1 = vPoly[1];
vVector2 = vPoly[2];
vVector1.x -= vPoly[0].x;
vVector1.y -= vPoly[0].y;
vVector1.z -= vPoly[0].z;
vVector2.x -= vPoly[0].x;
vVector2.y -= vPoly[0].y;
vVector2.z -= vPoly[0].z;
Vec4 tempVect = new Vec4( vVector1.y*vVector2.z - vVector1.z*vVector2.y,
vVector1.z*vVector2.x - vVector1.x*vVector2.z,
vVector1.x*vVector2.y - vVector1.y*vVector2.x );
for (int j=0; j<3; j++) {
int vertex = object.faces[i].vertIndex[j];
object.normals[vertex].x += tempVect.x;
object.normals[vertex].y += tempVect.y;
object.normals[vertex].z += tempVect.z;
}
}
}
// Normalize the normal vector of each vertex of every object
for (int index=0; index<numObjs; index++) {
Mesh object = model.getMesh(index);
for (int i=0; i<object.numOfVerts; i++) {
// Normalize
float mag = (float)Math.sqrt(object.normals[i].x*object.normals[i].x +
object.normals[i].y*object.normals[i].y +
object.normals[i].z*object.normals[i].z);
// dividing by the negative of the magnitude to get the opposite
// vector...not exactly sure why this is done to be honest but it works.
object.normals[i].x /= -mag;
object.normals[i].y /= -mag;
object.normals[i].z /= -mag;
}
}
}
Here is the code that works for the “old” code (Loader3DS.java) that I am currently using:
private void computeNormals(Model3DS model)
{
Vec3 vVector1 = new Vec3();
Vec3 vVector2 = new Vec3();
Vec3 vPoly[] = new Vec3[3];
int numObjs = model.getNumberOfObjects();
// Initialize the normals vectors for each vertex of each object
for (int index=0; index<numObjs; index++) {
Obj object = model.getObject(index);
object.normals = new Vec3[object.numOfVerts];
for (int i=0; i<object.numOfVerts; i++) {
object.normals[i] = new Vec3(0.0f, 0.0f, 0.0f);
}
}
// Calculate the normal for each face and add this normal value to each
// vertex of the face
for (int index=0; index<numObjs; index++) {
Obj object = model.getObject(index);
for (int i=0; i<object.numOfFaces; i++) {
vPoly[0] = new Vec3(object.verts[object.faces[i].vertIndex[0]]);
vPoly[1] = new Vec3(object.verts[object.faces[i].vertIndex[1]]);
vPoly[2] = new Vec3(object.verts[object.faces[i].vertIndex[2]]);
vVector1 = vPoly[1];
vVector2 = vPoly[2];
vVector1.x -= vPoly[0].x;
vVector1.y -= vPoly[0].y;
vVector1.z -= vPoly[0].z;
vVector2.x -= vPoly[0].x;
vVector2.y -= vPoly[0].y;
vVector2.z -= vPoly[0].z;
Vec3 tempVect = new Vec3( vVector1.y*vVector2.z - vVector1.z*vVector2.y,
vVector1.z*vVector2.x - vVector1.x*vVector2.z,
vVector1.x*vVector2.y - vVector1.y*vVector2.x );
for (int j=0; j<3; j++) {
int vertex = object.faces[i].vertIndex[j];
object.normals[vertex].x += tempVect.x;
object.normals[vertex].y += tempVect.y;
object.normals[vertex].z += tempVect.z;
}
}
}
// Normalize the normal vector of each vertex of every object
for (int index=0; index<numObjs; index++) {
Obj object = model.getObject(index);
for (int i=0; i<object.numOfVerts; i++) {
// Normalize
float mag = (float)Math.sqrt(object.normals[i].x*object.normals[i].x +
object.normals[i].y*object.normals[i].y +
object.normals[i].z*object.normals[i].z);
// dividing by the negative of the magnitude to get the opposite
// vector...not exactly sure why this is done to be honest.
object.normals[i].x /= -mag;
object.normals[i].y /= -mag;
object.normals[i].z /= -mag;
}
}
}