import os
from pyfbsdk import *
from pyfbsdk_additions import *
class PointCache():
def __init__(self):
self.lSystem = FBSystem()
self.lScene = FBSystem().Scene
self.lSysOnUIIdle = self.lSystem.OnUIIdle
self.lSysStory = FBStory()
self.lSysPlayer = FBPlayerControl()
self.lSysPcMgr = FBPointCacheManager()
self.SelectedList = []
self.CacheStopTime = self.lSysPlayer.ZoomWindowStop
self.DuplicatedModel = False
self.DisableEvaluationAfterCache = True
self.EnableInPlaceCacheDeformer = True
self.getDefaultPath()
self.TabTypes = ('Characters', 'Story Tracks')
def getDefaultPath(self):
"""
get default path from loaded file. If path is None, use
the current working directory
"""
if not FBApplication().FBXFileName:
self.DefaultPath = os.getcwd()
else:
self.DefaultPath = os.path.dirname(FBApplication().FBXFileName)
def resetButtons(self):
"""
Restore the default state of the cache and deformer buttons
"""
self.bCache.State = 0
self.bDeformer.State = 0
def update_EditInfo(self, text):
self.Edit_Info.Caption = repr(text)
def refresh_All(self):
"""
Refresh gui elements. Have GUI elements retreive values from variables
"""
self.refresh_CharacterList()
self.refresh_StoryTrackList()
self.refresh_DefaultPath()
self.update_EditInfo("Refresh Complete")
def refresh_CharacterList(self):
"""
Refresh character list
"""
self.CharacterListWidget.Items.removeAll()
for lCh in self.lScene.Characters:
self.CharacterListWidget.Items.append(lCh.LongName)
def refresh_StoryTrackList(self):
"""
Refresh story track list, limited to character tracks for simplicity
"""
self.StoryTrackListWidget.Items.removeAll()
for lTrack in self.lSysStory.RootFolder.Tracks:
if lTrack.Type == FBStoryTrackType.kFBStoryTrackCharacter:
self.StoryTrackListWidget.Items.append("%s (%s)" %(lTrack.Name,lTrack.Character.LongName))
def refresh_DefaultPath(self):
"""
Refresh the default cache save to path
"""
self.Edit_DefaultPath.Text = self.DefaultPath
def getSelectedList(self):
"""
collect selected from active tab
"""
self.SelectedList = []
currentTab = self.tab.TabPanel.Items[self.tab.TabPanel.ItemIndex]
if currentTab == 'Characters':
for lCharacter in self.lScene.Characters:
lName = lCharacter.LongName
for lItemIndex in range(len(self.CharacterListWidget.Items)):
if self.CharacterListWidget.IsSelected(lItemIndex) and self.CharacterListWidget.Items[lItemIndex] == lName:
self.SelectedList.append(lCharacter)
else:
for lTrack in self.lSysStory.RootFolder.Tracks:
if lTrack.Type == FBStoryTrackType.kFBStoryTrackCharacter:
lName = lTrack.Character.LongName
for lIndex, lItem in enumerate(self.StoryTrackListWidget.Items):
if self.StoryTrackListWidget.IsSelected(lIndex) and lTrack.Character.LongName == lName:
self.SelectedList.append(lTrack.Character)
def activeCharacterEvaluation(self, pCharacterList, pEnable):
"""
function to enable/disable the character evaluation (control rig, or story track)
"""
for pCharacter in pCharacterList:
pCharacter.Active = pEnable
for lTrack in self.lSysStory.RootFolder.Tracks:
if lTrack.Type == FBStoryTrackType.kFBStoryTrackCharacter and lTrack.Character == pCharacter:
lTrack.Mute = (not pEnable)
def activeCharacterInPlaceCacheDeformer(self, pEnable):
"""
Active Character Skin Model In place cache deformer
"""
self.getSelectedList()
if len(self.SelectedList) == 0:
self.update_EditInfo( "Nothing selected for caching!")
return False
lSkinModelList = FBModelList()
for lCh in self.SelectedList:
print lCh.LongName
lCh.GetSkinModelList(lSkinModelList)
for lSkinModel in lSkinModelList:
self.update_EditInfo(lSkinModel.LongName)
lSkinModel.PointCacheDeformable = pEnable
self.lSysPlayer.GotoStart()
self.activeCharacterEvaluation(self.SelectedList, (not pEnable));
def recordIdleCallback(self, pObject, pEventName):
"""
Record Idle Callback to control the record period
"""
if self.lSysPlayer.IsPlaying:
if self.lSystem.LocalTime.GetSecondDouble() >= self.CacheStopTime.GetSecondDouble() :
self.lSysOnUIIdle.Remove(self.recordIdleCallback)
self.lSysPlayer.GotoStart()
if self.DisableEvaluationAfterCache == True:
self.activeCharacterEvaluation(self.SelectedList, False)
def configurePointCacheManager(self):
"""
Configure the Point Cache Manager
"""
self.lSysPcMgr.ApplyGlobalTransform = True
self.lSysPcMgr.AlwaysAskForPath = False
self.lSysPcMgr.DefaultPath = self.DefaultPath
def list_OnChange(self, pList, event):
"""
UI Callback
"""
for lItemIndex in range(len(pList.Items)):
if pList.IsSelected(lItemIndex):
self.update_EditInfo(pList.Items[lItemIndex] + " has been selected!")
def refreshAll_OnClick(self,control=None, event=None):
"""
UI Callback
"""
self.refresh_All()
def duplicateCheckBox_OnClick(self, pCheckBox, event):
"""
UI Callback
"""
self.DuplicatedModel = (pCheckBox.State != 0)
self.update_EditInfo(self.DuplicatedModel)
def disableEvalBtn_OnClick(self, pCheckBox, event):
"""
UI Callback
"""
self.DisableEvaluationAfterCache = (pCheckBox.State != 0)
self.update_EditInfo(self.DisableEvaluationAfterCache)
def cacheStartTime_OnChange(self, pTimeCode, event):
"""
UI Callback
"""
self.CacheStopTime = pTimeCode.Value
self.update_EditInfo(self.CacheStopTime)
def defaultPathEdit_OnChange(self, pEdit, event):
"""
UI Callback
"""
self.DefaultPath = pEdit.Text
def defaultPath_OnClick(self, control, event):
"""
Path Callback
"""
lFp = FBFolderPopup()
lFp.Caption = "Choose save location for Point Cache files"
lFp.Path = self.DefaultPath
lRes = lFp.Execute()
if lRes:
self.DefaultPath = lFp.Path
self.refresh_DefaultPath()
def cache_OnClick(self, control, event):
"""
Cache Callback
"""
self.resetButtons()
self.getSelectedList()
if len(self.SelectedList) == 0:
self.update_EditInfo("No Character selected for caching!")
return;
lSkinModelList = FBModelList()
self.update_EditInfo("Create point cache for Characters:")
for l in self.SelectedList:
self.update_EditInfo(l.LongName)
l.GetSkinModelList(lSkinModelList)
if len(lSkinModelList) == 0:
self.update_EditInfo("No Recordable Skin Models Selected")
return
self.update_EditInfo("Create point cache for skin models:")
for lSkinModel in lSkinModelList:
self.update_EditInfo(lSkinModel.LongName)
self.configurePointCacheManager()
self.lSysPcMgr.Models.removeAll()
for lSkinModel in lSkinModelList:
self.update_EditInfo(lSkinModel.LongName)
self.lSysPcMgr.Models.append(lSkinModel)
self.lSysPlayer.GotoStart()
self.lSysPlayer.Record(True, False)
self.lScene.Evaluate()
if not self.lSysPlayer.IsRecording:
return
if self.DuplicatedModel:
self.lSysPcMgr.ApplyCacheOnNewModel = True
else:
self.update_EditInfo("Cache No Duplication")
self.lSysPcMgr.ApplyCacheOnNewModel = False
self.activeCharacterEvaluation(self.SelectedList, False)
self.lScene.Evaluate()
self.lSysPcMgr.SetTransformReference()
self.activeCharacterEvaluation(self.SelectedList, True)
self.lScene.Evaluate()
self.lSysOnUIIdle.Add(self.recordIdleCallback)
self.lSysPlayer.Play()
def enableCache_OnClick(self, control, event):
"""
UI Callback
"""
self.activeCharacterInPlaceCacheDeformer(True)
def disableCache_OnClick(self, control, event):
"""
UI Callback
"""
self.activeCharacterInPlaceCacheDeformer(False)
def populateLayout(self, mainLyt):
"""
Layout management & callback hookup
"""
x = FBAddRegionParam(5,FBAttachType.kFBAttachLeft,"")
y = FBAddRegionParam(5,FBAttachType.kFBAttachTop,"")
w = FBAddRegionParam(-5,FBAttachType.kFBAttachRight,"")
h = FBAddRegionParam(-5,FBAttachType.kFBAttachBottom,"")
mainLyt.AddRegion("main","main", x,y,w,h)
grid = FBGridLayout()
mainLyt.SetControl("main", grid)
label = FBLabel()
label.Caption = "Choose from a list of characters or a list of character story tracks"
grid.AddRange(label, 0, 0, 0, 4)
bRefresh = FBButton()
bRefresh.Caption = "Refresh"
bRefresh.Justify = FBTextJustify.kFBTextJustifyCenter
grid.Add(bRefresh, 0, 4)
bRefresh.OnClick.Add(self.refreshAll_OnClick)
self.tab = FBTabControl()
for name in self.TabTypes:
l = FBVBoxLayout()
x = FBAddRegionParam(10,FBAttachType.kFBAttachLeft,"")
y = FBAddRegionParam(10,FBAttachType.kFBAttachTop,"")
w = FBAddRegionParam(-10,FBAttachType.kFBAttachRight,"")
h = FBAddRegionParam(-10,FBAttachType.kFBAttachBottom,"")
l.AddRegion(name,'', x, y, w, h)
scroll = FBScrollBox()
l.SetControl('test',scroll)
lLabel = FBLabel()
lLabel.Caption = "Choose %s(s) for operation:" % name
l.Add(lLabel, 16)
if name == 'Characters':
self.CharacterListWidget = FBList()
self.CharacterListWidget.OnChange.Add(self. list_OnChange)
self.CharacterListWidget.Style = FBListStyle.kFBVerticalList
self.CharacterListWidget.MultiSelect = True
l.Add(self.CharacterListWidget, 141)
else:
self.StoryTrackListWidget = FBList()
self.StoryTrackListWidget.OnChange.Add(self. list_OnChange)
self.StoryTrackListWidget.Style = FBListStyle.kFBVerticalList
self.StoryTrackListWidget.MultiSelect = True
l.Add(self.StoryTrackListWidget, 141)
self.tab.Add(name,l)
self.tab.SetContent(0)
self.tab.TabPanel.TabStyle = 0
grid.AddRange(self.tab, 1, 7, 0, 4)
lPCOptionsLayout = FBHBoxLayout()
grid.AddRange(lPCOptionsLayout,8, 8, 0, 4)
bDupMod = FBButton()
bDupMod.Caption = "Duplicate Model"
bDupMod.Style = FBButtonStyle.kFBCheckbox
bDupMod.Justify = FBTextJustify.kFBTextJustifyCenter
bDupMod.State = self.DuplicatedModel
lPCOptionsLayout.Add(bDupMod,105)
bDupMod.OnClick.Add(self.duplicateCheckBox_OnClick)
bDeactivate = FBButton()
bDeactivate.Caption = "Deactive Eval After Cache"
bDeactivate.Style = FBButtonStyle.kFBCheckbox
bDeactivate.Justify = FBTextJustify.kFBTextJustifyCenter
bDeactivate.State = self.DisableEvaluationAfterCache
lPCOptionsLayout.Add(bDeactivate,160)
bDeactivate.OnClick.Add(self.disableEvalBtn_OnClick)
lLabel = FBLabel()
lLabel.Caption = "Cache End Time:"
lLabel.Justify = FBTextJustify.kFBTextJustifyRight
lPCOptionsLayout.Add(lLabel, 90)
eCacheEndTime = FBEditTimeCode()
eCacheEndTime.Caption = "Cache End Time"
eCacheEndTime.Value = self.CacheStopTime
eCacheEndTime.OnChange.Add(self.cacheStartTime_OnChange)
lPCOptionsLayout.Add(eCacheEndTime,60)
lPCPathLayout = FBHBoxLayout()
grid.AddRange(lPCPathLayout,9, 9, 0, 4)
lLabel = FBLabel()
lLabel.Caption = "Cache file path"
lLabel.Justify = FBTextJustify.kFBTextJustifyRight
lPCPathLayout.Add(lLabel,75)
self.Edit_DefaultPath = FBEdit()
self.Edit_DefaultPath.OnChange.Add(self.defaultPathEdit_OnChange)
lPCPathLayout.AddRelative(self.Edit_DefaultPath, 1)
bFileDialog = FBButton()
bFileDialog.Caption = "..."
bFileDialog.Justify = FBTextJustify.kFBTextJustifyCenter
lPCPathLayout.Add(bFileDialog,22)
bFileDialog.OnClick.Add(self.defaultPath_OnClick)
bProcessCache = FBButton()
bProcessCache.Caption = "Process Cache"
bProcessCache.Justify = FBTextJustify.kFBTextJustifyCenter
grid.AddRange(bProcessCache,10, 10, 0, 4)
bProcessCache.OnClick.Add(self.cache_OnClick)
lLabel = FBLabel()
lLabel.Caption = "View as:"
lLabel.Justify = FBTextJustify.kFBTextJustifyRight
grid.Add(lLabel,11, 0)
self.bCache = FBButton()
self.bCache.Caption = "Cache"
self.bCache.Style = FBButtonStyle.kFB2States
self.bCache.Look = FBButtonLook.kFBLookPush
self.bCache.Justify = FBTextJustify.kFBTextJustifyCenter
grid.AddRange (self.bCache,11, 11, 1, 2)
self.bCache.OnClick.Add(self.enableCache_OnClick)
self.bDeformer = FBButton()
self.bDeformer.Caption = "Deformer"
self.bDeformer.Style = FBButtonStyle.kFB2States
self.bDeformer.Look = FBButtonLook.kFBLookPush
self.bDeformer.Justify = FBTextJustify.kFBTextJustifyCenter
grid.AddRange(self.bDeformer,11, 11, 3, 4)
self.bDeformer.OnClick.Add(self.disableCache_OnClick)
group = FBButtonGroup()
group.Add(self.bCache)
group.Add(self.bDeformer)
self.Edit_Info = FBLabel()
self.Edit_Info.Caption = ''
grid.AddRange(self.Edit_Info,12, 12, 0, 4)
self.refresh_All()
def createTool():
"""
Tool creation will serve as the hub for all other controls
"""
t = FBCreateUniqueTool("Character Based Point Cache Example")
t.StartSizeX = 615
t.MinSizeX = 455
t.MinSizeY = 450
t.MaxSizeY = 450
pc = PointCache()
pc.populateLayout(t)
ShowTool(t)
createTool()