Shape Keys Import and Export

Scripting in Blender with Python, and working on the API

Moderators: jesterKing, stiv

Post Reply
Posts: 0
Joined: Tue Dec 11, 2012 8:45 pm

Shape Keys Import and Export

Post by regg » Tue Dec 11, 2012 8:56 pm

Hello Everyone

I'm creating a simple script for deforming some base mesh i am working with via Shape Keys.

I've got a basic question for it: Is it possible to import/export shapekey modifiers via Python?

For Example: I want to create set of few deformations to default monkey head (like nose lenght, ears size etc) and make them show each time i create a new monkey head in any scene.

Only thing I've got looking in web was that link: ... ing-python

Is that good hint to start?


P.S. Apologize for not so proper grammar english, hope i wrote everything quite understandable.

Posts: 0
Joined: Fri Dec 07, 2012 9:00 pm

Post by CliveP » Mon Dec 17, 2012 8:00 pm

Hi Regg

I've just started scripting, and am doing the same thing.

Here's my import code (I'm not trying to win prizes for good Python style!):

Code: Select all

# three global variables
#    curMorph  is a count of the number of morphs (or shapes) to be made -1
#    morphNames is a list of curMorph+1 names (as byte arrays)
#    morphData  is a list of truples for all the offsets for all the morphs
#                      each tuple has 5 members (index vertexId x y z)
#                           index is the morph that this offset belongs to(0..curMorph)
#                      vertexId is the index of the vertex in the model to be changed
#                     x y z are the changes in the vertex position required
curMorph   = -1
morphNames = []
morphData  = []

# This function does the wotk and is called with the model's name
def makeMorphs(modelName):
    global morphNames
    global curMorph
    global morphData

#    for morphN in morphNames:
#        print(morphN)    
    obj =[modelName] = True = obj 
#    objs =
#    for objN in objs:
#        print(
    # Make the basic state
    ob = bpy.context.object
    basis = ob.active_shape_key
    curMorph = -1
    # Make the morphs
    for morphN in morphNames:
        curMorph += 1
        numDelta = 0  # deltas added to this morph
        # Add shape 
        newShape = ob.active_shape_key = morphN.decode("utf-8")
        newShape.slider_min = 0.0
        newShape.slider_max = 1.0    
        for (index, vertexId, x, y, z) in morphData:
            if index == curMorph:
                numDelta += 1
                pt =[vertexId].co
                pt[0] = pt[0] + x
                pt[1] = pt[1] + y 
                pt[2] = pt[2] + z
I leave it to you how you set the three global data values up
Note: numDelta is actually redundant, but useful for diagnostic prints

For export the code is (where fw writes a string to a file):

Code: Select all

            # write the shape key data
            for keys in
                for key in keys.key_blocks[1:]:
                    fw('morph %s\n' % str(
                    basis_verts = keys.key_blocks[0].data
                    for j, kv in enumerate(
                        delta = - basis_verts[j].co
                        if delta.length > 0.000001:
                            fw('delta %d ' % j)
                            fw('%.6f,%.6f,%.6f\n' % (delta[0], delta[1], delta[2]))
Hope this helps

Posts: 0
Joined: Tue Dec 11, 2012 8:45 pm

Post by regg » Thu Dec 27, 2012 4:10 pm

Thank You CliveP! That code got me to understand more of shapekeys structure via code. (btw, good lucid Python style should be awarded!)

Exporting keys data works great, i've even quickly added slider values onto it, but I'm still going dizzy if I think how to read it right.

I dont want to take advantage of you, but could you help me how to properly fill those two arrays (morphNames, morphData) you've used in import code? I'm still crawling on all fours in programing so it would be a great help.

structure of data file looks like that:

Code: Select all

morph ShapeKeyName
values -0.600000, 0.800000 
delta 0 0.000000,-0.043533,-0.063295
delta 1 0.026817,-0.033047,-0.056711
delta 2 0.026790,-0.069979,-0.084045
delta 3 0.046614,-0.021710,-0.040648
delta 4 0.000000,-0.030252,-0.088519
I know first vertexid numbers are ascending but it looks that first verteces of a model are also changed in first shapekey. But how to get python to know, which value goes where?

Could i get some help how to import that data right?


Posts: 0
Joined: Fri Dec 07, 2012 9:00 pm

Post by CliveP » Thu Dec 27, 2012 6:21 pm

Hi Regg

How you set up the two data structures rather depends on where you're getting the data from - I'm reading it from a file I'm importing in a custom format - so probably not much help to you.

What may help is if I show you a simple example of what they should contain, given the set of vertices you are trying to modify.

Say I want two shape modifiers:
The first one is called: morph1
and modifies two vertices: index 21 by x1, y1, z1
and 22 by x2, y2, z2
(indices start from 0 - x,y,z are the required change in position )
The second one is called: morph2
and modifies just one vertex: index 42 by x3, y3, z3
The two data structures could be set up as:

Code: Select all

morphNames = []
morphNames.append( b'morph1' )   # note 'morph index' == 0
morphNames.append( b'morph2' )   # note 'morph index' == 1

Code: Select all

morphData  = [] 
morphData.append( (0, 21, x1, y1, z1 ) )    # first vertex of morph1
morphData.append( (0, 22, x2, y2, z2) )
morphData.append( (1, 42, x3, y3, z3) )     # first vertex of morph2
Note that the names are stored as byte arrays (because that's how I read them from the file). If you start with strings, the .decode("utf-8") in makeMorphs is unnecessary

Hope this clarifies what's required

Posts: 0
Joined: Tue Dec 11, 2012 8:45 pm

Post by regg » Thu Dec 27, 2012 8:23 pm

Hey CliveP, thank You for fast response...

The problem i have, is not to add a data in required arrays but to do it automaticaly from file. After exporting data from another project i've got a file with 1,7k line data for all shapekey morphs.

I need to read that file via python instead of rewrite all that data into append function.

I know what i want, but still dont know how to read that txt file properly to mine script.

If file line starts with 'morph' all after it is shapekey name (goes to morphNames[]) - new modifier is added to array with it index. - current morph index (cmi) - 0
If file line starts with 'values' data after it goes to slider_min and slider_max
If file line starts with 'delta' data after it is vertID, x, y, z so it goes to morphData[] as (cmi, vertID, x, y, z )

Third line is repeat till next line is morph again, and when it is, cmi goes +1, so next 'values' and 'delta' lines are added into next shape modifier data.

So script is reading the file line by line searching for new morph line, adding data to current one till it doesnt find next morph line or end of file.

Logical. And also alows me to add or change exported shapekey data file without changing manualy chosen fields in array.

So how to do that without dozens of lines looking like some monstrous creatures:

if chars)== 'morph'
morphName[].append(everything you got)
if firstchars == values, get numbers divided by comma and put them into slider min and max of current morph blah blah blah etc...

or how to export those values so they can be easily read through script like that?

I can easily describe how that file should be read but i have no idea how to write that so python could understand me where put what.


Posts: 0
Joined: Fri Dec 07, 2012 9:00 pm

Post by CliveP » Fri Dec 28, 2012 6:59 pm

Hi Regg

I suggest you look at scripts\addons\io_scene_obj which is the Wavefront OBJ importer (which is where I started)

A key bit of code is:

Code: Select all

   file = open(filepath, 'rb')
    for line in file:                 # read each line from the file
        line_split = line.split()  # break the line into a list of separate strings

        if not line_split:           # empty line

        line_start = line_split[0]  # we compare with the first string a _lot_

        if line_start == b'v':
            # what to do when a line starts v

        if line_start == b'vn':
            # what to do when a line starts vn

 ... and so on

Hope this helps

P.S. I'm probably not going to be much more help, as I'm giving up on Blender - it keeps locking up on my hardware and the 'tech support' response seems to be 'ATI's openGL drivers aren't very good' which doesn't help me!

Post Reply