' FaceFXPlugin ' Initial code generated by XSI SDK Wizard ' Executed Sat Jan 12 12:33:21 EST 2008 by mbarrett ' ' ' Tip: The wizard only exposes a small subset of the possible controls ' and layout that can be achieved on a Property Page. To find out more ' please refer to the Object Model reference documentation for PPGLayout, PPG ' and CustomProperty ' ' Tip: Don't be concerned about achieving the exact ordering of the parameters ' because they can easily be reordered in the second phase. ' ' Tip: Use the "Refresh" option on the Property Page context menu to ' reload your script changes and re-execute the DefineLayout callback. ' Tip: To add a command to this plug-in, right-click in the ' script editor and choose Tools > Add Command. ' ' Tip: To get help on a callback, highlight the callback name ' (for example, "Init", "Define", or "Execute") and press F1. function XSILoadPlugin( in_reg ) in_reg.Author = "mbarrett" in_reg.Name = "FxXSIUI" in_reg.Email = "" in_reg.URL = "" in_reg.Major = 1 in_reg.Minor = 1 in_reg.Help = XSIUtils.BuildPath( in_reg.OriginPath, "FxXSI.htm") in_reg.RegisterProperty "FaceFX" in_reg.RegisterMenu siMenuMainFileExportID, "Actor_Export",false in_reg.RegisterMenu siMenuMainFileImportID, "Actor_Import",false ' This is called from the C++ plugin to update the GUI when appropriate in_reg.RegisterCommand "FaceFX_Refresh_Script_GUI","FaceFX_Refresh_Script_GUI" 'RegistrationInsertionPoint - do not remove this line XSILoadPlugin = true end function function XSIUnloadPlugin( in_reg ) dim strPluginName strPluginName = in_reg.Name Application.LogMessage strPluginName & " has been unloaded.",siVerbose XSIUnloadPlugin = true end function function Actor_Export_Init( in_ctxt ) set oMenu = in_ctxt.source oMenu.AddCallbackItem "FaceFX ...", "OnActorMenu" end function function Actor_Import_Init( in_ctxt ) set oMenu = in_ctxt.source oMenu.AddCallbackItem "FaceFX ...", "OnActorMenu" end function function OnActorMenu( in_ctxt ) set oActorProp = ActiveSceneRoot.Properties("FaceFX") if typename(oActorProp) <> "CustomProperty" then set oActorProp = ActiveSceneRoot.AddProperty("FaceFX") else ' Delete the existing property. If we don't do this, new GUI elements like NodeType don't show up. DeleteObj oActorProp set oActorProp = ActiveSceneRoot.AddProperty("FaceFX") end if InspectObj oActorProp end function 'FaceFX property function FaceFX_Define( in_ctxt ) dim oCustomProperty set oCustomProperty = in_ctxt.Source oCustomProperty.AddParameter2 "ReferenceBone",siInt4,0,,,,,siClassifUnknown,siPersistable oCustomProperty.AddParameter2 "NodeType",siInt4,0,0,100,0,100,siClassifUnknown,siPersistable oCustomProperty.AddParameter2 "Nodes",siInt4,0,,,,,siClassifUnknown,siPersistable oCustomProperty.AddParameter2 "AnimationGroup",siInt4,0,0,100,0,100,siClassifUnknown,siPersistable oCustomProperty.AddParameter2 "Animation",siInt4,0,0,100,0,100,siClassifUnknown,siPersistable oCustomProperty.AddParameter2 "File",siString,"None",,,,,siClassifUnknown,siPersistable FaceFX_Define = true end function function FaceFX_DefineLayout( in_ctxt ) dim oLayout,oItem set oLayout = in_ctxt.Source oLayout.Clear oLayout.AddTab "Reference Pose" set oItem = oLayout.AddEnumControl( "ReferenceBone",Array() , "", siControlListBox ) oItem.SetAttribute siUICY, 80 oLayout.AddRow oLayout.AddButton "ImportReferencePose", "Import" oLayout.AddButton "ExportReferencePose", "Export" oLayout.EndRow oLayout.AddTab "Nodes" oLayout.AddRow set oItem = oLayout.AddEnumControl( "NodeType",Array() , "", siControlCombo) oLayout.EndRow oLayout.AddRow set oItem = oLayout.AddEnumControl( "Nodes",Array() , "", siControlListBox ) oLayout.EndRow 'oItem.SetAttribute siUICY, 80 oLayout.AddRow oLayout.AddButton "ImportNodes", "Import" oLayout.AddButton "ExportNodes", "Export" oLayout.AddButton "BatchImportNodes", "Batch import..." oLayout.AddButton "BatchExportNodes", "Batch export..." oLayout.EndRow oLayout.AddTab "Animations" oLayout.AddRow set oItem = oLayout.AddEnumControl( "AnimationGroup",Array() , "", siControlCombo) oLayout.AddButton "InsertAnimGroup", "New" oLayout.EndRow 'oItem.SetAttribute siUICY, 80 set oItem = oLayout.AddEnumControl( "Animation",Array() , "", siControlListBox) 'oItem.SetAttribute siUICY, 80 oLayout.AddRow oLayout.AddButton "ImportAnimation", "Import" oLayout.AddButton "Clean", "Clean" oLayout.AddButton "RenameAnimation", "Rename" oLayout.AddButton "DeleteAnimation", "Delete" oLayout.EndRow if FaceFXIsAnalysisTool then oLayout.AddRow oLayout.AddButton "GenerateAnimation", "Generate Animation" oLayout.AddButton "NewTake", "New Take" oLayout.EndRow end if oLayout.AddTab "File" set oFileItem = oLayout.AddItem( "File",,siControlFilePath) oFileItem.SetAttribute siUIFileFilter, "FaceFX Files |*.facefx:*.xml" oFileItem.SetAttribute siUIOpenFile, true oFileItem.SetAttribute siUIFileMustExist, true oFileItem.SetAttribute siUIInitialDir, Application.InstallationPath( siUserPath ) oLayout.AddGroup oLayout.AddRow oLayout.AddButton "OpenActorFile", "Open" oLayout.AddButton "SaveActorFile", "Save" oLayout.AddButton "NewActorFile", "New" if FaceFXIsAnalysisTool then oLayout.AddButton "ChangeAnalysisActor", "Change Analysis Actor" end if oLayout.EndRow oLayout.EndGroup FaceFX_DefineLayout = true end function function FaceFX_Helper_ReadMixerContents( in_mixer, oMorphClips ) for each trk in in_mixer.Tracks FaceFX_Helper_ReadClipsFromContainer trk, oMorphClips next end function function FaceFX_Helper_ReadClipsFromContainer( in_can, oMorphClips ) for each clp in in_can.Clips if clp.Type <> siClipShapeType then for each trk in clp.Tracks FaceFX_Helper_ReadClipsFromContainer trk, oMorphClips next elseif clp.Type <> siClipAnimationType then oMorphClips.Add(clp) end if next end function function FaceFX_Helper_GetMorphTargetClips( oMorphClips ) ' There are many ways to find the mixer we want to use to find morph targets. ' Below are a few examples. ' Look in the Scene root mixer ' FaceFX_Helper_ReadMixerContents ActiveSceneRoot.Mixer ' Look in all selected Model objects 'For Each obj in Selection ' if obj.IsClassOf(siModelID) then ' if obj.HasMixer() then ' FaceFX_Helper_ReadMixerContents obj.Mixer ' end if ' end if 'next ' This looks for all models in the scene Set oRoot = ActiveSceneRoot Set oMeshes = oRoot.FindChildren(,,si3DObjectFamily) For Each mesh in oMeshes if mesh.IsClassOf(siModelID) then if mesh.HasMixer() then FaceFX_Helper_ReadMixerContents mesh.Mixer, oMorphClips end if end if next end function function FaceFX_Helper_ImportAnimationOntoMorphs( groupName, animName ) set oMorphClips = CreateObject( "XSI.Collection" ) FaceFX_Helper_GetMorphTargetClips oMorphClips ' Get a list of FxMorphTargetNodes from the actor file sMorphNodes= FaceFXGetNodes( "FxMorphTargetNode") endFrame = FaceFXGetAnimDuration( groupName, animName ) * GetValue( "PlayControl.Rate" ) For Each obj in oMorphClips LogMessage "--- " & obj & " ---" if Right(obj, 5) = "_Clip" then targetname = Left(obj.name, Len(obj.name) - 5) for each morphName in sMorphNodes ' FaceFX Studio matches the FxMorphTargetNode's "target" property with the XSI morph target name, ' But here we match the node's name to the XSI morph target name. When morph nodes are created, ' the node name matches the "target" property, but renaming the node or changing the target property ' can cause these two to get out of synch and FaceFX Studio and the plugin will then behave differently. if targetname = morphName then keyTimes = FaceFXGetBakedCurveKeyTimes( groupName , animName , targetname) keyValues = FaceFXGetBakedCurveKeyValues( groupName , animName , targetname) keySlopeIn = FaceFXGetBakedCurveKeySlopeIn( groupName , animName , targetname) keySlopeOut = FaceFXGetBakedCurveKeySlopeOut( groupName , animName , targetname) ReDim aCurveKeys( ubound(keyTimes) * 2 + 1 ) ReDim aCurveKeyTangents( ubound(keyTimes) * 4 + 3 ) prevKeyTime = 0 nextKeyTime = 0 if ubound(keyTimes) > 0 then prevKeyTime = keyTimes(0) -.5 nextKeyTime = keyTimes(0) +.5 end if if ubound(keyTimes) > 1 then nextKeyTime = keyTimes(1) end if for i = 0 to ubound(keyTimes) frame = keyTimes(i) * GetValue( "PlayControl.Rate" ) if i < ubound(keyTimes) then nextKeyTime = keyTimes(i+1) else nextKeyTime = keyTimes(i)+ .5 end if aCurveKeys(i*2) = frame aCurveKeys(i*2 + 1) = keyValues(i) aCurveKeyTangents(i*4) = (prevKeyTime - keyTimes(i)) * GetValue( "PlayControl.Rate" )/ 3 aCurveKeyTangents(i*4 +1) = (prevKeyTime - keyTimes(i)) * keySlopeIn(i) / 3 aCurveKeyTangents(i*4 +2) = (nextKeyTime - keyTimes(i)) * GetValue( "PlayControl.Rate" )/ 3 aCurveKeyTangents(i*4 +3) = (nextKeyTime - keyTimes(i)) * keySlopeOut(i) / 3 prevKeyTime = keyTimes(i) next set fCurve = obj.Parameters( "weight" ).AddFCurve2(aCurveKeys) fCurve.SetKeyTangents(aCurveKeyTangents) end if next end if next end function function FaceFX_Helper_GetMorphTargetNames( ) set oMorphClips = CreateObject( "XSI.Collection" ) FaceFX_Helper_GetMorphTargetClips oMorphClips dim oMorphTargetNames() Redim oMorphTargetNames(oMorphClips.Count) For Each obj in oMorphClips int count = 0 if Right(obj, 5) = "_Clip" then targetname = Left(obj.name, Len(obj.name) - 5) oMorphTargetNames(count)= targetName count = count+1 end if Next FaceFX_Helper_GetMorphTargetNames = oMorphTargetNames end function function FaceFX_Helper_GetNodeType() nodeTypeName = "" set oNodeTypeItem = PPG.PPGLayout.Item("NodeType") aUIItems = oNodeTypeItem.UIitems if ubound(aUIItems ) > 0 then nodeTypeName = aUIItems(PPG.NodeType.Value *2) end if FaceFX_Helper_GetNodeType = nodeTypeName end function function FaceFX_Helper_GetAnimGroup() animGroupName = "" set oAnimationGroupItem = PPG.PPGLayout.Item("AnimationGroup") aUIItems = oAnimationGroupItem.UIitems if ubound(aUIItems ) > 0 then animGroupName = aUIItems(PPG.AnimationGroup.Value *2) end if FaceFX_Helper_GetAnimGroup = animGroupName end function function FaceFX_OpenActorFile_OnClicked() set filesys = CreateObject("Scripting.FileSystemObject") if not(filesys.FileExists(PPG.File.Value)) then set oFileBrowser = XSIUIToolkit.FileBrowser oFileBrowser.DialogTitle = "Select a FaceFX actor file" ' set the title of the file browser oFileBrowser.InitialDirectory = Application.InstallationPath( siUserPath ) oFileBrowser.Filter = "FaceFX Actor Files (*.facefx)|*.facefx|FaceFX XML Actor Files (*.xml)|*.xml||" ' oFileBrowser.ShowOpen ' show an open file dialog PPG.File.Value = oFileBrowser.FilePathName end if if filesys.FileExists(PPG.File.Value) then FaceFXOpenActor PPG.File.Value FaceFX_OnInit PPG.Refresh end if end function function FaceFX_SaveActorFile_OnClicked() bOverwrite = true set filesys = CreateObject("Scripting.FileSystemObject") if not Right(PPG.File.Value, 7) = ".facefx" and not Right(PPG.File.Value,4) = ".xml" then set oFileBrowser = XSIUIToolkit.FileBrowser oFileBrowser.DialogTitle = "Select a FaceFX Actor file" ' set the title of the file browser oFileBrowser.InitialDirectory = Application.InstallationPath( siUserPath ) oFileBrowser.Filter = "FaceFX Actor Files (*.facefx)|*.facefx|FaceFX XML Actor Files (*.xml)|*.xml||" ' oFileBrowser.ShowSave ' show an open file dialog PPG.File.Value = oFileBrowser.FilePathName elseif filesys.FileExists(PPG.File.Value) then buttonPressed = XSIUIToolkit.Msgbox( "File already exists do you want to overwrite it ?", siMsgYesNo or siMsgQuestion, "FaceFX Save") if buttonPressed = siMsgNo then bOverwrite = false end if end if if bOverwrite and not PPG.File.Value = "" then FaceFXSaveActor PPG.File.Value end if end function function FaceFX_OnInit() set oRefBonePoseItem = PPG.PPGLayout.Item("ReferenceBone") set oNodeTypeItem = PPG.PPGLayout.Item("NodeType") set oNodesItem = PPG.PPGLayout.Item("Nodes") set oAnimationItem = PPG.PPGLayout.Item("Animation") set oAnimationGroupsItem = PPG.PPGLayout.Item("AnimationGroup") oRefBonePoseItem.UIItems = [""] oNodesItem.UIItems = [""] oAnimationGroupsItem.UIItems = [""] oAnimationItem.UIItems = [""] oNodeTypeItem.UIItems = [""] ' Don't change the order or items without updating FxToolXSI.cpp as well. sNodeTypeChoices = Array("FxBonePoseNode", "FxMorphTargetNode", "FxCombinerNode") if typename(sNodeTypeChoices) <> "Empty" then dim nodeTypeChoicesUI Redim nodeTypeChoicesUI(ubound(sNodeTypeChoices) * 2 + 1) for i = 0 to ubound(sNodeTypeChoices) nodeTypeChoicesUI(i*2) = sNodeTypeChoices(i) nodeTypeChoicesUI(i*2+1) = i next oNodeTypeItem.UIItems = nodeTypeChoicesUI end if animGroupName = FaceFX_Helper_GetAnimGroup sRefBonePoses = FaceFXListRefBonePoses if typename(sRefBonePoses) <> "Empty" then if ubound(sRefBonePoses) >= 0 then dim aRefBonePoseUI Redim aRefBonePoseUI(ubound(sRefBonePoses) * 2 + 1) for i = 0 to ubound(sRefBonePoses) aRefBonePoseUI(i*2) = sRefBonePoses(i) aRefBonePoseUI(i*2+1) = i next oRefBonePoseItem.UIItems = aRefBonePoseUI end if end if sNodes = FaceFXGetNodes("FxBonePoseNode") if typename(sNodes) <> "Empty" then if ubound(sNodes) >= 0 then dim aNodesUI Redim aNodesUI(ubound(sNodes) * 2 + 1) for i = 0 to ubound(sNodes) aNodesUI(i*2) = sNodes(i) aNodesUI(i*2+1) = i next oNodesItem.UIItems = aNodesUI end if end if sAnimationGroups = FaceFXListAnimationGroups if typename(sAnimationGroups) <> "Empty" then if ubound(sAnimationGroups ) >= 0 then dim aAnimationGroupsUI Redim aAnimationGroupsUI(ubound(sAnimationGroups) * 2 + 1) for i = 0 to ubound(sAnimationGroups) aAnimationGroupsUI(i*2) = sAnimationGroups(i) aAnimationGroupsUI(i*2+1) = i next oAnimationGroupsItem.UIItems = aAnimationGroupsUI sAnimations = FaceFXListAnimationsFromGroup(sAnimationGroups(0)) if Ubound(sAnimations) >= 0 then dim aAnimationUI Redim aAnimationUI(ubound(sAnimations) * 2 + 1) for i = 0 to ubound(sAnimations) aAnimationUI(i*2) = sAnimations(i) aAnimationUI(i*2+1) = i next oAnimationItem.UIItems = aAnimationUI end if end if end if PPG.Refresh end function function FaceFX_ImportReferencePose_OnClicked() FaceFXImportRefBonePose Dictionary.GetObject("PlayControl").Current.value end function function FaceFX_ExportReferencePose_OnClicked() FaceFXExportRefBonePose FaceFX_RefBones_OnChanged end function function FaceFX_ImportNodes_OnClicked() set oBonePoseItem = PPG.PPGLayout.Item("Nodes") aUIItems = oBonePoseItem.UIitems poseName = aUIItems(PPG.Nodes.value*2) FaceFXImportBonePose poseName, Dictionary.GetObject("PlayControl").Current.value end function function FaceFX_ExportNodes_OnClicked() nodeName = InputBox("Node name", "FaceFX new node name","poseActor") if nodeName <> "" then selectedNodeType = FaceFX_Helper_GetNodeType if selectedNodeType = "FxBonePoseNode" then FaceFXExportBonePose Dictionary.GetObject("PlayControl").Current.value, nodeName elseif selectedNodeType = "FxCombinerNode" then FaceFXCreateNode nodeName, selectedNodeType, -1, 1 else FaceFXCreateNode nodeName, selectedNodeType, 0, 1 end if FaceFX_NodeType_OnChanged() end if PPG.Refresh end function function ShowDialogFileOpen() set oFileBrowser = XSIUIToolkit.FileBrowser oFileBrowser.DialogTitle = "Select a FaceFX pose batch file" ' set the title of the file browser oFileBrowser.InitialDirectory = Application.InstallationPath( siUserPath ) oFileBrowser.Filter = "All Files (*.txt)|*.txt||" ' Allow selection of txt files oFileBrowser.ShowOpen ' show an open file dialog ShowDialogFileOpen = oFileBrowser.FilePathName end function function FaceFX_BatchImportNodes_OnClicked() fileName = ShowDialogFileOpen() if fileName <> "" then FaceFXBatchImportNodes fileName end if end function function FaceFX_BatchExportNodes_OnClicked() selectedNodeType = FaceFX_Helper_GetNodeType if selectedNodeType = "FxMorphTargetNode" then sMorphTargets = FaceFX_Helper_GetMorphTargetNames for i = 0 to ubound(sMorphTargets) FaceFXCreateNode sMorphTargets(i), "FxMorphTargetNode", 0, 1 next FaceFX_NodeType_OnChanged() end if if selectedNodeType = "FxBonePoseNode" then fileName = ShowDialogFileOpen() if fileName <> "" then FaceFXBatchExportBonePoses fileName end if FaceFX_NodeType_OnChanged() end if PPG.Refresh end function function FaceFX_ImportAnimation_OnClicked() animGroupName = FaceFX_Helper_GetAnimGroup set oAnimations = PPG.PPGLayout.Item("Animation") aUIItemsAnimation = oAnimations.UIitems animationName = aUIItemsAnimation(PPG.Animation.Value*2) frameRate = Application.ActiveProject.Properties("Play Control").Rate.Value audioStartTime = (Dictionary.GetObject("PlayControl").Current.value) / (frameRate) FaceFXImportAnimation animGroupName, animationName, frameRate, audioStartTime ' Insert keys onto morph targets with the below script function FaceFX_Helper_ImportAnimationOntoMorphs animGroupName, animationName end function function FaceFX_Clean_OnClicked() FaceFXClean end function function FaceFX_InsertAnimGroup_OnClicked() groupName = InputBox("Enter new animation group name:", "New animation group name","newAnimGroup") if groupName <> "" then FaceFXInsertAnimGroup groupName end if FaceFX_OnInit FaceFX_AnimationGroup_OnChanged end function function FaceFX_RenameAnimation_OnClicked() animGroupName = FaceFX_Helper_GetAnimGroup set oAnimations = PPG.PPGLayout.Item("Animation") aUIItemsAnimation = oAnimations.UIitems animationName = aUIItemsAnimation(PPG.Animation.Value*2) newAnimationName = animationName newAnimationName = InputBox("New animation name", "FaceFX new aniamtion name",newAnimationName) if newAnimationName <> "" then FaceFXRenameAnim animGroupName, animationName, newAnimationName end if FaceFX_AnimationGroup_OnChanged end function function FaceFX_DeleteAnimation_OnClicked() animGroupName = FaceFX_Helper_GetAnimGroup set oAnimations = PPG.PPGLayout.Item("Animation") aUIItemsAnimation = oAnimations.UIitems animationName = aUIItemsAnimation(PPG.Animation.Value*2) FaceFXDeleteAnim animGroupName, animationName FaceFX_AnimationGroup_OnChanged end function function FaceFX_GenerateAnimation_OnClicked() animGroupName = FaceFX_Helper_GetAnimGroup FaceFXGenerateAnim animGroupName FaceFX_AnimationGroup_OnChanged end function function FaceFX_ChangeAnalysisActor_OnClicked() set oFileBrowser = XSIUIToolkit.FileBrowser oFileBrowser.DialogTitle = "Select an analysis actor" ' set the title of the file browser oFileBrowser.InitialDirectory = Application.InstallationPath( siUserPath ) oFileBrowser.Filter = "FaceFX Actor Files (*.facefx)|*.facefx||" oFileBrowser.ShowOpen ' show an open file dialog set filesys = CreateObject("Scripting.FileSystemObject") if filesys.FileExists(oFileBrowser.FilePathName) then FaceFXChangeAnalysisActor oFileBrowser.FilePathName end if end function function FaceFX_NewTake_OnClicked() animGroupName = FaceFX_Helper_GetAnimGroup set oAnimations = PPG.PPGLayout.Item("Animation") aUIItemsAnimation = oAnimations.UIitems animationName = aUIItemsAnimation(PPG.Animation.Value*2) 'Since start time is not less than end time, we'll get a new take of the entire animation. FaceFXNewTake animGroupName, animationName, 0, 0 end function function FaceFX_NewActorFile_OnClicked() actorName = InputBox("New actor name", "FaceFX new actor name","newActor") FaceFXNewActor actorName PPG.File.value = actorName & ".facefx" FaceFX_OnInit end function function FaceFX_RefBones_OnChanged() set oRefBonePoseItem = PPG.PPGLayout.Item("ReferenceBone") oRefBonePoseItem.UIItems = [""] sRefBonePoses = FaceFXListRefBonePoses if typename(sRefBonePoses) <> "Empty" then if ubound(sRefBonePoses) >= 0 then dim aRefBonePoseUI Redim aRefBonePoseUI(ubound(sRefBonePoses) * 2 + 1) for i = 0 to ubound(sRefBonePoses) aRefBonePoseUI(i*2) = sRefBonePoses(i) aRefBonePoseUI(i*2+1) = i next oRefBonePoseItem.UIItems = aRefBonePoseUI end if end if PPG.Refresh end function function FaceFX_NodeType_OnChanged() nodeTypeName = FaceFX_Helper_GetNodeType sNodes = FaceFXGetNodes(nodeTypeName) dim aUIItems Redim aUIItems(ubound(sNodes) *2 +1) for i = 0 to ubound(sNodes) aUIItems(i*2) = sNodes(i) aUIItems(i*2+1) = i next set oNodes = PPG.PPGLayout.Item("Nodes") oNodes.UIitems = aUIItems PPG.Refresh end function function FaceFX_AnimationGroup_OnChanged() animGroupName = FaceFX_Helper_GetAnimGroup sAnimations = FaceFXListAnimationsFromGroup(animGroupName) dim aUIItems Redim aUIItems(ubound(sAnimations) *2 +1) for i = 0 to ubound(sAnimations) aUIItems(i*2) = sAnimations(i) aUIItems(i*2+1) = i next set oAnimations = PPG.PPGLayout.Item("Animation") oAnimations.UIitems = aUIItems PPG.Refresh end function function FaceFX_Refresh_Script_GUI_Init ( in_ctxt ) end function function FaceFX_Refresh_Script_GUI_Execute () 'set oActorProp = ActiveSceneRoot.Properties("FaceFX") PPG.Refresh FaceFX_RefBones_OnChanged FaceFX_NodeType_OnChanged FaceFX_AnimationGroup_OnChanged end function