2.5: Top Menu
[blender.git] / release / scripts / animation_trajectory.py
1 #!BPY
2
3 """ Registration info for Blender menus: <- these words are ignored
4 Name: 'Trajectory'
5 Blender: 243
6 Group: 'Animation'
7 Tip: 'See Trajectory of selected object'
8 """
9
10 __author__ = '3R - R3gis'
11 __version__ = '2.43'
12 __url__ = ["Script's site , http://blenderfrance.free.fr/python/Trajectory_en.htm","Author's site , http://cybercreator.free.fr", "French Blender support forum, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender"]
13 __email__=["3R, r3gis@free.fr"]
14
15
16 __bpydoc__ = """
17
18 Usage:
19
20 * Launch with alt+P (or put it in .script folder)
21
22 Allow to see in real time trajectory of selected object.
23
24 On first run, it ask you
25 - If you want that actually selected object have they trajectory always shown
26 - If you want to use Space Handler or a Scriptlink in Redraw mode
27 - Future and Past : it is the frame in past and future
28 of the beggining and the end of the path
29 - Width of line that represent the trajectory
30
31 Then the object's trajectory will be shown in all 3D areas.
32 When trajectory is red, you can modifiy it by moving object.
33 When trajectory is blue and you want to be able to modify it, inser a Key (I-Key)
34
35 Points appears on trajectory :
36 - Left Clic to modify position
37 - Right Clic to go to the frame it represents
38
39 Notes:<br>
40 In scriptlink mode, it create one script link so make sure that 'Enable Script Link' toogle is on
41 In SpaceHandler mode, you have to go in View>>SpaceHandlerScript menu to activate Trajectory
42
43
44 """
45
46
47 # --------------------------------------------------------------------------
48 # ***** BEGIN GPL LICENSE BLOCK *****
49 #
50 # Copyright (C) 2004-2006: Regis Montoya
51 #
52 # This program is free software; you can redistribute it and/or
53 # modify it under the terms of the GNU General Public License
54 # as published by the Free Software Foundation; either version 2
55 # of the License, or (at your option) any later version.
56 #
57 # This program is distributed in the hope that it will be useful,
58 # but WITHOUT ANY WARRANTY; without even the implied warranty of
59 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
60 # GNU General Public License for more details.
61 #
62 # You should have received a copy of the GNU General Public License
63 # along with this program; if not, write to the Free Software Foundation,
64 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
65 #
66 # ***** END GPL LICENCE BLOCK *****
67 # --------------------------------------------------------------------------
68 #################################
69 # by 3R - 26/08/05
70 # for any problem :
71 #       r3gis@free.fr
72 #       ou sur le newsgroup:
73 # http://zoo-logique.org/3D.Blender/
74 #################################
75 #Many thanks to cambo for his fixes
76 #################################
77
78
79
80 import Blender
81
82
83 scene= Blender.Scene.GetCurrent()
84
85         
86 #Writing
87 def write_script(name, script):
88         global scene
89         #List texts and their name
90         #write : type of writing : 1->New, 2->Overwrite
91         scripting= None
92         for text in Blender.Text.Get():
93                 if text.name==name and text.asLines()[1] != "#"+str(__version__):
94                         scripting = text
95                         scripting.clear()
96                         scripting.write(script)
97                         break
98         
99         if not scripting:
100                 scripting= Blender.Text.New(name)
101                 scripting.write(script)
102
103 def link_script(name, type):
104         global scene
105         scriptlinks = scene.getScriptLinks(type) # none or list
106         if not scriptlinks or name not in scriptlinks:
107                 scene.addScriptLink(name, type)
108
109
110 #Deleting of a text
111 def text_remove(name):
112         global scene
113         #try to delete text if already linked
114         try:
115                 text= Blender.Text.Get(name)
116                 # Texte.clear()
117                 scene.clearScriptLinks([name])
118                 Blender.Text.unlink(text)
119         except:
120                 print('---Initialisation of Trajectory_'+str(__version__)+'.py---')
121
122 #Whether is already running, also check if it's the last version of the script : second line contain the version fo the script
123 ask_modif= 0 # Default
124 for text in Blender.Text.Get():
125         if text.name == 'Trajectory' and text.asLines()[1] == "#"+str(__version__):
126                 #We ask if script modify his seetings, keep it or stop script
127                 ask_modif= Blender.Draw.PupMenu("Script already launch %t|Modify settings%x0|Keep settings%x1|Stop script%x2|")
128                 if ask_modif==-1: # user canceled.
129                         ask_modif= 1 
130                 break
131
132 selection_mode= 0
133 future= 35
134 past= 20
135 width= 2
136
137 #In modify case
138 if ask_modif==0:
139         handle_mode= Blender.Draw.Create(0)
140         selection_mode= Blender.Draw.Create(0)
141         future= Blender.Draw.Create(35)
142         past= Blender.Draw.Create(20)
143         width= Blender.Draw.Create(2)
144
145         block= []
146         block.append(("Space Handlers", handle_mode, "You have to activate for each area by View>>SpaceHandler")) #You can delete this option...
147         block.append(("Always Draw", selection_mode, "Selected object will have their trajectory always shown"))
148         block.append(("Past :", past, 1, 900))
149         block.append(("Futur:", future, 1, 900))
150         block.append(("Width:", width, 1,5))
151         
152         if not Blender.Draw.PupBlock("Trajectory seetings", block):
153                 ask_modif=1
154         
155         handle_mode= handle_mode.val
156         selection_mode= selection_mode.val
157         future= future.val
158         past= past.val
159         width= width.val
160
161
162 #put names of selected objects in objects_select if option choosen by user
163 if selection_mode==1:
164         objects_select= [ob.name for ob in scene.objects.context]
165 else:
166         objects_select= []
167         
168
169 try:
170         if handle_mode==1:
171                 DrawPart="#SPACEHANDLER.VIEW3D.DRAW\n"
172         else:
173                 DrawPart="#!BPY\n"
174 except:DrawPart="#BadlyMade"
175         
176
177 #Here is the script to write in Blender and to link, options are also written now
178 DrawPart=DrawPart+"#"+str(__version__)+"""
179 #This script is a part of Trajectory.py and have to be linked to the scene in Redraw if not in HANDLER mode.
180 #Author : 3R - Regis Montoya
181 #It's better to use the Trajectory_"version_number".py
182 #You can modify the two following value to change the path settings
183 future="""+str(future)+"""
184 past="""+str(past)+"""
185 object_init_names="""+str(objects_select)+"""
186
187
188 import Blender, math
189 from Blender import BGL, Draw, Ipo
190 from Blender.BGL import *
191 from Blender.Draw import *
192 from math import *
193
194 from Blender.Mathutils import Vector
195
196 #take actual frame
197 frameC=Blender.Get('curframe')
198 scene = Blender.Scene.GetCurrent()
199 render_context=scene.getRenderingContext()
200 #ajust number of frames with NewMap and OldMapvalue values
201 k=1.00*render_context.oldMapValue()/render_context.newMapValue()
202 if k<1:
203         tr=-1*int(log(k*0.1, 10))
204 else:
205         tr=-1*int(log(k, 10))
206 #The real and integer frame to compare to ipos keys frames
207 frameCtr=round(frameC*k, tr)
208 frameCr=frameC*k
209 frameC=int(round(frameC*k, 0))
210
211
212 #List objects that we have to show trajectory in $objects
213 # In this case, using a dict for unique objects is the fastest way.
214 object_dict= dict([(ob.name, ob) for ob in scene.objects.context])
215 for obname in object_init_names:
216         if not object_dict.has_key(obname):
217                 try: # Object may be removed.
218                         object_dict[obname]= Blender.Object.Get(obname)
219                 except:
220                         pass # object was removed.
221
222 #This fonction give the resulting matrix of all parents at a given frame
223 #parent_list is the list of all parents [object, matrix, locX_ipo, locY, Z, rotX, Y, Z, sizeX, Y, Z] of current object
224 def matrixForTraj(frame, parent_list):
225         DecMatC=Blender.Mathutils.Matrix([1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1])
226
227         for parent_data in parent_list:
228                 parent_ob=      parent_data[0]
229                 
230                 try:    X=      parent_data[5][frame]*pi/18
231                 except: X=      parent_ob.RotX
232                 try:    Y=      parent_data[6][frame]*pi/18
233                 except: Y=      parent_ob.RotY
234                 try:    Z=      parent_data[7][frame]*pi/18
235                 except: Z=      parent_ob.RotZ
236                 try:    LX=     parent_data[2][frame]
237                 except: LX=     parent_ob.LocX
238                 try:    LY=     parent_data[3][frame]
239                 except: LY=     parent_ob.LocY
240                 try:    LZ=     parent_data[4][frame]
241                 except: LZ=     parent_ob.LocZ
242                 try:    SX=     parent_data[8][frame]
243                 except: SX=     parent_ob.SizeX
244                 try:    SY=     parent_data[9][frame]
245                 except: SY=     parent_ob.SizeY
246                 try:    SZ=     parent_data[10][frame]
247                 except: SZ=     parent_ob.SizeZ
248
249                 NMat=Blender.Mathutils.Matrix([cos(Y)*cos(Z)*SX,SX*cos(Y)*sin(Z),-SX*sin(Y),0],
250                 [(-cos(X)*sin(Z)+sin(Y)*sin(X)*cos(Z))*SY,(sin(X)*sin(Y)*sin(Z)+cos(X)*cos(Z))*SY,sin(X)*cos(Y)*SY,0],
251                 [(cos(X)*sin(Y)*cos(Z)+sin(X)*sin(Z))*SZ,(cos(X)*sin(Y)*sin(Z)-sin(X)*cos(Z))*SZ,SZ*cos(X)*cos(Y),0],
252                 [LX,LY,LZ,1])
253                 DecMatC=DecMatC*parent_data[1]*NMat
254         return DecMatC
255
256 #####
257 TestLIST=[]
258 matview=Blender.Window.GetPerspMatrix()
259 ###########
260 #Fonction to draw trajectories
261 ###########
262
263 def Trace_Traj(ob):
264                 global TestLIST, matview
265                 #we draw trajectories for all objects in list
266         
267                 LocX=[]
268                 LocY=[]
269                 LocZ=[]
270                 #List with trajectories' vertexs
271                 vertexX=[]
272                 
273                 contextIpo= ob.ipo
274                 if contextIpo:
275                         ipoLocX=contextIpo[Ipo.OB_LOCX]
276                         ipoLocY=contextIpo[Ipo.OB_LOCY]
277                         ipoLocZ=contextIpo[Ipo.OB_LOCZ]
278                         ipoTime=contextIpo[Ipo.OB_TIME]
279                 else: # only do if there is no IPO (if no ipo curves : return None object and don't go in this except)
280                         ipoLocX= ipoLocY= ipoLocZ= ipoTime= None
281                 
282                 if ipoTime:
283                         return 0
284                 
285                 #Get all parents of ob
286                 parent=ob.parent
287                 backup_ob= ob
288                 child= ob
289                 parent_list= []
290                 
291                 #Get parents's infos :
292                 #list of [name, initial matrix at make parent, ipo in X,Y,Z,rotX,rotY,rotZ,sizeX,Y,Z]
293                 while parent:
294                         Init_Mat=Blender.Mathutils.Matrix(child.getMatrix('worldspace')) #must be done like it (it isn't a matrix otherwise)
295                         Init_Mat.invert()
296                         Init_Mat=Init_Mat*child.getMatrix('localspace')
297                         Init_Mat=parent.getMatrix()*Init_Mat
298                         Init_Mat.invert()
299         
300                         contextIpo= parent.ipo # None or IPO
301                         if contextIpo:
302                                 ipo_Parent_LocX=contextIpo[Ipo.OB_LOCX]
303                                 ipo_Parent_LocY=contextIpo[Ipo.OB_LOCY]
304                                 ipo_Parent_LocZ=contextIpo[Ipo.OB_LOCZ]
305                                 ipo_Parent_RotX=contextIpo[Ipo.OB_ROTX]
306                                 ipo_Parent_RotY=contextIpo[Ipo.OB_ROTY]
307                                 ipo_Parent_RotZ=contextIpo[Ipo.OB_ROTZ]
308                                 ipo_Parent_SizeX=contextIpo[Ipo.OB_SIZEX]
309                                 ipo_Parent_SizeY=contextIpo[Ipo.OB_SIZEY]
310                                 ipo_Parent_SizeZ=contextIpo[Ipo.OB_SIZEZ]
311                         else:
312                                 ipo_Parent_LocX=ipo_Parent_LocY=ipo_Parent_LocZ=\
313                                 ipo_Parent_RotX=ipo_Parent_RotY=ipo_Parent_RotZ=\
314                                 ipo_Parent_SizeX=ipo_Parent_SizeY=ipo_Parent_SizeZ= None
315                         
316                         parent_list.append([parent, Init_Mat, ipo_Parent_LocX, ipo_Parent_LocY, ipo_Parent_LocZ, ipo_Parent_RotX, ipo_Parent_RotY, ipo_Parent_RotZ, ipo_Parent_SizeX, ipo_Parent_SizeY, ipo_Parent_SizeZ])
317         
318                         child=parent
319                         parent=parent.parent
320                         
321                 #security : if one of parents object are a path>>follow : trajectory don't work properly so it have to draw nothing
322                 for parent in parent_list:
323                         if parent[0].type == 'Curve':
324                                 if parent[0].data.flag & 1<<4: # Follow path, 4th bit
325                                         return 1
326                 
327                 #ob >> re-assign obj and not parent
328                 ob= backup_ob
329                 ob= backup_ob
330                 
331                 
332                 if ipoLocX: LXC= ipoLocX[frameC]
333                 else:           LXC= ob.LocX
334                 if ipoLocY:     LYC= ipoLocY[frameC]
335                 else:           LYC= ob.LocY
336                 if ipoLocZ:     LZC= ipoLocZ[frameC]
337                 else:           LZC= ob.LocZ
338
339                 vect= Vector([ob.LocX, ob.LocY, ob.LocZ, 1])
340                 color=[0, 1]    
341         
342                 #If trajectory is being modified and we are at a frame where a ipo key already exist
343                 if round(ob.LocX, 5)!=round(LXC, 5):
344                         for bez in ipoLocX.bezierPoints:
345                                 if round(bez.pt[0], tr)==frameCtr:
346                                         bez.pt = [frameCr, vect[0]]
347                         ipoLocX.recalc()
348                 if round(ob.LocY, 5)!=round(LYC, 5):
349                         for bez in ipoLocY.bezierPoints:
350                                 if round(bez.pt[0], tr)==frameCtr:
351                                         bez.pt = [frameCr, vect[1]]
352                         ipoLocY.recalc()
353                 if round(ob.LocZ, 5)!=round(LZC, 5):
354                         for bez in ipoLocZ.bezierPoints:
355                                 if round(bez.pt[0], tr)==frameCtr:
356                                         bez.pt = [frameCr, vect[2]]
357                         ipoLocZ.recalc()
358                 
359                 #change trajectory color if at an ipoKey
360                 VertexFrame=[]
361                 bezier_Coord=0
362                 if ipoLocX: # FIXED like others it was just in case ipoLocX==None
363                         for bez in ipoLocX.bezierPoints:
364                                 bezier_Coord=round(bez.pt[0], tr)
365                                 if bezier_Coord not in VertexFrame:
366                                         VertexFrame.append(bezier_Coord)
367                                 if bezier_Coord==frameCtr:
368                                                 color=[1, color[1]-0.3]
369                 if ipoLocY: # FIXED
370                         for bez in ipoLocY.bezierPoints:
371                                 bezier_Coord=round(bez.pt[0], tr)
372                                 if bezier_Coord not in VertexFrame:
373                                         VertexFrame.append(bezier_Coord)
374                                 if round(bez.pt[0], tr)==frameCtr:
375                                                 color=[1, color[1]-0.3]
376                 if ipoLocZ: # FIXED
377                         for bez in ipoLocZ.bezierPoints:
378                                 bezier_Coord=round(bez.pt[0], tr)
379                                 if bezier_Coord not in VertexFrame:
380                                         VertexFrame.append(bezier_Coord)
381                                 if round(bez.pt[0], tr)==frameCtr:
382                                                 color=[1, color[1]-0.3]
383                 
384         
385                 #put in LocX, LocY and LocZ all points of trajectory
386                 for frame in xrange(frameC-past, frameC+future):
387                         DecMat=matrixForTraj(frame, parent_list)
388
389                         if ipoLocX: LX= ipoLocX[frame]
390                         else:           LX= ob.LocX
391                         if ipoLocY:     LY= ipoLocY[frame]
392                         else:           LY= ob.LocY
393                         if ipoLocZ:     LZ= ipoLocZ[frame]
394                         else:           LZ= ob.LocZ
395                         
396                         vect=Vector(LX, LY, LZ)*DecMat
397                         LocX.append(vect[0])
398                         LocY.append(vect[1])
399                         LocZ.append(vect[2])
400         
401                 
402                 #draw part : get current view
403                 MatPreBuff= [matview[i][j] for i in xrange(4) for j in xrange(4)]
404                         
405                 MatBuff=BGL.Buffer(GL_FLOAT, 16, MatPreBuff)
406                 
407                 glLoadIdentity()
408                 glMatrixMode(GL_PROJECTION)
409                 glPushMatrix()
410                 glLoadMatrixf(MatBuff)
411                 
412                 #draw trajectory line
413                 glLineWidth("""+str(width)+""")
414                 
415                 glBegin(GL_LINE_STRIP)
416                 for i in xrange(len(LocX)):
417                         glColor3f((i+1)*1.00/len(LocX)*color[0], 0, (i+1)*1.00/len(LocX)*color[1])
418                         glVertex3f(LocX[i], LocY[i], LocZ[i])
419                 
420                 glEnd() 
421                 
422                 #draw trajectory's "vertexs"
423                 if not Blender.Window.EditMode():
424                         glPointSize(5)
425                         glBegin(GL_POINTS)
426                         TestPOINTS=[]
427                         TestFRAME=[]
428                         i=0
429                         for frame in VertexFrame:
430                                 ix=int(frame)-frameC+past
431                                 if ix>=0 and ix<len(LocX):
432                                         glColor3f(1, 0.7, 0.2)
433                                         glVertex3f(LocX[ix], LocY[ix], LocZ[ix])
434                                         TestPOINTS.append(Vector([LocX[ix], LocY[ix], LocZ[ix], 1]))
435                                         TestFRAME.append(int(frame))
436                                         i+=1
437                         glEnd()
438                         #this list contains info about where to check if we click over a "vertex" in 3D view
439                         TestLIST.append((ob, TestPOINTS, TestFRAME))
440                 
441                 glLineWidth(1)
442                 return 0
443
444
445 for ob in object_dict.itervalues():
446         Trace_Traj(ob)
447
448 ###########
449 #Fonction to handle trajectories
450 ###########
451
452 def Manip():
453         #use TestLIST and matview defined by Trace_Traj
454         global TestLIST, matview
455         for screen in Blender.Window.GetScreenInfo(Blender.Window.Types.VIEW3D):
456                 if screen['id']==Blender.Window.GetAreaID():
457                         x0, y0, x1, y1= screen['vertices']
458                         break
459         
460         #Projection of GL matrix in 3D view
461         glPushMatrix()
462         glMatrixMode(GL_PROJECTION)
463         glPushMatrix()
464         glLoadIdentity()
465         #Global coordinates' matrix
466         glOrtho(x0, x1, y0, y1, -1, 0)
467         glMatrixMode(GL_MODELVIEW)
468         glLoadIdentity()
469         #Test mouse clics and other events
470         
471         
472         if Blender.Window.QTest():
473                 evt, val= Blender.Window.QRead()
474                 if (evt==LEFTMOUSE or evt==RIGHTMOUSE) and not Blender.Window.EditMode():
475                         mouse_co=Blender.Window.GetMouseCoords()
476                         #if click on trajectory "vertexs"...
477                         for ob, TestPOINTS, TestFRAME in TestLIST: # ob is now used, line 552 to know what object it had to select
478                                 for k, Vect in enumerate(TestPOINTS):
479                                         proj=Vect*matview
480                                         
481                                         pt=[(proj[0]/proj[3])*(x1-x0)/2+(x1+x0)/2, (proj[1]/proj[3])*(y1-y0)/2+(y1+y0)/2]
482
483                                         if mouse_co[0]<pt[0]+4 and mouse_co[0]>pt[0]-4 and mouse_co[1]>pt[1]-4 and mouse_co[1]<pt[1]+4:
484                                                 if evt==LEFTMOUSE:
485                                                         #remember current selected object
486                                                         object_names=[obj.name for obj in Blender.Object.GetSelected()]
487                                                         #this script allow to simulate a GKey, but I have to write a script
488                                                         #another way would made a infinit redraw or don't allow to move object
489                                                         #it auto unlink and delete itself
490                                                         script=\"\"\"
491 import Blender
492 from Blender import Draw, Window
493 from Blender.Window import *
494 from Blender.Draw import *
495
496 from Blender.Mathutils import Vector
497
498 # The following code is a bit of a hack, it allows clicking on the points and dragging directly
499 #It simulate user press GKey 
500 #It also set the cursor position at center (because user have previously clic on area and moved the cursor): 
501 #And I can't get previous cursor position : redraw appear after it has been moved
502 #If there is no better way you can remove this comments
503 f= GetAreaID()
504 SetCursorPos(0,0,0)
505 #SetKeyQualifiers(1) #FIXED : the bug in older versions seems to have been fixed
506 SetKeyQualifiers(0)
507 QAdd(f, Blender.Draw.GKEY, 1, 0)
508 QHandle(f)
509 Blender.Redraw()
510 done=0
511 while not done:
512         while Blender.Window.QTest():
513                 ev=Blender.Window.QRead()[0]
514                 if ev not in (4, 5, 18, 112, 213): #all event needed to move object
515                         #SetKeyQualifiers(1) #FIXED too, same reason that above
516                         #SetKeyQualifiers(0)
517                         SetKeyQualifiers(Blender.Window.GetKeyQualifiers())
518                         QAdd(f, ev, 1, 0)
519                         QHandle(f)
520                         Blender.Redraw()
521                 if ev in (RIGHTMOUSE, LEFTMOUSE, ESCKEY):
522                         done=1
523 Blender.Set('curframe',\"\"\"+str(Blender.Get('curframe'))+\"\"\")
524 Blender.Object.GetSelected()[0].sel= False
525 for obname in \"\"\"+str(object_names)+\"\"\":
526         ob=Blender.Object.Get(obname)
527         ob.sel= True
528 SetCursorPos(0,0,0)
529 scripting=Blender.Text.Get('Edit_Trajectory')
530 scripting.clear()
531 Blender.Text.unlink(scripting)
532                                                 \"\"\"
533                                                         
534                                                         #FIXED Edit_Trajectory was longer : all SetKeyQualifiers removed
535                                                         scene=Blender.Scene.GetCurrent()
536                                                         try:
537                                                                 scripting=Blender.Text.Get('Edit_Trajectory')
538                                                                 scripting.clear()
539                                                         except:
540                                                                 scripting=Blender.Text.New('Edit_Trajectory')
541                                                         
542                                                         scripting.write(script)
543                                                         #script= scripting #FIXED seems not needed anymore
544                                                         
545                                                         #Go to frame that correspond to selected "vertex"
546                                                         Blender.Set('curframe', TestFRAME[k])
547                                                         
548                                                         scene.objects.selected = [] #un select all objects
549                                                         
550                                                         #FIXED TestLIST[j][0].sel=0, but no j. So ob.sel and above variable changed in obj
551                                                         ob.sel= True
552                                                         Blender.Run('Edit_Trajectory')
553                                                 
554                                                 #work well now !!!
555                                                 if evt==RIGHTMOUSE :
556                                                         Blender.Set('curframe', TestFRAME[k])
557
558 Manip()
559 #retrieve a normal matrix
560 glPopMatrix()
561 glMatrixMode(GL_PROJECTION)
562 glPopMatrix()
563 glMatrixMode(GL_MODELVIEW)
564 """
565
566 if ask_modif==0:
567         text_remove('Trajectory')
568         write_script('Trajectory', DrawPart)
569         if handle_mode==1:
570                 Blender.UpdateMenus()
571         else:
572                 link_script('Trajectory', 'Redraw')
573 if ask_modif==2:
574         text_remove('Trajectory')
575         print("---End of Trajectory_"+str(__version__)+".py---\n---   Thanks for use   ---")