Pages: 1
Author Topic: Calculating 3D model Normals  (19,607 Views)
Offline (Unknown gender) FroggestSpirit

Member
Joined: Mar 2013
Posts: 79
View profile
Posted on: July 07, 2014, 04:39:59 PM
For quite a while, I have been drawing only the face normals of the 3D models in Project Collidascope. This gives the game a very flat and jagged look. I started looking into calculating vertex normals, and smoothing the whole thing out. The basic idea was to calculate all the face normals, and add them to the vertices that make up that face, that way it averages the normals of the connecting faces.
I'm not sure if I missed something, but the code does not seem to be working.
Here is the code I used to generate the normals: http://pastebin.com/raw.php?i=Hc8Nb7YE
The function normal works, as it's from the kingspace engine, and returns the normal as rx, ry and rz
EDIT: if push comes to shove, I can always have the normals pre-calculated and saved to the model, but I figured it will become easier to be able to calculate them during runtime if I have objects morphing and moving around
Offline (Unknown gender) TheExDeus

Developer
Joined: Apr 2008
Posts: 1,860
View profile
Reply #1 Posted on: July 07, 2014, 04:58:28 PM
The code I used in the water demo was this: http://pastebin.com/zV4gXtQg
The normal code alone is this:
//Normal calculation
normals = ds_grid_create(3,ds_grid_width(heights)*ds_grid_height(heights));
ds_grid_clear(normals,0.0);
for(int i = 0; i < ds_list_size(indicies)-2; i += 1)
{
if (i%2){
index1 = ds_list_find_value(indicies,i+1);
index2 = ds_list_find_value(indicies,i);
index3 = ds_list_find_value(indicies,i+2);
}else{
index1 = ds_list_find_value(indicies,i);
index2 = ds_list_find_value(indicies,i+1);
index3 = ds_list_find_value(indicies,i+2);
}

x1 = ds_grid_get(vertices,0,index1); y1 = ds_grid_get(vertices,1,index1); z1 = ds_grid_get(vertices,2,index1);
x2 = ds_grid_get(vertices,0,index2); y2 = ds_grid_get(vertices,1,index2); z2 = ds_grid_get(vertices,2,index2);
x3 = ds_grid_get(vertices,0,index3); y3 = ds_grid_get(vertices,1,index3); z3 = ds_grid_get(vertices,2,index3);

//Get vector
nx1 = x3 - x1, ny1 = y3 - y1, nz1 = z3 - z1;
nx2 = x2 - x1, ny2 = y2 - y1, nz2 = z2 - z1;

//Cross
cx = ny2 * nz1 - nz2 * ny1;
cy = nz2 * nx1 - nx2 * nz1;
cz = nx2 * ny1 - ny2 * nx1;

//Normalize
L = sqrt(cx * cx + cy * cy + cz * cz);

cx /= L;
cy /= L;
cz /= L;

ds_grid_add(normals,0,index1,cx);
ds_grid_add(normals,1,index2,cy);
ds_grid_add(normals,2,index3,cz);
}
for(int i = 0; i < ds_grid_height(normals); ++i)
{
//Normalize total
L = sqrt(ds_grid_get(normals,0,i) * ds_grid_get(normals,0,i) + ds_grid_get(normals,1,i) * ds_grid_get(normals,1,i) + ds_grid_get(normals,2,i) * ds_grid_get(normals,2,i));
ds_grid_set(normals,0,i,ds_grid_get(normals,0,i)/L);
ds_grid_set(normals,1,i,ds_grid_get(normals,1,i)/L);
ds_grid_set(normals,2,i,ds_grid_get(normals,2,i)/L);
}
The calculation part is quite verbose, as I cannot use classed in EDL. But the idea is that I use indices to make triangles, and normals are then calculated for these triangles. The normals are all added up, so any vertex that is part of a triangle gets a smoother normal, which is what you described.

QuoteEDIT: if push comes to shove, I can always have the normals pre-calculated and saved to the model, but I figured it will become easier to be able to calculate them during runtime if I have objects morphing and moving around
It won't be possible to calculate them per-frame anyway. So just add them to the model or calculate at start.

edit: Also, that is about the same code that was previously in ENIGMA and Robert removed.
Pages: 1