svn merge -r 15392:15551 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender.git] / release / scripts / c3d_import.py
1 #!BPY
2
3 """
4 Name: 'Motion Capture  (.c3d)...'
5 Blender: 246
6 Group: 'Import'
7 Tooltip: 'Import a C3D Motion Capture file'
8 """
9 __script__ = "C3D Motion Capture file import"
10 __author__ = " Jean-Baptiste PERIN, Roger D. Wickes (rogerwickes@yahoo.com)"
11 __version__ = "0.9"
12 __url__ = ["Communicate problems and errors, BlenderArtists.org, Python forum"]
13 __email__= ["rogerwickes@yahoo.com", "c3d script"]
14 __bpydoc__ = """\
15 c3d_import.py v0.8
16
17 Script loading Graphics Lab Motion Capture file,  
18 Usage:<br>
19         - Run the script <br>
20         - Choose the file to open<br>
21         - Press Import C3D button<br>
22
23 Version History:
24  0.4: PERIN Released under Blender Artistic Licence
25  0.5: WICKES used marker names, fixed 2.45 depricated call
26  0.6: WICKES creates armature for each subject
27  0.7: WICKES constrains armature to follow the empties (markers). Verified for shake hands s
28  0.8: WICKES resolved DEC support issue
29  0.9: BARTON removed scene name change, whitespace edits. WICKES added IK layers
30 """
31
32 #----------------------------------------------
33 # (c) Jean-Baptiste PERIN  december 2005, released under Blender Artistic Licence
34 #    for the Blender 2.40 Python Scripts Bundle.
35 #----------------------------------------------
36
37 ######################################################
38 # This script imports a C3D file into blender. 
39 # Loader is based on MATLAB C3D loader from
40 # Alan Morris, Toronto, October 1998
41 # Jaap Harlaar, Amsterdam, april 2002
42 ######################################################
43
44 import string
45 import Blender
46 from Blender import *
47 import bpy
48 import struct
49 import BPyMessages
50 Vector= Blender.Mathutils.Vector
51 Euler= Blender.Mathutils.Euler
52 Matrix= Blender.Mathutils.Matrix
53 RotationMatrix = Blender.Mathutils.RotationMatrix
54 TranslationMatrix= Blender.Mathutils.TranslationMatrix
55
56 #=================
57 # Global Variables, Constants, Defaults, and Shorthand References
58 #=================
59 # set senstitivity for displaying debug/console messages. 0=few, 100=max, including clicks at major steps
60 # debug(num,string) to conditionally display status/info in console window
61 DEBUG=Blender.Get('rt')
62
63 # marker sets known in the world
64 HUMAN_CMU= "HumanRTKm.mkr" # The Human Real-Time capture marker set used by CMU
65 HUMAN_CMU2="HumanRT.mkr" # found in another file, seems same as others in that series
66 MARKER_SETS = [ HUMAN_CMU, HUMAN_CMU2 ] # marker sets that this program supports (can make an armature for)
67 XYZ_LIMIT= 10000 #max value for coordinates if in integer format
68
69 # what layers to put stuff on in scene. 1 is selected, so everything goes there
70 # selecting only layer 2 shows only the armature moving, 12 shows only the empties
71 LAYERS_ARMOB= [1,2]
72 LAYERS_MARKER=[1,12]
73 LAYERS_IK=[1,11]
74 IK_PREFIX="ik_" # prefix in empty name: ik_prefix+subject prefix+bone name
75
76 CLEAN=True # Should program ignore markers at (0,0,0) and beyond the outer limits?
77
78 scn = Blender.Scene.GetCurrent()
79
80 BCS=Blender.Constraint.Settings # shorthand dictionary - define with brace, reference with bracket
81 trackto={"+x":BCS.TRACKX, "+y":BCS.TRACKY, "+z":BCS.TRACKZ, "-x":BCS.TRACKNEGX, "-y":BCS.TRACKNEGY, "-z":BCS.TRACKNEGZ}
82 trackup={"x":BCS.UPX, "y":BCS.UPY, "z":BCS.UPZ}
83
84 #=============================#
85 # Classes 
86 #=============================#
87 class Marker:
88         def __init__(self, x, y, z):
89                 self.x=0.0
90                 self.y=0.0
91                 self.z=0.0
92
93         def __repr__(self): #report on self, as in if just printed
94                 return str("[x = "+str(self.x) +" y = " + str(self.y)+" z = "+ str(self.z)+"]")
95
96 class ParameterGroup:
97         def __init__(self, nom, description, parameter):
98                 self.name = nom
99                 self.description = description
100                 self.parameter = parameter
101
102         def __repr__(self):
103                 return self.name, " ", self.description, " ", self.parameter
104
105 class Parameter:
106         def __init__(self, name, datatype, dim, data, description):
107                 self.name = name
108                 self.datatype = datatype
109                 self.dim = dim
110                 self.data = data
111                 self.description = description
112  
113                 def __repr__(self):
114                         return self.name, " ", self.description, " ", self.dim
115
116 class MyVector:
117         def __init__(self, fx,fy,fz):
118                 self.x=fx
119                 self.y=fy
120                 self.z=fz
121
122 class Mybone:
123         "information structure for bone generation and posing"
124         def __init__(self, name,vec,par,head,tail,const):
125                 self.name=name      # name of this bone. must be unique within armature
126                 self.vec=vec         # edit bone vector it points
127                 self.parent=par     # name of parent bone to locate head and form a chain
128                 self.headMark=head  # list of 0+ markers where the head of this non-parented bone should be placed
129                 self.tailMark=tail  # list of 0+ markers where the tip should be placed
130                 self.const=const    # list of 0+ constraint tuples to control posing
131                 self.head=MyVector(0,0,0) #T-pose location
132                 self.tail=MyVector(0,0,0)
133         def __repr__(self):
134                 return '[Mybone "%s"]' % self.name
135
136
137 #=============================#
138 # functions/modules 
139 #=============================#
140 def error(str):
141         Draw.PupMenu('ERROR%t|'+str)
142         return
143 def status(str):
144         Draw.PupMenu('STATUS%t|'+str+"|Continue?")
145         return
146 def debug(num,msg): #use log4j or just console here.
147         if DEBUG >= num:
148                                         print 'debug:', (' '*num), msg
149         #TODO: if level 0, make a text file in Blender file to record major stuff
150         return
151
152 def names(ob): return ob.name
153
154
155 #########
156 # Cette fonction renvoie la liste des empties
157 # in  : 
158 # out : emp_list (List of Object) la liste des objets de type "Empty"
159 #########
160 def getEmpty(name):
161         obs = [ob for ob in scn.objects if ob.type=="Empty" and ob.name==name]
162         if len(obs)==0:
163                 return None
164         elif len(obs)==1:
165                 return obs[0]
166         else:
167                 error("FATAL ERROR: %i empties %s in file" % (len(obs),ob[0]))
168 #########
169 # Cette fonction renvoie un empty 
170 # in  : objname : le nom de l'empty recherche
171 # out : myobj : l'empty cree ou retrouve
172 #########
173 def getOrCreateEmpty(objname):
174         myobj= getEmpty(objname)
175         if myobj==None: 
176                 myobj = scn.objects.new("Empty",objname)
177                 debug(50,'Marker/Empty created %s' %  myobj)
178         return myobj
179
180 def getOrCreateCurve(ipo, curvename):
181         """
182         Retrieve or create a Blender Ipo Curve named C{curvename} in the C{ipo} Ipo
183
184         >>> import mylib 
185
186         >>> lIpo = GetOrCreateIPO("Une IPO")
187         >>> laCurve = getOrCreateCurve(lIpo, "RotX")
188
189         Either an ipo curve named C{curvename} exists before the call then this curve is returned,
190         Or such a curve doesn't exist before the call .. then it is created into the c{ipo} Ipo and returned 
191
192         @type  ipo: Blender Ipo
193         @param ipo: the Ipo in which the curve must be retrieved or created.
194         @type  curvename: string
195         @param curvename: name of the IPO.
196         @rtype:   Blender Curve
197         @return:  a Blender Curve named C{curvename} in the C{ipo} Ipo 
198         """
199         try:
200                 mycurve = ipo.getCurve(curvename)
201                 if mycurve != None:
202                         pass
203                 else:
204                         mycurve = ipo.addCurve(curvename)
205         except:
206                 mycurve = ipo.addCurve(curvename)
207         return mycurve
208
209 def eraseIPO (objectname):
210         object = Blender.Object.Get(objectname)
211         lIpo = object.getIpo()
212         if lIpo != None:
213                 nbCurves = lIpo.getNcurves()
214                 for i in range(nbCurves):
215                         nbBezPoints = lIpo.getNBezPoints(i)
216                         for j in range(nbBezPoints):
217                                 lIpo.delBezPoint(i)
218
219 def comp_loc(emptyNameList):
220         myloc=Vector(0,0,0)
221         for emName in emptyNameList:
222                 myobj = Blender.Object.Get(emName)
223                 for i in range(3):
224                         myloc[i]= myloc[i]+(myobj.loc[i]/len(emptyNameList)) #take the average loc of all marks
225         return myloc
226
227 def comp_len(head, tail): # computes the length of a bone
228         headvec=comp_loc(head)
229         tailvec=comp_loc(tail)
230         netvec=headvec-tailvec
231         return netvec.length
232
233 def createHumanCMU(): # human bone structure, makes a node set for CMU MoCap Lab
234         # order of bones: "spine","chest","neck","head",...face toward you in front view
235         # pose constraints are tuples of (type,target,influence,other-as-needed)
236         # constraint stack order is important. for proper bone pointing and orinetation:
237         #  IK, then TT +YZ in world space. then LR XZ to 0 in world space, this points the bone, twists it, but then
238         # limits the rotation to the sidebar enpty with the Z facing it, and Y pointing along the bone.
239         nodes=[]        # bonename, vector, parent, head targets, tail targets, constraint list
240         for i in range(23): nodes.append(Mybone("name","vec","par",[],[],[]))
241         nodes[0]= Mybone("root", "-Y","",["RBWT", "LBWT"],["RFWT", "LFWT", "RBWT", "LBWT"],[("LOC","RBWT",1.0),("LOC","LBWT",0.5),("IK","RFWT",1.0),("IK","LFWT",0.5),("TT","RBWT",1,"+YZ"),("LR","XZ",1)])
242         nodes[1]= Mybone("spine","+Z","root",[],["STRN","T10"],[("IK","STRN",1.0),("IK","T10",0.5),("TT","STRN",1,"+YZ"),("LR","XZ",1)])
243         nodes[2]= Mybone("chest","+Z","spine",[],["CLAV","C7"],[("IK","CLAV",1.0),("IK","C7",0.5),("TT","CLAV",1,"+YZ"),("LR","XZ",1)])
244         nodes[3]= Mybone("neck", "+Z","chest",[],["RBHD","LBHD"],[("IK","RBHD",1.0),("IK","LBHD",0.5),("TT","LBHD",1,"+YZ"),("LR","XZ",1)])
245         nodes[4]= Mybone("head" ,"-Y","neck",[],["RFHD","LFHD"],[("IK","RFHD",1.0),("IK","LFHD",0.5),("TT","LFHD",1,"+YZ"),("LR","XZ",1)])
246         
247         nodes[5]= Mybone("shoulder.R","-X","chest",[],["RSHO"],[("IK","RSHO",1.0)])
248         nodes[6]= Mybone("toparm.R",  "-X","shoulder.R",[],["RELB"],[("IK","RELB",1.0),("TT","RUPA",1,"+YZ"),("LR","XZ",1)])
249         nodes[7]= Mybone("lowarm.R",  "-X","toparm.R",[],["RWRA","RWRB"],[("IK","RWRA",1.0),("IK","RWRB",0.5),("TT","RFRM",1,"+YZ"),("LR","XZ",1)])
250         nodes[8]= Mybone("hand.R",    "-X","lowarm.R",[],["RFIN"],[("IK","RFIN",1.0),("TT","RWRA",1,"+YZ"),("LR","XZ",1)])  #missing ,"RTHM"
251
252         nodes[9]= Mybone("hip.R",   "-X","root",[],["RFWT","RBWT"],[("IK","RFWT",1.0),("IK","RBWT",0.5)])
253         nodes[10]=Mybone("topleg.R","-Z","hip.R",[],["RKNE"],[("IK","RKNE",1),("TT","RTHI",1,"+YZ"),("LR","XZ",1)])
254         nodes[11]=Mybone("lowleg.R","-Z","topleg.R",[],["RANK","RHEE"],[("IK","RHEE",1.0),("TT","RSHN",1,"+YZ"),("LR","XZ",1)])
255         nodes[12]=Mybone("foot.R",  "-Y","lowleg.R",[],["RTOE","RMT5"],[("IK","RTOE",1.0),("IK","RMT5",0.2),("TT","RMT5",1,"+YZ")])
256         nodes[13]=Mybone("toes.R",  "-Y","foot.R",[],["RTOE"],[("IK","RTOE",1.0)])
257         
258         nodes[14]=Mybone("shoulder.L","+X","chest",[],["LSHO"],[("IK","LSHO",1.0)])
259         nodes[15]=Mybone("toparm.L",  "+X","shoulder.L",[],["LELB"],[("IK","LELB",1.0),("TT","LUPA",1,"+YZ"),("LR","XZ",1)])
260         nodes[16]=Mybone("lowarm.L",  "+X","toparm.L",[],["LWRA","LWRB"],[("IK","LWRA",1.0),("IK","LWRB",0.5),("TT","LFRM",1,"+YZ"),("LR","XZ",1)])
261         nodes[17]=Mybone("hand.L",    "+X","lowarm.L",[],["LFIN"],[("IK","LFIN",1.0),("TT","RWRA",1,"+YZ"),("LR","XZ",1)]) #missing ,"LTHM"
262         
263         nodes[18]=Mybone("hip.L",   "+X","root",[],["LFWT","LBWT"],[("IK","LFWT",1.0),("IK","LBWT",0.5)])
264         nodes[19]=Mybone("topleg.L","-Z","hip.L",[],["LKNE"],[("IK","LKNE",1),("TT","LTHI",1,"+YZ"),("LR","XZ",1)])
265         nodes[20]=Mybone("lowleg.L","-Z","topleg.L",[],["LANK","LHEE"],[("IK","LHEE",1.0),("TT","LSHN",1,"+YZ"),("LR","XZ",1)])
266         nodes[21]=Mybone("foot.L",  "-Y","lowleg.L",[],["LTOE","LMT5"],[("IK","LTOE",1.0),("IK","LMT5",0.2),("TT","LMT5",1,"+YZ"),("LR","XZ",1)])
267         nodes[22]=Mybone("toes.L",  "-Y","foot.L",[],["LTOE"],[("IK","LTOE",1.0)])
268         return nodes
269
270 def createNodes(marker_set): # make a list of bone name, parent, edit head loc, edit tail loc, pose constraints
271         #ultimately, I want to read in an XML file here that specifies the node trees for various marker sets
272         if   marker_set==HUMAN_CMU:  nodes= createHumanCMU() #load up and verify the file has the CMU marker set
273         elif marker_set==HUMAN_CMU2: nodes= createHumanCMU()
274         else: nodes=[]
275         return nodes
276 def findEntry(item,list):
277         for i in range(len(list)):
278                 if item==list[i]: break
279         debug(100,"findEtnry %s is %i in list of %i items" % (item,i,len(list)))
280         return i
281 def makeNodes(prefix, markerList, empties, marker_set): #make sure the file has the nodes selected
282         nodes= createNodes(marker_set)  # list has generic marker names; replace them with the actual object names created
283         #each entry in markerlist has a corresponding entry in empties in the same order
284         errList=[]
285         for i in range(len(nodes)):
286                 node= nodes[i]
287                 debug(60,"Adapting node %s to prefix %s" % (node,prefix))
288
289                 #replace generic head markers with actual empty names
290                 for im in range(len(node.headMark)):
291                         marker= node.headMark[im]
292                         mark= prefix+marker
293                         imn= findEntry(mark,markerList)
294                         if imn < len(markerList):
295                                 debug(90,"Adapating head marker %s to %s" % (marker,empties[imn].name))
296                                 nodes[i].headMark[im]= empties[imn].name
297                         else: errList.append([node.name,"head location",mark,node,2])
298  
299                 #replace generic tail markers with actual empty names
300                 for im in range(len(node.tailMark)):
301                         marker= node.tailMark[im]
302                         mark= prefix+marker
303                         imn= findEntry(mark,markerList)
304                         if imn < len(markerList):
305                                 debug(90,"Adapating  marker %s to %s" % (marker,empties[imn].name))
306                                 nodes[i].tailMark[im]= empties[imn].name
307                         else: errList.append([node.name,"tail location",mark,node,2])
308                         
309                 #replace generic constraint markers (if the constraint references a marker) with empty name
310                 for im in range(len(node.const)):
311                         const=node.const[im]
312                         if const[0] in ("LOC","IK","TT"):
313                                 marker=const[1]
314                                 mark= prefix+marker
315                                 imn= findEntry(mark,markerList)
316                                 if imn < len(markerList):
317                                         debug(90,"Adapating %s constraint marker %s to %s" % (const[0],marker,empties[imn].name))
318                                         if const[0] in ("IK","LR","LOC"):
319                                                 nodes[i].const[im]=(const[0], empties[imn].name, const[2])
320                                         else: nodes[i].const[im]=(const[0], empties[imn].name, const[2], const[3])
321                                 else: errList.append([node.name,const[0]+" constraint",mark,node,4])
322                         
323         if errList!=[]: #we have issues. 
324                 for err in errList:
325                         debug(0,"Bone "+err[0]+" specifies "+err[2]+" as "+err[1]+"which was not specified in file.")
326                         #need a popup here to ignore/cleanup node tree, or add the marker(?) or abort
327                         usrOption= 1
328                         if usrOption==0: #ignore this marker (remove it)
329                                 for node in nodes: #find the bone in error
330                                         if node.name==err[0]:
331                                                 print "Before",node
332                                                 if err[3] in range(2,3):
333                                                         node[err[3]].remove(err[2]) #find the marker in error and remove it
334                                                 elif err[3]==4: #find the constraint and remove it
335                                                         for const in node.const:
336                                                                 if const[1]==err[2]: node.const.remove(const)
337                                                 print "After",node
338                         elif usrOption==1: #add these markers as static empties, and user will automate them later
339                                 #and the bones will be keyed to them, so it will all be good.
340                                 #file may have just mis-named the empty, or the location can be derived based on other markers
341                                 em= getOrCreateEmpty(err[2])
342                                 em.layers= LAYERS_MARKER
343                         else: abort() #abend
344                         if DEBUG==100: status("Nodes Updated")
345         return nodes #nodes may be updated
346
347 def makeBones(arm,nodes): 
348         debug(20,"Making %i edit bones" % len(nodes))
349         for node in nodes:
350                 bone= Blender.Armature.Editbone()
351                 bone.name= node.name
352                 arm.bones[bone.name]= bone #add it to the armature
353                 debug(50,"Bone added: %s" % bone)
354                 if bone.name <> node.name:
355                         debug(0,"ERROR: duplicate node % name specified" % node.name)
356                         node.name= bone.name #you may not get what you asked for
357                 if node.parent!="": #parent
358                         debug(60,"Bone parent: %s"%node.parent)
359                         bone.parent= arm.bones[node.parent]
360                         bone.options = [Armature.CONNECTED]
361                 #compute head = average of the reference empties
362                 if node.headMark==[]: # no head explicitly stated, must be tail of parent
363                         for parnode in nodes:
364                                 if node.parent==parnode.name: break
365                         node.headMark= parnode.tailMark
366                         node.head= parnode.tail
367                 else: node.head= comp_loc(node.headMark) #node head is specified, probably only for root.
368
369                 bone.head= node.head
370                 debug(60,"%s bone head: (%0.2f, %0.2f, %0.2f)" % (bone.name,bone.head.x, bone.head.y, bone.head.z))
371                 mylen=comp_len(node.headMark,node.tailMark) # length of the bone as it was recorded for that person
372                 # for our T position, compute the bone length, add it to the head vector component to get the tail
373                 if node.vec[0]=="-": mylen=-mylen
374                 debug(80,"Bone vector %s length %0.2f" %(node.vec,mylen))
375                 node.tail= Vector(node.head)
376                 myvec=node.vec[1].lower()
377                 if   myvec=="x": node.tail.x+=mylen
378                 elif myvec=="y": node.tail.y+=mylen
379                 elif myvec=="z": node.tail.z+=mylen
380                 else:
381                         debug(0,"%s %s %s %s" % (node.vec,myvec,node.vec[0],node.vec[1]))
382                         error("ERROR IN BONE SPEC ")
383                 bone.tail= node.tail
384                 debug(60,"Bone tail: (%i,%i,%i)" %(bone.tail.x, bone.tail.y, bone.tail.z))
385         #Armature created in the T postion, but with bone lengths to match the marker set and subject
386                 #when this is constrained to the markers, the recorded action will be relative to a know Rotation
387                 #so that all recorded actions should be interchangeable. wooot!
388                 #Only have to adjust starting object loc when matching up actions.
389         return #arm #updated
390
391 def makeConstLoc(pbone,const):
392         const_new= pbone.constraints.append(Constraint.Type.COPYLOC)
393         const_new.name = const[0]+"-"+const[1]
394         const_target=Blender.Object.Get(const[1])
395         const_new[BCS.TARGET]= const_target
396         const_new.influence = const[2]
397         return
398                                 
399 def makeConstLimRot(pbone,const):
400         const_new= pbone.constraints.append(Constraint.Type.LIMITROT)
401         const_new.name = const[0]+"-"+const[1]
402         for axis in const[1]:
403                 if axis.lower()=="x": const_new[BCS.LIMIT] |= BCS.LIMIT_XROT #set
404                 if axis.lower()=="y": const_new[BCS.LIMIT] |= BCS.LIMIT_YROT #set
405                 if axis.lower()=="z": const_new[BCS.LIMIT] |= BCS.LIMIT_ZROT #set
406         const_new[BCS.OWNERSPACE]= BCS.SPACE_LOCAL
407         const_new.influence = const[2]
408         # fyi, const[Constraint.Settings.LIMIT] &= ~Constraint.Settings.LIMIT_XROT #reset
409         return
410                                 
411 def makeConstIK(prefix,pbone,const):
412         #Blender 246 only supports one IK Solver per bone, but we might want many,
413         #  so we need to create a reference empty named after the bone
414         #  that floats between the markers, so the bone can point to it as a singularity
415         myob= getOrCreateEmpty(IK_PREFIX+prefix+pbone.name)
416         myob.layers= LAYERS_IK
417         # note that this empty gets all the IK constraints added on as location constraints
418         myconst= myob.constraints.append(Constraint.Type.COPYLOC)
419         myconst.name=const[0]+"-"+const[1]
420         myconst[Constraint.Settings.TARGET]= Blender.Object.Get(const[1])
421         myconst.influence = const[2]
422         
423         #point the bone once to the empty via IK
424         success=False
425         for myconst in pbone.constraints:
426                 if myconst.type == Constraint.Type.IKSOLVER: success=True
427         if not(success): #add an IK constraint to the bone to point to the empty
428                 #print pbone
429                 myconst= pbone.constraints.append(Constraint.Type.IKSOLVER)
430                 myconst.name = const[1]
431                 myconst[BCS.TARGET]= myob
432                 myconst.influence = const[2]
433                 #const_new[Constraint.Settings.BONE]= ?
434                 myconst[BCS.CHAINLEN]= 1
435                 myconst[BCS.USETIP]= True
436                 myconst[BCS.STRETCH]= False
437                 return
438                                         
439 def makeConstTT(pbone,const):
440         myconst= pbone.constraints.append(Constraint.Type.TRACKTO)
441         myconst.name=const[0]+"-"+const[1]
442         debug(70,"%s %s" % (myconst,const[3]))
443         myob= getEmpty(const[1])
444         if myob!= None:
445                 myconst[BCS.TARGET]= myob
446                 myconst.influence = const[2]
447                 #const[3] is the Track and the thrird char is the Up indicator
448                 myconst[BCS.TRACK]= trackto[const[3][0:2].lower()]
449                 myconst[BCS.UP]=trackup[const[3][2].lower()]#up direction
450                 myconst[BCS.OWNERSPACE]= BCS.SPACE_LOCAL
451                 myconst[BCS.TARGETSPACE]= [BCS.SPACE_LOCAL]
452                 if const[3][1]==const[3][2]: debug(0,"WARNING: Track To axis and up axis should not be the same. Constraint is INACTIVE")
453         else:   #marker not found. could be missing from this file, or an error in node spec
454                 error("TrackTo Constraint for %s |specifies unknown marker %s" % (pbone.name,const[1]))
455         return
456
457 def makePoses(prefix,arm_ob,nodes): # pose this armature object based on node requirements
458         #this is constraint-based posing, not hard-keyed posing.
459         #we do constraint-based first so that user can adjust the constraints, possibly smooth/tweak motion
460         #  add additional bones or referneces/constraints, before baking to hard keyframes
461
462         pose= arm_ob.getPose()
463         debug(0,"Posing %s %s" % (arm_ob, pose))
464         for node in nodes:
465                 debug(30, "examining %s" %node)
466                 if len(node.const)>0: #constraints for this bone are desired
467                         pbone = pose.bones[node.name]
468                         debug(40,"Posing bone %s" %pbone)
469                         for const in node.const:
470                                 debug(50,"Constraining %s by %s" %(pbone,const))
471                                 if   const[0]=="LOC":makeConstLoc(pbone,const)
472                                 elif const[0]=="IK": makeConstIK(prefix,pbone,const)
473                                 elif const[0]=="LR": makeConstLimRot(pbone,const)  
474                                 elif const[0]=="TT": makeConstTT(pbone,const)
475                                 else: 
476                                         error("FATAL: constraint %s not supported" %const[0])
477                                         break
478         debug(10, "Posing complete. Cycling pose and edit mode")
479         pose.update()
480         return
481
482 def make_arm(subject,prefix,markerList, emptyList,marker_set):
483         debug(10,"**************************")
484         debug(00, "**** Making Armature for %s..." % subject)
485         debug(10, "**************************")
486         # copied from bvh import bvh_node_dict2armature; trying to use similar process for further integtration down the road
487         # Add the new armature,
488         
489         nodes= makeNodes(prefix, markerList, emptyList, marker_set) #assume everyone in file uses the same mocap suit
490         # each person in the file may be different height, so each needs their own new armature to match marker location
491
492 ##  obs= Blender.Object.Get()
493 ##  success=False
494 ##  for ob in obs:
495 ##    if ob.name==subject:
496 ##      success=True
497 ##  if success:
498 ##    menu="Human Armature already exists for this subject."
499 ##    menu+="%t|Create another in this scene"
500 ##    menu+="%l|Start a new scene"
501 ##    menu+="%l|Use this armature"
502 ##    menusel= Draw.PupMenu(menu)
503         
504         arm= Blender.Armature.New(subject) #make an armature.
505         debug(10,"Created Armature %s" % arm)
506         # Put us into editmode
507         arm.makeEditable()
508         arm.drawType = Armature.OCTAHEDRON
509         makeBones(arm,nodes)
510         scn = Blender.Scene.GetCurrent() #add it to the current scene. could create new scenes here as yaf
511         arm_ob= scn.objects.new(arm) #instance it in the scene. this is the new way for 2.46 to instance objects
512         arm_ob.name= subject #name it something like the person it represents
513         arm_ob.layers= LAYERS_ARMOB
514         debug(20,"Instanced Armature %s" % arm_ob)
515         arm.update() #exit editmode. Arm must be instanced as an object before you can save changes or pose it
516         Blender.Redraw() # show the world
517         if DEBUG==100: status("T-Bones made.")
518
519         makePoses(prefix,arm_ob,nodes) #constrain arm_ob with these markers
520         
521         scn.update(1) #make everyone behave themselves in the scene, and respect the new constraints
522         return arm_ob
523
524 def setupAnim(StartFrame, EndFrame, VideoFrameRate):
525         debug(100, 'VideoFrameRate is %i' %VideoFrameRate)
526         if VideoFrameRate<1: VideoFrameRate=1
527         if VideoFrameRate>120: VideoFrameRate=120
528         # set up anim panel for them
529         context=scn.getRenderingContext() 
530         context.startFrame(StartFrame)
531         context.endFrame(EndFrame)
532         context.framesPerSec(int(VideoFrameRate))
533         Blender.Set("curframe",StartFrame)
534         Blender.Redraw()
535         return
536
537 def makeCloud(Nmarkers,markerList,StartFrame,EndFrame,Markers):
538         debug(10, "**************************")
539         debug(00, "*** Making Cloud Formation")
540         debug(10, "**************************")
541         empties=[]
542         ipos=[]
543         curvesX=[]
544         curvesY=[]
545         curvesZ=[]
546         debug(0, "%i Markers (empty cloud) will be put on layers %s" % (Nmarkers,LAYERS_MARKER))
547         # Empty Cloud formation
548         for i in range(Nmarkers):
549                 debug(100,"%i marker %s"%(i, markerList[i]))
550                 emptyname = markerList[i] # rdw: to use meaningful names from Points parameter
551                 em= getOrCreateEmpty(emptyname) #in this scene
552                 em.layers= LAYERS_MARKER
553                 #make a list of the actual empty
554                 empties.append(em)
555                 #assign it an ipo with the loc xyz curves
556                 lipo = Ipo.New("Object",em.name)
557                 ipos.append(lipo)
558                 curvesX.append(getOrCreateCurve(ipos[i],'LocX'))
559                 curvesY.append(getOrCreateCurve(ipos[i],'LocY'))
560                 curvesZ.append(getOrCreateCurve(ipos[i],'LocZ'))
561                 empties[i].setIpo(ipos[i])
562         debug(30,"Cloud of %i empties created." % len(empties))
563         NvideoFrames= EndFrame-StartFrame+1
564         debug(10, "**************************")
565         debug(00, "**** Calculating Marker Ipo Curves over %i Frames ..." % NvideoFrames)
566         debug(10, "**************************")
567         err= index=0 #number of errors, logical frame
568         for frame in range(StartFrame,EndFrame+1):
569                 if   index==0:   start=sys.time()
570                 elif index==100:
571                         tmp=(NvideoFrames-100)*(sys.time()-start)/6000
572                         debug(0,"%i minutes process time estimated" % tmp)
573                 elif index >100: print index*100/(NvideoFrames-1),"% complete\r",
574                 for i in range(Nmarkers):
575                         if Markers[index][i].z < 0: Markers[index][i].z= -Markers[index][i].z
576                         success=True
577                         if CLEAN:   #check for good data
578                                 # C3D marker decoding may have coordinates negative (improper sign bit decoding?)
579                                 myX= abs(Markers[index][i].x)
580                                 myY= abs(Markers[index][i].y)
581                                 myZ= Markers[index][i].z
582                                 if myX > 10000 or myY > 10000 or myZ > 10000: success=False
583                                 if myX <.01 and myY <.01 and myZ <.01: success=False # discontinuity in marker tracking (lost marker)
584                         
585                         if success:
586                                 curvesX[i].append((frame, Markers[index][i].x)) #2.46 knot method
587                                 curvesY[i].append((frame, Markers[index][i].y))
588                                 curvesZ[i].append((frame, Markers[index][i].z))
589                                 if frame==StartFrame: debug(40, "%s loc frame %i: (%0.2f, %0.2f, %0.2f)" % (markerList[i],frame,Markers[index][i].x,Markers[index][i].y,Markers[index][i].z))
590                         else:
591                                 err+=1 # some files have thousands...
592                                 #debug(30,"Point ignored for marker:%s frame %i: (%i, %i, %i)" %       (markerList[i],frame,Markers[index][i].x,Markers[index][i].y,Markers[index][i].z))
593                 index += 1
594         debug(70, "%i points ignored across all markers and frames. Recalculating..." % err)
595
596         for i in range(Nmarkers):
597                 curvesX[i].Recalc()
598                 curvesY[i].Recalc()
599                 curvesZ[i].Recalc()
600         Blender.Set('curframe', StartFrame)
601         Blender.Redraw()
602         if DEBUG==100: status("Clound formed")
603         return empties
604
605 def getNumber(str, length):
606                 if length==2: # unsigned short
607                         return struct.unpack('H',str[0:2])[0], str[2:]
608                 sum = 0
609                 for i in range(length):
610                                 #sum = (sum << 8) + ord(str[i]) for big endian
611                                 sum = sum + ord(str[i])*(2**(8*i))
612                 return sum, str[length:]
613 def unpackFloat(chunk,proctype):
614                 #print proctype
615                 myvar=chunk[0:4]
616                 if   proctype==2: #DEC-VAX
617                         myvar=chunk[2:4]+chunk[0:2] #swap lo=hi word order pair
618                 return struct.unpack('f',myvar[0:4])[0]
619                 
620 def getFloat(chunk,proctype):
621                 return unpackFloat(chunk, proctype), chunk[4:]
622 def parseFloat(chunk,ptr,proctype):
623         return unpackFloat(chunk[ptr:ptr+4], proctype), ptr+4
624
625  
626 def load_c3d(FullFileName):
627 # Input:        FullFileName - file (including path) to be read
628 #
629 # Variable:
630 # Markers            3D-marker data [Nmarkers x NvideoFrames x Ndim(=3)]
631 # VideoFrameRate     Frames/sec
632 # AnalogSignals      Analog signals [Nsignals x NanalogSamples ]
633 # AnalogFrameRate    Samples/sec
634 # Event              Event(Nevents).time ..value  ..name
635 # ParameterGroup     ParameterGroup(Ngroups).Parameters(Nparameters).data ..etc.
636 # CameraInfo         MarkerRelated CameraInfo [Nmarkers x NvideoFrames]
637 # ResidualError      MarkerRelated ErrorInfo  [Nmarkers x NvideoFrames]
638
639         Markers=[];
640         VideoFrameRate=120;
641         AnalogSignals=[];
642         AnalogFrameRate=0;
643         Event=[];
644         ParameterGroups=[];
645         CameraInfo=[];
646         ResidualError=[];
647
648         debug(10, "*********************")
649         debug(10, "**** Opening File ***")
650         debug(10, "*********************")
651
652         #ind=findstr(FullFileName,'\');
653         #if ind>0, FileName=FullFileName(ind(length(ind))+1:length(FullFileName)); else FileName=FullFileName; end
654         debug(0, "FileName = " + FullFileName)
655         fid=open(FullFileName,'rb'); # native format (PC-intel). ideasman says maybe rU
656         content = fid.read();
657         content_memory = content
658         #Header  section
659         NrecordFirstParameterblock, content = getNumber(content,1)     # Reading record number of parameter section
660
661         key, content = getNumber(content,1)
662         if key!=80:
663                 error('File: does not comply to the C3D format')
664                 fid.close()
665                 return
666         #Paramter section
667         content = content[512*(NrecordFirstParameterblock-1)+1:] # first word ignored
668         #file format spec says that  3rd byte=NumberofParmaterRecords... but is ignored here.
669         proctype,content =getNumber(content,1)
670         proctype = proctype-83
671         proctypes= ["unknown","(INTEL-PC)","(DEC-VAX)","(MIPS-SUN/SGI)"]
672         
673         if proctype in (1,2): debug(0, "Processor coding %s"%proctypes[proctype])
674         elif proctype==3: debug(0,"Program untested with %s"%proctypes[proctype])
675         else:
676                 debug(0, "INVALID processor type %i"%proctype)
677                 proctype=1
678                 debug(0,"OVERRIDE processor type %i"%proctype)
679
680         #if proctype==2,
681         #    fclose(fid);
682         #    fid=fopen(FullFileName,'r','d'); % DEC VAX D floating point and VAX ordering
683         #end
684         debug(10, "***********************")
685         debug(00, "**** Reading Header ***")
686         debug(10, "***********************")
687
688         # ###############################################
689         # ##                                           ##
690         # ##    read header                            ##
691         # ##                                           ##
692         # ###############################################
693
694         #%NrecordFirstParameterblock=fread(fid,1,'int8');     % Reading record number of parameter section
695         #%key1=fread(fid,1,'int8');                           % key = 80;
696
697         content = content_memory
698  #fseek(fid,2,'bof');
699         content = content[2:]
700
701         #
702         Nmarkers, content=getNumber(content, 2)
703         NanalogSamplesPerVideoFrame, content = getNumber(content, 2)
704         StartFrame,  content = getNumber(content, 2)
705         EndFrame,  content = getNumber(content, 2)
706         MaxInterpolationGap,  content = getNumber(content, 2)
707
708         Scale, content = getFloat(content,proctype)
709         
710         NrecordDataBlock,  content = getNumber(content, 2)
711         NanalogFramesPerVideoFrame,  content = getNumber(content, 2)
712
713         if NanalogFramesPerVideoFrame > 0:
714                 NanalogChannels=NanalogSamplesPerVideoFrame/NanalogFramesPerVideoFrame
715         else:
716                 NanalogChannels=0
717
718         VideoFrameRate, content = getFloat(content,proctype)
719
720         AnalogFrameRate=VideoFrameRate*NanalogFramesPerVideoFrame
721         NvideoFrames = EndFrame - StartFrame + 1
722
723         debug(0, "Scale= %0.2f" %Scale)
724         debug(0, "NanalogFramesPerVideoFrame= %i" %NanalogFramesPerVideoFrame)
725         debug(0, "Video Frame Rate= %i" %VideoFrameRate)
726         debug(0, "AnalogFrame Rate= %i"%AnalogFrameRate)
727         debug(0, "# markers= %i" %Nmarkers)
728         debug(0, "StartFrame= %i" %StartFrame)
729         debug(0, "EndFrame= %i" %EndFrame)
730         debug(0, "# Video Frames= %i" %NvideoFrames)
731         
732         if Scale>0:
733                 debug(0, "Marker data is in integer format")
734                 if Scale>(XYZ_LIMIT/32767):
735                         Scale=XYZ_LIMIT/32767.0
736                         debug(0, "OVERRIDE: Max coordinate is %i, Scale changed to %0.2f" % (XYZ_LIMIT,Scale))
737         else: debug(0, "Marker data is in floating point format")
738         if VideoFrameRate<1 or VideoFrameRate>120:
739                 VideoFrameRate= 120
740                 debug(0, "OVERRIDE Video Frame Rate= %i" %VideoFrameRate)
741         if proctype not in (1,2): # Intel, DEC are known good
742                 debug(0, "OVERRIDE|Program not tested with this encoding. Set to Intel")
743                 proctype= 1
744
745         debug(10, "***********************")
746         debug(10, "**** Reading Events ...")
747         debug(10, "***********************")
748
749         content = content_memory
750         content = content[298:] #bizarre .. ce devrait ĂȘtre 150 selon la doc rdw skips first 299 bytes?
751
752         EventIndicator,  content = getNumber(content, 2)
753         EventTime=[]
754         EventValue=[]
755         EventName=[]
756
757         debug(0, "Event Indicator = %i" %EventIndicator)
758         if EventIndicator==12345: #rdw: somehow, this original code seems fishy, but I cannot deny it.
759                 Nevents,  content = getNumber(content, 2)
760                 debug(0, "Nevents= %i" %Nevents)
761                 content = content[2:]
762                 if Nevents>0:
763                         for i in range(Nevents):
764                                 letime, content = getFloat(content,proctype)
765                                 EventTime.append(letime)
766                         content = content_memory
767                         content = content[188*2:]
768                         for i in range(Nevents):
769                                 lavalue, content = getNumber(content, 1)
770                                 EventValue.append(lavalue)
771                         content = content_memory
772                         content = content[198*2:]
773                         for i in range(Nevents):
774                                 lenom = content[0:4]
775                                 content = content[4:]
776                                 EventName.append(lenom)
777
778         debug(00, "***************************")
779         debug(00, "**** Reading Parameters ...")
780         debug(10, "***************************")
781         subjects=[]  # a name would be nice, but human will do
782         prefixes=[] # added on to mocap marker names, one for each subject
783         marker_subjects = [] # hopefully will be specified in the file and known to this program
784         markerList=[]
785         ParameterGroups = []
786         ParameterNumberIndex = []
787         
788         content = content_memory
789         content = content[512*(NrecordFirstParameterblock-1):] 
790
791         dat1, content = getNumber(content, 1)
792         key2, content = getNumber(content, 1)
793
794         NparameterRecords, content = getNumber(content, 1)
795         debug(100, "NparameterRecords=%i"%NparameterRecords)
796         proctype,content =getNumber(content,1)
797         proctype = proctype-83                 # proctype: 1(INTEL-PC); 2(DEC-VAX); 3(MIPS-SUN/SGI)
798
799         for i in range(NparameterRecords):
800                 leparam = ParameterGroup(None, None, [])
801                 ParameterGroups.append(leparam)
802                 ParameterNumberIndex.append(0)
803         #
804         Ncharacters, content = getNumber(content, 1)
805         if Ncharacters>=128:
806                 Ncharacters = -(2**8)+(Ncharacters)
807         GroupNumber, content = getNumber(content, 1)
808         if GroupNumber>=128:
809                 GroupNumber = -(2**8)+(GroupNumber)
810         debug(80,"GroupNumber = %i, Nchar=%i" %(GroupNumber,Ncharacters))
811
812         while Ncharacters > 0:
813                 if GroupNumber<0:
814                         GroupNumber=abs(GroupNumber)
815                         GroupName = content[0:Ncharacters]
816                         content = content[Ncharacters:]
817                         #print "Group Number = ", GroupNumber
818                         ParameterGroups[GroupNumber].name = GroupName
819                         #print "ParameterGroupName =", GroupName
820                         offset, content = getNumber(content, 2)
821                         deschars, content = getNumber(content, 1)
822                         GroupDescription = content[0:deschars]
823                         content = content[deschars:]
824                         ParameterGroups[GroupNumber].description = GroupDescription
825                         #
826                         ParameterNumberIndex[GroupNumber]=0
827                         content = content[offset-3-deschars:]
828                 else:
829                         
830                         ParameterNumberIndex[GroupNumber]=ParameterNumberIndex[GroupNumber]+1
831                         ParameterNumber=ParameterNumberIndex[GroupNumber]
832                         #print "ParameterNumber=", ParameterNumber
833                         ParameterGroups[GroupNumber].parameter.append(Parameter(None, None, [], [], None))
834                         ParameterName = content[0:Ncharacters]
835                         content = content[Ncharacters:]
836                         #print "ParameterName = ",ParameterName 
837                         if len(ParameterName)>0:
838                                 ParameterGroups[GroupNumber].parameter[ParameterNumber-1].name=ParameterName
839                         offset, content = getNumber(content, 2)
840                         filepos = len(content_memory)-len(content)
841                         nextrec = filepos+offset-2
842
843                         type, content=getNumber(content, 1)
844                         if type>=128:
845                                 type = -(2**8)+type
846                         ParameterGroups[GroupNumber].parameter[ParameterNumber-1].type=type
847
848                         dimnum, content=getNumber(content, 1)
849                         if dimnum == 0:
850                                 datalength = abs(type)
851                         else:
852                                 mult=1
853                                 dimension=[]
854                                 for j in range (dimnum):
855                                         ladim, content = getNumber(content, 1)
856                                         dimension.append(ladim)
857                                         mult=mult*dimension[j]
858                                         ParameterGroups[GroupNumber].parameter[ParameterNumber-1].dim.append(dimension[j])
859                                 datalength = abs(type)*mult
860
861                         #print "ParameterNumber = ", ParameterNumber, " Group Number = ", GroupNumber
862
863                         if type==-1:
864                                 data = ""
865                                 wordlength=dimension[0]
866                                 if dimnum==2 and datalength>0:
867                                         for j in range(dimension[1]):
868                                                 data=string.rstrip(content[0:wordlength])
869                                                 content = content[wordlength:]
870                                                 ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data.append(data)
871                                 elif dimnum==1 and datalength>0:
872                                                 data=content[0:wordlength]
873                                                 ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data.append(data) # ???
874
875                                 myParam=string.rstrip(ParameterName)
876                                 myGroup=string.rstrip(GroupName)
877                                 msg= "-%s-%s-" % (myGroup,myParam)
878                                 if myGroup == "POINT":
879                                         if myParam== "LABELS":
880                                                 # named in form of subject:marker.
881                                                 # the list "empties" is a corresponding list of actual empty object names that make up the cloud
882                                                 markerList= ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
883                                                 debug(0, "%sLABELS = %i %s" %(msg, len(markerList),markerList)) #list of logical markers from 0 to n corresponding to points
884                                         elif myParam== "LABELS2": #more labels
885                                                 # named in form of subject:marker.
886                                                 # the list "empties" is a corresponding list of actual empty object names that make up the cloud
887                                                 momarkList= ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
888                                                 markerList+=momarkList
889                                                 debug(0, "%sLABELS2 = %i %s" %(msg, len(momarkList),momarkList)) #list of logical markers from 0 to n corresponding to points
890                                         else: debug(70, "%s UNUSED = %s" %(msg,ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data))
891                                 elif myGroup in ["SUBJECT", "SUBJECTS"]: #info about the actor
892                                         if myParam in ["NAME", "NAMES"]:
893                                                 subjects= ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
894                                                 debug(0, "%sNames of Subjects = %s" %(msg, subjects)) # might be useful in naming armatures
895                                                 for i in range(len(subjects)):
896                                                         subjects[i]=subjects[i].rstrip()
897                                                         if subjects[i]=="": subjects[i]="Human"
898                                         elif myParam == "LABEL_PREFIXES":
899                                                 prefixes = ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
900                                                 debug(0, "%sMarker Prefixes = %s" %(msg, prefixes)) # to xlate marker name to that in file
901                                                 for i in range(len(prefixes)):
902                                                         prefixes[i]=prefixes[i].rstrip()
903                                         elif myParam== "MARKER_SETS":
904                                                 marker_subjects= ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
905                                                 debug(0, "%sMarker Set = %s"%(msg, marker_subjects)) # marker set that each subject was wearing
906                                         elif myParam== "MODEL_PARAM":
907                                                 action= ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
908                                                 debug(0, "%sModel Paramter = %s"%(msg,action)) # might be a good name for the blender scene
909                                         elif myParam== "LABELS":
910                                                 # named in form of subject:marker.
911                                                 # the list "empties" is a corresponding list of actual empty object names that make up the cloud
912                                                 markerList= ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
913                                                 debug(0, "%sLABELS = %i %s"%(msg, len(markerList),markerList)) #list of logical markers from 0 to n corresponding to points
914                                         else: debug(70, "%sUNUSED = %s"%(msg, ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data))
915                                 else:
916                                         debug(70, "%sUNUSED = %s"%(msg, ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data))
917                         elif type == 1:
918                                 debug(100,"Block type %i is largely unsupported and untested."%type)
919                                 data = []
920                                 Nparameters=datalength/abs(type)
921                                 debug(100, "Nparameters=%i"%Nparameters)
922                                 for i in range(Nparameters):
923                                         ladata,content = getNumber(content, 1)
924                                         data.append(ladata)
925                                 ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data=data
926                                 #print ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
927
928                                 #print "type boolean"
929                         elif type == 2 and datalength>0:
930                                 debug(100,"Block type %i is largely unsupported and untested."%type)
931                                 data = []
932                                 Nparameters=datalength/abs(type)
933                                 debug(100, "Nparameters=%i"%Nparameters)
934                                 for i in range(Nparameters):
935                                         ladata,content = getNumber(content, 2)
936                                         data.append(ladata)
937                                 #ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data=data
938                                 if dimnum>1:
939                                         #???? print "arg je comprends pas"
940                                         ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data=data
941                                         #???ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data=reshape(data,dimension)
942                                 else:
943                                         ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data=data
944                                 #print ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
945                                 #pass
946                                 #print "type integer"
947                         elif type == 4 and datalength>0:
948                                 debug(100,"Block type %i is largely unsupported and untested."%type)
949                                 data = []
950                                 Nparameters=datalength/abs(type)
951                                 debug(100, "Nparameters=%i"%Nparameters)
952                                 for i in range(Nparameters):
953                                         ladata,content = getFloat(content,proctype)  
954                                         data.append(ladata)
955                                 if dimnum>1:
956                                         ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data=data
957                                         #print "arg je comprends pas"
958                                         #???ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data=reshape(data,dimension)
959                                 else:
960                                         ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data=data
961                                 #print ParameterGroups[GroupNumber].parameter[ParameterNumber-1].data
962                         else:
963                                 debug(100,"Block type %i is largely unsupported and untested."%type)
964                                 #print "error"
965                                 pass
966                         deschars, content= getNumber(content, 1)
967                         if deschars>0:
968                                 description = content[0:deschars]
969                                 content = content[deschars:]
970                                 ParameterGroups[GroupNumber].parameter[ParameterNumber-1].description=description
971                         
972                         content = content_memory
973                         content = content[nextrec:] 
974
975                 Ncharacters,content = getNumber(content, 1)
976                 if Ncharacters>=128:
977                         Ncharacters = -(2**8)+(Ncharacters)
978                 GroupNumber,content = getNumber(content, 1)
979                 if GroupNumber>=128:
980                         GroupNumber = -(2**8)+(GroupNumber)
981                 debug(80,"GroupNumber = %i, Nchar=%i" %(GroupNumber,Ncharacters))
982
983         debug(00, "***************************")
984         debug(00, "**** Examining Parameters ...")
985         debug(10, "***************************")
986
987         if len(subjects)==0: subjects=["Test"] #well, somebody got mocapped!
988         for i in range(0, len(subjects)-len(prefixes)): prefixes.append("")
989         for i in range(0, len(subjects)-len(marker_subjects)): marker_subjects.append(subjects[i])
990
991         #make a markerlist if they didn't
992         debug(0, "%i Markers specified, %i marker names supplied" %(Nmarkers,len(markerList)))
993         if len(markerList)==0:
994                 debug(0, "File missing any POINT LABELS marker list. Making defaults")
995                 #I guess just make cloud of empty.xxx
996         if len(markerList)<Nmarkers:
997                 for i in range(len(markerList),Nmarkers): markerList.append("mark."+str(i))
998         #note that they may supply more markers than Nmarkers, extras are usually null or ignored
999         #an idea here to winnow down the marker List is to go through the nodes and see if there are markers
1000         # in the list that are not used in constraining the armature, and discard them or set them debug(0,
1001         # so that later on in processing we don't bother saving their location, possibly speeding up processing
1002         # because we can just skip over their data block.
1003         # put this on TODO list since it gets pretty complicated going throuch each marker set and all constraints etc.
1004         
1005         ## ###############################################
1006         ## ##                                           ##
1007         ## ##    Initalize Arrays and Allocate Memory
1008         ## ##                                           ##
1009         ## ###############################################
1010         ##  Get the coordinate and analog data
1011         #
1012
1013         content = content_memory
1014         content = content[(NrecordDataBlock-1)*512:] 
1015         debug(20,"Allocating memory for %i floats" %NvideoFrames*(Nmarkers*3+2))
1016         for i in range (NvideoFrames):
1017                 Markers.append([])
1018                 ResidualError.append([])
1019                 CameraInfo.append([])
1020                 for j in range (Nmarkers):
1021                         Markers[i].append(Marker(0.0,0.0,0.0))
1022                         ResidualError[i].append(0)
1023                         CameraInfo[i].append(0)
1024
1025         #print Markers
1026         #
1027         #if Scale < 0
1028         #    for i=1:NvideoFrames
1029         #        for j=1:Nmarkers
1030         #            Markers(i,j,1:3)=fread(fid,3,'float32')'; 
1031         #            a=fix(fread(fid,1,'float32'));  
1032         #            highbyte=fix(a/256);
1033         #            lowbyte=a-highbyte*256; 
1034         #            CameraInfo(i,j)=highbyte; 
1035         #            ResidualError(i,j)=lowbyte*abs(Scale); 
1036         #        end
1037         #        waitbar(i/NvideoFrames)
1038         #        for j=1:NanalogFramesPerVideoFrame,
1039         #            AnalogSignals(j+NanalogFramesPerVideoFrame*(i-1),1:NanalogChannels)=...
1040         #                fread(fid,NanalogChannels,'int16')';
1041         #        end
1042         #    end
1043
1044         debug(10, "***************************")
1045         debug(00, "**** Reading DataBlock of %i Frames...." % NvideoFrames)
1046         debug(10, "***************************")
1047         residuals= NanalogFramesPerVideoFrame*NanalogChannels*2
1048         err=0 #keep track of errors or serious data issues
1049         ptr_read = 0
1050
1051         if Scale < 0.0: # 3D Data - 4-byte Floating-point Format
1052                 for i in range (NvideoFrames):
1053                         if i==0: start=sys.time()
1054                         elif i==10:
1055                                 tmp=(sys.time()-start)*NvideoFrames/600
1056                                 debug(0,"%i minutes remaining..." % tmp)
1057                         else: print "%i percent complete. On Frame %i Points procesed: %i\r" % (i*100/NvideoFrames,i,i*Nmarkers),
1058                         for j in range (Nmarkers):
1059         
1060                                 x,ptr_read = parseFloat(content, ptr_read, proctype)
1061                                 y,ptr_read = parseFloat(content, ptr_read, proctype)
1062                                 z,ptr_read = parseFloat(content, ptr_read, proctype)
1063                                 myx= x * -Scale
1064                                 myy= y * -Scale
1065                                 myz= z * -Scale
1066
1067                                 if abs(myx)>XYZ_LIMIT or abs(myy)>XYZ_LIMIT or abs(myz)>XYZ_LIMIT:
1068                                         err+=1
1069                                         if err>100:
1070                                                 debug(0, "Warning: 100 data points for markers seem way out there")
1071                                                 debug(0, "data read: (%i, %i, %i)" %(x,y,z))
1072                                                 debug(0, "Consider revising Scale %0.2f" % Scale)
1073                                                 debug(0, "which now givs coordinates: (%i, %i, %i)" %(x*Scale,y*Scale,z*Scale))
1074                                                 err=-0
1075                                         if abs(myx)>XYZ_LIMIT: myx= XYZ_LIMIT*myx/abs(myx) #preserve sign
1076                                         if abs(myy)>XYZ_LIMIT: myy= XYZ_LIMIT*myy/abs(myy) #preserve sign
1077                                         if abs(myz)>XYZ_LIMIT: myz= XYZ_LIMIT*myz/abs(myz) #preserve sign
1078                                 Markers[i][j].x = myx
1079                                 Markers[i][j].y = myy
1080                                 Markers[i][j].z = myz 
1081
1082                                 a,ptr_read = parseFloat(content, ptr_read, proctype)
1083                                 a = int(a)
1084                                 highbyte = int(a/256)
1085                                 lowbyte=a-highbyte*256
1086                                 CameraInfo[i][j] = highbyte
1087                                 ResidualError[i][j] = lowbyte*abs(Scale)
1088                                 #Monitor marker location to ensure data block is being parsed properly
1089                                 if j==0: debug(90,"Frame %i loc of %s: (%i, %i, %i)" % (i,markerList[j],myx,myy,myz))
1090                                 if i==0: debug(50, "Initial loc of %s: (%i, %i, %i)" % (markerList[j],myx,myy,myz))
1091
1092                         ptr_read+=residuals #skip over the following  
1093                         #for j in range (NanalogFramesPerVideoFrame):
1094                         #  for k in range(NanalogChannels):
1095                         #    val, content = getNumber(content, 2)
1096                         #    AnalogSignals[j+NanalogFramesPerVideoFrame*(i)][k]=val #??? i-1
1097         #else
1098         #    for i=1:NvideoFrames
1099         #        for j=1:Nmarkers
1100         #            Markers(i,j,1:3)=fread(fid,3,'int16')'.*Scale;
1101         #            ResidualError(i,j)=fread(fid,1,'int8');
1102         #            CameraInfo(i,j)=fread(fid,1,'int8');
1103         #        end
1104         #        waitbar(i/NvideoFrames)
1105         #        for j=1:NanalogFramesPerVideoFrame,
1106         #            AnalogSignals(j+NanalogFramesPerVideoFrame*(i-1),1:NanalogChannels)=...
1107         #                fread(fid,NanalogChannels,'int16')';
1108         #        end
1109         #    end
1110         #end
1111
1112         else: #Scale is positive, but should be <1 to scale down, like 0.05
1113                 two16= -2**16
1114                 if len(content) < NvideoFrames*(Nmarkers*(6+2)+residuals):
1115                         error("%i bytes is not enough data for |%i frames|%i markers|%i residual" %(len(content),NvideoFrames,Nmarkers,residuals))
1116                 #Note: I really tried to optimize this loop, since it was taking hours to process
1117                 for i in range(NvideoFrames):
1118                         if i==0: start=sys.time()
1119                         elif i==10:
1120                                 tmp=(sys.time()-start)*NvideoFrames/600
1121                                 debug(0,"%i minutes remaining..." % tmp)
1122                         else: print "%i percent complete. On Frame %i Points procesed: %i\r" % (i*100/NvideoFrames,i,i*Nmarkers),
1123                                 
1124                         for j in range(Nmarkers):        
1125                                 #x, content = getNumber(content,2)
1126                                 # this is old skool signed int, not but not a short.
1127                                 x = ord(content[ptr_read+0]) + (ord(content[ptr_read+1])<<8)
1128                                 if x>32768: x+=two16
1129                                 y = ord(content[ptr_read+2]) + (ord(content[ptr_read+3])<<8)
1130                                 if y>32768: y+=two16
1131                                 z = ord(content[ptr_read+4]) + (ord(content[ptr_read+5])<<8)
1132                                 if z>32768: z+=two16
1133         
1134 ##        
1135 ##        x = ord(content[ptr_read]) + ord(content[ptr_read+1])*(2**8)
1136 ##        ptr_read+=2
1137 ##        if x > 32768:
1138 ##          x=-(2**16)+(x)
1139 ##        #y, content = getNumber(content,2)
1140 ##        y = ord(content[ptr_read]) + ord(content[ptr_read+1])*(2**8)
1141 ##        ptr_read+=2
1142 ##        if y > 32768:
1143 ##          y=-(2**16)+(y)
1144 ##        #z, content = getNumber(content,2)
1145 ##        z = ord(content[ptr_read]) + ord(content[ptr_read+1])*(2**8)
1146 ##        ptr_read+=2
1147 ##        if z > 32768:
1148 ##          z=-(2**16)+(z)
1149 ##
1150 ##        print "(%i=%i, %i=%i, %i=%i)" %(x,myx,y,myy,z,myz)
1151
1152                                 # for integers, I changed Scale above to avoid getting impossible numbers       
1153                                 Markers[i][j].x = x*Scale
1154                                 Markers[i][j].y = y*Scale
1155                                 Markers[i][j].z = z*Scale
1156
1157 ##        ResidualError[i][j], content = getNumber(content, 1)
1158 ##        CameraInfo[i][j], content = getNumber(content, 1)
1159                                 #try to improve performance by:
1160                                 ResidualError[i][j]= ord(content[ptr_read+6])
1161                                 CameraInfo[i][j]= ord(content[ptr_read+7])
1162                                 
1163                                 content= content[ptr_read+8:]
1164                                 ptr_read=0
1165
1166                                 if j==0: debug(100,"Frame %i loc of %s: %s" % (i,markerList[j],Markers[i][j]))
1167                                 if i==0: debug(50, "Initial loc of %s: (%s)" % (markerList[j],Markers[i][j]))
1168                                 
1169                         #for j in range (NanalogFramesPerVideoFrame):
1170                         #  for k in range(NanalogChannels):
1171                         #    val, content = getNumber(content, 2)
1172                         #AnalogSignals(j+NanalogFramesPerVideoFrame*(i-1),1:NanalogChannels)=val
1173                         ptr_read= residuals # skip over the above
1174         print "\ndone with file."
1175         fid.close()
1176
1177         cloud= makeCloud(Nmarkers,markerList,StartFrame,EndFrame,Markers)
1178
1179         setupAnim(StartFrame, EndFrame,VideoFrameRate)
1180
1181         debug(10, "**************************")
1182         debug(00, "**** Making %i Armatures" % len(subjects))
1183         debug(10, "**************************")
1184         for i in range(len(subjects)):
1185                 marker_set= marker_subjects[i]
1186                 success=False
1187                 if len(marker_set)>0:
1188                                 for trymark in MARKER_SETS: 
1189                                         if trymark[0:len(marker_set)]==marker_set:
1190                                                 marker_set=trymark
1191                                                 success=True
1192                 if success:
1193                         debug(0, "Armature for %s will be put on layers %s" % (subjects[i],LAYERS_ARMOB))
1194                         debug(0, "  based on an markers beginning with %s" % prefixes[i])
1195                         ob= make_arm(subjects[i],prefixes[i],markerList,cloud,marker_set)
1196                 else:
1197                         debug(00, "Presently, this program can automatically create a constrained armature for marker sets %s" % MARKER_SETS)
1198                         debug(00, "%s uses an unknown marker set %s" % (subjects[i],marker_set))
1199                         debug(10, "Have a nice day! If you figure out an armature node system for this cloud, please add it to the program.")
1200
1201         debug(10, "**************************")
1202         debug(00, "**** Conclusion")
1203         minmax=[0,0,0,0,0,0]
1204         for i in range(NvideoFrames):
1205                         for j in range(Nmarkers):
1206                                 if minmax[0]>Markers[i][j].x: minmax[0]=Markers[i][j].x
1207                                 if minmax[1]>Markers[i][j].y: minmax[1]=Markers[i][j].y
1208                                 if minmax[2]>Markers[i][j].z: minmax[2]=Markers[i][j].z
1209                                 if minmax[3]<Markers[i][j].x: minmax[3]=Markers[i][j].x
1210                                 if minmax[4]<Markers[i][j].y: minmax[4]=Markers[i][j].y
1211                                 if minmax[5]<Markers[i][j].z: minmax[5]=Markers[i][j].z
1212         debug(0,"Markers move in 3D space from (%i,%i,%i) to (%i,%i,%i). "%(minmax[0],minmax[1],minmax[2],minmax[3],minmax[4],minmax[5]))
1213         debug(0,"Set your 3D View Properties Clip End and zoom out your display.")
1214 def my_callback(filename):
1215         # processing options UI goes here, eventually
1216         Window.WaitCursor(1) 
1217         t = sys.time() 
1218         load_c3d(filename)
1219         # Timing the script is a good way to be aware on any speed hits when scripting 
1220         debug(0, '%s file processed in %.2f sec.' % (filename,sys.time()-t))
1221         Window.WaitCursor(0)
1222                                 
1223 def processFile():
1224         # select file and pass a handle to the processor
1225         Blender.Window.FileSelector(my_callback, "Import C3D") # makes a window a file selector and processes it
1226         #processing contiues while file is being worked
1227         
1228 def main(): 
1229         # Display the GUI
1230         
1231         # Run the  function 
1232         processFile()
1233
1234         #Close files, display stats, cleanup, advice on next steps
1235
1236 # This lets you import the script without running it 
1237 if __name__ == '__main__': 
1238         debug(00, "------------------------------------")
1239         debug(00, '%s %s script began at %.0f' % (__script__,__version__,sys.time()))
1240         main() 
1241         
1242  
1243