Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Register
    • Login

    Creating NGONs low level with NgonBase::BuildNgon() ?

    Cinema 4D SDK
    r23 sdk c++
    3
    8
    1.2k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • WTools3DW
      WTools3D
      last edited by WTools3D

      I spent a week trying to rewrite our plugin to valid ngons.

      I have Mesh already triangulated, uv mapped, I only need to add ngons for existing triangles.

      Modeling class doesn't have an option to create ngons low level with already existing triangles.
      NgonBase::BuildNgonFromPolys deosn't create holes therefore is useless. Also it is incorrectly documented!
      NgonBase::BuildNgon is creating valid ngons but only for specific simple cases. Also insufficiently documented.

      I did a brute force of testing all possible combinations how to use NgonBase::BuildNgon(), but it would be much easier if documentation would be a bit more comprehensive.

      I am setting the both inner and outer arrays. Simple cases works, but problems starts when ngon has a two and more holes.
      In this case, there are the triangles with all three edges hidden .
      This seems to be a problem, even if ngon is created, and it passes the validation check, but the result is still broken.

      I can post simple examples which are giving errors, but before I do, please let me know is BuildNgon function really works with arbitrary triangles and multiple holes cases?

      Thanks!

      1 Reply Last reply Reply Quote 0
      • WTools3DW
        WTools3D
        last edited by

        Hey guys.
        I spent almost two weeks to get stuck with this.
        I would really appreciate any reply to this problem.

        Just need an answer to these questions:

        1. Is NgonBase::BuildNgon() function intended to use for creating ngons with holes from the triangles ?
        2. If yes, are there any restrictions or rules for triangles provided ?

        Please anybody, can you give me a hint?

        1 Reply Last reply Reply Quote 0
        • ferdinandF
          ferdinand
          last edited by ferdinand

          Hi @WTools3D,

          thank you for reaching out to us and please accept our sincere apologies for the delay, but we have currently internally "a lot on our plate", so everything is a bit slower than usually. We have seen and discussed your question yesterday. Your question has been assigned and will be processed, but it might take a few work days, since we have first to assert some things ourselves.

          Thank you for your understanding,
          Ferdinand

          MAXON SDK Specialist
          developers.maxon.net

          1 Reply Last reply Reply Quote 0
          • WTools3DW
            WTools3D
            last edited by

            Thanks I'll wait of course.
            Viktor

            1 Reply Last reply Reply Quote 0
            • ManuelM
              Manuel
              last edited by

              hello,

              sorry for the delay of this answer. As you said, the documentation isn't really valuable.
              Those functions are not used a lot in our code. Most of the time, Ngon are created using the modeling kernel.

              As you can find in the forum already BuildNgonFromPolys seems to not support holes.
              and BuildNgon is not a friendly function. (to say the least)

              I've asked our tech team some help. (this is pretty old code also ^^)

              11 November is a bank holiday in France so i'll not work tomorrow. But be right back after ๐Ÿ™‚

              Cheers,
              Manuel

              MAXON SDK Specialist

              MAXON Registered Developer

              WTools3DW 1 Reply Last reply Reply Quote 0
              • WTools3DW
                WTools3D @Manuel
                last edited by

                @m_magalhaes
                I'll wait for what you get from your team.
                Thanks Manuel!

                1 Reply Last reply Reply Quote 0
                • ManuelM
                  Manuel
                  last edited by

                  Hello,

                  So we came to the conclusion that, holes are not supported by BuildNgonFromPolys

                  About BuildNgon the thing i missed is that the Inner edge index must be set twice. For the two polygons, the edge is part of. where
                  edgeIndex = 4 * polygonIndex + cpolygonSideIndex

                  Below is the code for a command that will remove all edge on a mesh (of course try it on a plane for example)
                  It's working with holes of course

                  static maxon::Result<void> PC23992_cmd(BaseDocument* doc)
                  {
                  	iferr_scope;
                  	
                  	CheckArgument(doc != nullptr);
                  		
                  	BaseObject* op = doc->GetFirstObject();
                  	if (op == nullptr)
                  	{
                  		return maxon::NullptrError(MAXON_SOURCE_LOCATION);
                  	}
                  
                  	PolygonObject* polyOp = ToPoly(op);
                  
                  	polyOp->GetAndBuildNgon();
                  	NgonBase* pNgons = polyOp->GetNgonBase();
                  
                  	if (pNgons == nullptr)
                  		return maxon::NullptrError(MAXON_SOURCE_LOCATION);
                  
                  	const CPolygon* polys = polyOp->GetPolygonR();
                  	const Vector* points = polyOp->GetPointR();
                  	Int32 polyCount = polyOp->GetPolygonCount();
                  	maxon::BaseArray<Int32> innerEdges;
                  	using UndirectedEdge = maxon::Pair<Int32, Int32>; // first index in the pair must always be the smaller in between the 2 indices
                  	maxon::HashMap<UndirectedEdge, Int32> undirectedEdges;
                  
                  	// in this case traverse all the polygons
                  	for (Int32 polygonIndex = 0; polygonIndex < polyCount; ++polygonIndex)
                  	{
                  		for (Int32 side = 0; side < 4; ++side) // we collect edges which are used more the once (only inner edges)
                  		{
                  			Int32 pA = NOTOK;
                  			Int32 pB = NOTOK;
                  
                  			polys[polygonIndex].EdgePoints(side, pA, pB); // get edge points
                  
                  			if (polys[polygonIndex].IsTriangle() && side == 2) // skip the invalid edge of triangles
                  				continue;
                  
                  			Int32 edgeIndex = polygonIndex * 4 + side; // build the unique edge index
                  
                  			Bool newInsert = false;
                  
                  			// we check if the point pair is already stored in the hash map, if it is then it means the edge is not on the boundary of the mesh or an hole edge
                  			Int32& entry = undirectedEdges.InsertKey(UndirectedEdge(Min(pA, pB), Max(pA, pB)), newInsert) iferr_return;
                  			if (newInsert)
                  			{
                  				entry = edgeIndex;
                  			}
                  			else
                  			{
                  				innerEdges.Append(edgeIndex) iferr_return;
                  				innerEdges.Append(entry) iferr_return;
                  			}
                  		}
                  	}
                  
                  	if (innerEdges.IsEmpty())
                  		return maxon::OK;
                  
                  	// we pass inner edges to BuildNgon
                  	pNgons->BuildNgon(innerEdges.GetFirst(), nullptr, (Int32)innerEdges.GetCount(), 0, polys, points);
                  	pNgons->InitMap();
                  	pNgons->SetFlags(NGON_FLAG_SETASVALID);
                  	polyOp->Message(MSG_UPDATE);
                  	EventAdd();
                  
                  	return maxon::OK;
                  }
                  
                  

                  I've created a code that will create two mesh, one using BuildNgon and the other one BuildNgonFromPolys

                  
                  static maxon::Result<void> PC12992(BaseDocument* doc)
                  {
                  	iferr_scope;
                  
                  	const Int32 pcnt = 12;
                  	const Int32 polyCnt = 14;
                  
                  	PolygonObject* obj1 = PolygonObject::Alloc(pcnt, polyCnt);
                  
                  	maxon::BaseArray<Vector> parray;
                  	parray.EnsureCapacity(pcnt) iferr_return;
                  
                  
                  
                  	parray.Append(Vector(-200, 0, -200)) iferr_return;
                  	parray.Append(Vector(200, 0, -200)) iferr_return;
                  	parray.Append(Vector(-200, 0, 200)) iferr_return;
                  	parray.Append(Vector(200, 0, 200)) iferr_return;
                  	parray.Append(Vector(-64.498, 0, -31.85)) iferr_return;
                  	parray.Append(Vector(0.287, 0, -138.37)) iferr_return;
                  	parray.Append(Vector(46.688, 0, 91.877)) iferr_return;
                  	parray.Append(Vector(57.903, 0, -29.617)) iferr_return;
                  	parray.Append(Vector(9.65, 0, 84.86)) iferr_return;
                  	parray.Append(Vector(-16.551, 0, -30.975)) iferr_return;
                  	parray.Append(Vector(-48.354, 0, -137.194)) iferr_return;
                  	parray.Append(Vector(4.068, 0, -11.035)) iferr_return;
                  	
                  
                  	Vector* padr = obj1->GetPointW();
                  	CPolygon* vadr = obj1->GetPolygonW();
                  
                  	if (vadr == nullptr || padr == nullptr)
                  		return maxon::NullptrError(MAXON_SOURCE_LOCATION);
                  
                  	if (parray.GetCount() != pcnt)
                  		return maxon::UnknownError(MAXON_SOURCE_LOCATION);
                  	// Sets points position
                  	for (Int32 i = 0; i < pcnt; i++)
                  	{
                  		padr[i] = parray[i];
                  	}
                  
                  	// Defines the polygons
                  	CPolygon poly;
                  
                  	poly.a = 2;	poly.b = 4;	poly.c = 0;	poly.d = poly.c;
                  	vadr[0] = poly;
                  
                  	poly.a = 2;	poly.b = 6;	poly.c = 8;	 poly.d = poly.c;
                  	vadr[1] = poly;
                  
                  	poly.a = 7;	poly.b = 3;	poly.c = 1;	poly.d = poly.c;
                  	vadr[2] = poly;
                  
                  	poly.a = 0;	poly.b = 5;	poly.c = 1;	poly.d = poly.c;
                  	vadr[3] = poly;
                  
                  	poly.a = 8;	poly.b = 9;	poly.c = 4; poly.d = poly.c;
                  	vadr[4] = poly;
                  
                  	poly.a = 0;	poly.b = 4;	poly.c = 10; poly.d = poly.c;
                  	vadr[5] = poly;
                  
                  	poly.a = 9;	poly.b = 1;	poly.c = 5; poly.d = poly.c;
                  	vadr[6] = poly;
                  
                  	poly.a = 10;	poly.b = 5;	poly.c = 0; poly.d = poly.c;
                  	vadr[7] = poly;
                  
                  	poly.a = 9;	poly.b = 7;	poly.c = 1; poly.d = poly.c;
                  	vadr[8] = poly;
                  
                  	poly.a = 3;	poly.b = 7;	poly.c = 6; poly.d = poly.c;
                  	vadr[9] = poly;
                  
                  	poly.a = 6;	poly.b = 2;	poly.c = 3; poly.d = poly.c;
                  	vadr[10] = poly;
                  
                  	poly.a = 4;	poly.b = 2;	poly.c = 8; poly.d = poly.c;
                  	vadr[11] = poly;
                  
                  	poly.a = 8;	poly.b = 11;	poly.c = 9; poly.d = poly.c;
                  	vadr[12] = poly;
                  
                  	poly.a = 7;	poly.b = 9;	poly.c = 11; poly.d = poly.c;
                  	vadr[13] = poly;
                  
                  
                  
                  	
                  	// Creates the ngons for object 1 
                  
                  	maxon::BaseArray<Int32> inner;
                  	
                  	// edge between poly 0 and 11 
                  	inner.Append(0) iferr_return; // edge index in poly 0
                  	inner.Append(44) iferr_return; // edge index inpoly 11
                  
                  	// edge between poly 11 and 1
                  	inner.Append(7) iferr_return; // edge index in poly 1
                  	inner.Append(45) iferr_return; // edge index in poly 11
                  
                  	NgonBase* pNgonsObj1;
                  
                  	obj1->GetAndBuildNgon();	// ensure ngonbase is ready
                  	pNgonsObj1 = obj1->GetNgonBase();
                  	
                  	if (pNgonsObj1)
                  	{
                  		Int32 ngonIndex = pNgonsObj1->BuildNgon(inner.GetFirst(), nullptr, inner.GetCount(), 0, obj1->GetPolygonR(), obj1->GetPointR());
                  		//Int32 ngonIndex = pNgonsObj1->BuildNgon(inner.GetFirst(), nullptr, inner.GetCount(), 0, obj1->GetPolygonR(), obj1->GetPointR());
                  		if (ngonIndex == -1)
                  			DebugStop();
                  
                  	}
                  
                  	pNgonsObj1->InitMap();	// rebuild the internal n-gon table data
                  	pNgonsObj1->SetFlags(NGON_FLAG_NOVALIDATION);
                  	
                  
                  	//--------------------   obj 2   ---------------------------------------------------------
                  	
                  	/*
                  	* vertices created
                  	2 ------ 3
                  	|				 |
                  	|				 4
                  	|				 |
                  	0 -----  1
                  	*/
                  
                  	const Int32 pcnt2 = 5;
                  	const Int32 polyCnt2 = 2;
                  	PolygonObject* obj2 = PolygonObject::Alloc(pcnt2, polyCnt2);
                  
                  	maxon::BaseArray<Vector> parray2;
                  	parray2.EnsureCapacity(pcnt2) iferr_return;
                  
                  	parray2.Append(Vector(-200, 0, -200)) iferr_return;
                  	parray2.Append(Vector(200, 0, -200)) iferr_return;
                  	parray2.Append(Vector(-200, 0, 200)) iferr_return;
                  	parray2.Append(Vector(200, 0, 200)) iferr_return;
                  	parray2.Append(Vector(250, 0, 140)) iferr_return;
                  				
                  	padr = obj2->GetPointW();
                  	vadr = obj2->GetPolygonW();
                  
                  	if (vadr == nullptr || padr == nullptr)
                  		return maxon::NullptrError(MAXON_SOURCE_LOCATION);
                  
                  	if (parray2.GetCount() != pcnt2)
                  		return maxon::UnknownError(MAXON_SOURCE_LOCATION);
                  	// Sets points position
                  	for (Int32 i = 0; i < pcnt2; i++)
                  	{
                  		padr[i] = parray2[i];
                  	}
                  
                  	poly.a = 0;	poly.b = 4;	poly.c = 1;	poly.d = poly.c;
                  	vadr[0] = poly;
                  
                  	poly.a = 0;	poly.b = 2;	poly.c = 3;	 poly.d = 4;
                  	vadr[1] = poly;
                  
                  
                  
                  
                  	// Creates ngons for object 2
                  
                  	NgonBase* pNgonsObj2;
                  
                  
                  	obj2->GetAndBuildNgon();	// ensure ngonbase is ready
                  	pNgonsObj2 = obj2->GetNgonBase();
                  	
                  	maxon::BaseArray<Int32> polyIdx;
                  	maxon::BaseArray<Int32> verticesIdx;
                  	
                  	polyIdx.EnsureCapacity(polyCnt2) iferr_return;
                  	verticesIdx.EnsureCapacity(5) iferr_return;
                  	
                  	polyIdx.Append(0) iferr_return;
                  	polyIdx.Append(1) iferr_return;
                  
                  	// Must respect the same order as the polygons (clock or counter clock)
                  	verticesIdx.Append(0) iferr_return;
                  	verticesIdx.Append(2) iferr_return;
                  	verticesIdx.Append(3) iferr_return;
                  	verticesIdx.Append(4) iferr_return;
                  	verticesIdx.Append(1) iferr_return;
                  							
                  				
                  	if (pNgonsObj2)
                  	{
                  		Int32 ngonIndex2 = pNgonsObj2->BuildNgonFromPolys(polyIdx.GetFirst(), verticesIdx.GetFirst(), polyIdx.GetCount(), verticesIdx.GetCount(), obj2->GetPolygonR(), obj2->GetPointR());
                  		if (ngonIndex2 == -1)
                  			DebugStop();
                  
                  	}
                  
                  	pNgonsObj2->InitMap();	// rebuild the internal n-gon table data
                  	pNgonsObj2->SetFlags(NGON_FLAG_NOVALIDATION);
                  
                  
                  
                  
                  	// Inserts object inside the document
                  	doc->InsertObject(obj1, nullptr, nullptr);
                  	doc->InsertObject(obj2, nullptr, nullptr);
                  	obj2->SetRelPos(Vector(0, 250, 0));
                  		
                  
                  	obj1->Message(MSG_UPDATE);
                  	obj2->Message(MSG_UPDATE);
                  	EventAdd();
                  
                  	return maxon::OK;
                  }
                  
                  

                  Cheers,
                  Manuel

                  MAXON SDK Specialist

                  MAXON Registered Developer

                  WTools3DW 1 Reply Last reply Reply Quote 0
                  • WTools3DW
                    WTools3D @Manuel
                    last edited by WTools3D

                    @m_magalhaes
                    Thanks Manuel!

                    When I fill-in only Inner edges in NgonBase::BuildNgon, then it surprisingly works ๐Ÿ˜‰
                    I don't know why I tested all combinations except this one ๐Ÿ™‚
                    There are still some minor glitches, some inner edges are randomly not hidden, but ngons created are valid for all the cases I have tested.
                    Random glitches are easily fixable with forcing all inner edges to be hidden.

                    I will test it more, but this seems to be reliable for now.
                    NGONS import - read outer edges directly from Pgon::*m_Edge array
                    NGONS export - with NgonBase::BuildNgon(), filling Inner edges only, and force hide inner edges on quads.

                    Thanks a lot Manuel,
                    Your help has been very valuable to me.

                    Regards,
                    Viktor.

                    1 Reply Last reply Reply Quote 1
                    • First post
                      Last post