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:
Is that good hint to start?
P.S. Apologize for not so proper grammar english, hope i wrote everything quite understandable.
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!):
# 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
# for morphN in morphNames:
obj = bpy.data.objects[modelName]
obj.select = True
bpy.context.scene.objects.active = obj
# objs = bpy.data.objects
# for objN in objs:
# 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
newShape.name = 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 = newShape.data[vertexId].co
pt = pt + x
pt = pt + y
pt = pt + 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):
# write the shape key data
for keys in bpy.data.shape_keys:
for key in keys.key_blocks[1:]:
fw('morph %s\n' % str(key.name))
basis_verts = keys.key_blocks.data
for j, kv in enumerate(key.data):
delta = kv.co - basis_verts[j].co
if delta.length > 0.000001:
fw('delta %d ' % j)
fw('%.6f,%.6f,%.6f\n' % (delta, delta, delta))
Hope this helps
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:
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?
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:
|morphNames = 
morphNames.append( b'morph1' ) # note 'morph index' == 0
morphNames.append( b'morph2' ) # note 'morph index' == 1
|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
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 file.read(first 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.
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:
| 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 # 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!