-- TODO: -- * Sync actions knopje, zodat je alle actions hebt. -- * Export actions knopje. Misschien in sync inbouwen met checkboxes per actie die niet in de source bestaat -- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** ScriptName = "LK_InsertActions" -- ************************************************** -- General information about this script -- ************************************************** LK_InsertActions = {} function LK_InsertActions:Name() return 'Insert Actions' end function LK_InsertActions:Version() return '1.0' end function LK_InsertActions:UILabel() return 'Insert Actions' end function LK_InsertActions:Creator() return 'Lukas Krepel, Frame Order' end function LK_InsertActions:Description() return 'Insert Poses and Animation saved in Actions into the Mainline via toolbar dropdown menus.' end function LK_InsertActions:ColorizeIcon() return true end -- ************************************************** -- Is Relevant / Is Enabled -- ************************************************** function LK_InsertActions:IsRelevant(moho) if MohoMode ~= nil then return not MohoMode.vanilla else return true end end function LK_InsertActions:IsEnabled(moho) return true end -- ************************************************** -- Draw everything in the viewport -- ************************************************** function LK_InsertActions:DrawMe(moho, view) FO_Utilities:DrawMeMod(moho, view, -1) end -- ************************************************** -- Keyboard/Mouse Control -- ************************************************** function LK_InsertActions:OnMouseDown(moho, mouseEvent) if LK_SelectBone ~= nil then LK_SelectBone:OnMouseDown(moho, mouseEvent) end end function LK_InsertActions:OnMouseMoved(moho, mouseEvent) if LK_SelectBone ~= nil then LK_SelectBone:OnMouseMoved(moho, mouseEvent) end end function LK_InsertActions:OnMouseUp(moho, mouseEvent) if LK_SelectBone ~= nil then LK_SelectBone:OnMouseUp(moho, mouseEvent) end end function LK_InsertActions:OnKeyDown(moho, keyEvent) if LK_Powertool ~= nil then LK_Powertool:OnKeyDown(moho, keyEvent) end end LK_InsertActions.TOGGLE_KEY_PREV_FRAME = MOHO.MSG_BASE LK_InsertActions.TOGGLE_RELATIVE_POS = MOHO.MSG_BASE + 1 LK_InsertActions.SAVE_NEW_POSE = MOHO.MSG_BASE + 17 LK_InsertActions.SAVE_NEW_ANIMATION = MOHO.MSG_BASE + 18 LK_InsertActions.MARK_ROOT_BONE = MOHO.MSG_BASE + 19 LK_InsertActions.TOGGLE_CHANNELS = MOHO.MSG_BASE + 50 -- * 50 t/m 65 LK_InsertActions.INSERT_POSE = MOHO.MSG_BASE + 100 -- 100 t/m 199 gereserveerd LK_InsertActions.INSERT_ANIMATION = MOHO.MSG_BASE + 200 -- 200 t/m 299 gereserveerd LK_InsertActions.keyPrevFrame = false LK_InsertActions.relativePos = true -- ************************************************** -- Load / Saves Preferences -- ************************************************** function LK_InsertActions:LoadPrefs(prefs) -- * Channels: FO_Channels:LoadPrefs(prefs) -- * Options: self.keyPrevFrame = prefs:GetBool("LK_InsertActions.keyPrevFrame", false) self.relativePos = prefs:GetBool("LK_InsertActions.relativePos", true) end function LK_InsertActions:SavePrefs(prefs) -- * Channels: FO_Channels:SavePrefs(prefs) -- * Options: prefs:SetBool("LK_InsertActions.keyPrevFrame", self.keyPrevFrame) prefs:SetBool("LK_InsertActions.relativePos", self.relativePos) end function LK_InsertActions:ResetPrefs() -- * Channels: FO_Channels:ResetPrefs() -- * Options: LK_InsertActions.keyPrevFrame = false LK_InsertActions.relativePos = true LK_InsertActions.stepInterpolation = true end -- ************************************************** -- Tool Panel Layout -- ************************************************** function LK_InsertActions:DoLayout(moho, layout) FO_Utilities.tinyUI = false -- ************************ -- *** Create Actions: *** -- ************************ FO_Utilities:Divider(layout, "Create Actions", true) layout:PushH(LM.GUI.ALIGN_CENTER, 2) -- * Pose: self.newPoseButton = LM.GUI.ImageButton("ScriptResources/FO_icons/add_pose", "Save New Pose", false, self.SAVE_NEW_POSE, true) layout:AddChild(self.newPoseButton) -- * Animation: self.newAnimationButton = LM.GUI.ImageButton("ScriptResources/FO_icons/add_animation", "Save New Animations", false, self.SAVE_NEW_ANIMATION, true) layout:AddChild(self.newAnimationButton) layout:Pop() -- ***************** -- *** Actions: *** -- ***************** FO_Utilities:Divider(layout, "Insert Actions", false) layout:PushH(LM.GUI.ALIGN_CENTER, 2) -- * Poses: self.PosesDropdown = LM.GUI.Menu('Pose') local posesDropdownSize = 75 local animationsDropdownSize = 75 if not FO_Utilities.tinyUI then -- posesDropdownSize = 150 -- animationsDropdownSize = 150 end self.PosesDropdown_popup = LM.GUI.PopupMenu(posesDropdownSize, false) self.PosesDropdown_popup:SetMenu(self.PosesDropdown) self.PosesDropdown_popup:SetToolTip("Insert a Pose into the Mainline") -- * Animations: layout:AddChild(self.PosesDropdown_popup, LM.GUI.ALIGN_LEFT, 0) self.AnimationsDropdown = LM.GUI.Menu('Animation') self.AnimationsDropdown_popup = LM.GUI.PopupMenu(animationsDropdownSize, false) self.AnimationsDropdown_popup:SetMenu(self.AnimationsDropdown) self.AnimationsDropdown_popup:SetToolTip("Insert Animation into the Mainline") layout:AddChild(self.AnimationsDropdown_popup, LM.GUI.ALIGN_LEFT, 0) layout:Pop() -- ************************** -- *** Layers & Channels: *** -- ************************** FO_Channels:DoLayout(moho, layout, self.TOGGLE_CHANNELS) -- ***************** -- *** Options: *** -- ***************** FO_Utilities:Divider(layout, "Options", false) -- * Key previous frame checkbox: self.KeyPoseOnPreviousFrameCheckbox = LM.GUI.CheckBox("Key previous frame", self.TOGGLE_KEY_PREV_FRAME) layout:AddChild(self.KeyPoseOnPreviousFrameCheckbox) -- * Relative position checkbox: self.relativePosCheckbox = LM.GUI.CheckBox("Relative placement", self.TOGGLE_RELATIVE_POS) layout:AddChild(self.relativePosCheckbox) -- * Mark Root Bone Button: self.markAsRootBoneButton = LM.GUI.ImageButton("ScriptResources/FO_icons/mark_root_bone", "Mark selected bone as root bone", false, self.MARK_ROOT_BONE, true) layout:AddChild(self.markAsRootBoneButton) layout:AddPadding(-14) self.rootBoneText = LM.GUI.DynamicText("Rootbone: Room for a long bone name") layout:AddChild(self.rootBoneText) end function LK_InsertActions:UpdateWidgets(moho) FO_Channels:UpdateWidgets(moho) local layer = moho.layer self.poses = {} self.animations = {} for a=0, layer:CountActions()-1 do actionName = layer:ActionName(a) if not layer:IsSmartBoneAction(actionName) then local duration = layer:ActionDuration(actionName) if duration == 1 then table.insert(self.poses, actionName) elseif duration > 1 then table.insert(self.animations, actionName) end end end -- * Sort alphabetically: table.sort(self.poses, function(a,b) return a < b end) table.sort(self.animations, function(a,b) return a < b end) -- * Clear dropdown items (so UpdateUI() doesn't create duplicates) self.PosesDropdown:RemoveAllItems() self.AnimationsDropdown:RemoveAllItems() -- * Do stuff: for i = 1, #self.poses do self.PosesDropdown:AddItem(self.poses[i], nil, self.INSERT_POSE + i) self.PosesDropdown:SetEnabled(self.INSERT_POSE + i, moho.frame ~= 0) end for i = 1, #self.animations do self.AnimationsDropdown:AddItem(self.animations[i], nil, self.INSERT_ANIMATION + i) self.AnimationsDropdown:SetEnabled(self.INSERT_ANIMATION + i, moho.frame ~= 0) end -- *** Checkboxes: -- * Options: self.KeyPoseOnPreviousFrameCheckbox:SetValue(self.keyPrevFrame) self.relativePosCheckbox:SetValue(self.relativePos) -- * Root bone: local skel = moho:Skeleton() local rootBoneButtonEnable = false if skel ~= nil then local selectedBoneID = skel:SelectedBoneID() if selectedBoneID ~= -1 then rootBoneButtonEnable = (skel:Bone(selectedBoneID).fParent == -1) end end self.markAsRootBoneButton:Enable(rootBoneButtonEnable) local ScriptInfo = moho.layer:ScriptData() self.rootBoneName = ScriptInfo:GetString("Rootbone") if self.rootBoneName ~= "" then self.rootBoneText:SetValue("Rootbone: "..self.rootBoneName) else self.rootBoneText:SetValue("Rootbone: -") end end function LK_InsertActions:HandleMessage(moho, view, msg) FO_Channels:HandleMessage(moho, view, msg, self.TOGGLE_LAYER_CHANNELS) if msg == self.MARK_ROOT_BONE then moho.document:PrepUndo() moho.document:SetDirty() local skel = moho:Skeleton() local selectedBoneID = skel:SelectedBoneID() self.rootBoneName = skel:Bone(selectedBoneID):Name() local ScriptInfo = moho.layer:ScriptData() ScriptInfo:Set("Rootbone", self.rootBoneName) elseif msg == self.SAVE_NEW_POSE or msg == self.SAVE_NEW_ANIMATION then -- * Inser mainline into action: local first = moho.frame local last = moho.frame if (msg == self.SAVE_NEW_ANIMATION) then first = MOHO.MohoGlobals.PlayStart last = MOHO.MohoGlobals.PlayEnd if first == -1 or last == -1 then local channels = {} FO_Channels:AddLayerChannels(moho, moho.layer, channels) for i = 1, #channels do local channel = channels[i] local endKey = channel:Duration() local keyCount = channel:CountKeys() - 1 local keysFound = 0 local frameNum = endKey while keysFound < keyCount do thisKey = channel:GetClosestKeyID(frameNum) local keyFrameNum = channel:GetKeyWhen(thisKey) keysFound = 1 + keysFound if channel:IsKeySelected(keyFrameNum) then if keyFrameNum < first or first == -1 then first = keyFrameNum end if keyFrameNum > last then last = keyFrameNum end end frameNum = keyFrameNum - 1 end end end if first == -1 or last == -1 then FO_Utilities:Alert("Either select a range of keys or set Play-Start and Play-End to use as in/out-points!") return end end local actionName = "" local n = 0 if (msg == self.SAVE_NEW_ANIMATION) then while moho.layer:HasAction(actionName) or actionName == "" do n = n + 1 actionName = "Animation "..#self.animations+n end else while moho.layer:HasAction(actionName) or actionName == "" do n = n + 1 actionName = "Pose "..#self.poses+n end end local dlog = FO_Utilities:InputTextDialog(moho, "Pose", "Name:", actionName):DoModal() if (dlog == LM.GUI.MSG_CANCEL) then return end actionName = FO_Utilities.input if moho.layer:HasAction(actionName) then FO_Utilities:Alert("Action '"..actionName.."' already exists!") return end -- * Prep Undo: moho.document:PrepUndo() moho.document:SetDirty() -- * FREEZE POSE: -- LK_Powertool:HandleMessage(moho, view, LK_Powertool.ADD_KEYSET_CURRENT) local fromAction = moho.layer:CurrentAction() local tempAction = "Temporary_Mainline_Copy" if fromAction == "" then -- * Trying to get from mainline, which is not possible. -- * Creating temporary action and copy mainline into it, we'll delete it after we're done. moho.layer:ActivateAction(tempAction) fromAction = tempAction moho.layer:InsertAction("", 1) -- *** Freeze first and last frame in temporary action: -- * Get layers: local layers = {} FO_Channels:AddLayerWithChildren(moho, moho.layer, layers) -- * Get channels for layers: local channels = {} for i = 1, #layers do local layer = layers[i] FO_Channels:AddLayerChannels(moho, layer, channels) end channels = FO_Channels:ChannelsAsTypes(moho, channels) for i = 1, #channels do local channel = channels[i] channel:SetValue(first, channel:GetValue(first)) channel:SetValue(last, channel:GetValue(last)) end -- *** End Freeze. end -- * Get frame: local frame = moho.frame -- * Create action: moho.layer:ActivateAction(actionName) self:InsertAction(moho, fromAction, 1, first, last) -- * Clear markers: moho.layer.fTimelineMarkers:Clear() -- * Clean channels: Syn_Clean_Keys_Severe:Run(moho, view) -- * Go back to Mainline: moho.layer:DeleteAction(tempAction) moho.layer:ActivateAction("") -- * Return to mainline (temp disabled for debugging) moho.layer:DeleteAction("") -- * Notify: if (moho.layer:ActionDuration(actionName) == 0) then moho.layer:DeleteAction(actionName) -- * Update: moho.layer:UpdateCurFrame(true) moho:UpdateUI() MOHO:Redraw() FO_Utilities:Alert("Created action '"..actionName.."' was empty and has been deleted!") return end -- * Update: moho.layer:UpdateCurFrame(true) moho:UpdateUI() MOHO:Redraw() FO_Utilities:Alert("Created new Action: '"..actionName.."'.") elseif msg == self.TOGGLE_KEY_PREV_FRAME then self.keyPrevFrame = not self.keyPrevFrame elseif msg == self.TOGGLE_RELATIVE_POS then self.relativePos = not self.relativePos elseif msg > self.INSERT_POSE and msg < (self.INSERT_POSE + 99) then -- * Prep Undo: moho.document:PrepUndo() moho.document:SetDirty() local option = (msg - self.INSERT_POSE) local actionName = self.poses[option] self:InsertAction(moho, actionName, moho.frame) elseif msg > self.INSERT_ANIMATION and msg < (self.INSERT_ANIMATION + 99) then -- * Prep Undo: moho.document:PrepUndo() moho.document:SetDirty() local option = (msg - self.INSERT_ANIMATION) local actionName = self.animations[option] self:InsertAction(moho, actionName, moho.frame) end FO_Utilities:PaintKeys(moho) moho.layer:UpdateCurFrame(true) moho:UpdateUI() MOHO:Redraw() end function LK_InsertActions:InsertAction(moho, actionName, when, first, last) local length = 0 when = when or moho.frame first = first or 1 last = last or 999999999 if when < 1 then return end -- * Get layers: local layers = {} FO_Channels:AddLayerWithChildren(moho, moho.layer, layers) -- * Get channels for layers: local channels = {} for i = 1, #layers do local layer = layers[i] FO_Channels:AddLayerChannels(moho, layer, channels) end channels = FO_Channels:ChannelsAsTypes(moho, channels) -- **** Relative: -- * Layer Translation: local translationChannel = moho:ChannelAsAnimVec3(moho.layer.fTranslation) local layerTranslationOffsetValue = LM.Vector3:new_local() layerTranslationOffsetValue = translationChannel.value - translationChannel:GetValue(0) local translationActionChannel = translationChannel:ActionByName(actionName) if translationActionChannel ~= nil then translationActionChannel = FO_Channels:ChannelAsType(moho, translationActionChannel) layerTranslationOffsetValue = layerTranslationOffsetValue - translationActionChannel:GetValue(1) + translationActionChannel:GetValue(0) end -- * Layer Scale: local scaleChannel = moho:ChannelAsAnimVec3(moho.layer.fScale) local layerScaleOffsetValue = LM.Vector3:new_local() layerScaleOffsetValue = scaleChannel.value -- * Bone Translation: local rootBonePosChannel = nil local rootBoneOffsetValue = LM.Vector2:new_local() local rootBoneTranslationChannels = {} if moho.layer:IsBoneType() then local boneLayer = moho:LayerAsBone(moho.layer) local skel = boneLayer:Skeleton() local rootBone = nil -- * Look for selected rootbone and all other rootbones: for i = 0, skel:CountBones() - 1 do local bone = skel:Bone(i) if bone:Name() == self.rootBoneName then rootBone = bone end -- * Also check for all rootbones without parents: if bone.fParent == -1 then table.insert(rootBoneTranslationChannels, bone.fAnimPos) end end -- * If no bone with a Hip tag is found, find the first root bone without a parent: if rootBone == nil then for i = 0, skel:CountBones() - 1 do local bone = skel:Bone(i) if bone.fParent == -1 and not bone.fHidden then rootBone = bone break end end end -- * if rootBone ~= nil then rootBonePosChannel = rootBone.fAnimPos rootBonePosChannel = FO_Channels:ChannelAsType(moho, rootBonePosChannel) rootBoneOffsetValue = rootBonePosChannel.value - rootBonePosChannel:GetValue(0) local rootBonePosActionChannel = rootBonePosChannel:ActionByName(actionName) if rootBonePosActionChannel ~= nil then rootBonePosActionChannel = FO_Channels:ChannelAsType(moho, rootBonePosActionChannel) rootBoneOffsetValue = rootBoneOffsetValue - rootBonePosActionChannel:GetValue(1) + rootBonePosActionChannel:GetValue(0) end end end -- * for c = 1, #channels do local channel = channels[c] local actionChannel = channel:ActionByName(actionName) local keyedChannel = false if actionChannel ~= nil then actionChannel = FO_Channels:ChannelAsType(moho, actionChannel) local endKey = actionChannel:Duration() if endKey > last then endKey = last end local thisKey = 0 local keyCount = actionChannel:CountKeys() - 1 local keysFound = 0 local frameNum = endKey if self.keyPrevFrame and keyCount > 0 and when ~= 1 then channel:SetValue(when-1, channel:GetValue(when-1)) end while keysFound < keyCount do thisKey = actionChannel:GetClosestKeyID(frameNum) local keyFrameNum = actionChannel:GetKeyWhen(thisKey) if keyFrameNum > length then length = keyFrameNum end keysFound = keysFound + 1 local interp = MOHO.InterpSetting:new_local() actionChannel:GetKeyInterp(keyFrameNum, interp) -- * local actionKeyValue = actionChannel:GetValue(keyFrameNum) local frame = when + keyFrameNum - first -- * if self.relativePos then -- * Relative layer position: if channel == translationChannel then -- * TODO...Adjust translation to scale? (actionKeyValue:Set(actionKeyValue.x * layerScaleOffsetValue.x, actionKeyValue.y * layerScaleOffsetValue.y, actionKeyValue.z) -- * NOT MULTIPLYING Z-AXIS! actionKeyValue = actionKeyValue + layerTranslationOffsetValue end -- * Relative layer scale: if channel == scaleChannel then -- * Assuming Scale=1 is the scale used in Actions. Note that it can be different from frame 0. actionKeyValue:Set(actionKeyValue.x * layerScaleOffsetValue.x, actionKeyValue.y * layerScaleOffsetValue.y, actionKeyValue.z * layerScaleOffsetValue.z) end -- * Relative root bone position: if table.contains(rootBoneTranslationChannels, channel) then actionKeyValue = actionKeyValue + rootBoneOffsetValue end end -- if keyFrameNum >= first and keyFrameNum <= last then -- * Set key! channel:SetValue(frame, actionKeyValue) -- * Adjust interpolation (for poses only): if (moho.layer:ActionDuration(actionName) == 1) or (first == last) then if MOHO.MohoGlobals.DefaultInterp ~= -1 then -- * -1 = COPY PREVIOUS KEY interp.interpMode = MOHO.MohoGlobals.DefaultInterp else prevKey = channel:GetClosestKeyID(when-1) prevKeyFrameNum = channel:GetKeyWhen(prevKey) local prevInterp = MOHO.InterpSetting:new_local() channel:GetKeyInterp(prevKeyFrameNum, prevInterp) if prevInterp.interpMode ~= 5 then -- * 5 = INTERP_CYCLE interp.interpMode = prevInterp.interpMode else interp.interpMode = 1 -- * 1 = INTERP_SMOOTH end end end channel:SetKeyInterp(frame, interp) keyedChannel = true end frameNum = keyFrameNum - 1 end end -- * Key channels that are not keyed in the action: if not keyedChannel then -- * Only on channels that already have a key: if channel:CountKeys() > 1 then if channel.value ~= channel:GetValue(0) then if self.keyPrevFrame then channel:SetValue(when-1, channel:GetValue(when-1)) end -- * Relative layer position: if self.relativePos and channel == translationChannel then local newVal = channel.value channel:SetValue(when, newVal) elseif self.relativePos and channel == scaleChannel then local newVal = channel.value channel:SetValue(when, newVal) -- * Relative root bone position: elseif table.contains(rootBoneTranslationChannels, channel) then local newVal = channel.value actionKeyValue = newVal + rootBoneOffsetValue channel:SetValue(when, newVal) else channel:Reset(when) end end end end end self.markActions = true if self.markActions then local markerChannel = moho.layer.fTimelineMarkers markerChannel:SetValue(when, actionName) local markerInterp = MOHO.InterpSetting:new_local() markerInterp.hold = length - 1 markerInterp.tags = 2 -- * 2 = Orange markerChannel:SetKeyInterp(when, markerInterp) end -- * Update UI: FO_Utilities:PaintKeys(moho) moho.layer:UpdateCurFrame(true) moho:UpdateUI() MOHO:Redraw() end
Insert Actions
Unlisted
Author: Lukas
View Script
Script type: Tool
Uploaded: May 15 2023, 07:37
Last modified: May 16 2023, 04:37
Insert actions
This script, and all other scripts on this site are distributed as free software under the GNU General Public License 3.0 or later.
Downloads count: 48

Insert Actions
Unlisted
Author: Lukas
View Script
Script type: Tool
Uploaded: May 15 2023, 07:37
Last modified: May 16 2023, 04:37
Insert actions
This script, and all other scripts on this site are distributed as free software under the GNU General Public License 3.0 or later.
Downloads count: 48