Best way to create a mesh object in C


#1

Hello everyone,

In my code I compute a certain amount of points and I triangulate them to get a mesh. However, I need to create an actual Mesh Object in the scene, using the computed points and triangles. What is the best way to do that?

I found a few solutions on Google to create a mesh manually, but in python. I would like to do this from C code.

Thank you in advance!

Simone


#2

Best thing to do here is to check how Blender does it itself, I think… :wink:

Check and follow codepath of e.g. ED_object_add_type() in editors/object/object_add.c.
Or also, the code of the operators generating the mesh primitives, e.g. MESH_OT_primitive_cube_add, in editors/mesh/editmesh_add.c


#3

Thank you for the suggestion, I managed to do it :slight_smile:


#4

Not sure if it’s “the best way” but i’ve manged to do it like this

bool mytest(bContext *C)
{
	Main *bmain = CTX_data_main(C);
	Scene *scene = CTX_data_scene(C);
	ViewLayer *view_layer = CTX_data_view_layer(C);

	struct Object *obedit = BKE_object_add(bmain, scene, view_layer, OB_MESH, "xxx");

	struct Mesh * mesh = (struct Mesh *)obedit->data;

	mesh->totvert = 4; /* total number of vertices */
	mesh->mvert   = (struct MVert *)CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CALLOC, NULL, mesh->totvert);
	mesh->mvert[0].co[0] = -1; mesh->mvert[0].co[1] = -1; mesh->mvert[0].co[2] = 0;
	mesh->mvert[1].co[0] = -1; mesh->mvert[1].co[1] =  1; mesh->mvert[1].co[2] = 0;
	mesh->mvert[2].co[0] =  1; mesh->mvert[2].co[1] =  1; mesh->mvert[2].co[2] = 0;
	mesh->mvert[3].co[0] =  1; mesh->mvert[3].co[1] = -1; mesh->mvert[3].co[2] = 0;

	mesh->totpoly = 1;	/* this is the total number of faces */
	mesh->totloop = 4;	/* this is the total number of vertices required to describe the faces */
						/* Since we're making a single quad here, this value is 4, if we had chosen */
						/* to make 2 triangles, we would have needed 6 to properly describe both triangles */

	mesh->mpoly   = (MPoly *)CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CALLOC, NULL, mesh->totpoly);
	mesh->mloop   = (MLoop *)CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CALLOC, NULL, mesh->totloop);
	
	mesh->mloop[0].v = 0;
	mesh->mloop[1].v = 1;
	mesh->mloop[2].v = 2;
	mesh->mloop[3].v = 3;

	mesh->mpoly[0].loopstart = 0;
	mesh->mpoly[0].totloop = 4;

	/* Too lazy to add normals + edges myself, edges seem really needed */
	/* BKE_mesh_validate will do the tedious work for us. */
	BKE_mesh_calc_normals(mesh);
	BKE_mesh_validate(mesh,false, false);

	/* Tell blender things have changed */
	DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE);
	DEG_relations_tag_update(bmain);
	WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
	return true;
}```

#5

Thank you @LazyDodo :slight_smile: I love your solution!

I previously implemented a method similar to the methods in editmesh_add.c as suggested by @mont29. Your method is much more readable though, and it is also slightly faster in the execution :stuck_out_tongue:

Thank you both for your time!


#6

I kinda stumbled on my implementation, it may or may not have glaring issues. @mont29 would be a better judge here.


#7

Hello again,

I need a bit more help :sweat_smile:

Basically, the mesh I generate is based on an image. I need now to apply a material to this mesh, and use this image as texture.

I managed to create the material and assign it as texture:

Material *mat = BKE_material_add( bmain, DATA_( "Material" ) );

ED_node_shader_default( C, &mat->id );

assign_material( bmain, ob_mesh, mat, ob_mesh->actcol, BKE_MAT_ASSIGN_EXISTING );

By doing it manually on Blender, I’ve undestood that I also need to use nodes and use as “Base Color” an “Image Texture”, and I’ve managed to do this with this code:

mat->use_nodes = true;

bNode *imanode;
bNodeTree *ntree = mat->nodetree;

imanode = nodeAddStaticNode( C, ntree, SH_NODE_TEX_IMAGE );

imanode->id = &image->id;

nodeSetActive( ntree, imanode );

bNode *in_node = ntreeFindType( ntree, SH_NODE_BSDF_PRINCIPLED );
bNode *out_node = imanode;

if ( in_node != NULL )
{
	bNodeSocket *out_sock = nodeFindSocket( out_node, SOCK_OUT, "Color" );
	bNodeSocket *in_sock = nodeFindSocket( in_node, SOCK_IN, "Base Color" );

	bNodeLink *link = in_sock ? in_sock->link : NULL;
	if ( in_sock != NULL && link == NULL )
	{
		nodeAddLink( ntree, out_node, out_sock, in_node, in_sock );

		nodePositionRelative( out_node, in_node, out_sock, in_sock );
	}
}

ntreeUpdateTree( CTX_data_main( C ), ntree );

However, this is not enough. I need also to set “Vector” in “Base Color” as “Texture Coordinates | Generated”. I can do that manually, but I cannot understand how to do that with the code. Any suggestion?


#8

Those are nodes, you need to add a TextureCoordinate node (SH_NODE_TEX_COORD I believe), and link its Generated output to the Vector input of the Image texture node.


#9

Yeah I managed to do it yesterday :slight_smile:

I made it this way:

bNode *tex_node = nodeAddNode( C, ntree, "ShaderNodeTexCoord" );

if ( tex_node != NULL )
{
	bNodeSocket *out_sock = nodeFindSocket( tex_node, SOCK_OUT, "Generated" );
	bNodeSocket *in_sock = nodeFindSocket( ima_node, SOCK_IN, "Vector" );

	/* Check if the socket in already connected to something */
	bNodeLink *link = in_sock ? in_sock->link : NULL;
	if ( in_sock != NULL && link == NULL )
	{
		nodeAddLink( ntree, tex_node, out_sock, ima_node, in_sock );

		nodePositionRelative( tex_node, ima_node, out_sock, in_sock );
	}
}