Adding MDD import and export from patch 4969 with modifications, (import and export...
authorCampbell Barton <ideasman42@gmail.com>
Wed, 27 Sep 2006 16:33:02 +0000 (16:33 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 27 Sep 2006 16:33:02 +0000 (16:33 +0000)
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.

release/scripts/export_mdd.py [new file with mode: 0644]
release/scripts/import_mdd.py [new file with mode: 0644]
source/blender/python/api2_2x/Mesh.c
source/blender/python/api2_2x/doc/Mesh.py

diff --git a/release/scripts/export_mdd.py b/release/scripts/export_mdd.py
new file mode 100644 (file)
index 0000000..0b0baaa
--- /dev/null
@@ -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'))
\ No newline at end of file
diff --git a/release/scripts/import_mdd.py b/release/scripts/import_mdd.py
new file mode 100644 (file)
index 0000000..fa594fa
--- /dev/null
@@ -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')
\ No newline at end of file
index 393a9b573740428b9014bec149690d10b6240f5e..7f6f6734074fd39d78d730ad017f166ec7037c2e 100644 (file)
@@ -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",
index 584b9c32ab887991f3486d3ca99f0c67db5d229b..10098759951e857e4ebee9d907f54eeb90ffdf13 100644 (file)
@@ -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.