Skip to content

Random material Assigner V2 – assign by vertex groups or loose parts!

March 21, 2013

Heya!

Been a while since I posted last, had lots of work, scripting most of the day, so at home I’ve been clearing my head with some “Deus Ex: Human Revolutions” action rather than bloggin’.

But yesterday I had an opportunity to add some features people requested for the random material assigner.

The two new features I added are distributing materials to vertex groups and to loose parts (islands) on the selected mesh object.

So it’s pretty easy to use and install, as described in the post about the first version of this script.

To DOWNLOAD V.2 and view the source, visit the github page.

screenshot01

Lessons learned from writing the new features

No direct method to find all the vertices associated with a vertex group

To iterate over the verts of each vert group, initially I explored the vertex group objects:

obj = bpy.context.object     # Reference to selected object in scene
vgroups = obj.vertex_groups  # Access the object's vgroup collection
for vgrp in vgroups:
    # ...and there's no method or property that directly returns
    # the list of vertices assigned to the group. Bummer.

But unfortunately, you can’t access the vert indices directly from the vertex group
collection of the mesh object.

So, what I decided to do is to create a dictionary that will store the index of each
vertex group as the key, and the list of indices of each of the individual verts
assigned to each group as the value.

So for a cube with 16 verts split into two vert groups, the dictionary will look something like this:

{
# vgroup index : [ list of vert indices associated with vgroup ]
  '1': [0, 1, 2, 3, 8, 10, 12, 14],
  '0': [4, 5, 6, 7, 9, 11, 13, 15]
}


This would be our 16 vert cube, which has two vert groups. The selected verts belong to group1 (index: 0)

So anyway, later this dictionary was used to iterate over the groups, select
the verts of each, and assign materials to the enclosed faces.

This is the code that created the dictionary. Pretty straight forward:

ob = bpy.context.object
groups = {}
for group in ob.vertex_groups:
    groups[str(group.index)] = []

for v in ob.data.vertices:
    # iterate over this particular vertex's vgroups
    for group in v.groups:
        # And add the vert index to the group dictionary and each group's list of verts
        groups[str(group.group)].append( v.index )

print( groups )

A nice tip that helped phrase this code came from a relevant BlenderArtists thread, thanks to ni-ko-o-kin.

Loose parts

This is another case where you don’t get direct access to the data and have to find a hacky way to your goal.

When I explored a mesh object’s data, I found no direct way to get a list of loose parts and their associated mesh elements. So, again, I searched the BA forums and posted a question. It never ceases to amaze me how quickly you can get quality answers there. This one was provided by Supergra, a mere 35 minutes after I posted the question. Thanks dude!

Instead of storing the data of which verts belong to each loose part, as I did with the vertex groups, I simply opted (as suggested),  to find each island’s verts and immediately assign a random material to the enclosed faces. The command that enables this is:

bpy.ops.mesh.select_linked( limit=False )

When you run this in edit mode, with a vertex selected, it selects all linked verts
(i.e. the verts that share the same mesh island).

To iterate over all loose parts, I ran this code:

bpy.ops.object.mode_set(mode = 'EDIT')  # Go to edit mode to create bmesh
ob = bpy.context.object                 # Reference active object
bm = bmesh.from_edit_mesh(ob.data)      # Create bmesh object from object mesh

vert_indices = [ vert.index for vert in bm.verts ]  # Reference all vertex indices

for vert in vert_indices:
bpy.ops.mesh.select_all(action='DESELECT')  # Deselect all verts

bm.verts[vert].select = True # Select one vert (the current index)

# Select all verts linked to this one (on the same island or "loose part")
bpy.ops.mesh.select_linked( limit=False )

# Go to face selection mode
bm.select_mode = {'FACE'}
bm.select_flush(True)

rand_mat_index = random.randint(0, no_of_materials - 1)

# iterate over all selected (linked) faces and assign material
for face in bm.faces:
    if face.select:
        face.material_index = rand_mat_index  # Assign random material to face

# remove selected vertices from list
for vert in bm.verts:
    if vert.select:
        removed = vert_indices.pop( vert_indices.index(vert.index) )

The nifty trick hidden in this code is the dynamic for loop.
The contents of the list that the for-loop iterates over changes during the loop’s
execution. I actually didn’t know that this was possible, and had to test it on a small list first.

mylist = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

for i in mylist:
    print( i )
    excluded_item = mylist.pop()

>>>>
1
2
3
4
5

This loop will only run 5 times, and print out only the first 5 numbers, even though it was executed with a list of 10 items.

The same thing happens with the code above. The loop starts with the list of all vert indices. But after the linked verts associated with a specific index are selected and processed, they’re promptly removed from the index list,  thus all these indices will not be iterated over again (preventing the code from changing the material of a specific loose part twice).

So eventually, the code iterates over each loose part just once, since a loose part (and its associated verts) is removed from the vertex index list with each iteration after being processed. A nice solution to the problem I think.

That sums the two small challanges I faced in writing the new features.

Until next time!

Advertisements
6 Comments
  1. Pawel permalink

    doesn’t work with 2.72

    • Thanks Pawel,
      Fixed the issue, please try reinstalling the new script (updated on github).

  2. Hi Bio, I asked it in the other post earlier. When i use color by loose parts it goes into edit mode with blender 2.77. I cant use vertex groups cause the mesh has to many other vertex groups and blender turn non responsive then. WHen i use ‘loose parts’ in the output i see the code “bpy.ops.object.editmode_toggle()”. Perhaps i can comment out this part in your script and see what happens

  3. schroef permalink

    Hi there, Im wondering why this thing takes so long to loop through a mesh. I went in and added a print item and a facecount. At the end of the part loop, there is a variable remove. If i understand well it removes the done vertice indices. Im guesing its still looping through parts which are already done

  4. schroef permalink

    the remove part doesnt do anything its get code assigned buts its never used.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: