7
$\begingroup$

I am currently porting some code from the 2.49 API to the 2.6 API and looking for a nice way to replace this function call

vert_list["bone"] = b_obj.data.getVertsFromGroup("bone", 1)

This function returned a list of vertex tuples (index, weight).

Edit: A nice side effect of this function was that it also threw an AttributeError if the had no associated vertices group. Empty VertGroups are not allowed by our exporter.

I coded up a working sample for what I think I need based on the 2.49 API to show how I intend to access these values. This has been simplified with hard coded values.

import bpy

b_obj = bpy.data.objects['Cube']
b_verts = b_obj.data.vertices
b_arm = bpy.data.objects['Armature']


b_vert_group = b_obj.vertex_groups['Bone']
b_group_index = b_vert_group.index
b_list = []

for b_vert in b_verts:
    for g in b_vert.groups:
        if g.group == b_group_index:
            b_list.append((b_vert.index, g.weight))

What I am wondering is if anyone has an alternative way of accessing these values or perhaps someone would give a more pythonic version.

Most of the samples I have encountered relating to vertex groups are geared towards assignment rather than access. Even if I don't get an answer it might help someone else down the road.

$\endgroup$
  • $\begingroup$ if that works, it looks readable to me. $\endgroup$ – zeffii Jun 5 '13 at 10:47
  • $\begingroup$ @Zeffi - I reworked the solution to be more pythonic using list comprehension. The main goal I hoped was an alternative way to access the associated values. The new way storage method is inverse to 2.4, instead of groups pointing to verts, verts point to groups. $\endgroup$ – neomonkeus Jun 5 '13 at 13:09
  • 1
    $\begingroup$ I rewrote it here for myself with a list comprehension and it became less readable :) $\endgroup$ – zeffii Jun 5 '13 at 13:36
  • $\begingroup$ I looked through the fbx export script, found the BPyMesh_meshWeight2List func and it is simliar to the sample code. $\endgroup$ – neomonkeus Jun 5 '13 at 13:56
  • $\begingroup$ hint - you can add a break after the append (last line). $\endgroup$ – ideasman42 Jun 5 '13 at 17:19
3
$\begingroup$

No, there are no built-in functions which do this in a more pythonic way, however, unless you only ever want to access a single bone, Id suggest collecting all vertex groups at once.

def mesh_to_weight_list(ob, me):
    """
    Takes a mesh and return its group names and a list of lists,
    one list per vertex.
    aligning the each vert list with the group names,
    each list contains float value for the weight.
    """

    # clear the vert group.
    group_names = [g.name for g in ob.vertex_groups]
    group_names_tot = len(group_names)

    if not group_names_tot:
        # no verts? return a vert aligned empty list
        return [[] for i in range(len(me.vertices))], []
    else:
        weight_ls = [[0.0] * group_names_tot for i in range(len(me.vertices))]

    for i, v in enumerate(me.vertices):
        for g in v.groups:
            # possible weights are out of range
            index = g.group
            if index < group_names_tot:
                weight_ls[i][index] = g.weight

    return group_names, weight_ls

Note, that this example could use pythons array module for greater effeciency but using lists since its an example.

$\endgroup$
  • $\begingroup$ GUess you wrote the fbx exporter, very similiar. The question was really geared towards data access, to potentially avoid iterating over the data repeatability to get the various attributes, vcol, uv, etc, Could do it once and cram all in one large mess of doing everything at once :P $\endgroup$ – neomonkeus Jun 6 '13 at 9:19
2
$\begingroup$

I have accepted ideasman42's solution as the way to go for the example that I gave.

For completeness I am adding the solution that we are using for the exporter we am working on. This includes some additional complexity like replicating the AttributeError where a group did not map to one of our bones.

Additional constraint that was not mentioned in the original question:

  • All vertices must be weighted by at least one bone, select them to display to the user because we are nice people

vert_list = {}
vert_norm = {}
unassigned_verts = []

for bone_group in boneinfluences:
    b_list_weight = []
    b_vert_group = b_obj.vertex_groups[bone_group]

    for b_vert in b_obj.data.vertices:
       if len(b_vert.groups) == 0: #check vert has weight_groups
           unassigned_verts.append(b_vert)
           continue

       for g in b_vert.groups:
           if b_vert_group.name in boneinfluences:
               if g.group == b_vert_group.index:
                   b_list_weight.append((b_vert.index, g.weight))
                   break

       vert_list[bone_group] = b_list_weight             

   #create normalisation groupings
   for v in vert_list[bone_group]:
       if v[0] in vert_norm:
           vert_norm[v[0]] += v[1]
       else:
           vert_norm[v[0]] = v[1]

   # vertices must be assigned at least one vertex group
   # lets be nice and display them for the user 
   if len(unassigned_verts) > 0:
       for b_scene_obj in self.context.scene.objects:
           b_scene_obj.select = False

           self.context.scene.objects.active = b_obj
           b_obj.select = True

           # select unweighted vertices
           for v in mesh.vertices:
               v.select = False    

           for b_vert in unassigned_verts:
               b_obj.data.vertices[b_vert.index].select = True

           # switch to edit mode and raise exception
           bpy.ops.object.mode_set(mode='EDIT',toggle=False)
           raise NifExportError(
               "Cannot export mesh with unweighted vertices."
               " The unweighted vertices have been selected"
               " in the mesh so they can easily be"
               " identified.")
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.