Point normals (again)



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 23/01/2009 at 14:24, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   10.1 
    Platform:   Windows  ; Mac  ;  
    Language(s) :     C++  ;

    ---------
    Hi,

    I know this question has often been discussed, but I still don't completely understand it: How do I create the normals for each point of a geometry?

    I understand I have to iterate the point array and calculate the avarage from the face normals of all polygons the use the same point. But how do I find out which polygons use a specific point? Is there a helpful function for that?

    Also, I always get totally crappy results when using CreatePhongNormals(), it seems the does not output the correct phong normal for each point.

    Thanks for any help!

    Greetings,
    Jack



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 23/01/2009 at 14:41, xxxxxxxx wrote:

    You can use the Neighbor class.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 23/01/2009 at 15:42, xxxxxxxx wrote:

    Ah, that's something to start with.
    Thanks, Samir. I'll read about that class.

    Cheers,
    Jack



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/01/2009 at 12:59, xxxxxxxx wrote:

    Here you go... some quick-n-dirty code to generate Vertex Normals from a mesh object.  Returns the same type of memory chunk that CreatePhongNormals() does (I call this when no Phong Tag exists)...

        
        
          
        // helper struct  
        struct NormalStruct  
        {  
            NormalStruct(_DONTCONSTRUCT dc) : a(DC), b(DC), c(DC), d(DC) { }  
            NormalStruct(void) {}  
            NormalStruct(const Vector &t_a, const Vector &t_b, const Vector &t_c, const Vector &t_d) { a=t_a; b=t_b; c=t_c; d=t_d; }  
            NormalStruct(const Vector &t_a, const Vector &t_b, const Vector &t_c) { a=t_a; b=t_b; c=t_c; }
        
        
        
        
            Vector a,b,c,d;  
        };
        
        
        
        
        //------------------------------------------------------------------------------------------------------
        
        
        
        
        //******************************************************************************************************
        
        
        
        
        // ComputeVertexNormals()
        
        
        
        
        //******************************************************************************************************
        
        
        
        
        //------------------------------------------------------------------------------------------------------  
        NormalStruct *ogreMesh::ComputeVertexNormals(void)  
        {  
            CPolygon *pMeshPoly, *pNeighborPoly;  
            LONG j, faceCnt, *pFaces = NULL;  
            DWORD i;  
            Vector v1, v2, vNorm, *pNorm, *pNorms;  
            Neighbor neighbor;
        
        
        
        
            // initialize neighbor class  
            if( !neighbor.Init(m_numVerts, m_pPolys, m_numFaces, NULL) )  
                return NULL;
        
        
        
        
            // this array needs to be large enough for a unique Normal per quad-face vertex  
            pNorms = (Vector * )GeAlloc((4 * m_numFaces) * sizeof(Vector));  
            if( !pNorms )  
                return NULL;
        
        
        
        
            pNorm = &pNorms[0];  
            pMeshPoly = &m_pPolys[0];
        
        
        
        
            for(i=0; i<m_numFaces; i++)  
            {  
                //====================== Mesh Poly Point A ========================  
                vNorm = Vector(); // (re)intitialize   
                neighbor.GetPointPolys(pMeshPoly->a, &pFaces, &faceCnt);  
                for(j=0; j<faceCnt; j++)  
                {  
                    pNeighborPoly = &m_pPolys[pFaces[j]];
        
        
        
        
                    // Compute face normal  
                    v1 = m_pPoints[pNeighborPoly->b] - m_pPoints[pNeighborPoly->a];  
                    v2 = m_pPoints[pNeighborPoly->c] - m_pPoints[pNeighborPoly->a];  
                    vNorm += v1 % v2; // get cross-product  
                }  
                vNorm /= faceCnt; // average the normal(s)  
                *pNorm = !vNorm; // normalize and store  
                pNorm++;
        
        
        
        
        
        
        
                //====================== Mesh Poly Point B ========================  
                vNorm = Vector(); // (re)intitialize   
                neighbor.GetPointPolys(pMeshPoly->b, &pFaces, &faceCnt);  
                for(j=0; j<faceCnt; j++)  
                {  
                    pNeighborPoly = &m_pPolys[pFaces[j]];  
                    // Compute face normal  
                    v1 = m_pPoints[pNeighborPoly->b] - m_pPoints[pNeighborPoly->a];  
                    v2 = m_pPoints[pNeighborPoly->c] - m_pPoints[pNeighborPoly->a];  
                    vNorm += v1 % v2; // get cross-product  
                }  
                vNorm /= faceCnt; // average the normal(s)  
                *pNorm = !vNorm; // normalize and store  
                pNorm++;
        
        
        
        
        
        
        
                //====================== Mesh Poly Point C ========================  
                vNorm = Vector(); // (re)intitialize   
                neighbor.GetPointPolys(pMeshPoly->c, &pFaces, &faceCnt);  
                for(j=0; j<faceCnt; j++)  
                {  
                    pNeighborPoly = &m_pPolys[pFaces[j]];  
                    // Compute face normal  
                    v1 = m_pPoints[pNeighborPoly->b] - m_pPoints[pNeighborPoly->a];  
                    v2 = m_pPoints[pNeighborPoly->c] - m_pPoints[pNeighborPoly->a];  
                    vNorm += v1 % v2; // get cross-product  
                }  
                vNorm /= faceCnt; // average the normal(s)  
                *pNorm = !vNorm; // normalize and store  
                pNorm++;
        
        
        
        
                if( pMeshPoly->c != pMeshPoly->d )  
                {  
                    //====================== Mesh Poly Point D ========================  
                    vNorm = Vector(); // (re)intitialize   
                    neighbor.GetPointPolys(pMeshPoly->d, &pFaces, &faceCnt);  
                    for(j=0; j<faceCnt; j++)  
                    {  
                        pNeighborPoly = &m_pPolys[pFaces[j]];  
                        // Compute face normal  
                        v1 = m_pPoints[pNeighborPoly->b] - m_pPoints[pNeighborPoly->a];  
                        v2 = m_pPoints[pNeighborPoly->c] - m_pPoints[pNeighborPoly->a];  
                        vNorm += v1 % v2; // get cross-product  
                    }  
                    vNorm /= faceCnt; // average the normal(s)  
                    *pNorm = !vNorm; // normalize and store  
                    pNorm++;  
                }  
                else  
                {  
                    // if this is a triangle, just re-use C normal, computed above  
                    *pNorm = !vNorm; // normalize and store  
                    pNorm++;  
                }  
                pMeshPoly++;  
            }
        
        
        
        
            return (NormalStruct * )pNorms;  
        }
        
        
        
        
         
        
        
        
    

    I think there are only a few member variables being used in that (mostly just values you can get from the PolyObject, which you could pass into that routine).  You might also want to throw some if( faceCnt ) checks in there to avoid divide-by-zero (though I've never seen it happen).
    Cheers,
    Keith



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 24/01/2009 at 13:31, xxxxxxxx wrote:

    ...Just to further comment on the above...
    It just generates averaged face normals, which is less useful than the additional control that a Phong Tag gives you over normal generation (phong angle and handling edge-breaks), so you really ought to figure out what's going wrong with your use of CreatePhongNormals().
    Again, the above code returns a chunk of memory formated in the same manor as CreatePhongNormals(), which is:
    4 Vectors (the point normals) per polygon - whether it's a triangle or not, and in polygon index order.
    ...so if you're having trouble with CreatePhongNormals(), then you'll likely have trouble with the above code as well (that code is only meant as a fall-back solution, when no Phong Tag exists).



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 25/01/2009 at 03:40, xxxxxxxx wrote:

    Cool, thank you very much :-)

    I'll check that out.

    With CreatePhongNormals() I have the problem that the normals often seem to point into senseless directions. As I understood it, CreatePhongNormals() will return an array of normals, one for each point of the mesh. By weighting the normals of the points of a polygon with the barycentric coordinates, I should get a good normal for each position on a polygon. But I just get crap ;-)

    Have to figure out what going wrong, you are right. Anyway, a fallback solutions for meshes without Phong tag is a good think, so thanks again!

    Greetings,
    Jack



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 25/01/2009 at 04:15, xxxxxxxx wrote:

    Quote: Originally posted by c4dJack on 25  January 2009
    >
    > * * *
    >
    > Cool, thank you very much :-)
    >
    > I'll check that out.
    >
    > With CreatePhongNormals() I have the problem that the normals often seem to point into senseless directions. As I understood it, CreatePhongNormals() will return an array of normals, one for each point of the mesh. By weighting the normals of the points of a polygon with the barycentric coordinates, I should get a good normal for each position on a polygon. But I just get crap ;-)
    >
    > Have to figure out what going wrong, you are right. Anyway, a fallback solutions for meshes without Phong tag is a good think, so thanks again!
    >
    > Greetings,
    > Jack
    >
    > * * *
    See your comment in bold above...   this is a misunderstanding, or at least a mis-statement.
    The array has one for each point of every polygon (quad) in the mesh.   For example, let's say the mesh is 2 side-by-side quads, that share a common edge where they meet, so the mesh has 6 vertices.
    The array returned from CreatePhongNormals() has 8 (eight) normals in it - one for each vertex of each polygon - not 6 (if the mesh was 2 triangles, you'd still get 8 normals).
    The normals are in the same order that the vertex indices are, so for example:

        
        
          
        //--------- S N I P ----------
        
        
        
        
        NormalStruct *pNorms = (NormalStruct * )op->CreatePhongNormals();  
        if( !pNorms ) pNorms = ComputeVertexNormals(op); // call the code above, after fixing it up a bit  
        if( !pNorms ) { complain(); return; }
        
        
        
        
        Vector *pVertices = op->GetPointW();  
        CPolygon *pPolys = op->GetPolygonW();  
        LONG numPolys = op->GetPolygonCount();
        
        
        
        
        LONG polyNdx;  
        for(polyNdx=0; polyNdx<numPolys ; polyNdx++)  
        {  
            Vector pVert;  
            Vector pNorm;  
          
            pVert = pVertices[pPolys[polyNdx].a];  
            pNorm = pNorms[polyNdx].a;  
            do_something(pVert, pNorm);
        
        
        
        
            pVert = pVertices[pPolys[polyNdx].b];  
            pNorm = pNorms[polyNdx].b;  
            do_something(pVert, pNorm);
        
        
        
        
            pVert = pVertices[pPolys[polyNdx].c];  
            pNorm = pNorms[polyNdx].c;  
            do_something(pVert, pNorm);
        
        
        
        
            if( pPolys[polyNdx].c != pPolys[polyNdx].d )  
            {  
                pVert = pVertices[pPolys[polyNdx].d];  
                pNorm = pNorms[polyNdx].d;  
               do_something(pVert, pNorm);  
            }
        
        
        
        
        }  
        //--------- S N I P ----------  
        
    

    Does that help?



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 25/01/2009 at 04:22, xxxxxxxx wrote:

    ...also note that the above implies that you will likely end up with many 'duplicate' normals (anywhere polygons share a common vertex and there is no phong-edge-break).  This is fine if you're just interested in what the normals are, but may require further processing if you want to eliminate duplicates and convert it to a table lookup (ie. my Riptide / Riptide Pro .obj exporters do that before saving the normals).



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 25/01/2009 at 04:43, xxxxxxxx wrote:

    Oops.. I left out something in my latest code sample above... whether you get a buffer returned from CreatePhongNormals() or my other code above, in each case be sure to GeFree() the buffer when done :).
    EDIT: I see that I also left off the '*' on Vector *pVert ; Vector *pNorm ; declarations above (I typed that example in by hand - spur of the moment).
    EDIT2: Actually, because of the way the rest of that is coded, those would just be Vectors instead of pointers to Vectors, so just ignore the 'p' in front of those variable names :) .



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 25/01/2009 at 05:00, xxxxxxxx wrote:

    Ah!! OK, then either I misunderstood the documentation, or it should be made more clear there. Thanks again! This is the best "Point Normals" thread ever :D

    Additional question: What if my mesh is triangulated? Regarding your example with 2 quad polygons = 6 vertices = 8 elements in CreatePhongNormals() array... if I triangulate your example mesh, I would get 4 tri polygons = 6 vertices = 12 elements in normal array. Right? Because there are three vertices per polygon? Or would I still get 4 vertices per polygon (= 16 elements), just the 4th one is not used?

    Cheers,
    Jack



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 25/01/2009 at 05:25, xxxxxxxx wrote:

    Quote: Originally posted by c4dJack on 25  January 2009
    >
    > * * *
    >
    > ...I would get 4 tri polygons = 6 vertices = 12 elements in normal array. Right? Because there are three vertices per polygon? Or would I still get 4 vertices per polygon (= 16 elements), just the 4th one is not used?
    >
    >
    > * * *

    Underlined.
    Basically, you always get 4 x number_of_polygons ... one for each (a,b,c,d) corner of the quad - whether the 4th is used, or not.
    As an aside, this is also how the UVW Tag works... except that it has routines to let you get each uv-polygon:  UVWStruct uvw = pUVs->Get(polyNdx);   I modeled the NormalStruct on the UVWStruct



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 25/01/2009 at 05:37, xxxxxxxx wrote:

    It's best to think of it this way:
    CPolygon = vertex-polygon
    UVWStruct = uv-polygon
    NormalStruct = normal-polygon
    ...all of the above always have 4 elements (a,b,c,d).   The only practical difference is that the CPolygon contains indices into the vertex array as it's elements and the other two contain the actual UV/Normal Vector itself instead of an index, as the 4 elements.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/08/2009 at 10:40, xxxxxxxx wrote:

    Sorry, to push this up again, but:

    What , if I don't work with polygons, but just with points? I can't use NormalStruct results here, since I just have a point index. How can I find out the Vertex Normal for a specific point?

    Thanks in advance!

    Cheers,
    Jack



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/08/2009 at 11:33, xxxxxxxx wrote:

    I'm trying to understand this, so I wrote a function to get the Vertex Normal of just one point (I know this will be slow if I want them for several points, but it's just a test).

    Strangely, I get the weirdest results. The normals point in all kinds of directions, but not into the correct one.

    > \> Vector GetVertexNormal(LONG PointIndex, PolygonObject \*op) \> { \>      // Variables \>      CPolygon \*pNeighborPoly; \>      Neighbor neighbor; \>      LONG j, faceCnt, \*pFaces = NULL; \>      Vector v1, v2, vNorm = Vector(0,0,0); \>      CPolygon \*m_pPolys = op->GetPolygonW(); \>      Vector \*m_pPoints = op->GetPointW(); \> \>      // Init Neighbor class \>      if (!neighbor.Init(op->GetPointCount(), m_pPolys, op->GetPolygonCount(), NULL)) \>           return vNorm; \> \>      // Get polygons attached to point \>      neighbor.GetPointPolys(PointIndex, &pFaces;, &faceCnt;); \> \>      for(j=0; j<faceCnt; j++) \>     { \>         pNeighborPoly = &m;\_pPolys[pFaces[j]]; \> \>         // Compute face normal \>         v1 = m_pPoints[pNeighborPoly->b] - m_pPoints[pNeighborPoly->a]; \>         v2 = m_pPoints[pNeighborPoly->c] - m_pPoints[pNeighborPoly->a]; \>         vNorm += v1 % v2; // get cross-product \>     } \>      vNorm /= faceCnt; \> \>      // Return resuling normal vector \>      return !vNorm; \> } \>

    Thanks for any help!

    Greetings,
    Jack



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/08/2009 at 12:07, xxxxxxxx wrote:

    I'm just waking up, but your code "looks" ok (should be working), so I'm wondering if maybe the code that's asking for 'some' point's normal is maybe asking about the wrong point?  (or something).
    Anyway, for debugging purposes, I sometimes call a utility routine like this...
    >

    \>     
    \>     
    \>       
    \>     > //-------------------------------------------------------------  
    \>     > // AddSplineObject()  
    \>     > //-------------------------------------------------------------  
    \>     > void AddSplineObject(BaseDocument *pDoc, const String& name, const Vector& pos, const Vector& axis, const Real& length)  
    \>     > {  
    \>     >  SplineObject *pSpline = SplineObject::Alloc(2,Tlinear);  // allocate spline object  
    \>     >  if( pSpline )  
    \>     >  {  
    \>     >   if( pSpline->MakeVariableTag(Tsegment,1) )  
    \>     >   {  
    \>     >    Vector *padr = pSpline->GetPointW();  
    \>     >    Segment *sadr = pSpline->GetSegmentW();
    \>     
    \>     
    \>     
    \>     
    \>        sadr[0].cnt    = 2;  
    \>     >    sadr[0].closed = false;
    \>     
    \>     
    \>     
    \>     
    \>        padr[0] = pos;  
    \>     >    padr[1] = pos + (axis * length);
    \>     
    \>     
    \>     
    \>     
    \>        pSpline->SetName(name);  
    \>     >    pDoc->InsertObject(pSpline, NULL, NULL);  
    \>     >    pDoc->AddUndo(UNDO_NEW, pSpline);  
    \>     >    pSpline->Message(MSG_UPDATE);  
    \>     >   }  
    \>     >  }  
    \>     > }  
    \>     > 
    \> 
    \> 
    

    ...you can pass the point your asking about as the 'pos' arg, and the generated normal as the 'axis' arg and use 1 or 10 or whatever for the length.  It adds a spline object, showing a visual representation of the normal (be sure to either StartUndo() or comment out that AddUndo() line).



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/08/2009 at 12:16, xxxxxxxx wrote:

    ....in your code, I'd also change:

        
        
          
        vNorm = Vector(0,0,0);
        
        
        
        
        to
        
        
        
        
        vNorm = Vector(0,1,0); //default to pointing straight up  
        
    

    ...and also, after:

        
        
          
        // Get polygons attached to point  
             neighbor.GetPointPolys(PointIndex, &pFaces, &faceCnt);
        
        
        
        
        ...you should add:
        
        
        
        
             if( !faceCnt ) return vNorm;  
        
    

    ...to avoid any potential divide-by-zero below there.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/08/2009 at 12:24, xxxxxxxx wrote:

    Also keep in mind that if edge-breaks are involved, each vertex can have more than one Normal.  In other words, (vertex) Normals are really polygon-relative - none of the above code takes edge-breaks into account, so you end up with smoothed (averaged) normals at each vertex.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/08/2009 at 14:40, xxxxxxxx wrote:

    In this particular case I won't care about edge breaks. Each Vertex Normal should just be the average of it's surrounding polygons.

    But I HAVE to get the normal vectors by addressing them with the point index.

    My code is now like this.
    I pass an initialized Neighbor class object to the function, that speeds it up a lot. Anyway, the normals still point into totally stupid directions.

    Also I noticed, that faceCnt is always 3 for each point. But the geometry is (for testing) just a cube subdivided object, so there *should* be 4 faces for each point.

    > Vector GetVertexNormal(LONG PointIndex, PolygonObject \*op, Neighbor \*neighbor) \> { \>      // Variables \>      CPolygon \*pNeighborPoly; \>      LONG j, faceCnt, \*pFaces = NULL; \>      Vector v1, v2, vNorm = Vector(C_FLOATZERO, C_FLOATONE, C_FLOATZERO); \>      CPolygon \*m_pPolys = op->GetPolygonW(); \>      Vector \*m_pPoints = op->GetPointW(); \> \>      // Get polygons attached to point \>      neighbor->GetPointPolys(PointIndex, &pFaces;, &faceCnt;); \>      if(!faceCnt) return vNorm; \> \>      for(j=0; j<faceCnt; j++) \>     { \>         pNeighborPoly = &m;\_pPolys[pFaces[j]]; \> \>         // Compute face normal \>         v1 = m_pPoints[pNeighborPoly->b] - m_pPoints[pNeighborPoly->a]; \>         v2 = m_pPoints[pNeighborPoly->c] - m_pPoints[pNeighborPoly->a]; \>         vNorm += v1 % v2; // get cross-product \>     } \>      vNorm /= faceCnt; \> \>      // Return resuling normal vector \>      return !vNorm; \> }

    Greetings,
    Jack



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 05/08/2009 at 15:10, xxxxxxxx wrote:

    Oh wait. My mistake. Code works fine. If I modify the geometry while calculating an array of Vertex normals, it's no wonder they point into seemingly stupid directions.

    Thanks a thousand times!

    Cheers,
    Jack



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 06/08/2009 at 00:32, xxxxxxxx wrote:

    That looks ok to me. Just some stuff you can improve (taking triangles or quadrangles into account) :

    > \> for (j=0; j<faceCnt; j++) \> { \>      pNeighborPoly = &m;\_pPolys[pFaces[j]]; \>       \>      // Compute face normal \>      if (pNeighborPoly->c == pNeighborPoly->d)     //triangle \>      { \>           v1 = m_pPoints[pNeighborPoly->b] - m_pPoints[pNeighborPoly->a]; \>           v2 = m_pPoints[pNeighborPoly->c] - m_pPoints[pNeighborPoly->a]; \>      } \>      else     //quadrangle \>      { \>           v1 = m_pPoints[pNeighborPoly->c] - m_pPoints[pNeighborPoly->a]; \>           v2 = m_pPoints[pNeighborPoly->d] - m_pPoints[pNeighborPoly->b]; \>      } \>       \>      vNorm += v1 % v2; // get cross-product \> } \> \> //vNorm /= faceCnt; not necessary, normalizing is enough \> \> // Return resuling normal vector \> return !vNorm; \>

    cheers,
    Matthias


Log in to reply