BIM Coordinator Program (INT) April 22, 2024

Find the next step in your career as a Graphisoft Certified BIM Coordinator!

Archicad C++ API
About Archicad add-on development using the C++ API.

Using the ModelerAPI

Anonymous
Not applicable
Can anybody give me some pointers about the ModelerAPI api?

I've started experimenting, with the given ModelAccess example addon and read all the info (not much sadly) on this forum about the topic, but no success. The code below seems to be working, at least it doesn't crash archicad, but, the GetElementCount() is always 0, no matter what view is currently open, or how many elements were placed.
	GSErrCode  err;

	void* newSightPtr;
	err = ACAPI_3D_CreateSight(&newSightPtr);

	Modeler::SightPtr * sight = reinterpret_cast<Modeler::SightPtr*>(&newSightPtr);
	ModelerAPI::Model model;
	AttributeReader attrReader;

	// This seems to be working
	DBPrintf("sight name is: %s", sight->Get()->GetName().ToCStr().Get());
	Modeler::SunSettings sun = sight->Get()->GetSunSettings();

	err = EXPGetModel(sight->Get()->GetConstMainModelPtr(), &model, &attrReader);
	//err = EXPGetModel(*sight, &model, &attrReader);
	// Both seem to work but on the next line the count is always 0.
	int cnt = model.GetElementCount();

	ModelerAPI::Element elem;

        // strange indexing from the example
	for (int i = 1; i <= cnt; i++) {
		model.GetElement(i, &elem);

		DBPrintf("model guid is %s", model.GetGuid().ToUniString().ToCStr().Get());
		DBPrintf("elem guid is %s", elem.GetElemGuid().ToUniString().ToCStr().Get());
		DBPrintf("Succesfully got element");
	}
	DBPrintf("Yaay No Segfault");
	ACAPI_3D_DeleteSight(newSightPtr);
My ultimate goal would be getting the currently selected element/elements 3d information, but I've yet to find any crossover from the GUID oriented C api to this heavily object oriented c++ world.
Edit: title, naming
6 REPLIES 6
Ralph Wessel
Mentor
Try making a few changes - I don't have the resources available to check this now, so I'm writing this off the top of my head. You may have to tweak this slightly:

Modeler::SightPtr sight;
err = ACAPI_3D_CreateSight((void**) &sight);
ModelerAPI::Model model;
AttributeReader attrReader;
err = EXPGetModel(sight, &model, &attrReader);
Ralph Wessel BArch
Anonymous
Not applicable
Thanks Ralph, will be my first task to check in the morning!
Anonymous
Not applicable
So I found out that the error was with the
ACAPI_3D_CreateSight 
function. If I'm using the
ACAPI_3D_GetCurrentWindowSight 
to get a sight object, and the perspective view is on top, it works. If I had to guess, the sight is an empty object without the database.
But this raises the question, that if the 3D manager - (The ACAPI_3D_* functions) is obsolete:
How to get the same functionality from the ModelerAPI? How to get a 3D information of an element by guid?
I got the impression that it is not quite efficient to iterate through the entire database and checking every type, then, if the type matches, checking the guids too. (Maybe ACAPI_Element_Get3DInfo does that in the background?)
Also, there is a concern that this only works on a 3D view. Suppose that the 3D information is needed in a GDL element, which is linked to an other element, and a change is made in the 2D view, the modified elements 3D info couldn't be retrieved until a 3D view is opened.
How to get a sight, or what is the entry point to the 3D database from an arbitrary view?

Nevertheless I've managed to dissect an element's 3D info and understand what is what with the following code:
GSErrCode  err;

	//Modeler::SightPtr sight;
	//err = ACAPI_3D_CreateSight((void**)&sight);
	// err = ACAPI_3D_GetCurrentWindowSight((void**)&sight);

	void* newSightPtr;
	err = ACAPI_3D_GetCurrentWindowSight(&newSightPtr);

	Modeler::SightPtr * sight = reinterpret_cast<Modeler::SightPtr*>(&newSightPtr);

	if (err == NoError) {
		//sight.Get()->Attach(*this);

		ModelerAPI::Model model;
		AttributeReader attrReader;
		AttributeReader atr();
		// This seems to be working
		DBPrintf("sight name is: %s\n", sight->Get()->GetName().ToCStr().Get());
		Modeler::SunSettings sun = sight->Get()->GetSunSettings();

		err = EXPGetModel(sight->Get()->GetConstMainModelPtr(), &model, &attrReader);
		//err = EXPGetModel(*sight, &model, &attrReader);
		// Both seem to work but on the next line the count is always 0.

		DBPrintf("model guid is %s\n", model.GetGuid().ToUniString().ToCStr().Get());
		int cnt = model.GetElementCount();

		ModelerAPI::MeshBody	body;
		ModelerAPI::Element		elem;
		for (int i = 1; i <= cnt; i++) {
			model.GetElement(i, &elem);

			// continue if not interested guid.
			if (!(guids->Contains(GSGuid2APIGuid(elem.GetElemGuid()))))
				continue;
				// Take a look on EdgeAttributes class.
			int nBodies = elem.GetMeshBodyCount();

			for (Int32 iBody = 1; iBody <= nBodies; iBody++) { // Bodies

				elem.GetMeshBody(iBody, &body);
				bool wire = body.IsWireBody();
				bool mesh = body.IsVisibleIfContour(); // if this is true this might be a contur poly.
				bool closed = body.IsClosed();
				bool solid = body.IsSolidBody();

				DBPrintf("These are all the vertices of %s \n", elem.GetElemGuid().ToUniString().ToCStr().Get());
				Int32 vertexCnt = body.GetVertexCount();
				GS::Array<ModelerAPI::Vertex> vertexContainer;
				for (Int32 iVertex = 1; iVertex <= vertexCnt; iVertex++) {
					ModelerAPI::Vertex vtx;
					body.GetVertex(iVertex, &vtx);
					vertexContainer.Push(vtx);
					DBPrintf("Body vertex idx: %d [%2.1f %2.1f %2.1f] \n", iVertex, vtx.x, vtx.y, vtx.z);
				}

				DBPrintf("These are all the edges of %s \n", elem.GetElemGuid().ToUniString().ToCStr().Get());

				Int32 edgeCnt = body.GetEdgeCount();
				GS::Array<ModelerAPI::Edge> edgeContainer;
				for (Int32 iEdge = 1; iEdge <= edgeCnt; iEdge++) {
					ModelerAPI::Edge edge;
					body.GetEdge(iEdge, &edge);
					Int32 idV1 = edge.GetVertexIndex1();
					Int32 idV2 = edge.GetVertexIndex2();
					DBPrintf("Body edge nr.:%d between vertices %d - %d \n", iEdge, idV1, idV2);
					DBPrintf("Body edge nr.:%d between [%2.1f %2.1f %2.1f]-[%2.1f %2.1f %2.1f] \n",
						iEdge,
						vertexContainer[idV1-1].x, vertexContainer[idV1-1].y, vertexContainer[idV1-1].z,
						vertexContainer[idV2-1].x, vertexContainer[idV2-1].y, vertexContainer[idV2-1].z
					);
					Int32 idP1 = edge.GetPolygonIndex1();
					Int32 idP2 = edge.GetPolygonIndex2();
					DBPrintf("Body edge nr.:%d related to polygons %d - %d \n", iEdge, idP1, idP2);
					bool cont = edge.IsVisibleIfContour();
					DBPrintf("Is edge[%d] visible %s \n", iEdge, cont ? "true" : "false" );
					edgeContainer.Push(edge);
				}

				Int32 polCnt = body.GetPolygonCount();
				DBPrintf("These are all the Polygons of %s\n", elem.GetElemGuid().ToUniString().ToCStr().Get());
				ModelerAPI::Polygon poly;
				for (Int32 iPoly = 1; iPoly <= polCnt; iPoly++) {
					body.GetPolygon(iPoly, &poly);
					Int32 polyEdge = poly.GetEdgeCount();
					for (Int32 iPEdge = 1; iPEdge <= polyEdge; iPEdge++) {
						Int32 edgeIdx = poly.GetEdgeIndex(iPEdge);
						DBPrintf("Edges in this [%d] pgon idx:%d \n", iPoly, edgeIdx);
					}
					
					for (Int32 iPVec = 1; iPVec <= polyEdge; iPVec++) {
						Int32 vecIdx = poly.GetVertexIndex(iPVec);
						DBPrintf("This pgon[%d] consists of vertex[%d] which is globally [%d] \n", iPoly, iPVec, vecIdx);

						// 1. Seems to be returning the normal to this pgon by vertex which is good - 
                        // 2. unreliable if there is a hole in the poly - crashes archicad
						// 3. if I'm using the first vertex in each pgon its working?
						ModelerAPI::Vector nVec = poly.GetNormalVectorByVertex(1, ModelerAPI::CoordinateSystem::ElemLocal);
 						DBPrintf("T[%2.1f %2.1f %2.1f] \n", nVec.x, nVec.y, nVec.z);
					}

					Int32 polvecCnt = body.GetPolygonVectorCount(); // Dunno what is this.
					ModelerAPI::Vector bodyVec;
					for (Int32 iVec = 1; iVec <= polvecCnt; iVec++) {
						body.GetVector(iVec, &bodyVec, ModelerAPI::CoordinateSystem::ElemLocal);
						DBPrintf("Body vector idx: %d [%2.1f %2.1f %2.1f] \n", iVec, bodyVec.x, bodyVec.y, bodyVec.z);
					}

				}
			} // Bodies
			ModelerAPI::Transformation tran = elem.GetElemLocalToWorldTransformation();
			DBPrintf("TranMat\n[%2.1f][%2.1f][%2.1f]\n[%2.1f][%2.1f][%2.1f]\n[%2.1f][%2.1f][%2.1f]\n[%2.1f][%2.1f][%2.1f]\n",
				tran.matrix[0][0], tran.matrix[0][1], tran.matrix[0][2],
				tran.matrix[1][0], tran.matrix[1][1], tran.matrix[1][2],
				tran.matrix[2][0], tran.matrix[2][1], tran.matrix[2][2],
				tran.matrix[3][0], tran.matrix[3][1], tran.matrix[3][2]);
		}
	}
	else
		DBPrintf(ErrID_To_Name(err));


	if (newSightPtr != nullptr)
		ACAPI_3D_DeleteSight(newSightPtr);
This output was made for a simple, non-composit wall with 1m length 3m height and 3cm thickness placed in the origo.
An earlier version of the above snippet produced it, but the logic is the same as the above:
Body vertex idx: 1 [1.0 0.3 0.0] 
These are all the vertices of the body
Body vertex idx: 2 [1.0 0.0 0.0] 
Body vertex idx: 3 [0.0 0.0 0.0] 
Body vertex idx: 4 [0.0 0.3 0.0] 
Body vertex idx: 5 [1.0 0.3 3.0] 
Body vertex idx: 6 [0.0 0.3 3.0] 
Body vertex idx: 7 [1.0 0.0 3.0] 
Body vertex idx: 8 [0.0 0.0 3.0] 

These are all the edges of the body
Body edge nr.:1 between vertices 1 - 2 
Body edge nr.:1 between [1.0 0.3 0.0]-[1.0 0.0 0.0] 
                Body edge nr.:1 related to polygons 3 - 1 
Body edge nr.:2 between vertices 2 - 3 
Body edge nr.:2 between [1.0 0.0 0.0]-[0.0 0.0 0.0] 
                Body edge nr.:2 related to polygons 4 - 1 
Body edge nr.:3 between vertices 4 - 3 
Body edge nr.:3 between [0.0 0.3 0.0]-[0.0 0.0 0.0] 
                Body edge nr.:3 related to polygons 1 - 5 
Body edge nr.:4 between vertices 1 - 4 
Body edge nr.:4 between [1.0 0.3 0.0]-[0.0 0.3 0.0] 
                Body edge nr.:4 related to polygons 1 - 2 
Body edge nr.:5 between vertices 5 - 1 
Body edge nr.:5 between [1.0 0.3 3.0]-[1.0 0.3 0.0] 
                Body edge nr.:5 related to polygons 3 - 2 
Body edge nr.:6 between vertices 6 - 4 
Body edge nr.:6 between [0.0 0.3 3.0]-[0.0 0.3 0.0] 
                Body edge nr.:6 related to polygons 2 - 5 
Body edge nr.:7 between vertices 5 - 6 
Body edge nr.:7 between [1.0 0.3 3.0]-[0.0 0.3 3.0] 
                Body edge nr.:7 related to polygons 2 - 6 
Body edge nr.:8 between vertices 7 - 5 
Body edge nr.:8 between [1.0 0.0 3.0]-[1.0 0.3 3.0] 
                Body edge nr.:8 related to polygons 3 - 6 
Body edge nr.:9 between vertices 2 - 7 
Body edge nr.:9 between [1.0 0.0 0.0]-[1.0 0.0 3.0] 
                Body edge nr.:9 related to polygons 3 - 4 
Body edge nr.:10 between vertices 7 - 8 
Body edge nr.:10 between [1.0 0.0 3.0]-[0.0 0.0 3.0] 
                Body edge nr.:10 related to polygons 6 - 4 
Body edge nr.:11 between vertices 3 - 8 
Body edge nr.:11 between [0.0 0.0 0.0]-[0.0 0.0 3.0] 
                Body edge nr.:11 related to polygons 4 - 5 
Body edge nr.:12 between vertices 8 - 6 
Body edge nr.:12 between [0.0 0.0 3.0]-[0.0 0.3 3.0] 
                Body edge nr.:12 related to polygons 6 - 5 

These are all the Polygons of the body
Edges in this [1] pgon idx:-4 
Edges in this [1] pgon idx:1 
Edges in this [1] pgon idx:2 
Edges in this [1] pgon idx:-3 
NormalVector idx: 1 [0.0 0.0 -1.0] 
Edges in this [2] pgon idx:-7 
Edges in this [2] pgon idx:5 
Edges in this [2] pgon idx:4 
Edges in this [2] pgon idx:-6 
NormalVector idx: 2 [0.0 0.0 -1.0] 
Edges in this [3] pgon idx:-1 
Edges in this [3] pgon idx:-5 
Edges in this [3] pgon idx:-8 
Edges in this [3] pgon idx:-9 
NormalVector idx: 3 [0.0 0.0 1.0] 
Edges in this [4] pgon idx:-2 
Edges in this [4] pgon idx:9 
Edges in this [4] pgon idx:10 
Edges in this [4] pgon idx:-11 
NormalVector idx: 4 [0.0 -1.0 0.0] 
Edges in this [5] pgon idx:6 
Edges in this [5] pgon idx:3 
Edges in this [5] pgon idx:11 
Edges in this [5] pgon idx:12 
NormalVector idx: 5 [-1.0 0.0 0.0] 
Edges in this [6] pgon idx:-10 
Edges in this [6] pgon idx:8 
Edges in this [6] pgon idx:7 
Edges in this [6] pgon idx:-12 
NormalVector idx: 6 [-1.0 0.0 0.0] 
Here is an explanatory sketch:


So my questions again:
How to get the same the 3D manager functionality from the ModelerAPI? How to get a 3D information of an element by guid?
How to get a sight, or what is the entry point to the 3D database from an arbitrary view?
Ralph Wessel
Mentor
I can't definitively explain the design intent for the API, but (like you) I suspect that that the ACAPI_3D functions are not made redundant by the ModelerAPI - they just serve a different purpose, e.g.:
1) ModelerAPI for dealing with a 3D model already defined and viewed by the user which we to dissect for export or analysis.
2) ACAPI_3D functions for getting arbitrary 3D geometry linked to the project database, e.g. taking a selected element in the floor plan and extracting 3D geometry to populate covering objects (linings/finishes) linked to the element.

We've written our own C++ framework for the API to simplify this (and to smooth over API changes between versions), so we have classes for Element3D, Body, Face, Edge and Vertex that act in the same way and deliver the same information irrespective of whether it was obtained via ModelerAPI or ACAPI_3D functions. The objects determine which route is required based on the information provided when they are constructed.

So if Element3D is constructed with a link to an element in the project database, we call ACAPI_Element_Get3DInfo and drill down into the that data to extract the element 3D body, faces etc. In that respect, we don't need a Sight object.
Ralph Wessel BArch
Anonymous
Not applicable
Hi Ralph! Thanks for getting back to me, those are really valuable architecture insights.

I wasn't sure (still not) that both of these "entry points" provide the same 3D data in tha same resolution/detail.
Like there is a GetTesselatedBody() method for the Element class (which is, in fact used by the given example addon), but there is also a GetMeshBody(). This is not differentiated in the C api structures, at least I've yet to find indication of it.

Or there were that really promising IsVisibleIfContuor() for the Edges in C++. The name suggested that its related to edges visible at wireframe or contours. Would have simplified my problems greatly, but couldn't get any useful info out of it, returned false to all of the edges.

Maybe one day Graphisoft will provide access to those wikis mentioned in some of the header files.
Ralph Wessel
Mentor
I can't be certain, but I suspect that ModelerAPI is mostly a C++ wrapper for the C functions – I've never seen anything that suggested the data we extract is different.
Ralph Wessel BArch
Learn and get certified!