• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Legacy GM (SOLVED) Help with Heavy Maths in 3d. Vertex normals.

NickKall

Member
Hi All,

The story so far...

I am making a 3d model making program. I have come to a hurdle which is really driving me nuts.

I have made some vertex points and made some triangles. But now I have to try and work out the normals of the triangles.

I have looked on line and done some reading and watching and this is what I have worked out. I need to get the cross product of two vectors. Then there was a whole bunch of squiggly line and different brackets everywhere and I got completely lost.

It isn't the programming I have a problem with, it is the maths.

Can anyone either:
a) Explain to me how I go about getting the normal?

or

b) I am willing to give a $50 steam voucher to anyone that can write the code for me (commented).

Thank you for reading. Nick.

PS. I am serious about the voucher. PM me for parameters.
 

TheSnidr

Heavy metal viking dentist
GMC Elder
I've made a library for this kind of stuff. It's not done yet, but some of it is available in this topic.
Here are a couple of script for generating flat and smooth normals:
Code:
/// @description smf_mbuff_create_flat_normals(modelBuffer, bytesPerVert)
/// @param modelBuffer
/// @param bytesPerVert
/*
Generates flat normals
modelBuffer must be a regular buffer, not a vertex buffer.

Script made by TheSnidr
www.TheSnidr.com
*/
var mBuff, bytesPerVert, bufferSize, i, j, P, N;
mBuff = argument0;
bytesPerVert = argument1;
bufferSize = buffer_get_size(mBuff);
for (i = 0; i < bufferSize; i += 3 * bytesPerVert)
{
    //Read the three vertices of the triangle
    for (j = 0; j < 9; j += 3)
    {
        buffer_seek(mBuff, buffer_seek_start, i + (j div 3) * bytesPerVert)
        P[j]   = buffer_read(mBuff, buffer_f32);
        P[j+1] = buffer_read(mBuff, buffer_f32);
        P[j+2] = buffer_read(mBuff, buffer_f32);
    }
    //Generate flat normal
    N = vector_normalize(vector_cross([P[0] - P[3], P[1] - P[4], P[2] - P[5]], [P[0] - P[6], P[1] - P[7], P[2] - P[8]]));
  
    //Write the flat normal to the buffer
    for (j = 0; j < 3; j ++)
    {
        buffer_seek(mBuff, buffer_seek_start, i + j * bytesPerVert + 3 * 4);
        buffer_write(mBuff, buffer_f32, N[0]);
        buffer_write(mBuff, buffer_f32, N[1]);
        buffer_write(mBuff, buffer_f32, N[2]);
    }
}
Code:
/// @description smf_mbuff_create_smooth_normals(modelBuffer, bytesPerVert)
/// @param modelBuffer
/// @param bytesPerVert
/*
Generates smooth normals
modelBuffer must be a regular buffer, not a vertex buffer.

Script made by TheSnidr
www.TheSnidr.com
*/
var mBuff, bytesPerVert, bufferSize, normalMap, i, j, P, N, p1, p2, p3, t1, t2, t3, Nx, Ny, Nz, l, key, n;
mBuff = argument0;
bytesPerVert = argument1;
bufferSize = buffer_get_size(mBuff);
normalMap = ds_map_create();
P = array_create(9);
for (i = 0; i < bufferSize; i += 3 * bytesPerVert)
{
    //Read the three vertices of the triangle
    for (j = 0; j < 9; j += 3)
    {
        buffer_seek(mBuff, buffer_seek_start, i + (j div 3) * bytesPerVert)
        P[j]   = buffer_read(mBuff, buffer_f32);
        P[j+1] = buffer_read(mBuff, buffer_f32);
        P[j+2] = buffer_read(mBuff, buffer_f32);
    }
    //Generate flat normal
    p1 = P[0] - P[3];
    p2 = P[1] - P[4];
    p3 = P[2] - P[5];
    t1 = P[0] - P[6];
    t2 = P[1] - P[7];
    t3 = P[2] - P[8];
    Nx = p2 * t3 - t2 * p3;
    Ny = p3 * t1 - p1 * t3;
    Nz = p1 * t2 - p2 * t1;
    l = sqrt(sqr(Nx) + sqr(Ny) + sqr(Nz));
    if l > 0
    {
        l = 1 / l;
        Nx *= l;
        Ny *= l;
        Nz *= l;
    }
    else
    {
        Nx = 0;
        Ny = 0;
        Nz = 1;
    }
      
    //Loop through the vertices of this triangle and add the normal of this triangle to each vertex' normal
    for (j = 0; j < 9; j += 3)
    {
        key = string(P[j]) + "," + string(P[j+1]) + "," + string(P[j+2]);
        n = normalMap[? key];
        if is_undefined(n){normalMap[? key] = [Nx, Ny, Nz];}
        else{normalMap[? key] = [n[0] + Nx, n[1] + Ny, n[2] + Nz];}
    }
}

//Loop through all vertices, normalize their new normals, and write them to the buffer
for (i = 0; i < bufferSize; i += bytesPerVert)
{
    buffer_seek(mBuff, buffer_seek_start, i)
    P[0] = buffer_read(mBuff, buffer_f32);
    P[1] = buffer_read(mBuff, buffer_f32);
    P[2] = buffer_read(mBuff, buffer_f32);
    key = string(P[0]) + "," + string(P[1]) + "," + string(P[2]);
    N = normalMap[? key];
    l = 1 / sqrt(sqr(N[0]) + sqr(N[1]) + sqr(N[2]));
    buffer_write(mBuff, buffer_f32, N[0] * l);
    buffer_write(mBuff, buffer_f32, N[1] * l);
    buffer_write(mBuff, buffer_f32, N[2] * l);
}
ds_map_destroy(normalMap);
You need to turn your vertex buffer into a regular buffer to use these, then turn it back into a vertex buffer afterwards. You also need to supply the number of bytes per vertex of your format. It'll modify your buffer directly, so it doesn't return anything.
Good luck! No voucher necessary, these scripts are already available for free.

Edit:
Err, I see the flat normals script uses a couple of custom vector scripts. I will remove them from the code in future versions, but for the time being, here they are:
Code:
/// @description vector_normalize(v[3])
/// @param v[3]
//Returns the unit vector with the same direction
//Also returns the length of the original vector
gml_pragma("forceinline");
var v, l, j;
v = argument0;
l = sqr(v[0]) + sqr(v[1]) + sqr(v[2]);
if l == 0{return [0, 0, 1, 0];}
l = sqrt(l);
j = 1 / l;
return [v[0] * j, v[1] * j, v[2] * j, l];
Code:
/// @description vector_cross_product(u[3], v[3])
/// @param u[3]
/// @param v[3]
gml_pragma("forceinline");
var u, v;
u = argument0;
v = argument1;
return [u[1] * v[2] - u[2] * v[1],
        u[2] * v[0] - u[0] * v[2],
        u[0] * v[1] - u[1] * v[0]];
 

NickKall

Member
Thanks for the quick response Snidr. That code looks intense. And it looks like it is for GMS2.

My program is in GMS1.4. I am using d3d_primitive... etc etc to do all my drawing and modelling. I assume that this wont work.

But I think I can reverse engineer it to use GMS1.4.

I will keep you posted.

Thanks again
 

GMWolf

aka fel666
For a triangle a,b,c, it's normal ist (b-a) × (c-a).

A cross product is like so:
cx= aybz − azby
cy = azbx − axbz
cz = axby − aybx


If you want smooth normals, first get the normal of each triangle. Then, for every vertex, find all triangles it brings to and average their normals.
 

NickKall

Member
For a triangle a,b,c, it's normal ist (b-a) × (c-a).

A cross product is like so:
cx= aybz − azby
cy = azbx − axbz
cz = axby − aybx


If you want smooth normals, first get the normal of each triangle. Then, for every vertex, find all triangles it brings to and average their normals.
So step by step:
1. work out the normals for the three edges of the triangle. that will give me a,b and c
2. Get the normal of the surface of the triangle using (b-a)x(c-a).
3. then do the cross product using these values found in step 2. Will that give the up normals of the triangle?
 

GMWolf

aka fel666
So step by step:
1. work out the normals for the three edges of the triangle. that will give me a,b and c
2. Get the normal of the surface of the triangle using (b-a)x(c-a).
3. then do the cross product using these values found in step 2. Will that give the up normals of the triangle?
So, a b a d c are the verices of the triangle.
 

NickKall

Member
upload_2019-1-16_23-30-32.png

WIN! WIN! WIN! WIN! WIN! WIN! WIN! WIN! WIN! WIN! WIN!

thank you guys for your help. Here is how I did it.

Code:
axv = x2-x1
ayv = y2-y1
azv = z2-z1
dista = point_distance_3d(x1,y1,z1,x2,y2,z2)
axn = axv/dista         //Normals of a
ayn = ayv/dista
azn = azv/dista

bxv = x3-x2
byv = y3-y2
bzv = z3-z2
distb = point_distance_3d(x2,y2,z2,x3,y3,z3)
bxn = bxv/distb         //Normals of b
byn = byv/distb
bzn = bzv/distb

cxv = x1-x3
cyv = y1-y3
czv = z1-z3
distc = point_distance_3d(x3,y3,z3,x1,y1,z1)
cxn = cxv/distc         //Normals of c
cyn = cyv/distc
czn = czv/distc

nx = (ayn*bzn)-(azn*byn)
ny = (azn*bxn)-(axn*bzn)
nz = (axn*byn)-(ayn*bxn)
Voucher is still available. You guys can split if you like? Let me know if you do.
 

GMWolf

aka fel666
Nice job!
Haha it's fine. I'm not here for money :)

Btw, I think you don't need to normalize your vectors. You can just normalize the final normal vector.

Good luck with the rest of your coding adventure.
 
Top