-- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** ScriptName = "MR_BakeBoneDynamics" -- ************************************************** -- General information about this script -- ************************************************** MR_BakeBoneDynamics = {} function MR_BakeBoneDynamics:Name() return self:Localize('UILabel') end function MR_BakeBoneDynamics:Version() return '1.0' end function MR_BakeBoneDynamics:UILabel() return self:Localize('UILabel') end function MR_BakeBoneDynamics:Creator() return 'Eugene Babich' end function MR_BakeBoneDynamics:Description() return self:Localize('Description') end -- ************************************************** -- Is Relevant / Is Enabled -- ************************************************** function MR_BakeBoneDynamics:IsRelevant(moho) local skel = moho:Skeleton() if (skel == nil) then return false end return true end function MR_BakeBoneDynamics:IsEnabled(moho) local skel = moho:Skeleton() if (moho:CountBones() < 1) then return false end return true end -- ************************************************** -- Recurring Values -- ************************************************** MR_BakeBoneDynamics.from = 1 MR_BakeBoneDynamics.to = 50 MR_BakeBoneDynamics.interval = 1 MR_BakeBoneDynamics.preroll = 10 MR_BakeBoneDynamics.isMouseDragging = false MR_BakeBoneDynamics.addDynamicsKeys = true MR_BakeBoneDynamics.selRect = LM.Rect:new_local() -- ************************************************** -- Prefs -- ************************************************** function MR_BakeBoneDynamics:LoadPrefs(prefs) self.from = prefs:GetInt("MR_BakeBoneDynamics.from", 1) self.to = prefs:GetInt("MR_BakeBoneDynamics.to", 50) self.interval = prefs:GetInt("MR_BakeBoneDynamics.interval", 1) self.preroll = prefs:GetInt("MR_BakeBoneDynamics.preroll", 10) self.addDynamicsKeys = prefs:GetBool("MR_BakeBoneDynamics.addDynamicsKeys", true) end function MR_BakeBoneDynamics:SavePrefs(prefs) prefs:SetInt("MR_BakeBoneDynamics.from", self.from) prefs:SetInt("MR_BakeBoneDynamics.to", self.to) prefs:SetInt("MR_BakeBoneDynamics.interval", self.interval) prefs:SetInt("MR_BakeBoneDynamics.preroll", self.preroll) prefs:SetBool("MR_BakeBoneDynamics.addDynamicsKeys", self.addDynamicsKeys) end function MR_BakeBoneDynamics:ResetPrefs() self.from = 1 self.to = 50 self.interval = 1 self.preroll = 10 self.addDynamicsKeys = true end -- ************************************************** -- Keyboard/Mouse Control -- ************************************************** function MR_BakeBoneDynamics:OnMouseDown(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end self.isMouseDragging = true if not mouseEvent.shiftKey and not mouseEvent.altKey and skel ~= nil then skel:SelectNone() end self.selRect.left = mouseEvent.startPt.x self.selRect.top = mouseEvent.startPt.y self.selRect.right = mouseEvent.pt.x self.selRect.bottom = mouseEvent.pt.y mouseEvent.view:Graphics():SelectionRect(self.selRect) mouseEvent.view:DrawMe() end function MR_BakeBoneDynamics:OnMouseMoved(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end mouseEvent.view:Graphics():SelectionRect(self.selRect) self.selRect.right = mouseEvent.pt.x self.selRect.bottom = mouseEvent.pt.y mouseEvent.view:Graphics():SelectionRect(self.selRect) mouseEvent.view:RefreshView() mouseEvent.view:DrawMe() end function MR_BakeBoneDynamics:OnMouseUp(moho, mouseEvent) local skel = moho:Skeleton() local mouseDist = math.abs(mouseEvent.pt.x - mouseEvent.startPt.x) + math.abs(mouseEvent.pt.y - mouseEvent.startPt.y) local id = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, moho.layer, true) if not mouseEvent.shiftKey and not mouseEvent.altKey then skel:SelectNone() end if id ~= -1 then skel:Bone(id).fSelected = not mouseEvent.altKey end self.isMouseDragging = false local v = LM.Vector2:new_local() local screenPt = LM.Point:new_local() local m = LM.Matrix:new_local() self.selRect:Normalize() moho.layer:GetFullTransform(moho.frame, m, moho.document) if moho.layer:LayerType() == MOHO.LT_BONE then if (skel ~= nil) then for i = 0, skel:CountBones() - 1 do local bone = skel:Bone(i) local boneMatrix = bone.fMovedMatrix for j = 0, 10 do v:Set(bone.fLength * j / 10.0, 0) boneMatrix:Transform(v) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, screenPt) if (self.selRect:Contains(screenPt)) then if (mouseEvent.altKey) then bone.fSelected = false else bone.fSelected = true end break end end end end end moho:UpdateSelectedChannels() end function MR_BakeBoneDynamics:DrawMe(moho, view) if self.isMouseDragging then local g = view:Graphics() g:SelectionRect(self.selRect) end end -- ************************************************** -- Tool Panel Layout -- ************************************************** MR_BakeBoneDynamics.SELECT_DYNAMIC_BONES = MOHO.MSG_BASE + 1 MR_BakeBoneDynamics.PREROLL = MOHO.MSG_BASE + 2 MR_BakeBoneDynamics.FROM = MOHO.MSG_BASE + 3 MR_BakeBoneDynamics.TO = MOHO.MSG_BASE + 4 MR_BakeBoneDynamics.INTERVAL = MOHO.MSG_BASE + 5 MR_BakeBoneDynamics.INTERVAL_1 = MOHO.MSG_BASE + 6 MR_BakeBoneDynamics.INTERVAL_2 = MOHO.MSG_BASE + 7 MR_BakeBoneDynamics.INTERVAL_3 = MOHO.MSG_BASE + 8 MR_BakeBoneDynamics.INTERVAL_4 = MOHO.MSG_BASE + 9 MR_BakeBoneDynamics.SET_PROJECT_RANGE = MOHO.MSG_BASE + 10 MR_BakeBoneDynamics.SET_PLAYBACK_RANGE = MOHO.MSG_BASE + 11 MR_BakeBoneDynamics.ADD_DYNAMICS_KEYS = MOHO.MSG_BASE + 12 MR_BakeBoneDynamics.BAKE = MOHO.MSG_BASE + 13 function MR_BakeBoneDynamics:DoLayout(moho, layout) self.selectDynamicsBonesButton = LM.GUI.Button(self:Localize('Select Dynamic Bones'), self.SELECT_DYNAMIC_BONES) layout:AddChild(self.selectDynamicsBonesButton, LM.GUI.ALIGN_LEFT, 0) self.preRollInput = LM.GUI.TextControl(0, '10000', self.PREROLL, LM.GUI.FIELD_INT, self:Localize('Pre Roll:')) layout:AddChild(self.preRollInput, LM.GUI.ALIGN_LEFT, 0) self.fromInput = LM.GUI.TextControl(0, '10000', self.FROM, LM.GUI.FIELD_INT, self:Localize('From:')) layout:AddChild(self.fromInput, LM.GUI.ALIGN_LEFT, 0) self.toInput = LM.GUI.TextControl(0, '10000', self.TO, LM.GUI.FIELD_INT, self:Localize('To:')) layout:AddChild(self.toInput, LM.GUI.ALIGN_LEFT, 0) self.setProjectRangeButton = LM.GUI.ImageButton("ScriptResources/mr_bake_bone_dynamics/mr_set_project_range",self:Localize('Set Project Range'),false, self.SET_PROJECT_RANGE, false) layout:AddChild(self.setProjectRangeButton, LM.GUI.ALIGN_LEFT, 0) self.setPlaybackRangeButton = LM.GUI.ImageButton("ScriptResources/mr_bake_bone_dynamics/mr_set_playback_range",self:Localize('Set Playback Range'),false, self.SET_PLAYBACK_RANGE, false) layout:AddChild(self.setPlaybackRangeButton, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.StaticText(self:Localize("IntervalText"))) self.intervalMenu = LM.GUI.Menu(MOHO.Localize("Interval=Interval")) self.intervalMenu:AddItem(MOHO.Localize("1=1"), 0, self.INTERVAL_1) self.intervalMenu:AddItemAlphabetically(MOHO.Localize("2=2"), 0, self.INTERVAL_2) self.intervalMenu:AddItemAlphabetically(MOHO.Localize("3=3"), 0, self.INTERVAL_3) self.intervalMenu:AddItemAlphabetically(MOHO.Localize("4=4"), 0, self.INTERVAL_4) self.intervalPopup = LM.GUI.PopupMenu(50, true) self.intervalPopup:SetMenu(self.intervalMenu) layout:AddChild(self.intervalPopup) self.addDynamicsKeysCheckbox = LM.GUI.CheckBox(self:Localize('Add Dynamics Keys'), self.ADD_DYNAMICS_KEYS) self.addDynamicsKeysCheckbox:SetToolTip(self:Localize('Add Dynamics Keys Tooltip')) layout:AddChild(self.addDynamicsKeysCheckbox, LM.GUI.ALIGN_LEFT, 0) self.bakeBoneDynamicsButton = LM.GUI.Button(self:Localize('Bake'), self.BAKE) layout:AddChild(self.bakeBoneDynamicsButton, LM.GUI.ALIGN_LEFT, 0) end function MR_BakeBoneDynamics:UpdateWidgets(moho) self.preRollInput:SetValue(self.preroll) self.fromInput:SetValue(self.from) self.toInput:SetValue(self.to) if self.interval < 1 or self.interval > 4 or self.interval == nil then self.interval = 1 end if (self.interval == 1) then self.intervalMenu:SetChecked(self.INTERVAL_1, true) elseif (self.interval == 2) then self.intervalMenu:SetChecked(self.INTERVAL_2, true) elseif (self.interval == 3) then self.intervalMenu:SetChecked(self.INTERVAL_3, true) elseif (self.interval == 4) then self.intervalMenu:SetChecked(self.INTERVAL_4, true) end self.intervalPopup:Redraw() self.addDynamicsKeysCheckbox:SetValue(self.addDynamicsKeys) end function MR_BakeBoneDynamics:HandleMessage(moho, view, msg) if msg == self.SELECT_DYNAMIC_BONES then self:SelectDynamicsBones(moho) elseif msg == self.PREROLL then self.preroll = LM.Clamp(self.preRollInput:IntValue(), 0, 5000) self.preRollInput:SetValue(self.preroll) elseif msg == self.FROM then self.from = self.fromInput:IntValue() if self.from < 1 then self.from = 1 self.fromInput:SetValue(self.from) end if self.to < self.from then self.to = self.from self.toInput:SetValue(self.to) end elseif msg == self.TO then self.to = self.toInput:IntValue() if self.to < self.from then if self.to < 1 then self.to = 1 self.toInput:SetValue(self.to) end self.from = self.to self.fromInput:SetValue(self.from) end elseif msg == self.SET_PROJECT_RANGE then self.from = moho.document:StartFrame() self.to = moho.document:EndFrame() self.toInput:SetValue(self.to) self.fromInput:SetValue(self.from) elseif msg == self.SET_PLAYBACK_RANGE then local startP = MOHO.MohoGlobals.PlayStart local endP = MOHO.MohoGlobals.PlayEnd if startP > 0 then self.from = startP end if endP > 0 then self.to = endP end self.toInput:SetValue(self.to) self.fromInput:SetValue(self.from) elseif (msg >= self.INTERVAL_1 and msg <= self.INTERVAL_4) then local int = 1 if (msg == self.INTERVAL_1) then int = 1 elseif (msg == self.INTERVAL_2) then int = 2 elseif (msg == self.INTERVAL_3) then int = 3 elseif (msg == self.INTERVAL_4) then int = 4 end self.interval = int self.isReady = false self:UpdateWidgets(moho) elseif msg == self.ADD_DYNAMICS_KEYS then self.addDynamicsKeys = self.addDynamicsKeysCheckbox:Value() elseif msg == self.BAKE then self:Bake(moho) end end function MR_BakeBoneDynamics:Bake(moho) local skel = moho:Skeleton() if (skel == nil) then return end if moho:CountSelectedBones() < 1 then return end moho.document:PrepUndo(moho.layer, true) moho.document:SetDirty() local isSoundtrackMuted = false local isDynamicsOn = false if not MOHO.MohoGlobals.MuteSoundtrack then isSoundtrackMuted = true MOHO.MohoGlobals.MuteSoundtrack = true end if MOHO.MohoGlobals.EnableBoneDynamics then isDynamicsOn = true end local preroll = self.preroll if preroll >= self.from then preroll = self.from - 1 end local targetFrame = self.from - preroll local nextIntervalFrame = self.from local curFrame = moho.frame local frameList = {} frameList.frame = {} local bonesList = {} for i=0, skel:CountBones()-1 do local bone = skel:Bone(i) if bone.fSelected then local dynamicsChannel = moho:ChannelAsAnimBool(bone.fBoneDynamics) if dynamicsChannel:CountKeys() > 1 or dynamicsChannel:GetValue(0) then table.insert(bonesList, i) end end end if #bonesList < 1 then return end MOHO.MohoGlobals.EnableBoneDynamics = true repeat moho:SetCurFrame(targetFrame) if targetFrame == nextIntervalFrame then local bonesValuesList = {} bonesValuesList.boneID = {} bonesValuesList.angle = {} for i, id in pairs(bonesList) do table.insert(bonesValuesList.boneID, id) local bone = skel:Bone(id) table.insert(bonesValuesList.angle, bone.fAngle) end table.insert(frameList, bonesValuesList) table.insert(frameList.frame, targetFrame) nextIntervalFrame = targetFrame + self.interval end targetFrame = targetFrame + 1 until targetFrame >= self.to if frameList.frame[#frameList.frame] < self.to then moho:SetCurFrame(self.to) local bonesValuesList = {} bonesValuesList.boneID = {} bonesValuesList.angle = {} for i, id in pairs(bonesList) do table.insert(bonesValuesList.boneID, id) local bone = skel:Bone(id) table.insert(bonesValuesList.angle, bone.fAngle) end table.insert(frameList, bonesValuesList) table.insert(frameList.frame, self.to) end local keyInterp = MOHO.InterpSetting:new_local() keyInterp.interval = self.interval for i, id in pairs(bonesList) do local bone = skel:Bone(id) local dynamicsChannel = moho:ChannelAsAnimBool(bone.fBoneDynamics) local angleChannel = moho:ChannelAsAnimVal(bone.fAnimAngle) if self.addDynamicsKeys then dynamicsChannel:SetValue(self.to +1, dynamicsChannel:GetValue(self.to +1)) dynamicsChannel:SetValue(self.from, false) end MOHO.MohoGlobals.EnableBoneDynamics = false local angleKeysToDeleteList = {} if self.addDynamicsKeys then local dynamicsKeysToDeleteList = {} for keyID = 0, dynamicsChannel:CountKeys() - 1 do local channelFrame = dynamicsChannel:GetKeyWhen(keyID) if (channelFrame > self.from and channelFrame <= self.to) then table.insert(dynamicsKeysToDeleteList, channelFrame) end end for c, frame in pairs(dynamicsKeysToDeleteList) do dynamicsChannel:DeleteKey(frame) end end for keyID = 0, angleChannel:CountKeys() - 1 do local channelFrame = angleChannel:GetKeyWhen(keyID) if (channelFrame > self.from and channelFrame < self.to) then table.insert(angleKeysToDeleteList, channelFrame) end end for a, frame in pairs(angleKeysToDeleteList) do angleChannel:DeleteKey(frame) end end for p=1, #frameList do for i=1, #frameList[p].boneID do local bone = skel:Bone(frameList[p].boneID[i]) local angleChannel = moho:ChannelAsAnimVal(bone.fAnimAngle) angleChannel:SetValue(frameList.frame[p], frameList[p].angle[i]) angleChannel:SetKeyInterp(frameList.frame[p], keyInterp) end end if isSoundtrackMuted then MOHO.MohoGlobals.MuteSoundtrack = false end MOHO.MohoGlobals.EnableBoneDynamics = isDynamicsOn moho:SetCurFrame(curFrame) self:UpdateWidgets(moho) moho:UpdateSelectedChannels() moho.view:DrawMe() moho:UpdateUI() moho.layer:UpdateCurFrame() end function MR_BakeBoneDynamics:SelectDynamicsBones(moho) local skel = moho:Skeleton() if (skel == nil) then return end skel:SelectNone() for i=0, skel:CountBones()-1 do local bone = skel:Bone(i) local dynamicsChannel = moho:ChannelAsAnimBool(bone.fBoneDynamics) if dynamicsChannel:CountKeys() > 1 or dynamicsChannel:GetValue(0) then bone.fSelected = true end end end -- ************************************************** -- Localization -- ************************************************** function MR_BakeBoneDynamics:Localize(text) local phrase = {} phrase['Description'] = 'This script allows you to bake the movement of the bones created by bone dynamics' phrase['UILabel'] = 'Bake Bone Dynamics' phrase['Select Dynamic Bones'] = 'Select dynamic bones' phrase['Pre Roll:'] = 'Preroll:' phrase['From:'] = 'From:' phrase['To:'] = 'To:' phrase['Set Project Range'] = 'Set project range' phrase['Set Playback Range'] = 'Set playback range' phrase['IntervalText'] = 'Interval:' phrase['Add Dynamics Keys'] = 'Add dynamics keys' phrase['Add Dynamics Keys Tooltip'] = 'Turn off dynamics for baked bones in selected range' phrase['Bake'] = 'Bake' return phrase[text] end
MR Bake Bone Dynamics
Listed
Author: eugenebabich
View Script
Script type: Tool
Uploaded: Jan 29 2023, 19:40
Script Version: 1.0
This script allows you to bake the movement of the bones created by bone dynamics.
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: 1376
MR Bake Bone Dynamics
Listed
Author: eugenebabich
View Script
Script type: Tool
Uploaded: Jan 29 2023, 19:40
Script Version: 1.0
This script allows you to bake the movement of the bones created by bone dynamics.
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: 1376