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



  • 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!



  • 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



  • 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?



  • 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



  • Thanks I'll wait of course.
    Viktor



  • 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



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



  • 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



  • @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.