Vertex weights and indices for a bmesh?

I’m currently attempting to create an import addon. So far I’ve managed to add the import option to the menu, and read all of the data from the source file. And now I’m in the process of converting raw values I have to blender structures so they can be added to the scene.

Right now I’m running into the problem of what do to after creating a basic mesh. In terms of coding, I’ve managed to find two approaches to making a basic mesh. One is calling the from_pydata function:

        vertices = []
        for vertex in self.verts:
            pos = vertex['pos']
            x = pos[0]
            y = pos[1]
            z = pos[2]
            vertices.append( (x, z, y) )

        faces = []
        for group in self.faces:
            for i in range(0, group['faceCount']):
                a = group['indices'][i * 3 + 0]['index']
                b = group['indices'][i * 3 + 1]['index']
                c = group['indices'][i * 3 + 2]['index']
                faces.append( (a, b, c) )

        mesh = bpy.data.meshes.new('Mesh')
        mesh.from_pydata(vertices, [], faces)
        mesh_object = bpy.data.objects.new('Mesh_Object', mesh)
        bpy.context.scene.objects.link(mesh_object)
        bpy.context.scene.objects.active = mesh_object

And the other is to create the mesh using the bmesh structure

        bm = bmesh.new()
        mesh = bpy.data.meshes.new('Mesh')

        for vertex in self.verts:
            pos = vertex['pos']
            x = pos[0]
            y = pos[1]
            z = pos[2]
            vert = bm.verts.new( (x, z, y) )

        bm.verts.ensure_lookup_table()

        for group in self.faces:
            for i in range(0, group['faceCount']):
                a = bm.verts[group['indices'][i * 3 + 0]['index']]
                b = bm.verts[group['indices'][i * 3 + 1]['index']]
                c = bm.verts[group['indices'][i * 3 + 2]['index']]
                face = bm.faces.new( (a, b, c) )
                face.material_index = group['materialIndex']
                # face.loop bmloop sequence to add uv


        bm.to_mesh(mesh)
        bm.free()

        mesh_object = bpy.data.objects.new('Mesh_Object', mesh)
        bpy.context.scene.objects.link(mesh_object)

But I’m at a loss to where to go from there. I still have materials, textures and uv’s to implement. But I want to prioritize the bones, skinning and animation to put my time into getting those to work before going back and adding in the textures and materials.

For vertex weights, it’s pretty vanilla. For every vertex I have a list of up to four indices which reference a bone by index. And four weights between 0 and 1 corresponding to the weight for each indice.

0 (x, y, z) ( 0, 0, 0, 0) (1.0, 0.0, 0.0, 0.0)
1 (x, y, z) (0, 1, 0, 0), (0.5, 0.5, 0.0, 0.0)

So for example, like above, I have for vertex 0, i have four indices for bones (all 0) and 100% of the vertex is weighted to bone 0. And for vertex 1, four indices, 0 1 0 0 and 50% of the vertex is weighted to bone 0 and 50% is weighted to bone 1.

So I’m trying to find the syntax for how to implement this using the blender python api on my import script. For either bmesh, or the normal way. bmesh seems easier, but I can’t seem to find any code examples of it in the wild. And the normal way has examples, but it takes a long time to dig and read other people’s code in order to gain enough understanding to implement it into your own code.

So from what I can tell, for bmesh, there implementation has something to do with BMLayerAccessVert, and for normal, something to do with vertex groups. But all of the code and examples I’ve read only seem to attach a weight value and I can’t tell how bone indices are actually being assigned for each weight. I’m looking for code on how to do this, and can’t post intermediate code. Because I really am having a hard time finding hints on how to do anything beyond declaring a mesh from a list of vertices and faces.

I’m not sure if I’m making any progress or not.

So far I’m able to create an armature, and I’m able to create a mesh. I’m able to set the parent of the mesh to the armature and I’m able to create a list of vertex groups for each bone with a list of weights. The problem is the when I go into pose mode, select a bone and move it, I don’t get any deformation of the mesh. The bone just moves on it’s own independently of the mesh. And now I’ reading over the mmd import addon trying to igure out what I missed because the only place I can find any relevant information.

So what I have so far is:

	def createBMesh(self):
		
		# Create Armature

		armature = bpy.data.armatures.new('Armature')
		armature_object=bpy.data.objects.new('Armature_object', armature)
		armature_object.show_x_ray=True
		armature.show_names=True
		armature.draw_type = "STICK"
			
		bpy.context.scene.objects.link(armature_object)
		bpy.context.scene.objects.active = armature_object
		bpy.context.scene.update()

		bpy.ops.object.mode_set(mode='EDIT')
		
		print("Creating bones")

		for bone in self.bones:
			id = bone['id']
			pid = bone['parent_id']
			mtx = bone['matrix']

			joint = armature.edit_bones.new("bone_%03d" % id)
			joint.head = Vector( (0,0,0) )

			if pid != -1:
				joint.parent = armature.edit_bones[pid]
				joint.head = joint.parent.tail

			tail = Vector( (mtx[12], mtx[14], mtx[13]) )
			joint.tail = joint.head + tail

Make the armature and a list of bones for the mesh. The head and tail calculation for the bones is probably wrong, but that’s okay, I can go back and figure it out once I’ve confirmed that vertex weights work.

		bm = bmesh.new()
		mesh = bpy.data.meshes.new('Mesh')

		for vertex in self.verts:
			pos = vertex['pos']
			x = pos[0]
			y = pos[1]
			z = pos[2]
			vert = bm.verts.new( (x, z, y) ) 
		
		bm.verts.ensure_lookup_table()

		for group in self.faces:
			for i in range(0, group['faceCount']):
				a = bm.verts[group['indices'][i * 3 + 0]['index']]
				b = bm.verts[group['indices'][i * 3 + 1]['index']]
				c = bm.verts[group['indices'][i * 3 + 2]['index']]
				face = bm.faces.new( (a, b, c) )
				face.material_index = group['materialIndex']
				# face.loop bmloop sequence to add uv


		bm.to_mesh(mesh)
		bm.free()

		mesh_object = bpy.data.objects.new('Mesh_Object', mesh)

From there i make a mesh from a bmesh. There shouldn’t be anything too different going on here at all, since what I have here basically amounts to mesh.from_py_data(vertices, edges, faces);

		# For Vertex Weights, first we create a group for each bone
		for i in range(0, len(self.bones)):
			mesh_object.vertex_groups.new("bone_%03d" % i)

		#Loop over all of the vertices
		for vertex in self.verts:
			index = vertex['index']
			indices = vertex['indices']
			weights = vertex['weights']
			mesh_object.vertex_groups[indices[0]].add([index], 1.0, "REPLACE")
		
		bpy.ops.object.mode_set(mode='OBJECT')
		
		mesh_object.parent = armature_object
		bpy.context.scene.objects.link(mesh_object)
		bpy.context.scene.objects.active = mesh_object

And here’s where the crazy pills start to kick in. First I create a vertex group for each bone, with the name of each bone. Then I loop over all of the vertices in the mesh, and for each vertice, I select one of the vertex groups, assign the vertex index to it, assign a weight of 1.0 and then select “REPLACE” (i tried “ADD” too same result).

So after that, we have an armature, we have a mesh, we have a mesh group. I then set the parent of the mesh to the armature, since that looked to be the secret sauce from what I could parse out of the mmd import addon, and then link the mesh to the scene.

The problem is that the armature moves independent of the mesh and I am getting no deformation from the import plugin. And right now I still can’t figure out what I’m doing differently compared to the fbx and mmd import addons.

I think it’s missing an Armature modifier on the mesh object? If you do Ctrl+P in Blender it gives you the option to parent as armature and add the modifier immediately, but mesh_object.parent = .. will not do it, you need to create the modifier yourself.

I’m still completely in the dark with respect to creating modifiers.

I can select the mesh and select the armature and press ctrl+p, and then select “Armature deform” from the context menu. And after that, the vertex weights actually work (if only my bones were correct).

What I’m really confused on is that the context menu gives the code for bpy.ops.object.parent_set(type=‘ARMATURE’). Which kind of seems to be what I’m doing anyways? Also I have no idea of how i’m supposed to get from mesh = bpy.data.meshes.new('Mesh') to bpy.ops.object. Which object is being accessed from bpy.ops exactly?

And this is in terms of doing this operation from the UI. I still don’t know what syntax I need to make these changes in the python api. Or if there is another way to create a deform modifier for the mesh object.

Edit:

Okay I found https://docs.blender.org/api/current/bpy.types.ArmatureModifier.html?highlight=modifier

Which has class bpy.types.ArmatureModifier(Modifier), so I guess I can make a modifier with “new”. And then it has the property of use_vertex_groups. So the next question is in the API, how do I add a modifier to a mesh object?

From what I can tell this is how you are setting the parent?

mesh_object.parent = armature_object

Creating the modifier as well would be done like this:

mesh_object.parent = armature_object
modifier = mesh_object.modifiers.new(type='ARMATURE', name="Armature")
modifier.object = armature_object

Omg, thank you, that worked!!!

So the next step is to figure out how to calculate the correct bone.head and tail positions from a 4x4 transformation matrix. Also, it might not be needed, but I keep running into forums like this when googling, so for anyone else that might stumble across this looking for hints, it’ll include the function below (for good measure).

[code] def createBMesh(self):

	# Create Armature

	armature = bpy.data.armatures.new('Armature')
	armature_object=bpy.data.objects.new('Armature_object', armature)
	armature_object.show_x_ray=True
	armature.show_names=True
	armature.draw_type = "STICK"
		
	bpy.context.scene.objects.link(armature_object)
	bpy.context.scene.objects.active = armature_object
	bpy.context.scene.update()

	bpy.ops.object.mode_set(mode='EDIT')
	
	print("Creating bones")

	for bone in self.bones:
		id = bone['id']
		pid = bone['parent_id']
		mtx = bone['matrix']

		joint = armature.edit_bones.new("bone_%03d" % id)
		joint.head = Vector( (0,0,0) )

		if pid != -1:
			joint.parent = armature.edit_bones[pid]
			joint.head = joint.parent.tail

		tail = Vector( (mtx[12], mtx[14], mtx[13]) )
		joint.tail = joint.head + tail
		
	# Create Mesh

	bm = bmesh.new()
	mesh = bpy.data.meshes.new('Mesh')

	for vertex in self.verts:
		pos = vertex['pos']
		x = pos[0]
		y = pos[1]
		z = pos[2]
		vert = bm.verts.new( (x, z, y) ) 
	
	bm.verts.ensure_lookup_table()

	for group in self.faces:
		for i in range(0, group['faceCount']):
			a = bm.verts[group['indices'][i * 3 + 0]['index']]
			b = bm.verts[group['indices'][i * 3 + 1]['index']]
			c = bm.verts[group['indices'][i * 3 + 2]['index']]
			face = bm.faces.new( (a, b, c) )
			face.material_index = group['materialIndex']
			# face.loop bmloop sequence to add uv


	bm.to_mesh(mesh)
	bm.free()

	mesh_object = bpy.data.objects.new('Mesh_Object', mesh)

	# For Vertex Weights, first we create a group for each bone
	for i in range(0, len(self.bones)):
		mesh_object.vertex_groups.new("bone_%03d" % i)

	#Loop over all of the vertices
	for vertex in self.verts:
		index = vertex['index']
		indices = vertex['indices']
		weights = vertex['weights']
		mesh_object.vertex_groups[indices[0]].add([index], 1.0, "REPLACE")
	
	bpy.ops.object.mode_set(mode='OBJECT')
	
	mesh_object.parent = armature_object
	modifier = mesh_object.modifiers.new(type='ARMATURE', name="Armature")
	modifier.object = armature_object

	bpy.context.scene.objects.link(mesh_object)
	bpy.context.scene.objects.active = mesh_object
	return 0[/code]