Create faces with new SketchUp SDK C API

Hi everybody!
I am developing a new version of a program using SketchUp SDK 2014, old version of this program used SkpWriter 8.
I have a issue with create faces on SDK 2014. I am using this code to create faces:

// Create the face
SUFaceRef face1 = SU_INVALID;
SUPoint3D vertices1[4] = { { 0, 0, 0 },
{ 100, 0, 0 },
{ 100, 100, 0 },
{ 0, 100, 0 } };
SUFaceCreateSimple(&face1, vertices1, 4);
SUEntitiesAddFaces(entities, 1, &face1);
....
SUFaceCreateSimple(&face6, vertices6, 4);
SUEntitiesAddFaces(entities, 1, &face6);

And get this model

But when I used old version

ISketchUpEntityContainerPtr pContainer = pDoc->GetModel()->GetRoot();
atlast::geometry::CPoint3d vertices1 [4] =  { { 0, 0, 0 },
{ 100, 0, 0 },
{ 100, 100, 0 },
{ 0, 100, 0 } };
pContainer ->CreateFace(vertices1, 4);
...
pContainer ->CreateFace(vertices6, 4);

I got this model

I do not change color and use the default settings
How to solve issue with color of side cube?

From your figure it appears that Faces are reversed in the new version? In current versions, the “outside” direction is determined by the order in which you list the vertices when you create it. If they appear to go around the perimeter counterclockwise as you look at the face, you are looking from the “outside”. You can either reverse the ordering of the vertices in the problem faces or reverse the face normal after you create it.

1 Like

Thanks for answer!
I can’t change order of vertices list(in the example I set them manually, but in real program I get them from outside). How can I detect this(order in counterclockwise or clockwise?) in my program to make reversing this face(or change order if it’s possible)?

There is more than just the reversed faces - it appear that they do not merge. Got a more complete example?
@bugra or @Paul - go any idea why the faces doesn’t merge?

Hi
I’m solved issue of reversing faces with back initialization of vertices.
Now faces is displayed correctly. But I have a new issue with displaying of textures. Texture hidden by side of cube.

Here is my code on new API

#include <assert.h>
#include <vector>

#include <slapi/slapi.h>
#include <slapi/geometry.h>
#include <slapi/initialize.h>
#include <slapi/model/model.h>
#include <slapi/model/entities.h>
#include <slapi/model/face.h>
#include <slapi/model/edge.h>
#include "slapi/model/texture.h"
#include "slapi/model/material.h"

#define SU_CALL(func) if ((func) != SU_ERROR_NONE) throw std::exception()

struct point3d
{
    double x;
    double y;
    double z;
};
class SKPModel
{
public:
    SKPModel()
    {
	model.ptr = nullptr;
	SUInitialize();
	SU_CALL(SUModelCreate(&model));
    }

    ~SKPModel()
    {
	if (SUIsValid(model))
		SUModelRelease(&model);
	SUTerminate();
    }

void AddFace(point3d *pPoints, int Count)
{
	SUEntitiesRef entities = SU_INVALID;
	SU_CALL(SUModelGetEntities(model, &entities));

	SUPoint3D* vertices = new SUPoint3D[Count];
	for (int i = 0; i < Count; ++i) {
                    //reverse faces for new API
		vertices[Count - i - 1] = SUPoint3D({ pPoints[i].x, pPoints[i].y, pPoints[i].z });
            }

	if (Count > 2) {
		SUFaceRef face = SU_INVALID;
		SU_CALL(SUFaceCreateSimple(&face, vertices, Count));
		SU_CALL(SUEntitiesAddFaces(entities, 1, &face));
	}
	else {
		SUEdgeRef edge = SU_INVALID;
		SU_CALL(SUEdgeCreate(&edge, vertices, vertices + 1));
		SU_CALL(SUEntitiesAddEdges(entities, 1, &edge));
	}
	delete[] vertices;
}

void AddBitmap(point3d *pPoints, int Count, char* FileName)
{
	assert(Count > 2);

	SUPoint3D* points = new SUPoint3D[Count];
	for (int i = 0; i < Count; ++i) {
		points[Count - i - 1] = SUPoint3D({ pPoints[i].x, pPoints[i].y, pPoints[i].z });
	}
	SUGeometryInputRef input = SU_INVALID;
	SU_CALL(SUGeometryInputCreate(&input));
	SU_CALL(SUGeometryInputSetVertices(input, Count, points));
	delete[] points;

	SULoopInputRef loop = SU_INVALID;
	SU_CALL(SULoopInputCreate(&loop));

	for (size_t i = 0; i < Count; ++i) {
		SU_CALL(SULoopInputAddVertexIndex(loop, i));
	}

	size_t face_index0;
	SU_CALL(SUGeometryInputAddFace(input, &loop, &face_index0));


	SUTextureRef tex = SU_INVALID;
	SU_CALL(SUTextureCreateFromFile(&tex, FileName, 1.0, 1.0));

	SUMaterialRef mat = SU_INVALID;
	SU_CALL(SUMaterialCreate(&mat));
	SU_CALL(SUMaterialSetTexture(mat, tex));
	SU_CALL(SUMaterialSetName(mat, "material"));

	SUMaterialInput mat_input;
	mat_input.material = mat;
	mat_input.num_uv_coords = 4;
	SUPoint2D uv_coords[4] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } };
	for (size_t i = 0; i < 4; ++i) {
		mat_input.vertex_indices[i] = i;
		mat_input.uv_coords[i] = uv_coords[i];
	}

	SU_CALL(SUGeometryInputFaceSetFrontMaterial(input, face_index0, &mat_input));
	SU_CALL(SUGeometryInputFaceSetBackMaterial(input, face_index0, &mat_input));

	SUEntitiesRef entities = SU_INVALID;
	SU_CALL(SUModelGetEntities(model, &entities));
	SU_CALL(SUEntitiesFill(entities, input, false));

	SU_CALL(SUGeometryInputRelease(&input));
    }

    void Save(char* FileName)
    {
        SU_CALL(SUModelSaveToFile(model, FileName));
    }

private:
    SUModelRef model;
};

Sample for using:

{
	point3d cb_vrtcs1[4] = {
		{ 0, 0, 0 },
		{ 100, 0, 0 },
		{ 100, 100, 0 },
		{ 0, 100, 0 }
	};

	point3d cb_vrtcs2[4] = {
		{ 0, 0, 100 },
		{ 0, 100, 100 },
		{ 100, 100, 100 },
		{ 100, 0, 100 }
	};

	point3d cb_vrtcs3[4] = {
		{ 0, 0, 0 },
		{ 0, 100, 0 },
		{ 0, 100, 100 },
		{ 0, 0, 100 }
	};

	point3d cb_vrtcs4[4] = {
		{ 100, 0, 0 },
		{ 100, 0, 100 },
		{ 100, 100, 100 },
		{ 100, 100, 0 }
	};

	point3d cb_vrtcs5[4] = {
		{ 0, 0, 0 },
		{ 0, 0, 100 },
		{ 100, 0, 100 },
		{ 100, 0, 0 }
	};

	point3d cb_vrtcs6[4] = {
		{ 0, 100, 0 },
		{ 100, 100, 0 },
		{ 100, 100, 100 },
		{ 0, 100, 100 }
	};


	SKPModel skp_model;
	skp_model.AddFace(cb_vrtcs1, 4);
	skp_model.AddFace(cb_vrtcs2, 4);
	skp_model.AddFace(cb_vrtcs3, 4);
	skp_model.AddFace(cb_vrtcs4, 4);
	skp_model.AddFace(cb_vrtcs5, 4);
	skp_model.AddFace(cb_vrtcs6, 4);

	point3d txtr_vrtc1[4] = {
		{ 40, 40, 0 },
		{ 60, 40, 0 },
		{ 60, 60, 0 },
		{ 40, 60, 0 }
	};
	skp_model.AddBitmap(txtr_vrtc1, 4, "tst.jpg");

	point3d txtr_vrtc2[4] = {
		{ 40, 40, 100 },
		{ 60, 40, 100 },
		{ 60, 60, 100 },
		{ 40, 60, 100 }
	};
	skp_model.AddBitmap(txtr_vrtc2, 4, "tst.jpg");

	point3d txtr_vrtc3[4] = {
		{ 0, 40, 40 },
		{ 0, 40, 60 },
		{ 0, 60, 60 },
		{ 0, 60, 40 }
	};
	skp_model.AddBitmap(txtr_vrtc3, 4, "tst.jpg");

	point3d txtr_vrtc4[4] = {
		{ 100, 40, 40 },
		{ 100, 40, 60 },
		{ 100, 60, 60 },
		{ 100, 60, 40 }
	};
	skp_model.AddBitmap(txtr_vrtc4, 4, "tst.jpg");
	skp_model.Save("cube.skp");
}

On old version of SDK this issue is not reproduced.
How can I fix this?

Bitmap texture hidden by both side.

I use this code to create texture

SUGeometryInputRef input = SU_INVALID;
SU_CALL(SUGeometryInputCreate(&input));
SU_CALL(SUGeometryInputSetVertices(input, Count, points));

SULoopInputRef loop = SU_INVALID;
SU_CALL(SULoopInputCreate(&loop));

for (size_t i = 0; i < Count; ++i) {
	SU_CALL(SULoopInputAddVertexIndex(loop, i));
}

size_t face_index0;
SU_CALL(SUGeometryInputAddFace(input, &loop, &face_index0));

SUTextureRef tex = SU_INVALID;
SU_CALL(SUTextureCreateFromFile(&tex, FileName, 1.0, 1.0));

SUMaterialRef mat = SU_INVALID;
SU_CALL(SUMaterialCreate(&mat));
SU_CALL(SUMaterialSetTexture(mat, tex));
SU_CALL(SUMaterialSetName(mat, "material"));

SUMaterialInput mat_input;
mat_input.material = mat;
mat_input.num_uv_coords = 4;
SUPoint2D uv_coords[4] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } };
for (size_t i = 0; i < 4; ++i) {
	mat_input.vertex_indices[i] = i;
	mat_input.uv_coords[i] = uv_coords[i];
}

SU_CALL(SUGeometryInputFaceSetFrontMaterial(input, face_index0, &mat_input));
SU_CALL(SUGeometryInputFaceSetBackMaterial(input, face_index0, &mat_input));

SUEntitiesRef entities = SU_INVALID;
SU_CALL(SUModelGetEntities(model, &entities));
SU_CALL(SUEntitiesFill(entities, input, false));

SU_CALL(SUGeometryInputRelease(&input));

On older versions of the SDK the problem was not!
How to fix this issue?
What did I do wrong?
Maybe, Did I forget enable any options?

That looks like Z fighting because you have faces overlapping on the exact same plane.
Without knowing what you did with the old SDK I’m not sure what the source of the difference you experience.

Here is code with SkpWriter 8.0

//ISketchUpApplicationPtr pApp = GetSkpApplication();
//ISketchUpDocumentPtr    pDoc = pApp->NewDocument()->GetModel()->GetDocument()

//.... 

ISketchUpEntityContainerPtr pContainer = pDoc->GetModel()->GetRoot();
ISketchUpFacePtr pFace;

pFace = pContainer->CreateFace(points, Count);

if(pFace)
{
    atlast::sketchup::ISketchUpMaterialPtr pMat = pDoc->GetModel()->GetMaterials()->CreateMaterial(L"MyFillMaterial");
    
    LPCWSTR fNameLPCW = A2BSTR(FileName); 
    atlast::sketchup::ISketchUpTexturePtr pTexture = pMat->CreateTexture(fNameLPCW);
    
    ASSERT(pTexture != NULL);
    
    atlast::geometry::CPoint2d uvs[4];
    uvs[0] = atlast::geometry::CPoint2d(0,0);
    uvs[1] = atlast::geometry::CPoint2d(1,0);
    uvs[2] = atlast::geometry::CPoint2d(1,1);
    uvs[3] = atlast::geometry::CPoint2d(0,1);
    
    pFace->SetFrontMaterial(pMat, points, Count, uvs);
    pFace->SetBackMaterial(pMat, points, Count, uvs);
}

This code was simple and worked properly.

How does the result from the SkpWriter look like?

In your SLAPI version, if you create the faces so they don’t overlap and z-fight - the texture is applied correctly, right?

Hi
Here ia skp files
cube_newapi.skp (37.4 KB)
cube_oldapi.skp (15.6 KB)

And VS2013 projects:
AppNewAPI.zip (2.0 MB)
AppOldAPI.zip (1.6 MB)

@sam - When downloading these two files they both download as cube.skp. Is this a bug or “feature” of some sort?

Thanks for this example. I’ll need to poke about a bit to see what’s going on. The old C++ SDK was deprecated before I started working here so I need to dig up some history.

Sorry for the delay, but we need to dig deeper into this. I’m filing a few issues in our tracker for a few things in this thread. I’ll try to post back with info, but right now I don’t have an immediate answer.

One thing, have you tried SUFaceCreate instead of SUFaceCreateSimple?

Hi
Yes, I use SUFaceCreate and also SUGeometryInputAddFace, everything remains still

In that last screenshot, you have created two faces on top of each other?

Yes, I create side of cube(with SUFaceCreate or SUFaceCreateSimple), than I create textured face on this side with SUGeometryInputAddFace
Yuo canchek this in source

#include <assert.h>
#include <vector>

#include <slapi/slapi.h>
#include <slapi/geometry.h>
#include <slapi/initialize.h>
#include <slapi/model/model.h>
#include <slapi/model/entities.h>
#include <slapi/model/face.h>
#include <slapi/model/edge.h>
#include "slapi/model/texture.h"
#include "slapi/model/material.h"

#define SU_CALL(func) if ((func) != SU_ERROR_NONE) throw std::exception()

struct point3d
{
double x;
double y;
double z;
};
class SKPModel
{
public:
SKPModel()
{
	model.ptr = nullptr;
	SUInitialize();
	SU_CALL(SUModelCreate(&model));
}
~SKPModel()
{
	if (SUIsValid(model))
		SUModelRelease(&model);
	SUTerminate();
}

void AddFace(point3d *pPoints, int Count)
{
	SUEntitiesRef entities = SU_INVALID;
	SU_CALL(SUModelGetEntities(model, &entities));

	SUPoint3D* vertices = new SUPoint3D[Count];
	for (int i = 0; i < Count; ++i)
		vertices[Count - i - 1] = SUPoint3D({ pPoints[i].x, pPoints[i].y, pPoints[i].z });

	if (Count > 2) {
		SUFaceRef face = SU_INVALID;
		//SU_CALL(SUFaceCreateSimple(&face, vertices, Count));
		SULoopInputRef loop = SU_INVALID;
		SU_CALL(SULoopInputCreate(&loop));
		for (size_t i = 0; i < Count; ++i)
			SU_CALL(SULoopInputAddVertexIndex(loop, i));

		SU_CALL(SUFaceCreate(&face, vertices, &loop));
		SU_CALL(SUEntitiesAddFaces(entities, 1, &face));
	}
	else {
		SUEdgeRef edge = SU_INVALID;
		SU_CALL(SUEdgeCreate(&edge, vertices, vertices + 1));
		SU_CALL(SUEntitiesAddEdges(entities, 1, &edge));
	}
	delete[] vertices;
}
void AddBitmap(point3d *pPoints, int Count, char* FileName)
{
	assert(Count > 2);

	SUPoint3D* points = new SUPoint3D[Count];
	for (int i = 0; i < Count; ++i) {
		points[Count - i - 1] = SUPoint3D({ pPoints[i].x, pPoints[i].y, pPoints[i].z });
	}
	SUGeometryInputRef input = SU_INVALID;
	SU_CALL(SUGeometryInputCreate(&input));
	SU_CALL(SUGeometryInputSetVertices(input, Count, points));
	delete[] points;

	SULoopInputRef loop = SU_INVALID;
	SU_CALL(SULoopInputCreate(&loop));

	for (size_t i = 0; i < Count; ++i) {
		SU_CALL(SULoopInputAddVertexIndex(loop, i));
	}

	size_t face_index0;
	SU_CALL(SUGeometryInputAddFace(input, &loop, &face_index0));


	SUTextureRef tex = SU_INVALID;
	SU_CALL(SUTextureCreateFromFile(&tex, FileName, 1.0, 1.0));

	SUMaterialRef mat = SU_INVALID;
	SU_CALL(SUMaterialCreate(&mat));
	SU_CALL(SUMaterialSetTexture(mat, tex));
	SU_CALL(SUMaterialSetName(mat, "material"));

	SUMaterialInput mat_input;
	mat_input.material = mat;
	mat_input.num_uv_coords = 4;
	SUPoint2D uv_coords[4] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } };
	for (size_t i = 0; i < 4; ++i) {
		mat_input.vertex_indices[i] = i;
		mat_input.uv_coords[i] = uv_coords[i];
	}

	SU_CALL(SUGeometryInputFaceSetFrontMaterial(input, face_index0, &mat_input));
	SU_CALL(SUGeometryInputFaceSetBackMaterial(input, face_index0, &mat_input));

	SUEntitiesRef entities = SU_INVALID;
	SU_CALL(SUModelGetEntities(model, &entities));
	SU_CALL(SUEntitiesFill(entities, input, true));

	SU_CALL(SUGeometryInputRelease(&input));
}
void Save(char* FileName)
{
	SU_CALL(SUModelSaveToFile(model, FileName));
}
private:
SUModelRef model;
};

int main()
{
point3d cb_vrtcs1[4] = {
	{ 0, 0, 0 },
	{ 100, 0, 0 },
	{ 100, 100, 0 },
	{ 0, 100, 0 }
};

point3d cb_vrtcs2[4] = {
	{ 0, 0, 100 },
	{ 0, 100, 100 },
	{ 100, 100, 100 },
	{ 100, 0, 100 }
};

point3d cb_vrtcs3[4] = {
	{ 0, 0, 0 },
	{ 0, 100, 0 },
	{ 0, 100, 100 },
	{ 0, 0, 100 }
};

point3d cb_vrtcs4[4] = {
	{ 100, 0, 0 },
	{ 100, 0, 100 },
	{ 100, 100, 100 },
	{ 100, 100, 0 }
};

point3d cb_vrtcs5[4] = {
	{ 0, 0, 0 },
	{ 0, 0, 100 },
	{ 100, 0, 100 },
	{ 100, 0, 0 }
};

point3d cb_vrtcs6[4] = {
	{ 0, 100, 0 },
	{ 100, 100, 0 },
	{ 100, 100, 100 },
	{ 0, 100, 100 }
};


SKPModel skp_model;
skp_model.AddFace(cb_vrtcs1, 4);
skp_model.AddFace(cb_vrtcs2, 4);
skp_model.AddFace(cb_vrtcs3, 4);
skp_model.AddFace(cb_vrtcs4, 4);
skp_model.AddFace(cb_vrtcs5, 4);
skp_model.AddFace(cb_vrtcs6, 4);

point3d txtr_vrtc1[4] = {
	{ 40, 40, 0 },
	{ 60, 40, 0 },
	{ 60, 60, 0 },
	{ 40, 60, 0 }
};
skp_model.AddBitmap(txtr_vrtc1, 4, "tst.jpg");

point3d txtr_vrtc2[4] = {
	{ 40, 40, 100 },
	{ 60, 40, 100 },
	{ 60, 60, 100 },
	{ 40, 60, 100 }
};
skp_model.AddBitmap(txtr_vrtc2, 4, "tst.jpg");

point3d txtr_vrtc3[4] = {
	{ 0, 40, 40 },
	{ 0, 40, 60 },
	{ 0, 60, 60 },
	{ 0, 60, 40 }
};
skp_model.AddBitmap(txtr_vrtc3, 4, "tst.jpg");

point3d txtr_vrtc4[4] = {
	{ 100, 40, 40 },
	{ 100, 40, 60 },
	{ 100, 60, 60 },
	{ 100, 60, 40 }
};
skp_model.AddBitmap(txtr_vrtc4, 4, "tst.jpg");
skp_model.Save("cube.skp");
return 0;
}

You shouldn’t create a face to go on top of another simply to add a texture. Create the face once, then apply the material to that face. Otherwise you will end up with these z-fighting issues.

But in old version this(creating face on face) work, I was always making faces in such way. Also this work if I manually create faces on SketchUp app.

test.skp (150.3 KB)

We’ve identified a problem with the new API. Then you use SUFaceCreate* and then SUEntitiesAddFaces it currently doesn’t merge the entities like the previous version did. This is a big issue and we’ll be addressing that in a future release.

But as is is now, you want to use SUGeometryInput to make sure vertices are merged.

Sorry for the inconvenience.

dear Sketchup Team,

Is there any complete reference manual for the new SDK 16 ?

I am still confused with the outer_loop variable:

SU_RESULT SUFaceCreate(SUFaceRef* face,
                       const struct SUPoint3D vertices3d[],
                       SULoopInputRef* outer_loop);

What is the function of this variable and how to fill it ?
I read from the sample given:

SULoopInputCreate(&outer_loop);
  for (size_t i = 0; i < 4; ++i) {
    SULoopInputAddVertexIndex(outer_loop, i);
  }

So, i variable will count from 0,1,2,3 I assumed.
I tried to run the sample using SDK 16 but it failed to run.

SULoopInputCreate(&outer_loop);
  for (size_t i = 0; i < 4; ++i) {
    SULoopInputAddVertexIndex(outer_loop, i);
  }

  // Create the face
  SUFaceRef face = SU_INVALID;
  SUPoint3D vertices[4] = { { 0,   0,   0 },
                            { 100, 100, 0 },
                            { 100, 100, 100 },
                            { 0,   0,   100 } };
  SUFaceCreate(&face, vertices, &outer_loop);

If I change the command to :

  SUFaceCreateSimple(&face, vertices, 4);

Then it works. What happened ? Any Idea ?

Thanks and Best Regards,

Nathan Madutujuh
Bandung
INDONESIA