Help with SketchUp C API Example regarding the face indices

Hello,

The SketchUp C API sample I’m using demonstrates how to read SketchUp files, but I’m not sure how to load the face indices from the associated model.
The code following does works but it just affects the vertex data.
Please check the code below to assist with loading the face indices from the sample code.

#include <slapi/slapi.h>
#include <slapi/geometry.h>
#include <slapi/initialize.h>
#include <slapi/unicodestring.h>
#include <slapi/model/model.h>
#include <slapi/model/entities.h>
#include <slapi/model/face.h>
#include <slapi/model/edge.h>
#include <slapi/model/vertex.h>
#include
int main() {
// Always initialize the API before using it
SUInitialize();
// Load the model from a file
SUModelRef model = SU_INVALID;
SUResult res = SUModelCreateFromFile(&model, “model.skp”);
// It’s best to always check the return code from each SU function call.
// Only showing this check once to keep this example short.
if (res != SU_ERROR_NONE)
return 1;
// Get the entity container of the model.
SUEntitiesRef entities = SU_INVALID;
SUModelGetEntities(model, &entities);
// Get all the faces from the entities object
size_t faceCount = 0;
SUEntitiesGetNumFaces(entities, &faceCount);
if (faceCount > 0) {
std::vector faces(faceCount);
SUEntitiesGetFaces(entities, faceCount, &faces[0], &faceCount);
// Get all the edges in this face
for (size_t i = 0; i < faceCount; i++) {
size_t edgeCount = 0;
SUFaceGetNumEdges(faces[i], &edgeCount);
if (edgeCount > 0) {
std::vector edges(edgeCount);
SUFaceGetEdges(faces[i], edgeCount, &edges[0], &edgeCount);
// Get the vertex positions for each edge
for (size_t j = 0; j < edgeCount; j++) {
SUVertexRef startVertex = SU_INVALID;
SUVertexRef endVertex = SU_INVALID;
SUEdgeGetStartVertex(edges[j], &startVertex);
SUEdgeGetEndVertex(edges[j], &endVertex);
SUPoint3D start;
SUPoint3D end;
SUVertexGetPosition(startVertex, &start);
SUVertexGetPosition(endVertex, &end);
// Now do something with the point data
}
}
}
}
// Get model name
SUStringRef name = SU_INVALID;
SUStringCreate(&name);
SUModelGetName(model, &name);
size_t name_length = 0;
SUStringGetUTF8Length(name, &name_length);
char* name_utf8 = new char[name_length + 1];
SUStringGetUTF8(name, name_length + 1, name_utf8, &name_length);
// Now we have the name in a form we can use
SUStringRelease(&name);
delete name_utf8;
// Must release the model or there will be memory leaks
SUModelRelease(&model);
// Always terminate the API when done using it
SUTerminate();
return 0;
}

Thanks in advance

SUFaceRef faceRef = faces[i];

// Load face geometry.
SUMeshHelperRef polyMesh = SU_INVALID;
SUMeshHelperCreate(&polyMesh, faceRef);

size_t numberOfVertices = 0;
size_t numberOfIndices = 0;

if (SUIsValid(polyMesh))
{
	SUMeshHelperGetNumVertices(polyMesh, &numberOfVertices);

	std::vector<SUPoint3D> points(numberOfVertices);
	std::vector<SUPoint3D> uvs(numberOfVertices);
	std::vector<SUPoint3D> backUvs(numberOfVertices);
	std::vector<SUVector3D> normals(numberOfVertices);

	// Get data from polyMesh
	SUMeshHelperGetVertices(polyMesh, numberOfVertices, &points[0], &result);
	SUMeshHelperGetNormals(polyMesh, numberOfVertices, &normals[0], &result);

	SUMeshHelperGetFrontSTQCoords(polyMesh, numberOfVertices, &uvs[0], &result);
	SUMeshHelperGetBackSTQCoords(polyMesh, numberOfVertices, &backUvs[0], &result);

	// Load indices.
	SUMeshHelperGetNumTriangles(polyMesh, &numberOfIndices);

	numberOfIndices = numberOfIndices * 3;
	std::vector<size_t> indices(numberOfIndices);

	SUMeshHelperGetVertexIndices(polyMesh, numberOfIndices, &indices[0], &result);

	// Release polyMesh helper
	SUMeshHelperRelease(&polyMesh);
}

Please use the “code block” format next time.

Here what I tried so far.
From where can I get the total part/assembly count. I need to fill the data part wise.
Here I am getting triangles for all the parts. How can I get this partwise?

SUInitialize();
Part partData;
Face faceData;
Assembly assembly;
partData.partName = "PartCube";
partData.m_isVisible = true;

size_t EntityCount = 0;
// Load the model from a file
SUModelRef model = SU_INVALID;
SUModelLoadStatus status;
SUResult res = SUModelCreateFromFileWithStatus(&model, path, &status);

// It's best to always check the return code from each SU function call.
// Only showing this check once to keep this example short.
if (res != SU_ERROR_NONE) {
	std::cout << "Failed creating model from a file" << std::endl;
}

if (status == SUModelLoadStatus_Success_MoreRecent) 
{
	std::cout
		<< "This model was created in a more recent SketchUp version than that of the SDK. "
		"It contains data which will not be read. Saving the model over the original file may "
		"lead to permanent data loss."
		<< std::endl;
}

// Get the entity container of the model.
SUEntitiesRef entities = SU_INVALID;
SUModelGetEntities(model, &entities);

SUEntityListRef list = SU_INVALID;

// Get all the faces from the entities object
size_t faceCount = 0;
SUEntitiesGetNumFaces(entities, &faceCount);
std::cout << "Number of faces: " << faceCount << std::endl;
if (faceCount > 0) 
{
	std::vector<SUFaceRef> faces(faceCount);
	SUEntitiesGetFaces(entities, faceCount, &faces[0], &faceCount);

	for (size_t i = 0; i < faceCount; i++)
	{
		std::vector<double> vertexCoordinate;
		std::vector<double> normalCoordinate;
		std::vector<int> faceindices;
		SUFaceRef faceRef = faces[i];
		// Load face geometry.
		SUMeshHelperRef polyMesh = SU_INVALID;
		SUMeshHelperCreate(&polyMesh, faceRef);
		size_t numberOfVertices = 0;
		size_t numberOfIndices = 0;
		size_t numberOfNormals = 0;
		size_t result = 0;
		if (SUIsValid(polyMesh))
		{
			SUMeshHelperGetNumVertices(polyMesh, &numberOfVertices);

			std::vector<SUPoint3D> points(numberOfVertices);
			std::vector<SUPoint3D> uvs(numberOfVertices);
			std::vector<SUPoint3D> backUvs(numberOfVertices);
			std::vector<SUVector3D> normals(numberOfVertices);

			// Get data from polyMesh
			SUMeshHelperGetVertices(polyMesh, numberOfVertices, &points[0], &result);
			SUMeshHelperGetNormals(polyMesh, numberOfNormals, &normals[0], &result);
			numberOfNormals = result;

			SUMeshHelperGetFrontSTQCoords(polyMesh, numberOfVertices, &uvs[0], &result);
			SUMeshHelperGetBackSTQCoords(polyMesh, numberOfVertices, &backUvs[0], &result);

			// Load indices.
			SUMeshHelperGetNumTriangles(polyMesh, &numberOfIndices);
			numberOfIndices = numberOfIndices * 3;
			std::vector<size_t> indices(numberOfIndices);

			SUMeshHelperGetVertexIndices(polyMesh, numberOfIndices, &indices[0], &result);

			for (int i = 0; i < numberOfVertices; i++)
			{	
				vertexCoordinate.push_back(points[i].x);
				vertexCoordinate.push_back(points[i].y);
				vertexCoordinate.push_back(points[i].z);
				
				normalCoordinate.push_back(normals[i].x);
				normalCoordinate.push_back(normals[i].y);
				normalCoordinate.push_back(normals[i].z);				
			}
			for (const auto& index : indices) 
			{
				faceindices.push_back(static_cast<int>(index));
			}						
			// Release polyMesh helper
			SUMeshHelperRelease(&polyMesh);
			faceData.coordinates = vertexCoordinate;
			faceData.indices = faceindices;
		}
		partData.faces.push_back(faceData);
		faceData.faceColor.red = 160;
		faceData.faceColor.green = 160;
		faceData.faceColor.blue = 160;
		faceData.faceColor.opacity = 255;
	}
}
assembly.parts.push_back(partData);
assembly.assemblyName = "Cube";
assembly.m_isVisible = true;

// Get model name
SUStringRef name = SU_INVALID;
SUStringCreate(&name);
SUModelGetName(model, &name);
size_t name_length = 0;
SUStringGetUTF8Length(name, &name_length);
char* name_utf8 = new char[name_length + 1];
SUStringGetUTF8(name, name_length + 1, name_utf8, &name_length);
// Now we have the name in a form we can use
SUStringRelease(&name);
delete[] name_utf8;

// Must release the model or there will be memory leaks
SUResult result = SUModelRelease(&model);

if (result != SU_ERROR_NONE) 
{
	std::cout << "Reading SketchUp model: Failed" << std::endl;
}
else 
{
	std::cout << "Reading SketchUp model: Success" << std::endl;
}
// Always terminate the API when done using it
SUTerminate();

Just Checking , if there any reply to the previous query.

You mean total count of faces/entities etc?

SUModelStatistics modelStats;
SUModelGetStatistics(modelRef, &modelStats);

Thank you for your reply.
I am not able to export the assembly using the above code.

Single part is getting exported using above code.

And one more question.
How to get the part name?

Just Checking , if there any reply to the previous query.

You need to learn more about SU scene structure and API.
The simplest way is to play a bit with Ruby API under SketchUp Ruby Console.

The above code can export only a ‘raw’ faces from the ‘root level’.
For components and groups you’ll need to get them from root level entities, and traverse whole scene tree to collect all faces from all instances/groups of all branches of the scene tree.

Check this:

SUEntitiesGetNumInstances(entitiesRef, &numInstances) ;
SUEntitiesGetNumGroups(entitiesRef, &numGroups);
SUEntitiesGetNumFaces(entitiesRef, &numFaces);

then look to this:

SUEntitiesGetInstances(entitiesRef, numInstances, &instances[0], &numInstances);
SUEntitiesGetGroups(entitiesRef, numGroups, &groups[0], &numGroups)

and this:

// Groups
SUGroupGetEntities(groupRef, &entitiesRef);

// Component instances
SUComponentInstanceGetDefinition(instanceRef, &defRef);
SUComponentDefinitionGetEntities(defRef, &entitiesRef);

To get additional information you’ll need to cast object to SUDrawingElementRef (to get visibility, materials, layer info etc) or to SUEntityRef (to get object name, attributes etc).

Check:

SUComponentInstanceToEntity(instanceRef)
SUComponentInstanceToDrawingElement(instanceRef);
SUGroupToDrawingElement(groupRef);
SUGroupToEntity(groupRef);
SUFaceToDrawingElement(faceRef);
SUDrawingElementToEntity(drawRef);

Don’t forget to calculate the world transformation for faces inside groups/instances:

SUComponentInstanceGetTransform()
SUGroupGetTransform()

Hi @RaPIT,
Thanks for the guidance on how to collect the faces from the group and component and how to apply the transformation.
I tried to get the edge data from the model and export them. While doing that I encountered the issue of the transformation. I have collected the edge from the root, group and the component. Please refer the below code on how I traverse the edge from model and the screenshot regarding the issue.

		//Edge entity export
		SUEntitiesGetNumEdges(entities, false, &edgeCount);
		std::vector<SUEdgeRef> edges(edgeCount);
		if (edgeCount > 0)
		{
			short red, green, blue, opacity = 0;
			red = 128;
			green = 128;
			blue = 128;
			opacity = 255;

			SUEntitiesGetEdges(entities, true, edgeCount, &edges[0], &edgeCount);

			// Get the vertex positions for each edge
			for (size_t j = 0; j < edgeCount; j++) {
				SUVertexRef startVertex = SU_INVALID;
				SUVertexRef endVertex = SU_INVALID;
				SUEdgeGetStartVertex(edges[j], &startVertex);
				SUEdgeGetEndVertex(edges[j], &endVertex);
				SUPoint3D start;
				SUPoint3D end;
				SUVertexGetPosition(startVertex, &start);
				SUVertexGetPosition(endVertex, &end);

				vector<double> pointlist = GetLineFromSketchEntity(start, end, commandData, currentTransform);
				Sketch newSketch;
				newSketch.pointlist = pointlist;
				newSketch.sketchColor.red = red;
				newSketch.sketchColor.green = green;
				newSketch.sketchColor.blue = blue;
				newSketch.sketchColor.opacity = opacity;
				partData.sketches.push_back(newSketch);
			}
		}
		//
vector<double> GetLineFromSketchEntity(SUPoint3D start, SUPoint3D end, CLRPrecisionCommand commandData, SUTransformation parenttransform)
{
	std::vector<double> PointsList;
        double x = start.x;
	double y = start.y;
	double z = start.z;
	PointsList.push_back(x);
	PointsList.push_back(y);
	PointsList.push_back(z);	 
	double x1 = end.x;
	double y1 = end.y;
	double z1 = end.z;
	PointsList.push_back(x1);
	PointsList.push_back(y1);
	PointsList.push_back(z1);
	return PointsList;
}


Just checking, is there any reply to the previous query?

All vertices are in local coordinate system, you’ll need to multiply them by ‘world’ transform, where world transform is parent transform multiplied by local transform.

Thanks for the updates.

I’m having trouble maintaining the transparency of a material. After exporting, a transparent object appears solid. Could you please review the snippet and let me know the same?

SUResult res = SUFaceGetFrontMaterial(face, &front_material);
// Get the color from the material

if (res == SU_ERROR_NONE)
{
	res = SUMaterialGetColor(front_material, &face_color);
}

Thanks in advance

See:

Your code snippet does not initialize the face_color struct.

Hi DanRathbun,
Thanks for the reply. We have invetsigated the issue at our end and observed that when we attempt to get the front material using the method SU_RESULT SUFaceGetFrontMaterial(SUFaceRef face, SUMaterialRef* material); we are not getting the appropriate material value, preventing us from fetching and setting the face color of the material. Our model has three transparent green pendants. During export, two out of three exported successfully, while the remaining one exported with default RGBA value because we are not getting the ‘front_material’ from it.

		SUResult res = SUFaceGetFrontMaterial(face, &front_material);

		// Get the color from the material
		if (res == SU_ERROR_NONE)
		{
			res = SUMaterialGetColor(front_material, &face_color);
		}
		else
		{
			face_color.red = 128;
			face_color.green = 128;
			face_color.blue = 128;
			face_color.alpha = 255;
		}

Please refer to the attached sketch up file for you reference
Green+pendant+light.skp (173.2 KB)

Which group is the problem group ? They differ in nesting level and whether the outer group or the internal faces are assigned a material or not.

If you had painted them each a different color it would be a better test, IMO.


Also, the code you posted is not a complete working code snippet. It does not for example assign gray color to any faces or groups, yet there are groups assigned a gray color (even though the faces within are painted green. NOTE: SketchUp uses face color to override group color in its display engine.)

I also notice in the model that some faces have material assigned to both the front and back sides. Others only one side. Switching to monochrome style it looks like all the faces are properly front-side-out.


Also … what version was the SKP file made with?

What version of the C API are you using ?