Saturday, February 2, 2019

Blender Skin Weights import and exporter

Hi,
Still haven't made this into an addon yet.
But the code snippets might be helpful for learning.
Modify and use at your own risk.

The purpose is to be able to export skin weights of a model. And also ability to import skin weights to model.
It Assumes armature is the same for both import and export. It doesn't create an armature on import (it assumes armature is already bound to same bones as were exported).
There is a part where it writes weights to disc.

Tested in blender 2.79.

Happy Sketching!
Nate

#Usage:
#assumes your character mesh is selected
#assumes you enter in a weight file path
#little error checking. use at your own risk.
#exportWeightOnSelected(weightFile = '/Users/Nathaniel/Documents/src_blender/python/snippets/weights1.naSkin')
#importWeightOnSelected(weightFile = '/Users/Nathaniel/Documents/src_blender/python/snippets/weights1.naSkin')


import bpy
import json
import os

def exportWeightOnSelected(weightFile = '/Users/Nathaniel/Documents/src_blender/python/snippets/weights1.naSkin'):
    """
    export skin weights on selected character
    """
    #assumes your character is selected
    #no error checking
    outDir = os.path.dirname(weightFile)
    if not os.path.exists(outDir):
        print('Requires an out directory that exists to write weight file')
        return
    
    vertexIds = []
    weightDict = {} #will hold weights info

    bpy.ops.object.mode_set(mode="EDIT")
    charObj = bpy.context.edit_object

    vtxGroups = []
    vtxGroups = [ (grp.name,grp.index) for grp in charObj.vertex_groups ]

    mesh = charObj.data

    vertexIds = [ v.index for v in mesh.vertices ]

    bpy.ops.object.mode_set(mode="OBJECT")

    for vtxId in vertexIds:
        boneDict = {}
        for bone, boneIndex in vtxGroups:
            weight = charObj.data.vertices[vtxId].groups[boneIndex].weight
            weight = round(weight,4)
            print('export weights on vtx >>> %s for bone >>> %s weight >> %s' %(vtxId,bone,weight) )
            boneDict[ bone ] = weight
        weightDict[vtxId] = boneDict #update weight info
        
    print(weightDict)
        
    #export weights to file
    with open( weightFile, 'w' ) as f:
        f.write( json.dumps(weightDict) )
    
    

def importWeightOnSelected(weightFile = '/Users/Nathaniel/Documents/src_blender/python/snippets/weights1.naSkin'):
    """
    import skin weights on selected character
    """
    if not os.path.exists(weightFile):
        print('Requires weightfile path to exist')
        return
        
    #assumes your character is selected
    #no error checking
    
    weightDict = {} #will hold weights info

    with open( weightFile, 'r' ) as f:
        weightDict = json.load(f)
    
    bpy.ops.object.mode_set(mode="EDIT")
    charObj = bpy.context.edit_object

    bpy.ops.object.mode_set(mode="OBJECT")

    for vtxId, bones in weightDict.items():
        print( vtxId )
        for boneName, boneWeight in bones.items():
            print('import weights on vtx >>> %s for bone >>> %s weight >> %s' %(vtxId,boneName,boneWeight) )
            #change weights here. No error checking. example wouldnt work if couldnt find bone in armature
            charObj.vertex_groups[ boneName ].add( [int(vtxId)], float(boneWeight), 'REPLACE' )


#inspired by online post: https://blender.stackexchange.com/questions/39653/how-to-set-vertex-weights-using-blenders-python-api