Home Coding, Scripting, Shaders

Blender Python 2.79: Writing Vertex Indices From Vertex Groups?

polycounter lvl 6
Offline / Send Message
MrQuetch polycounter lvl 6
Hello, everyone.

I've found a topic that is already leading me in the right direction: https://www.blender.org/forum/viewtopic.php?t=28466
However, this script only writes what vertex group contains what amount of vertices.
What I would like to do is write out the vertex indices that are used within that vertex group to a file.
Currently, I cannot think of a good way to go about doing this. Does anyone have any suggestions?

Any help is greatly appreciated.

Replies

  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    I managed to get this. It appears to be working properly. I added arrays for bone and vertex IDs, and appended values to them. Here's my edited code:

    # Armature Test
    
    import bpy
    
    path = 'C:\\Users\\USER\\Desktop\\User\\Models\\LowPoly\\Model.txt'
    
    outfile = open(path, 'w')
    
    ob = bpy.context.object
    
    bone_ids = []
    vert_ids = []
    
    for v in ob.data.vertices:
        for g in v.groups:
            bone_ids.append(ob.vertex_groups[g.group].name)
            vert_ids.append(v.index)
    
    for i in vert_ids:
        outfile.write("%s: %s \n" % (bone_ids[i], vert_ids[i]))

  • RN
    Offline / Send Message
    RN sublime tool
    I'd prefer to have a single collection of all groups, like this:
    # Armature Test<br>import bpy<br>import json<br>ob = bpy.context.object<br><br>allVertexGroups = {group.name:[ ] for group in ob.vertex_groups}<br>for v in ob.data.vertices:<br>    for g in v.groups:<br>        allVertexGroups[g.group.name].append((v.index, g.weight))<br><br># allVertexGroups: dictionary of vertex-group names that map to lists of (vertexID, weight) pairs.<br><br>path = 'C:\\Users\\USER\\Desktop\\User\\Models\\LowPoly\\Model.txt'<br>with open(path, 'w', encoding='utf-8') as outfile:<br>    outfile.write(json.dumps(allVertexGroups, indent=4, ensure_ascii=False))



  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    RN said:
    I'd prefer to have a single collection of all groups, like this:
    # Armature Test<br>import bpy<br>import json<br>ob = bpy.context.object<br><br>allVertexGroups = {group.name:[ ] for group in ob.vertex_groups}<br>for v in ob.data.vertices:<br>    for g in v.groups:<br>        allVertexGroups[g.group.name].append((v.index, g.weight))<br><br># allVertexGroups: dictionary of vertex-group names that map to lists of (vertexID, weight) pairs.<br><br>path = 'C:\\Users\\USER\\Desktop\\User\\Models\\LowPoly\\Model.txt'<br>with open(path, 'w', encoding='utf-8') as outfile:<br>    outfile.write(json.dumps(allVertexGroups, indent=4, ensure_ascii=False))



    Thank you, RN! This is nearly perfect, but I'm getting an error for: allVertexGroups[g.group.name].append((v.index, g.weight)).

    Keep in mind, I'm using 2.79. I'm not sure if that means anything, though.
  • RN
    Offline / Send Message
    RN sublime tool
    You know, I've never ever written a script that didn't have an error in it, hahaha. The error is...
    'int' object has no attribute 'name'
    That .group attribute is an integer index into the .vertex_groups collection of the object, not a reference to the vertex-group itself.
    So it should really be this:
    vertexGroupsExport = {group.name:[ ] for group in vertexGroupsCollection}
    for v in ob.data.vertices:
    for g in v.groups:
    vertexGroupsExport[vertexGroupsCollection[g.group].name].append((v.index, g.weight))

    # Write 'vertexGroupsExport' to a file.
    </code><code><code>vertexGroupsCollection = ob.vertex_groups<br>




  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    RN said:
    You know, I've never ever written a script that didn't have an error in it, hahaha. The error is...
    'int' object has no attribute 'name'
    That .group attribute is an integer index into the .vertex_groups collection of the object, not a reference to the vertex-group itself.
    So it should really be this:
    vertexGroupsExport = {group.name:[ ] for group in vertexGroupsCollection}
    for v in ob.data.vertices:
    for g in v.groups:
    vertexGroupsExport[vertexGroupsCollection[g.group].name].append((v.index, g.weight))

    # Write 'vertexGroupsExport' to a file.
    </code><code><code>vertexGroupsCollection = ob.vertex_groups<br>




    Thanks again, RN! This works perfectly, now. I apologize for asking you to look at it, again. But, I'm glad that it wasn't a problem on my part. I can see why you wanted to format the text this way... I like that the bones are in proper order.
  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    Just out of curiosity, is there a way I can get my bones printed in a vertical list instead of a horizontal one? I believe it would make better readability. So, instead of something like this:
    {'Bone': [0, 1, 2, 3, 8, 9, 10, 11], 'Bone.001': [4, 5, 6, 7]}
    It would look something like that:
    Bone: 0, 1, 2, 3, 8, 9, 10, 11
    Bone.001: 4, 5, 6, 7
    How would I go about doing that? Thanks in advance.
  • RN
    Offline / Send Message
    RN sublime tool
    You want to make a string out of the key / value pairs of that dictionary, separated by newline characters.
    >>> stringOutput = '\n'.join('%s: %s' % (key, repr(value)) for key, value in dictionary.items())<br>>>> print(stringOutput) # Or write to file.<br>Bone.001: [4, 5, 6, 7]<br>Bone: [0, 1, 2, 3, 8, 9, 10, 11]
    The problem is if you want to parse this string later. This is why using JSON saves you time, for every json.dumps() you can use a json.loads() on the contents of the text file, instead of using the several string operations that you'd need.
  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    RN said:
    You want to make a string out of the key / value pairs of that dictionary, separated by newline characters.
    >>> stringOutput = '\n'.join('%s: %s' % (key, repr(value)) for key, value in dictionary.items())<br>>>> print(stringOutput) # Or write to file.<br>Bone.001: [4, 5, 6, 7]<br>Bone: [0, 1, 2, 3, 8, 9, 10, 11]
    The problem is if you want to parse this string later. This is why using JSON saves you time, for every json.dumps() you can use a json.loads() on the contents of the text file, instead of using the several string operations that you'd need.
    Thanks again, RN! You're really on top of this.
  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    Hello, everyone.

    I know this is getting older. After having looked at the Blender Python API, and some other sources on the internet, I have managed to come up with this method of getting the correct vertices from the correct bones - all as integers. Here's the code if anyone wants to use it. I know it's not the nicest looking thing, but it works like it's supposed to.
    import bpy
    import os
    
    mesh = bpy.context.active_object
    
    # Loop through existing vertex groups.
    for i in range(0, len(mesh.vertex_groups)):
        for j in (mesh.data.vertices):
            for k in (j.groups):
                vg = mesh.vertex_groups[i].index
                if k.group == vg:
                    print("bone %d: vertex %s " % (i, j.index))
    
  • RN
    Offline / Send Message
    RN sublime tool
    @MrSquetch good to see you producing, keep going!

    A recommendation: looping through all vertices of a mesh should be done with extreme care, because it might be a very long loop.
    In your case it's a magnitude larger, looping through all vertices per each vertex group.

    If you want to associate a vertex group with the vertices that belong to it, there's this simpler way:

    <code># Line below uses "dictionary comprehension" to create a dictionary.<br>myVertexGroups = {i: [ ] for i in range(len(mesh.vertex_groups))}

    for v in mesh.data.vertices:
    for g in v.groups:
    myVertexGroups[g.group].append(v)

    for groupIndex, groupVertices in myVertexGroups.items():
    print('Bone %i:\n\t%s\n' % (groupIndex, groupVertices))mesh = bpy.context.active_object<br>
  • MrQuetch
    Offline / Send Message
    MrQuetch polycounter lvl 6
    RN said:
    @MrSquetch good to see you producing, keep going!

    A recommendation: looping through all vertices of a mesh should be done with extreme care, because it might be a very long loop.
    In your case it's a magnitude larger, looping through all vertices per each vertex group.

    If you want to associate a vertex group with the vertices that belong to it, there's this simpler way:

    <code># Line below uses "dictionary comprehension" to create a dictionary.<br>myVertexGroups = {i: [ ] for i in range(len(mesh.vertex_groups))}

    for v in mesh.data.vertices:
    for g in v.groups:
    myVertexGroups[g.group].append(v)

    for groupIndex, groupVertices in myVertexGroups.items():
    print('Bone %i:\n\t%s\n' % (groupIndex, groupVertices))mesh = bpy.context.active_object<br>
    Hey, RN.

    Thank you! Most of my models are low polygon - generally 550 triangles or less. So, I don't think my code is too bad. I'm probably very wrong, though. But, I'll definitely keep this dictionary method in mind. I'm beginning to see why you do it this way. Not only does it take up less code - but it looks like it's becoming easier to reference.
Sign In or Register to comment.