Samples/PointCache/CharacterPointCache.py

Samples/PointCache/CharacterPointCache.py
1 # Copyright 2011 Autodesk, Inc. All rights reserved.
2 # Use of this software is subject to the terms of the Autodesk license agreement
3 # provided at the time of installation or download, or which otherwise accompanies
4 # this software in either electronic or hard copy form.
5 #
6 # Script description:
7 # Create a tool to demonstrate the Character based point cache creation workflow.
8 #
9 # Topic: FBPointCacheManager
10 #
11 import os
12 
13 from pyfbsdk import *
14 from pyfbsdk_additions import *
15 
16 
17 class PointCache():
18  def __init__(self):
19  self.lSystem = FBSystem()
20  self.lScene = FBSystem().Scene
21  self.lSysOnUIIdle = self.lSystem.OnUIIdle
22  self.lSysStory = FBStory()
23  self.lSysPlayer = FBPlayerControl()
24  self.lSysPcMgr = FBPointCacheManager()
25 
26  self.SelectedList = []
27  self.CacheStopTime = self.lSysPlayer.ZoomWindowStop
28  self.DuplicatedModel = False
29  self.DisableEvaluationAfterCache = True
30  self.EnableInPlaceCacheDeformer = True
31  self.getDefaultPath()
32  self.TabTypes = ('Characters', 'Story Tracks')
33 
34 
35  def getDefaultPath(self):
36  """
37  get default path from loaded file. If path is None, use
38  the current working directory
39  """
40  if not FBApplication().FBXFileName:
41  self.DefaultPath = os.getcwd()
42  else:
43  self.DefaultPath = os.path.dirname(FBApplication().FBXFileName)
44 
45  def resetButtons(self):
46  """
47  Restore the default state of the cache and deformer buttons
48  """
49  self.bCache.State = 0
50  self.bDeformer.State = 0
51 
52 
53  def update_EditInfo(self, text):
54  self.Edit_Info.Caption = repr(text)
55 
56 
57  def refresh_All(self):
58  """
59  Refresh gui elements. Have GUI elements retreive values from variables
60  """
61  self.refresh_CharacterList()
62  self.refresh_StoryTrackList()
63  self.refresh_DefaultPath()
64  self.update_EditInfo("Refresh Complete")
65 
66 
67  def refresh_CharacterList(self):
68  """
69  Refresh character list
70  """
71  self.CharacterListWidget.Items.removeAll()
72  for lCh in self.lScene.Characters:
73  self.CharacterListWidget.Items.append(lCh.LongName)
74 
75 
76  def refresh_StoryTrackList(self):
77  """
78  Refresh story track list, limited to character tracks for simplicity
79  """
80  self.StoryTrackListWidget.Items.removeAll()
81  for lTrack in self.lSysStory.RootFolder.Tracks:
82  if lTrack.Type == FBStoryTrackType.kFBStoryTrackCharacter:
83  self.StoryTrackListWidget.Items.append("%s (%s)" %(lTrack.Name,lTrack.Character.LongName))
84 
85 
86  def refresh_DefaultPath(self):
87  """
88  Refresh the default cache save to path
89  """
90  self.Edit_DefaultPath.Text = self.DefaultPath
91 
92 
93  def getSelectedList(self):
94  """
95  collect selected from active tab
96  """
97  self.SelectedList = []
98 
99  currentTab = self.tab.TabPanel.Items[self.tab.TabPanel.ItemIndex]
100  if currentTab == 'Characters':
101  for lCharacter in self.lScene.Characters:
102  lName = lCharacter.LongName
103  for lItemIndex in range(len(self.CharacterListWidget.Items)):
104  if self.CharacterListWidget.IsSelected(lItemIndex) and self.CharacterListWidget.Items[lItemIndex] == lName:
105  self.SelectedList.append(lCharacter)
106  else:
107  for lTrack in self.lSysStory.RootFolder.Tracks:
108  if lTrack.Type == FBStoryTrackType.kFBStoryTrackCharacter:
109  lName = lTrack.Character.LongName
110  for lIndex, lItem in enumerate(self.StoryTrackListWidget.Items):
111  if self.StoryTrackListWidget.IsSelected(lIndex) and lTrack.Character.LongName == lName:
112  self.SelectedList.append(lTrack.Character)
113 
114 
115  def activeCharacterEvaluation(self, pCharacterList, pEnable):
116  """
117  function to enable/disable the character evaluation (control rig, or story track)
118  """
119  for pCharacter in pCharacterList:
120  pCharacter.Active = pEnable # Turn on/off character solving
121  for lTrack in self.lSysStory.RootFolder.Tracks:
122  if lTrack.Type == FBStoryTrackType.kFBStoryTrackCharacter and lTrack.Character == pCharacter:
123  lTrack.Mute = (not pEnable) # Turn on/off story character track
124 
125 
126  def activeCharacterInPlaceCacheDeformer(self, pEnable):
127  """
128  Active Character Skin Model In place cache deformer
129  """
130  self.getSelectedList()
131  if len(self.SelectedList) == 0:
132  self.update_EditInfo( "Nothing selected for caching!")
133  return False
134 
135  lSkinModelList = FBModelList()
136  for lCh in self.SelectedList:
137  print lCh.LongName
138  lCh.GetSkinModelList(lSkinModelList)
139 
140  for lSkinModel in lSkinModelList:
141  self.update_EditInfo(lSkinModel.LongName)
142  lSkinModel.PointCacheDeformable = pEnable
143 
144  # In Place Cache Create with the first frame's transform as reference
145  self.lSysPlayer.GotoStart()
146  self.activeCharacterEvaluation(self.SelectedList, (not pEnable));
147 
148 
149  def recordIdleCallback(self, pObject, pEventName):
150  """
151  Record Idle Callback to control the record period
152  """
153  if self.lSysPlayer.IsPlaying:
154  if self.lSystem.LocalTime.GetSecondDouble() >= self.CacheStopTime.GetSecondDouble() :
155  self.lSysOnUIIdle.Remove(self.recordIdleCallback)
156  self.lSysPlayer.GotoStart()
157  if self.DisableEvaluationAfterCache == True:
158  self.activeCharacterEvaluation(self.SelectedList, False)
159 
160 
161  def configurePointCacheManager(self):
162  """
163  Configure the Point Cache Manager
164  """
165  self.lSysPcMgr.ApplyGlobalTransform = True
166  self.lSysPcMgr.AlwaysAskForPath = False
167  self.lSysPcMgr.DefaultPath = self.DefaultPath
168 
169 
170 
171  def list_OnChange(self, pList, event):
172  """
173  UI Callback
174  """
175  for lItemIndex in range(len(pList.Items)):
176  if pList.IsSelected(lItemIndex):
177  self.update_EditInfo(pList.Items[lItemIndex] + " has been selected!")
178 
179 
180  def refreshAll_OnClick(self,control=None, event=None):
181  """
182  UI Callback
183  """
184  self.refresh_All()
185 
186 
187  def duplicateCheckBox_OnClick(self, pCheckBox, event):
188  """
189  UI Callback
190  """
191  self.DuplicatedModel = (pCheckBox.State != 0)
192  self.update_EditInfo(self.DuplicatedModel)
193 
194 
195  def disableEvalBtn_OnClick(self, pCheckBox, event):
196  """
197  UI Callback
198  """
199  self.DisableEvaluationAfterCache = (pCheckBox.State != 0)
200  self.update_EditInfo(self.DisableEvaluationAfterCache)
201 
202 
203  def cacheStartTime_OnChange(self, pTimeCode, event):
204  """
205  UI Callback
206  """
207  self.CacheStopTime = pTimeCode.Value
208  self.update_EditInfo(self.CacheStopTime)
209 
210 
211  def defaultPathEdit_OnChange(self, pEdit, event):
212  """
213  UI Callback
214  """
215  self.DefaultPath = pEdit.Text
216 
217 
218  def defaultPath_OnClick(self, control, event):
219  """
220  Path Callback
221  """
222  lFp = FBFolderPopup()
223  lFp.Caption = "Choose save location for Point Cache files"
224  lFp.Path = self.DefaultPath
225  lRes = lFp.Execute()
226 
227  if lRes:
228  self.DefaultPath = lFp.Path
229 
230  self.refresh_DefaultPath()
231 
232 
233  def cache_OnClick(self, control, event):
234  """
235  Cache Callback
236  """
237  self.resetButtons()
238 
239  self.getSelectedList() #Select the Character Skin Models to reocrd.
240 
241  if len(self.SelectedList) == 0:
242  self.update_EditInfo("No Character selected for caching!")
243  return;
244 
245  lSkinModelList = FBModelList()
246  self.update_EditInfo("Create point cache for Characters:")
247  for l in self.SelectedList:
248  self.update_EditInfo(l.LongName)
249  l.GetSkinModelList(lSkinModelList)
250 
251  if len(lSkinModelList) == 0:
252  self.update_EditInfo("No Recordable Skin Models Selected")
253  return
254 
255  self.update_EditInfo("Create point cache for skin models:")
256  for lSkinModel in lSkinModelList:
257  self.update_EditInfo(lSkinModel.LongName)
258 
259  self.configurePointCacheManager() # setup the point cache manager
260 
261  self.lSysPcMgr.Models.removeAll()
262  for lSkinModel in lSkinModelList:
263  self.update_EditInfo(lSkinModel.LongName)
264  self.lSysPcMgr.Models.append(lSkinModel) #Add model to be recorded
265 
266  self.lSysPlayer.GotoStart()
267  self.lSysPlayer.Record(True, False)
268  self.lScene.Evaluate() #Important, to allow setup take effects.
269 
270  if not self.lSysPlayer.IsRecording:
271  return
272 
273  if self.DuplicatedModel:
274  self.lSysPcMgr.ApplyCacheOnNewModel = True
275  else:
276  self.update_EditInfo("Cache No Duplication")
277  self.lSysPcMgr.ApplyCacheOnNewModel = False
278  # Set Transformation Reference, we need to turn off the Character Evaluation first.
279  self.activeCharacterEvaluation(self.SelectedList, False)
280  self.lScene.Evaluate() # Important, to allow setup take effects.
281  self.lSysPcMgr.SetTransformReference()
282  # Restore Character Evaluation
283  self.activeCharacterEvaluation(self.SelectedList, True)
284  self.lScene.Evaluate() # Important, to allow setup take effects.
285 
286  self.lSysOnUIIdle.Add(self.recordIdleCallback)
287 
288  self.lSysPlayer.Play() # Record
289 
290 
291  def enableCache_OnClick(self, control, event):
292  """
293  UI Callback
294  """
295  self.activeCharacterInPlaceCacheDeformer(True)
296 
297 
298  def disableCache_OnClick(self, control, event):
299  """
300  UI Callback
301  """
302  self.activeCharacterInPlaceCacheDeformer(False)
303 
304 
305 
306 
307 
308  def populateLayout(self, mainLyt):
309  """
310  Layout management & callback hookup
311  """
312  x = FBAddRegionParam(5,FBAttachType.kFBAttachLeft,"")
313  y = FBAddRegionParam(5,FBAttachType.kFBAttachTop,"")
314  w = FBAddRegionParam(-5,FBAttachType.kFBAttachRight,"")
315  h = FBAddRegionParam(-5,FBAttachType.kFBAttachBottom,"")
316  mainLyt.AddRegion("main","main", x,y,w,h)
317 
318  grid = FBGridLayout()
319  mainLyt.SetControl("main", grid)
320 
321  label = FBLabel()
322  label.Caption = "Choose from a list of characters or a list of character story tracks"
323  grid.AddRange(label, 0, 0, 0, 4)
324 
325  bRefresh = FBButton()
326  bRefresh.Caption = "Refresh"
327  bRefresh.Justify = FBTextJustify.kFBTextJustifyCenter
328  grid.Add(bRefresh, 0, 4)
329  bRefresh.OnClick.Add(self.refreshAll_OnClick)
330 
331  self.tab = FBTabControl()
332 
333  for name in self.TabTypes:
334 
335  l = FBVBoxLayout()
336  x = FBAddRegionParam(10,FBAttachType.kFBAttachLeft,"")
337  y = FBAddRegionParam(10,FBAttachType.kFBAttachTop,"")
338  w = FBAddRegionParam(-10,FBAttachType.kFBAttachRight,"")
339  h = FBAddRegionParam(-10,FBAttachType.kFBAttachBottom,"")
340  l.AddRegion(name,'', x, y, w, h)
341 
342  # provide a scrolling list
343  scroll = FBScrollBox()
344  l.SetControl('test',scroll)
345 
346  lLabel = FBLabel()
347  lLabel.Caption = "Choose %s(s) for operation:" % name
348  l.Add(lLabel, 16)
349 
350  if name == 'Characters':
351  self.CharacterListWidget = FBList()
352  self.CharacterListWidget.OnChange.Add(self. list_OnChange)
353  self.CharacterListWidget.Style = FBListStyle.kFBVerticalList
354  self.CharacterListWidget.MultiSelect = True
355  l.Add(self.CharacterListWidget, 141)
356  else:
357  self.StoryTrackListWidget = FBList()
358  self.StoryTrackListWidget.OnChange.Add(self. list_OnChange)
359  self.StoryTrackListWidget.Style = FBListStyle.kFBVerticalList
360  self.StoryTrackListWidget.MultiSelect = True
361  l.Add(self.StoryTrackListWidget, 141)
362 
363  self.tab.Add(name,l)
364 
365  self.tab.SetContent(0)
366  self.tab.TabPanel.TabStyle = 0
367  grid.AddRange(self.tab, 1, 7, 0, 4)
368 
369  lPCOptionsLayout = FBHBoxLayout()
370  grid.AddRange(lPCOptionsLayout,8, 8, 0, 4)
371 
372  bDupMod = FBButton()
373  bDupMod.Caption = "Duplicate Model"
374  bDupMod.Style = FBButtonStyle.kFBCheckbox
375  bDupMod.Justify = FBTextJustify.kFBTextJustifyCenter
376  bDupMod.State = self.DuplicatedModel
377  lPCOptionsLayout.Add(bDupMod,105)
378  bDupMod.OnClick.Add(self.duplicateCheckBox_OnClick)
379 
380  bDeactivate = FBButton()
381  bDeactivate.Caption = "Deactive Eval After Cache"
382  bDeactivate.Style = FBButtonStyle.kFBCheckbox
383  bDeactivate.Justify = FBTextJustify.kFBTextJustifyCenter
384  bDeactivate.State = self.DisableEvaluationAfterCache
385  lPCOptionsLayout.Add(bDeactivate,160)
386  bDeactivate.OnClick.Add(self.disableEvalBtn_OnClick)
387 
388  lLabel = FBLabel()
389  lLabel.Caption = "Cache End Time:"
390  lLabel.Justify = FBTextJustify.kFBTextJustifyRight
391  lPCOptionsLayout.Add(lLabel, 90)
392 
393  eCacheEndTime = FBEditTimeCode()
394  eCacheEndTime.Caption = "Cache End Time"
395  eCacheEndTime.Value = self.CacheStopTime
396  eCacheEndTime.OnChange.Add(self.cacheStartTime_OnChange)
397  lPCOptionsLayout.Add(eCacheEndTime,60)
398 
399  lPCPathLayout = FBHBoxLayout()
400  grid.AddRange(lPCPathLayout,9, 9, 0, 4)
401  lLabel = FBLabel()
402  lLabel.Caption = "Cache file path"
403  lLabel.Justify = FBTextJustify.kFBTextJustifyRight
404  lPCPathLayout.Add(lLabel,75)
405 
406  self.Edit_DefaultPath = FBEdit()
407  self.Edit_DefaultPath.OnChange.Add(self.defaultPathEdit_OnChange)
408  lPCPathLayout.AddRelative(self.Edit_DefaultPath, 1)
409 
410  bFileDialog = FBButton()
411  bFileDialog.Caption = "..."
412  bFileDialog.Justify = FBTextJustify.kFBTextJustifyCenter
413  lPCPathLayout.Add(bFileDialog,22)
414  bFileDialog.OnClick.Add(self.defaultPath_OnClick)
415 
416  bProcessCache = FBButton()
417  bProcessCache.Caption = "Process Cache"
418  bProcessCache.Justify = FBTextJustify.kFBTextJustifyCenter
419  grid.AddRange(bProcessCache,10, 10, 0, 4)
420  bProcessCache.OnClick.Add(self.cache_OnClick)
421 
422  lLabel = FBLabel()
423  lLabel.Caption = "View as:"
424  lLabel.Justify = FBTextJustify.kFBTextJustifyRight
425  grid.Add(lLabel,11, 0)
426 
427  # bCache is a class attribute rather than a method attribute because
428  # it is accessed in another method
429  self.bCache = FBButton()
430  self.bCache.Caption = "Cache"
431  self.bCache.Style = FBButtonStyle.kFB2States
432  self.bCache.Look = FBButtonLook.kFBLookPush
433  self.bCache.Justify = FBTextJustify.kFBTextJustifyCenter
434  grid.AddRange (self.bCache,11, 11, 1, 2)
435  self.bCache.OnClick.Add(self.enableCache_OnClick)
436 
437  # bDeformer is a class attribute rather than a method attribute because
438  # it is accessed in another method
439  self.bDeformer = FBButton()
440  self.bDeformer.Caption = "Deformer"
441  self.bDeformer.Style = FBButtonStyle.kFB2States
442  self.bDeformer.Look = FBButtonLook.kFBLookPush
443  self.bDeformer.Justify = FBTextJustify.kFBTextJustifyCenter
444  grid.AddRange(self.bDeformer,11, 11, 3, 4)
445  self.bDeformer.OnClick.Add(self.disableCache_OnClick)
446 
447  #group these buttons for better UI visuals
448  group = FBButtonGroup()
449  group.Add(self.bCache)
450  group.Add(self.bDeformer)
451 
452  self.Edit_Info = FBLabel()
453  self.Edit_Info.Caption = ''
454  grid.AddRange(self.Edit_Info,12, 12, 0, 4)
455 
456  # refresh all of the gui elements in order to get relevant data
457  self.refresh_All()
458 
459 
460 
461 
462 def createTool():
463  """
464  Tool creation will serve as the hub for all other controls
465  """
466  t = FBCreateUniqueTool("Character Based Point Cache Example")
467  t.StartSizeX = 615
468  t.MinSizeX = 455
469  t.MinSizeY = 450
470  t.MaxSizeY = 450
471 
472  pc = PointCache()
473 
474  pc.populateLayout(t)
475  ShowTool(t)
476 
477 createTool()
478