Plane normal

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
Halan
Posts: 447
Joined: Tue Oct 04, 2005 8:17 pm
Location: Germany, Freak City
Contact:

Plane normal

Post by Halan »

For my quad-based terrain node I need to calculate the normals. I am not much experienced with 3d-maths so I feel a bit lost.

What I basically have is grid of vertices, that get rendered via QUADS (or later QUAD_STRIP).

So I have 8 vertices surrounding each vertex. How can I calculate a normal (and tangent) of this plane?

greets,
Halan
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

I think the easiest way is to calculate the normals of all the triangles surrounding the vertex, and then average them out.

So for each triangle create a triangle3df and call getNormal() (This basically just does a cross product to get the plane normal.), and then average those out. For the quads you can just use any 3 vertices and pretend it's a triangle. :P

Cheers
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
Halan
Posts: 447
Joined: Tue Oct 04, 2005 8:17 pm
Location: Germany, Freak City
Contact:

Post by Halan »

Alright. I know that you can do a cross-product with 2 vectors but how with a quad or triangle?
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

you need two vectors, which means two sides of the triangle. The third is not relevant, just ignore it. The resulting vector is perpendicular to the two vectors, i.e. the plane/triangle. Averaging will do the rest in case of more faces. Just as BlindSide told you.
zillion42
Posts: 324
Joined: Wed Aug 29, 2007 12:32 am
Location: Hamburg, Germany

Post by zillion42 »

interesting...
but somehow I can't really figure how the third vertex could be irrelevant when trying to calculate a vector perpendicular to a plane... By all means, doesn't a plane consists of at least 3 vertices ?
And what about concave quads, don't they have to be split into two triangles first ?
And since that is, and always will be quite a fundematal thing can someone point to, or post, already existing code ? Would be greatly appreciated...

EDIT:
By averaging do you mean
x=(x1+x2+x3) / 3
y=(y1+y2+y3) / 3
z=(z1+z2+z3) / 3

?

EDIT2:
maybe take a look in the hillplane mesh ?
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Funny ideas :lol:
You need two vectors which span a plane. So you cannot use two vectors from two vertices, but you need to take the vectors AB and BC (or AB AC, or AC BC - guess you get it by now). Taking a perpendicular vector from those two vectors will give you the normal.
And yes, looking at the Irrlicht source would help, but only terrain has smoothing (as well as the mesh manipulator). Oh, for the simple normal calculation just take plane2d.
Halan
Posts: 447
Joined: Tue Oct 04, 2005 8:17 pm
Location: Germany, Freak City
Contact:

Post by Halan »

okay so ill try to do so. My last problem is i want a normal that faces up (z-Axis) but theoretically the normals could all face downwards...

edit: Oh there is plane2d. Thanks hybrid

edit2: well i guess you meant plane3d ;)
Halan
Posts: 447
Joined: Tue Oct 04, 2005 8:17 pm
Location: Germany, Freak City
Contact:

Post by Halan »

okay i didn't work on this for some time.

My code looks like this:

Code: Select all

            //! Calculate normal of the 4 ajusting planes (cross-product)
            irr::core::vector3df normals[4];

            normals[0] = irr::core::vector3df(-1,-1, terrain->getHeight(Position(x,y) + pos)-terrain->getHeight(Position(x-1,y-1) + pos))
                         .crossProduct(irr::core::vector3df(0, 0, terrain->getHeight(Position(x+1,y+1) + pos)-terrain->getHeight(Position(x,y) + pos)));

            normals[1] = irr::core::vector3df(-1, 0, terrain->getHeight(Position(x,y) + pos)-terrain->getHeight(Position(x-1,y) + pos))
                         .crossProduct(irr::core::vector3df(0, 1, terrain->getHeight(Position(x+1,y+2) + pos)-terrain->getHeight(Position(x,y+1) + pos)));

            normals[2] = irr::core::vector3df(0, 0, terrain->getHeight(Position(x,y) + pos)-terrain->getHeight(Position(x,y) + pos))
                         .crossProduct(irr::core::vector3df(1, 1, terrain->getHeight(Position(x+2,y+2) + pos)-terrain->getHeight(Position(x+1,y+1) + pos)));

            normals[1] = irr::core::vector3df(0, -1, terrain->getHeight(Position(x,y) + pos)-terrain->getHeight(Position(x,y-1) + pos))
                         .crossProduct(irr::core::vector3df(1, 0, terrain->getHeight(Position(x+2,y+1) + pos)-terrain->getHeight(Position(x+1,y) + pos)));

            //! Now we take the average
            vertices[x*VIEW_RANGE*2 + y].Normal = normals[0] + normals[1] + normals[2] + normals[3];
            vertices[x*VIEW_RANGE*2 + y].Normal.normalize();
The result is this:
Image

You can see that on the edges the normals are broken.

Greets,
Halan
arras
Posts: 1622
Joined: Mon Apr 05, 2004 8:35 am
Location: Slovakia
Contact:

Post by arras »

This is strait from my terrain. It does what Blindside told you to do, that is to calculate normals of triangles around:

Code: Select all

// recalculate normal at terrain coordinates
void ShTlTerrainSceneNode::recalculateNormal(s32 w, s32 h)
{
    core::vector3df v0, v1;
    core::vector3df n0, n1, n2, n3, n4, n5;

    // calculate vector to point 0,-1
    if(h > 0) // check if not out of array
        v0 = core::vector3df(0, getHeight(w,h-1)-getHeight(w,h), -TileSize);
    else
        v0 = core::vector3df(0, 0, -TileSize);
    // calculate vector to point -1,-1
    if(w > 0 && h > 0) // check if not out of array
        v1 = core::vector3df(-TileSize, getHeight(w-1,h-1)-getHeight(w,h), -TileSize);
    else
        v1 = core::vector3df(-TileSize, 0, -TileSize);
    n0 = v0.crossProduct(v1);

    // calculate vector to point -1,-1
    v0 = v1;
    // calculate vector to point -1,0
    if(w > 0)
        v1 = core::vector3df(-TileSize, getHeight(w-1,h)-getHeight(w,h), 0);
    else
        v1 = core::vector3df(-TileSize, 0, 0);
    n1 = v0.crossProduct(v1);

    // calculate vector to point -1,0
    v0 = v1;
    // calculate vector to point 0,1
    if(h < Size.Height)
        v1 = core::vector3df(0, getHeight(w,h+1)-getHeight(w,h), TileSize);
    else
        v1 = core::vector3df(0, 0, TileSize);
    n2 = v0.crossProduct(v1);

    // calculate vector to point 0,1
    v0 = v1;
    // calculate vector to point 1,1
    if(w < Size.Width && h < Size.Height)
        v1 = core::vector3df(TileSize, getHeight(w+1,h+1)-getHeight(w,h), TileSize);
    else
        v1 = core::vector3df(TileSize, 0, TileSize);
    n3 = v0.crossProduct(v1);

    // calculate vector to point 1,1
    v0 = v1;
    // calculate vector to point 1,0
    if(w < Size.Width)
        v1 = core::vector3df(TileSize, getHeight(w+1,h)-getHeight(w,h), 0);
    else
        v1 = core::vector3df(TileSize, 0, 0);
    n4 = v0.crossProduct(v1);

    // calculate vector to point 1,0
    v0 = v1;
    // calculate vector to point 0,-1
    if(h > 0)
        v1 = core::vector3df(0, getHeight(w,h-1)-getHeight(w,h), -TileSize);
    else
        v1 = core::vector3df(0, 0, -TileSize);
    n5 = v0.crossProduct(v1);

    // calculate normals of 4 tiles around point
    core::vector3df m0, m1, m2, m3;
    m0 = (n1 - n0) /2 + n0;
    m1 = n2;
    m2 = (n4 - n3) /2 + n3;
    m3 = n5;

    // calculate normals between oposing tiles
    core::vector3df k0, k1;
    k0 = (m2 - m0) /2 + m0;
    k1 = (m3 - m1) /2 + m1;

    // calculate normal of point
    core::vector3df n = (k1 - k0) /2 + k0;
    n.normalize();
    setNormal(w,h,n);
}
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

edit: Oh there is plane2d. Thanks hybrid

Code: Select all

typedef line2df plane2df
:lol:

Although Arras already provided a solution, for learning purposes your code should look more like:

Code: Select all

(Assuming pos is the vector3df position of the vertex)

vector3df v1 = pos, v2 = pos, v3 = pos;
v1.Y = terrain->getHeight(Position(x, y) + pos);
v2.X += 1.0f;
v2.Y = terrain->getHeight(Position(x + 1, y) + pos);
v3.Z += 1.0f;
v3.Y = terrain->getHeight(Position(x, y + 1) + pos);

// So what we want is 3 3d positions on the surface. 
// One at our current position and 2 offset in different directions. Then we do...

normals[0] = (v1 - v2).crossProduct(v1 - v3); 
Hope that helps your understanding a little better.
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
Post Reply