Ngon info



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

    On 10/04/2008 at 12:01, xxxxxxxx wrote:

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

    ---------
    This post is partially just an FYI, based on my findings as well as a general question (if any of it turns out to be false assumptions)...
    I've recently been re-writing the Ngon handling code in my Riptide plugin and the SDK documentation on Ngons tends to trip over it's own terminology in places on the subject (Ngon vs Pgon, etc.).  Anyway, I've done some exploring/experimenting and am posting my results here for future reference (some of this is a reposting from an earlier thread)...
    NgonBase::BuildngonFromPolys()
    I haven't gone back to study this, but had problems getting this to work at all in previous efforts.  In either case, since it requires that you pass in the 'outer' array (not optional - the docs are wrong), it does not appear to provide any benefit over NgonBase::Buildngon().

    NgonBase::Buildngon()
    Note that the documentation that reads "You can use the internal mark ( PGONEDGE_ENDSEGMENT ) to mark segments in the N-gon outline" should read: "... must use..." for it to function properly.   Also not mentioned is that you must also set the PGONEDGE_NOEDGES flag for any polys included in the ngon, but have no outer (or potentially inner) edges that make up the edge list.

    Determining an ordered list of vertex indices that make up an Ngon.
    This particular subject confounded me for quite some time, as I navigated about the various class and Modelling library references trying to find all the info I needed ( Pgon class from the PolygonObject::GetAndBuildNgon() vs Pgon class from NgonBase::GetNgons() vs Ngon class from Modeling::GetNgon() , various translation/mapping routines related to one or the other, etc.)
    In my earlier code, I ended up using PolygonObject::GetAndBuildNgon() along with both PolygonObject::GetPolygonTranslationMap() and PolygonObject::GetNGonTranslationMap()  along with some brute-force and whacky loop-control to work out the list of vertex indices, along with the Polygon info I needed for each (which polygon it was from, along with which a/b/c/d index it was).  This mostly worked, but unfortunately it was messy because you have to do extra work to determine the ordering and it didn't really provide a good way to know which index was the 'start' of the list (the index maps returned from the routines above don't have any implicit order, relatve to any particular Ngon indices).
    **
    Determining whether an Ngon has 'holes' in it.**
    I also needed some way to determine whether an Ngon was a singular outline of some set of polys or whether it had holes cut out of it's interior (no way that I know of to represent the later in a .obj file, in my case).
    Anyway, I still have to jump through a few hoops due to the nature of my needs, but below is some related information that I gleaned along the way...
    **
    Ngon class**
    It turns out that the Ngon class has at least some of the information I was looking for, but not all.  See the comments below...

        
        
          
        ----- S N I P -----
        
        
        
        
        PolygonObject *pObj = ToPoly(doc->GetActiveObject());
        
        
        if( !pObj )
        
        
        {
        
        
            GePrint("No Active Object");
        
        
            return false;
        
        
        }
        
        
        
        
        if( pObj->GetType() != Opolygon )
        
        
        {
        
        
            GePrint("Active Object not a PolygonObject");
        
        
            return false;
        
        
        }
        
        
        
        
        LONG numNgons = pObj->GetNgonCount();
        
        
        LONG numPolys = pObj->GetPolygonCount();
        
        
        
        
        AutoAlloc<Modeling> model;
        
        
        Ngon ngon;
        
        
        
        
        if (!model->InitObject(pObj))
        
        
        {
        
        
            GePrint("InitObject() failed");
        
        
            return false;
        
        
        }
        
        
        
        
        LONG i,j,k;
        
        
        for (i=0;i<numNgons;i++)
        
        
        {
        
        
        
        
            //-----------------------------------------------------------------
        
        
            // ngon indices start at polyObj->GetPolygonCount(), so we need to
        
        
            // add that offset when fetching...
        
        
            //-----------------------------------------------------------------
        
        
            if (!model->GetNgon(pObj,numPolys+i,&ngon))
        
        
            {
        
        
                GePrint("GetNgon() failed");
        
        
                return false;
        
        
            }
        
        
        
        
            //-----------------------------------------------------------------
        
        
            // ngon.count - tells you the total number of vertex indices that
        
        
            // make up the ngon
        
        
            //-----------------------------------------------------------------
        
        
            LONG numPts = ngon.count;
        
        
            GePrint("numPts = "+LongToString(numPts));
        
        
        
        
        
            //-----------------------------------------------------------------
        
        
            // ngon.segcount - tells you the total number of linked perimeter
        
        
            // lists those indices are divided into (either an outer perimeter,
        
        
            // or the perimeter around some 'hole' within the interior of the
        
        
            // ngon).
        
        
            //
        
        
            // So, for an ngon without any interior 'holes', the segcount will
        
        
            // be 1. For an ngon with holes, segcount will be 1+number_of_holes
        
        
            //-----------------------------------------------------------------
        
        
            LONG numSegs = ngon.segcount;
        
        
            GePrint("numSegs = "+LongToString(numSegs));
        
        
        
        
        
            //-----------------------------------------------------------------
        
        
            // for the purpose of testing, just build up and display the list
        
        
            // of indices that make up each ngon
        
        
            //-----------------------------------------------------------------
        
        
            String pStr = String("ngon (");
        
        
            for(j=0; j<numPts; j++)
        
        
            {
        
        
                if(j != 0)
        
        
                    pStr += ",";
        
        
        
        
                pStr += " "+LongToString(ngon.points[j]);
        
        
            }
        
        
            pStr += " )";
        
        
            GePrint(pStr);
        
        
        
        
            //-----------------------------------------------------------------
        
        
            // for the purpose of testing, also display the segment list...
        
        
            //-----------------------------------------------------------------
        
        
            pStr = String("segs [");
        
        
            for(k=0; k<numSegs; k++)
        
        
            {
        
        
                if(k != 0)
        
        
                    pStr += ",";
        
        
        
        
                pStr += " "+LongToString(ngon.segments[k]);
        
        
            }
        
        
            pStr += " ]";
        
        
            GePrint(pStr);
        
        
        }
        
        
        
        
        ----- S N I P -----  
        
    

    ...So, if you just need a list of vertex indices that make up an Ngon, you can do something similar to the above.  Note that you can also determine whether an Ngon has holes.  What the Ngon class doesn't provide is a method of accessing the actual segments/edges and polygons that make up the Ngon.  For that, you need something like the...
    **
    Pgon class**
    The Pgon class is primarily concerned with tracking and handling the list of polygon edges that make up an Ngon (and whether they are hidden/interior edges  or outer/perimeter edges, among other things).  Thankfully, the list of edges available appears to be in edge-order (starting with the first edge (first and second indices that make up the perimeter of the Ngon)), but it doesn't track the raw list of indices like the Ngon class does.
    You can also determine which polygons are being used, as well as whether the Ngon has holes in it...

        
        
          
        ----- S N I P -----
        
        
        
        
        PolygonObject *pObj = ToPoly(doc->GetActiveObject());
        
        
        if( !pObj )
        
        
        {
        
        
            GePrint("No Active Object");
        
        
            return false;
        
        
        }
        
        
        
        
        if( pObj->GetType() != Opolygon )
        
        
        {
        
        
            GePrint("Active Object not a PolygonObject");
        
        
            return false;
        
        
        }
        
        
        
        
        #ifdef _V96SDK_
        
        
        Pgon *pPgons = pObj->GetAndBuildNgon();
        
        
        #else
        
        
        Pgon *pPgons = pObj->GetNgon();
        
        
        #endif
        
        
        
        
        if( !pPgons )
        
        
        {
        
        
            GePrint("GetAndBuildNgon() failed");
        
        
            return false;
        
        
        }
        
        
        
        
        const CPolygon *pPolys = pObj->GetPolygonR();
        
        
        LONG numPolys = pObj->GetPolygonCount();
        
        
        LONG numNgons = pObj->GetNgonCount();
        
        
        
        
        LONG i,j,k;
        
        
        
        
        for (i=0;i<numNgons;i++)
        
        
        {
        
        
            PgonEdge *pEdge;
        
        
            LONG polyndx, edge, numEdges, numPoints;
        
        
        
        
            numPoints = pPgons[i].GetPointCount(); // number of vertex indices
        
        
            numEdges = pPgons[i].GetCount();
        
        
            GePrint("numEdges = "+LongToString(numEdges));
        
        
        
        
            //-----------------------------------------------------------------
        
        
            // Try to determine whether or not this Ngon has any 'holes' in it.
        
        
            // As far as I can tell, any Ngon with more than one edge with the
        
        
            // PGONEDGE_ENDSEGMENT flag set has a hole in it (at least, Ngons
        
        
            // that do have holes, WILL have more than one and so far, I haven't
        
            // found any that have more than one if they don't have holes).
        
        
            //-----------------------------------------------------------------
        
        
            LONG endSegs = 0;
        
        
            for(k=0; k<numEdges; k++)
        
        
            {
        
        
                //-----------------------------------------------------------------
        
        
                // Note that IsSegmentEnd() doesn't actually return a boolean true
        
        
                // or false (1 or 0) value as listed in the SDK docs (since it uses
        
        
                // a bit-mask, you get either 0 or 0x20000000)
        
        
                //-----------------------------------------------------------------
        
        
                endSegs += pPgons[i].IsSegmentEnd(k) ? 1 : 0;
        
        
            }
        
        
            GePrint("endSegs = "+LongToString(endSegs));
        
        
        
        
         
        
        
            //-----------------------------------------------------------------
        
        
            // the Pgon class doesn't keep the raw vertex list like the Ngon
        
        
            // class does, but we can build one if needed, based on the edge
        
        
            // indices...
        
        
            //
        
        
            // once we have the first vert index of the list, we just need to
        
        
            // add the trailing index of each edge (since the leading index
        
        
            // is the same as the trailing index of the preceeding edge pair).
        
        
            //
        
        
            // [NOTE: the code below doesn't really handle Ngons with holes in
        
        
            // them... in my case, I would bail out before getting here
        
        
            // if they were found]
        
        
            //-----------------------------------------------------------------
        
        
            LONG firstPt = false;
        
        
            LONG ptCnt = 0;
        
        
        
        
            pStr = String("Edges {");
        
        
            for(k=0; k<numEdges; k++)
        
        
            {
        
        
                // skip any interior polys that don't contribute outer edges
        
        
                if( !pPgons[i].State(k) )
        
        
                    continue;
        
        
        
        
                pEdge = pPgons[i].GetEdge(k);
        
        
                polyndx = pEdge->ID();
        
        
                edge = pEdge->Edge();
        
        
                switch(edge)
        
        
                {
        
        
                    case 0: // a..b
        
        
                        if( !firstPt ) 
        
        
                        {
        
        
                            firstPt = true;
        
        
                            pStr += " "+LongToString(pPolys[polyndx].a);
        
        
                            ptCnt++;
        
        
                        }
        
        
                        pStr += ", "+LongToString(pPolys[polyndx].b);
        
        
                        ptCnt++;
        
        
                        break;
        
        
        
        
                    case 1: // b..c
        
        
                        if( !firstPt )
        
        
                        {
        
        
                            firstPt = true;
        
        
                            pStr += " "+LongToString(pPolys[polyndx].b);
        
        
                            ptCnt++;
        
        
                        }
        
        
                        pStr += ", "+LongToString(pPolys[polyndx].c);
        
        
                        ptCnt++;
        
        
                        break;
        
        
        
        
                    case 2: // c..d
        
        
                        if( !firstPt )
        
        
                        {
        
        
                            firstPt = true;
        
        
                            pStr += " "+LongToString(pPolys[polyndx].c);
        
        
                            ptCnt++;
        
        
                        }
        
        
                        pStr += ", "+LongToString(pPolys[polyndx].d);
        
        
                        ptCnt++;
        
        
                        break;
        
        
        
        
                    case 3: // d..a
        
        
                        if( !firstPt )
        
        
                        {
        
        
                            firstPt = true;
        
        
                            pStr += " "+LongToString(pPolys[polyndx].d);
        
        
                            ptCnt++;
        
        
                        }
        
        
                        pStr += ", "+LongToString(pPolys[polyndx].a);
        
        
                        ptCnt++;
        
        
                        break;
        
        
                }
        
                if( ptCnt == numPoints )
        
        
                    break;
        
        
            }
        
        
            pStr += " }";
        
        
            GePrint(pStr);
        
        
        }
        
        
        
        
        ----- S N I P -----  
        
    

    ...(hopefully, all my formatting worked).
    Again, the above is just some from some results of my experimenting... the SDK recommends using the Modeling library whenever feasable for future compatibility, but it doesn't always fit the purpose at hand, so take the above with a grain of salt.
    Cheers,
    Keith



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

    On 11/04/2008 at 01:00, xxxxxxxx wrote:

    Keith, thanks for your effort to put this together, much appreciated.

    cheers,
    Matthias



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

    On 12/04/2008 at 11:56, xxxxxxxx wrote:

    Sure thing... extensions of that code are now working in my latest Riptide update, so I figured I'd (hopefully) save others some trouble.
    Needing to know the exact start->end order of the vertices that make up an Ngon (and/or correlating that information to the polygons/edges in question) is usually not required for many operations, but one of my goals was to preserve that order for .obj file import/export.
    Knowing if an Ngon has holes in it might be usefull information though (figuring out how to deal with those will depend on the situation and is left to the reader :) ).



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

    On 26/11/2010 at 08:24, xxxxxxxx wrote:

    Howdy,

    Thanks for the information, Keith.

    How would you build an Ngon when importing a file?

    Adios,
    Cactus Dan



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

    On 27/11/2010 at 08:08, xxxxxxxx wrote:

    Howdy,

    OK, I found an example in the SDK Docs in the Modeling library using CreateNgon().

    But now my question is how do you build the UV's for the ngons using the imported UV coordinates? The UVWStruct has a maximum or 4 elements.

    Adios,
    Cactus Dan



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

    On 27/11/2010 at 13:37, xxxxxxxx wrote:

    Howdy,

    OK, I think I've got it working now, but it's kind of a pain in the excitement.

    What I'm doing is storing an array of a structure for each ngon encountered on import that simply has the import polygon index and the created ngon index as member variables. Then after the mesh is constructed, cycle through the storage array to get and set the uv's.

    Unless there is a better way to do it that would allow me to set the uv's when the ngon is created?

    Adios,
    Cactus Dan



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

    On 02/12/2010 at 23:17, xxxxxxxx wrote:

    Hi Dan,

    Sorry for the delay - I'm just now seeing this thread again. Having said that, it sounds like you figured it out and it sounds like you're doing something similar to what I am doing...

    Due to the complexity/flexibility of how .obj files are Imported (and all the various options Riptide Pro offers), I tend to have many related structures that store all sorts of info about the polygons/ngons being read in (grouping info, material info, UV/Normal info for the verts, etc)... so it's just a matter of digging around to find the right info within my internal structures when I go to set them up in the scene ;).

    I'm also using the moddeling library to set up the Ngons (ie. instead of quad/triangulating the Ngon by hand, I wrote some code to get the moddeling library do it for me - it does a better job than I could). When I encounter an Ngon in the .obj file, I set up a verts array and call that routine to generate a PolygonObject that contains the single Ngon (made up of multiple quads and/or triangles). It looks like this:

      
    //------------------------------------------------------------------------------------------------------   
    //******************************************************************************************************   
    // ObjLoader::NgPolygonize(LONG vCount, Vector *pVerts)   
    //******************************************************************************************************   
    //------------------------------------------------------------------------------------------------------   
    PolygonObject *ObjLoader::NgPolygonize(LONG vCount, Vector *pVerts)   
    {   
         PolygonObject *pObj;   
         LONG *padr;   
         LONG i;   
      
         pObj = PolygonObject::Alloc(0,0);     // start with an empty PolygonObject   
         if (!pObj) return NULL;   
      
         AutoAlloc<Modeling> mod;   
         if (!mod || !mod->InitObject(pObj))   
         {   
              PolygonObject::Free(pObj);   
              return NULL;   
         }   
      
         //---------------------------------------------------------------------   
         // we're only creating a single Ngon, so/also assume that the order is   
         // already established in the vertex ordering (caller is responsible   
         // for any index re-mapping)   
         //---------------------------------------------------------------------   
         padr = (LONG * )GeAlloc(vCount * sizeof(LONG));   
         for(i=0; i<vCount; i++)   
         {   
              padr[i] = mod->AddPoint(pObj, pVerts[i]);   
              if( !padr[i] )   
              {   
                   GeFree(padr);   
                   PolygonObject::Free(pObj);   
                   return NULL;   
              }   
         }   
      
         // create the Ngon   
         i = mod->CreateNgon(pObj, padr, vCount);   
         GeFree(padr);                                   // done with this now   
         if (!i)   
         {   
              PolygonObject::Free(pObj);   
              return NULL;   
         }   
      
         // Commit the changes   
         if (!mod->Commit())   
         {   
              PolygonObject::Free(pObj);   
              return NULL;   
         }   
      
         return pObj;     // return the PolygonObject - caller must free it   
    }   
    

    ...and then I parse that back into my own internal structures (adding the new triangles/quads) before adding the entire mesh to the scene. My internal structures still carry the UV, Grouping, Material selection and other info related to the polygons/Ngons.



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

    On 03/12/2010 at 07:00, xxxxxxxx wrote:

    Howdy,

    Thanks Keith.

    The way I'm handling the ngons is in the same loop that's creating the polygons. What I did is first create a function that get's the valid polygon count (less than 5 points) :

    static LONG GetValidPolygonCount(KFbxMesh *pMesh, LONG pCnt)
    {
         LONG i, cnt = 0;
    	
         for(i=0; i<pCnt; i++)
         {
              LONG pSize = pMesh->GetPolygonSize(i);
              if(pSize < 5) cnt++;
         }
    	
         return cnt;
    }
    

    ... then in my mesh import function I created the polygon object and resized it like this:

    LONG cpCnt = GetValidPolygonCount(pMesh,kpCnt);
    ToPoly(mesh)->ResizeObject(kvCnt, cpCnt);
    

    That way I could handle the polygons and ngons in the same loop by incrementing a plygon index for the polygons, and letting the modeling library handle the ngons:

    AutoAlloc<Modeling> mod;
    if(!mod || !mod->InitObject(mesh)) return mesh;
      
    LONG plyInd = 0;
    for(i=0; i<kpCnt; i++)
    {
         LONG pSize = pMesh->GetPolygonSize(i);
    	
         if(pSize > 4)
         {
              //....handle ngons
         }
         else
         {
              if(pSize == 4 && plyInd < cpCnt)
              {
                   //....handle quad's
              }
              else if(pSize == 3 && plyInd < cpCnt)
              {
                   //....handle tri's
              }
              plyInd++;
         }
    }
    if(mod) mod->Commit(mesh);
    

    Adios,
    Cactus Dan


Log in to reply