Adding MDD import and export from patch 4969 with modifications, (import and export rvks, MDD is from lightwave AFAIK)

Added Mesh .key .removeAllKeys() and .insertKey() for MDD support (was using NMesh just for keys before)
Since this is aparently an experemental feature in NMesh we may want to change this.
This commit is contained in:
Campbell Barton 2006-09-27 16:33:02 +00:00
parent 2d11fbe192
commit ad51edd3bf
4 changed files with 384 additions and 1 deletions

@ -0,0 +1,125 @@
#!BPY
"""
Name: 'Save Mesh RVKs as MDD'
Blender: 242
Group: 'Animation'
Tooltip: 'baked vertex animation fromo selected model.'
"""
__author__ = "Bill L.Nieuwendorp"
__bpydoc__ = """\
This script Exports Lightwaves MotionDesigner format.
The .mdd format has become quite a popular Pipeline format<br>
for moving animations from package to package.
"""
# mdd export
#
#
#
# Warning if the vertex order or vertex count differs from frame to frame
# The script will fail because the resulting file would be an invalid mdd file.
#
# mdd files should only be applied to the the origonating model with the origonal vert order
#
#Please send any fixes,updates,bugs to Slow67_at_Gmail.com
#Bill Niewuendorp
import Blender
from Blender import *
import BPyMessages
try:
from struct import pack
except:
pack = None
def mdd_export(filepath, ob, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS):
Window.EditMode(0)
Blender.Window.WaitCursor(1)
mesh_orig = ob.getData(mesh=1)
#Flip y and z matrix
mat_flip= Mathutils.Matrix(\
[1,0,0,0],\
[0,0,1,0],\
[0,-1,0,0],\
[0,0,0,1],\
)
me_tmp = Mesh.New() # container mesh
numverts = len(mesh_orig.verts)
numframes = PREF_ENDFRAME-PREF_STARTFRAME+1
PREF_FPS= float(PREF_FPS)
f = open(filepath, 'wb') #no Errors yet:Safe to create file
# Write the header
f.write(pack(">2i", numframes-1, numverts))
# Write the frame times (should we use the time IPO??)
f.write( pack(">%df" % (numframes-1), *[frame/PREF_FPS for frame in xrange(numframes-1)]) ) # seconds
Blender.Set('curframe', PREF_STARTFRAME)
for frame in xrange(numframes+1):
Blender.Set('curframe', frame)
# Blender.Window.RedrawAll() # not needed
me_tmp.getFromObject(ob.name)
if len(me_tmp.verts) != numverts:
Blender.Draw.PupMenu('Error%t|Number of verts has changed during animation|cannot export')
Blender.Window.WaitCursor(0)
f.close() # should we zero?
return
me_tmp.transform(ob.matrixWorld * mat_flip)
# Write the vertex data
f.write(pack(">%df" % (numverts*3), *[axis for v in me_tmp.verts for axis in v.co]))
me_tmp.verts= None
f.close()
print'MDD Exported: %s frames:%d\n'% (filepath, numframes-1)
Blender.Window.WaitCursor(0)
def mdd_export_ui(filepath):
# Dont overwrite
if not BPyMessages.Warning_SaveOver(filepath):
return
scn= Scene.GetCurrent()
ob_act= scn.objects.active
if not ob_act or ob_act.type != 'Mesh':
BPyMessages.Error_NoMeshActive()
ctx = scn.getRenderingContext()
orig_frame = Blender.Get('curframe')
PREF_STARTFRAME= Blender.Draw.Create(ctx.startFrame())
PREF_ENDFRAME= Blender.Draw.Create(ctx.endFrame())
PREF_FPS= Blender.Draw.Create(ctx.fps)
block = [\
("Start Frame: ", PREF_STARTFRAME, 1, 30000, "Start Bake from what frame?: Default 1"),\
("End Frame: ", PREF_ENDFRAME, 1, 30000, "End Bake on what Frame?"),\
("FPS: ", PREF_FPS, 1, 100, "Frames per second")\
]
PREF_STARTFRAME, PREF_ENDFRAME=\
min(PREF_STARTFRAME.val, PREF_ENDFRAME.val),\
max(PREF_STARTFRAME.val, PREF_ENDFRAME.val)
if not Blender.Draw.PupBlock("Export MDD", block):
return
print (filepath, ob_act, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS.val)
mdd_export(filepath, ob_act, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS.val)
Blender.Set('curframe', orig_frame)
if __name__=='__main__':
if not pack:
Draw.PupMenu('Error%t|This script requires a full python install')
Blender.Window.FileSelector(mdd_export_ui, 'EXPORT MDD', sys.makename(ext='.mdd'))

@ -0,0 +1,167 @@
#!BPY
#"""
#Name: 'Load MDD to Mesh RVKs'
#Blender: 242
#Group: 'Animation'
#Tooltip: 'baked vertex animation to active mesh object.'
#"""
__author__ = "Bill L.Nieuwendorp"
__bpydoc__ = """\
This script Imports Lightwaves MotionDesigner format.
The .mdd format has become quite a popular Pipeline format<br>
for moving animations from package to package.
"""
# mdd importer
#
# Warning if the vertex order or vertex count differs from the
# origonal model the mdd was Baked out from their will be Strange
# behavior
#
#
#vertex animation to ShapeKeys with ipo and gives the frame a value of 1.0
#A modifier to read mdd files would be Ideal but thats for another day :)
#
#Please send any fixes,updates,bugs to Slow67_at_Gmail.com
#Bill Niewuendorp
try:
import struct
except:
struct= None
import Blender
from Blender import Mesh, Object, Scene
import BPyMessages
def mdd_import(filepath, ob, PREF_IPONAME, PREF_START_FRAME, PREF_JUMP):
print '\n\nimporting mdd "%s"' % filepath
Blender.Window.DrawProgressBar (0.0, "Importing mdd ...")
Blender.Window.EditMode(0)
Blender.Window.WaitCursor(1)
file = open(filepath, 'rb')
toUnpack = file.read(8)
frames, points = struct.unpack(">2i",toUnpack)
floatBytes = frames * 4
furtherUnpack = file.read(floatBytes)
floatBytes2 = points * 12
floatBytes3 = 12 * points * frames
restsize = points * 3
pfsize = points * frames * 3
restPoseUnpack = file.read(floatBytes2)
PerFrameUnpack = file.read(floatBytes3)
pattern = ">%df" % frames
pattern2 = ">%df" % restsize
pattern3 = ">%df" % pfsize
time = struct.unpack(pattern, furtherUnpack)
rest_pose = struct.unpack(pattern2, restPoseUnpack)
PerFramexyz = struct.unpack(pattern3, PerFrameUnpack)
print '\tpoints:%d frames:%d' % (points,frames)
scn = Scene.GetCurrent()
ctx = scn.getRenderingContext()
#ctx.startFrame(PREF_START_FRAME)
#ctx.endFrame(PREF_START_FRAME+frames)
Blender.Set("curframe", PREF_START_FRAME)
me = ob.getData(mesh=1)
xyzs = PerFramexyz
Point_list = []
for i in xrange(len(xyzs)/3):
xpos, zpos = i*3, (i*3)+3
Point_list.append(xyzs[xpos:zpos])
Frm_points = []
Blender.Window.DrawProgressBar (0.2, "3 Importing mdd ...")
for i in xrange(len(Point_list)):
first, last = i*points, (i*points)+points
Frm_points.append(Point_list[first:last])
def UpdateMesh(me,fr):
for vidx, v in enumerate(me.verts):
v.co[:] = Frm_points[fr][vidx][0], Frm_points[fr][vidx][2], Frm_points[fr][vidx][1]
me.update()
Blender.Window.DrawProgressBar (0.4, "4 Importing mdd ...")
curfr = ctx.currentFrame()
print'\twriting mdd data...'
for i in xrange(frames):
Blender.Set("curframe", i+PREF_START_FRAME)
if len(me.verts) > 1 and (curfr >= PREF_START_FRAME) and (curfr <= PREF_START_FRAME+frames):
UpdateMesh(me, i)
ob.insertShapeKey()
ob.makeDisplayList()
Blender.Window.RedrawAll()
Blender.Window.DrawProgressBar (0.5, "5 Importing mdd ...")
key= me.key
# Add the key of its not there
if not key:
me.insertKey(1, 'relative')
key= me.key
key.ipo = Blender.Ipo.New("Key", PREF_IPONAME)
ipo = key.ipo
block = key.getBlocks()
all_keys = ipo.curveConsts
for i in xrange(PREF_JUMP, len(all_keys), PREF_JUMP):
curve = ipo.getCurve(i)
if curve == None:
ipo.addCurve(all_keys[i])
Blender.Window.DrawProgressBar (0.8, "appending to ipos")
for i in xrange(PREF_JUMP, len(all_keys), PREF_JUMP):# Key Reduction
mkpoints = ipo.getCurve(i)
mkpoints.append((1+PREF_START_FRAME+i-1,1))
mkpoints.append((1+PREF_START_FRAME+i- PREF_JUMP -1,0))
mkpoints.append((1+PREF_START_FRAME+i+ PREF_JUMP-1,0))
mkpoints.setInterpolation('Linear')
mkpoints.recalc()
print 'done'
Blender.Window.WaitCursor(0)
Blender.Window.DrawProgressBar (1.0, '')
def mdd_import_ui(filepath):
if BPyMessages.Error_NoFile(filepath):
return
scn= Scene.GetCurrent()
ob_act= scn.objects.active
if ob_act == None or ob_act.type != 'Mesh':
BPyMessages.Error_NoMeshActive()
return
PREF_IPONAME = Blender.Draw.Create(filepath.split('/')[-1].split('\\')[-1].split('.')[0])
PREF_START_FRAME = Blender.Draw.Create(1)
PREF_JUMP = Blender.Draw.Create(1)
block = [\
("Ipo Name: ", PREF_IPONAME, 0, 30, "Ipo name for the new shape key"),\
("Start Frame: ", PREF_START_FRAME, 1, 3000, "Start frame for the animation"),\
("Key Skip: ", PREF_JUMP, 1, 100, "KeyReduction, Skip every Nth Frame")\
]
if not Blender.Draw.PupBlock("Import MDD", block):
return
orig_frame = Blender.Get('curframe')
mdd_import(filepath, ob_act, PREF_IPONAME.val, PREF_START_FRAME.val, PREF_JUMP.val)
Blender.Set('curframe', orig_frame)
if __name__ == '__main__':
if not struct:
Draw.PupMenu('Error%t|This script requires a full python install')
Blender.Window.FileSelector(mdd_import_ui, 'IMPORT MDD', '*.mdd')

@ -49,6 +49,7 @@
#include "BIF_editdeform.h"
#include "BIF_editkey.h" /* insert_meshkey */
#include "BIF_space.h" /* REMAKEIPO - insert_meshkey */
#include "BIF_editview.h"
#include "BIF_editmesh.h"
#include "BIF_meshtools.h"
@ -6324,6 +6325,54 @@ static PyObject *Mesh_getVertexInfluences( BPy_Mesh * self, PyObject * args )
return influence_list;
}
static PyObject *Mesh_removeAllKeys( BPy_Mesh * self )
{
Mesh *mesh = self->mesh;
if( !mesh || !mesh->key )
Py_RETURN_FALSE;
mesh->key->id.us--;
mesh->key = NULL;
Py_RETURN_TRUE;
}
static PyObject *Mesh_insertKey( BPy_Mesh * self, PyObject * args )
{
Mesh *mesh = self->mesh;
int fra = -1, oldfra = -1;
char *type = NULL;
short typenum;
if( !PyArg_ParseTuple( args, "|is", &fra, &type ) )
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected nothing or an int and optionally a string as arguments" );
if( !type || !strcmp( type, "relative" ) )
typenum = 1;
else if( !strcmp( type, "absolute" ) )
typenum = 2;
else
return EXPP_ReturnPyObjError( PyExc_AttributeError,
"if given, type should be 'relative' or 'absolute'" );
if( fra > 0 ) {
fra = EXPP_ClampInt( fra, 1, MAXFRAME );
oldfra = G.scene->r.cfra;
G.scene->r.cfra = fra;
}
insert_meshkey( mesh, typenum );
allspace(REMAKEIPO, 0);
if( fra > 0 )
G.scene->r.cfra = oldfra;
Py_RETURN_NONE;
}
static PyObject *Mesh_Tools( BPy_Mesh * self, int type, void **args )
{
Base *base;
@ -6584,7 +6633,11 @@ static struct PyMethodDef BPy_Mesh_methods[] = {
"Get names of vertex groups"},
{"getVertexInfluences", (PyCFunction)Mesh_getVertexInfluences, METH_VARARGS,
"Get list of the influences of bones for a given mesh vertex"},
/* Shape Keys */
{"removeAllKeys", (PyCFunction)Mesh_removeAllKeys, METH_NOARGS,
"Remove all the shape keys from a mesh"},
{"insertKey", (PyCFunction)Mesh_insertKey, METH_VARARGS,
"(frame = None, type = 'relative') - inserts a Mesh key at the given frame"},
/* Mesh tools */
{"smooth", (PyCFunction)Mesh_smooth, METH_NOARGS,
"Flattens angle of selected faces (experimental)"},
@ -7015,6 +7068,15 @@ static int Mesh_setMode( BPy_Mesh *self, PyObject *value )
return 0;
}
static PyObject *Mesh_getKey( BPy_Mesh * self )
{
if( self->mesh->key )
return Key_CreatePyObject(self->mesh->key);
else
Py_RETURN_NONE;
}
static PyObject *Mesh_getActiveFace( BPy_Mesh * self )
{
TFace *face;
@ -7273,6 +7335,10 @@ static PyGetSetDef BPy_Mesh_getseters[] = {
(getter)Mesh_getMode, (setter)Mesh_setMode,
"The mesh's mode bitfield",
NULL},
{"key",
(getter)Mesh_getKey, (setter)NULL,
"The mesh's key",
NULL},
{"faceUV",
(getter)Mesh_getFlag, (setter)Mesh_setFlag,
"UV-mapped textured faces enabled",

@ -961,6 +961,31 @@ class Mesh:
and weight is a float value.
"""
def removeAllKeys():
"""
Remove all mesh keys stored in this mesh.
@rtype: bool
@return: True if successful or False if the Mesh has no keys.
"""
def insertKey(frame = None, type = 'relative'):
"""
Insert a mesh key at the given frame.
@type frame: int
@type type: string
@param frame: The Scene frame where the mesh key should be inserted. If
None or the arg is not given, the current frame is used.
@param type: The mesh key type: 'relative' or 'absolute'. This is only
relevant on meshes with no keys.
@warn: This and L{removeAllKeys} were included in this release only to
make accessing vertex keys possible, but may not be a proper solution
and may be substituted by something better later. For example, it
seems that 'frame' should be kept in the range [1, 100]
(the curves can be manually tweaked in the Ipo Curve Editor window in
Blender itself later).
"""
def smooth():
"""
Flattens angle of selected faces. Experimental mesh tool.