1
$\begingroup$

I have an object with vertex groups already set up and materials already added. I want to run through all of vertex groups in that object and randomly assign one of the materials to it. How do I do this? I can run through the vertex groups but I'm struggling to assign the material to it.

Thanks

$\endgroup$
3
  • 1
    $\begingroup$ You can't assign materials to vertex, materials go into faces only, and vertex groups don't unequivocally define mutually exclusive groups of faces. See blender.stackexchange.com/questions/51296/… $\endgroup$ Oct 7, 2022 at 9:03
  • $\begingroup$ Thank you! At least that explains why I couldn't work it out!! :) $\endgroup$ Oct 7, 2022 at 9:05
  • $\begingroup$ You might be able to convert a vertex group into a selection of faces, but there will likely be some overlap $\endgroup$ Oct 7, 2022 at 9:14

1 Answer 1

0
$\begingroup$

A vertex group, as the name says, is a group of vertices. A material is assigned to a face, not to a group. So how do you translate between the two? This will cause issues in a moment.

How to count the number of faces of each vertex group?

I think the most reasonable approach is the "select a face if all its vertices are selected" - it's also intuitive because that's how selection in Blender works. So I'm using the Bmesh - All Corners solution. First let's prepare Suzanne for make-up:

Whoops! You can't select lips without selecting the mouth as well. Again, this will cause a problem in a moment. Also, notice how the vertex groups and the material slots appear in the same order - this is needed, because the script will operate on indices rather than names - bmesh doesn't even store the names (neither does a mesh, only an object stores this information, to my knowledge).

import bpy, bmesh
from bpy import context as C, data as D
from collections import Counter

me = C.object.data
bm = bmesh.from_edit_mesh(me)

layer = bm.verts.layers.deform.active
for f in bm.faces:
    groups = (v[layer].keys() for v in f.verts)
    groups_set = set(next(groups))
    for g in groups:
        groups_set.intersection_update(g)
    if groups_set:
        f.material_index = groups_set.pop() + 1
    else:
        f.material_index = 0

Whoops! The entire mouth (the inside) has the lip color. Or at least for me this is the case, since I'm just popping from a set, I'm getting the first vertex group found for a given face - Lips is indexed before Mouth so it was found first, and is popped first. However, fixing this by swapping both vertex groups would be a mistake - while current CPython implementation is deterministic in this regard, there's no formal guarantee that this will always be so:

A set is an unordered collection

source

Instead, just explicitly prioritize the index of the Mouth vertex group:

import bpy, bmesh
from bpy import context as C, data as D
from collections import Counter

me = C.object.data
bm = bmesh.from_edit_mesh(me)

layer = bm.verts.layers.deform.active
for f in bm.faces:
    groups = (v[layer].keys() for v in f.verts)
    groups_set = set(next(groups))
    for g in groups:
        groups_set.intersection_update(g)
    if 3 in groups_set:  # prioritize Mouth
        f.material_index = 4
    elif groups_set:
        f.material_index = groups_set.pop() + 1
    else:
        f.material_index = 0
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .