-- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** ScriptName = "MR_SmartBoneFixer" -- ************************************************** -- General information about this script -- ************************************************** MR_SmartBoneFixer = {} function MR_SmartBoneFixer:Name() return self:Localize('UILabel') end function MR_SmartBoneFixer:Version() return '1.0' end function MR_SmartBoneFixer:UILabel() return self:Localize('UILabel') end function MR_SmartBoneFixer:Creator() return 'Eugene Babich' end function MR_SmartBoneFixer:Description() return self:Localize('Description') end -- ************************************************** -- Is Relevant / Is Enabled -- ************************************************** function MR_SmartBoneFixer:IsRelevant(moho) return true end function MR_SmartBoneFixer:IsEnabled(moho) return true end -- ************************************************** -- Keyboard/Mouse Control -- ************************************************** function MR_SmartBoneFixer:OnMouseDown(moho, mouseEvent) end function MR_SmartBoneFixer:LoadPrefs(prefs) self.considerNewParentRotation = prefs:GetBool('MR_SmartBoneFixer.considerNewParentRotation', true) self.considerOldParentRotation = prefs:GetBool('MR_SmartBoneFixer.considerOldParentRotation', true) end function MR_SmartBoneFixer:SavePrefs(prefs) prefs:SetBool('MR_SmartBoneFixer.considerNewParentRotation', self.considerNewParentRotation) prefs:SetBool('MR_SmartBoneFixer.considerOldParentRotation', self.considerOldParentRotation) end function MR_SmartBoneFixer:ResetPrefs() MR_SmartBoneFixer.considerNewParentRotation = false MR_SmartBoneFixer.considerOldParentRotation = false end -- ************************************************** -- Recurring Values -- ************************************************** MR_SmartBoneFixer.considerNewParentRotation = false MR_SmartBoneFixer.considerOldParentRotation = false MR_SmartBoneFixer.boneList = {} MR_SmartBoneFixer.boneRefList = {} MR_SmartBoneFixer.boneOrigPosList = {} MR_SmartBoneFixer.boneOrigGlobalPosList = {} MR_SmartBoneFixer.boneAngleList = {} MR_SmartBoneFixer.boneAngleExtraList = {} MR_SmartBoneFixer.boneAngleOffsetList = {} MR_SmartBoneFixer.boneParentAngleList = {} MR_SmartBoneFixer.boneParentsPosList = {} MR_SmartBoneFixer.boneParentList = {} MR_SmartBoneFixer.boneParentMatrixList = {} MR_SmartBoneFixer.boneLayerId = nil MR_SmartBoneFixer.status = 'No bones transformation collected yet.' MR_SmartBoneFixer.fixedBonesList = {} MR_SmartBoneFixer.fixedActionsList = {} MR_SmartBoneFixer.totalBones = 0 MR_SmartBoneFixer.refKey = 1 -- ************************************************** -- settingsDialog -- ************************************************** local settingsDialog = {} settingsDialog.CONSIDER_NEW_PARENT_ROTATION = MOHO.MSG_BASE settingsDialog.CONSIDER_OLD_PARENT_ROTATION = MOHO.MSG_BASE + 1 function settingsDialog:new() local d = LM.GUI.SimpleDialog(MR_SmartBoneFixer:Localize('UILabel'), settingsDialog) local l = d:GetLayout() d.considerNewParentRotationCheckbox = LM.GUI.CheckBox(MR_SmartBoneFixer:Localize('Consider new parent rotation'), d.CONSIDER_NEW_PARENT_ROTATION) l:AddChild(d.considerNewParentRotationCheckbox, LM.GUI.ALIGN_LEFT, 0) d.considerOldParentRotationCheckbox = LM.GUI.CheckBox(MR_SmartBoneFixer:Localize('Consider old parent rotation'), d.CONSIDER_OLD_PARENT_ROTATION) l:AddChild(d.considerOldParentRotationCheckbox, LM.GUI.ALIGN_LEFT, 0) return d end function settingsDialog:UpdateWidgets(moho) self.considerNewParentRotationCheckbox:SetValue(MR_SmartBoneFixer.considerNewParentRotation) self.considerOldParentRotationCheckbox:SetValue(MR_SmartBoneFixer.considerOldParentRotation) end function settingsDialog:OnOK(moho) MR_SmartBoneFixer.considerNewParentRotation = self.considerNewParentRotationCheckbox:Value() MR_SmartBoneFixer.considerOldParentRotation = self.considerOldParentRotationCheckbox:Value() end function settingsDialog:HandleMessage(msg) if msg == self.CONSIDER_NEW_PARENT_ROTATION then self.considerNewParentRotation = self.considerNewParentRotationCheckbox:Value() elseif msg == self.CONSIDER_OLD_PARENT_ROTATION then self.considerOldParentRotation = self.considerOldParentRotationCheckbox:Value() end end -- ************************************************** -- Tool Panel Layout -- ************************************************** MR_SmartBoneFixer.GET_OLD_TRANSFORMATION = MOHO.MSG_BASE MR_SmartBoneFixer.FIX_ACTIONS = MOHO.MSG_BASE + 1 MR_SmartBoneFixer.CLEAR = MOHO.MSG_BASE + 2 function MR_SmartBoneFixer:DoLayout(moho, layout) self.dlog = settingsDialog:new() self.settingsPopup = LM.GUI.PopupDialog(self:Localize('Settings'), false, 0) self.settingsPopup:SetDialog(self.dlog) layout:AddChild(self.settingsPopup, LM.GUI.ALIGN_LEFT, 0) self.getOldTransformationBtn = LM.GUI.Button(self:Localize('Get original transformation'), self.GET_OLD_TRANSFORMATION) layout:AddChild(self.getOldTransformationBtn, LM.GUI.ALIGN_LEFT, 0) self.fixActionsBtn = LM.GUI.Button(self:Localize('Fix Actions'), self.FIX_ACTIONS) layout:AddChild(self.fixActionsBtn, LM.GUI.ALIGN_LEFT, 0) self.clearBtn = LM.GUI.Button(self:Localize('Clear'), self.CLEAR) layout:AddChild(self.clearBtn, LM.GUI.ALIGN_LEFT, 0) layout:AddPadding(10) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) layout:AddPadding(10) self.statusText = LM.GUI.DynamicText(self:Localize('Status:'), 500) layout:AddChild(self.statusText, LM.GUI.ALIGN_LEFT, 0) end function MR_SmartBoneFixer:HandleMessage(moho, view, msg) if msg == self.GET_OLD_TRANSFORMATION then self:GetBoneTransformation(moho) elseif msg == self.FIX_ACTIONS then self:ApplyBoneTransformation(moho) elseif msg == self.CLEAR then self:Cancel(moho) end end function MR_SmartBoneFixer:UpdateWidgets(moho) local isMarker = self:CheckMarker(moho) local curLayerId = moho.layer:UUID() if self.boneLayerId ~= curLayerId or not isMarker then self.fixActionsBtn:Enable(false) else self.fixActionsBtn:Enable(true) end if moho.frame ~= 0 then self.getOldTransformationBtn:Enable(false) else self.getOldTransformationBtn:Enable(true) end if isMarker then self.clearBtn:Enable(true) self.getOldTransformationBtn:Enable(false) else self.clearBtn:Enable(false) end if moho.layer:LayerType() ~= MOHO.LT_BONE or moho.document:CurrentDocAction() ~= "" then self.getOldTransformationBtn:Enable(false) self.fixActionsBtn:Enable(false) end self.statusText:SetValue('Status: '.. self.status) end function MR_SmartBoneFixer:Cancel(moho) moho.document:PrepUndo(moho.layer) moho.document:SetDirty() local markerChannels = moho.layer.fTimelineMarkers local framesToDel = {} local isNeedUpdate = false if markerChannels:Duration() > 0 then for i=1, markerChannels:CountKeys()-1 do local markerTime = markerChannels:GetKeyWhen(i) local markerText = markerChannels:GetValue(markerTime) if markerText == 'Do not delete or edit!' then self.refKey = markerTime table.insert(framesToDel, markerTime) isNeedUpdate = true end end for y in pairs(framesToDel) do markerChannels:DeleteKey(framesToDel[y]) self:ClearFrame(moho, framesToDel[y]) if framesToDel[y] > 1 then self:ClearFrame(moho, framesToDel[y]-1) end end self.status = 'Collected data cleared.' self:UpdateWidgets(moho) if isNeedUpdate then self:HardUpdate(moho) end end end function MR_SmartBoneFixer:CheckMarker(moho) local found = false local markerChannels = moho.layer.fTimelineMarkers if markerChannels:Duration() > 0 then for i=1, markerChannels:CountKeys()-1 do local markerTime = markerChannels:GetKeyWhen(i) local markerText = markerChannels:GetValue(markerTime) if markerText == 'Do not delete or edit!' then found = true break end end end return found end function MR_SmartBoneFixer:FindLastKey(moho) local lastKey = 0 local skel = moho:Skeleton() for i=0, skel:CountBones()-1 do local myBone = skel:Bone(i) local channelAngle = myBone.fAnimAngle local channelPos = myBone.fAnimPos local channelScale = myBone.fAnimScale if channelAngle:Duration() > lastKey then lastKey = channelAngle:Duration() end if channelPos:Duration() > lastKey then lastKey = channelPos:Duration() end if channelScale:Duration() > lastKey then lastKey = channelScale:Duration() end end return lastKey end function MR_SmartBoneFixer:RejoinDimensionsInSelectedBones(moho) local skel = moho:Skeleton() local isAllowRejoin = false local isSuccessful = true local isIgnoreMainLine = false for i=0, skel:CountBones()-1 do local myBone = skel:Bone(i) if myBone.fSelected then for act=0, myBone.fAnimPos:CountActions()-1 do local actName = myBone.fAnimPos:ActionName(act) local isSmartBone = moho.layer:IsSmartBoneAction(actName) if isSmartBone then local actionChannel = moho:ChannelAsAnimVec2(myBone.fAnimPos:Action(act)) if actionChannel and actionChannel.AreDimensionsSplit then if actionChannel:AreDimensionsSplit() then if not isAllowRejoin then local ans = LM.GUI.Alert(LM.GUI.ALERT_WARNING, "Channels with separated dimensions were found.", "You can Rejoin Dimensions to continue or cancel operation.", "", "Rejoin Dimensions", "Cancel","") if ans == 0 then isAllowRejoin = true elseif ans == 1 then isSuccessful = false return isSuccessful end end if isAllowRejoin then actionChannel:SplitDimensions(false) end end end end end end end return isSuccessful end function MR_SmartBoneFixer:GetBoneTransformation(moho) for k in pairs(self.boneList) do self.boneList[k] = nil self.boneOrigPosList[k] = nil self.boneOrigGlobalPosList[k] = nil self.boneAngleList[k] = nil self.boneAngleExtraList[k] = nil self.boneAngleOffsetList[k] = nil self.boneParentAngleList[k] = nil self.boneParentsPosList[k] = nil self.boneParentList[k] = nil self.boneParentMatrixList[k] = nil end for k in pairs(self.boneRefList) do self.boneRefList[k] = nil end local skel = moho:Skeleton() self.totalBones = 0 local bonesFound = moho:CountSelectedBones(true) if bonesFound > 0 then moho.document:PrepUndo(moho.layer) moho.document:SetDirty() local curLayerId = moho.layer:UUID() if self.boneLayerId ~= curLayerId then self.refKey = 1 end local markerChannels = moho.layer.fTimelineMarkers local framesToDel = {} local isNeedUpdate = false if markerChannels:Duration() > 0 then for i=1, markerChannels:CountKeys()-1 do local markerTime = markerChannels:GetKeyWhen(i) local markerText = markerChannels:GetValue(markerTime) if markerText == 'Do not delete or edit!' then self.refKey = markerTime table.insert(framesToDel, markerTime) isNeedUpdate = true end end for y in pairs(framesToDel) do markerChannels:DeleteKey(framesToDel[y]) self:ClearFrame(moho, framesToDel[y]) if framesToDel[y] > 1 then self:ClearFrame(moho, framesToDel[y]-1) end end if isNeedUpdate then self:HardUpdate(moho) end end local lastFrame = self:FindLastKey(moho) if lastFrame >= 1 then self.refKey = lastFrame + 2 else self.refKey = 1 end for i=0, skel:CountBones()-1 do table.insert(self.boneRefList,i) end if self.refKey > 1 then self:SetKey(moho, self.refKey -1, self.refKey -1) end self:SetKey(moho, self.refKey, 0) local keyInterp = MOHO.InterpSetting:new_local() keyInterp.tags = 1 moho.layer.fTimelineMarkers:SetValue(self.refKey,'Do not delete or edit!') moho.layer.fTimelineMarkers:SetKeyInterp(self.refKey, keyInterp) end local isSuccessful = self:RejoinDimensionsInSelectedBones(moho) if not isSuccessful then self.status = 'Canceled.' self:UpdateWidgets(moho) self:ClearFrame(moho, self.refKey) if self.refKey > 1 then self:ClearFrame(moho, self.refKey-1) end self:HardUpdate(moho) return end for i=0, skel:CountBones()-1 do local myBone = skel:Bone(i) self.totalBones = self.totalBones + 1 if myBone.fSelected then table.insert(self.boneList, i) local parentBone = skel:Bone(myBone.fParent) if myBone.fParent >= 0 then local parentMatrix = LM.Matrix:new_local() parentMatrix:Set(parentBone.fRestMatrix) table.insert(self.boneParentList, myBone.fParent) table.insert(self.boneParentMatrixList, parentMatrix) else local emtyMatrix = LM.Matrix:new_local() emtyMatrix:Identity() table.insert(self.boneParentMatrixList, emtyMatrix) table.insert(self.boneParentList, -1) end local bonePos = LM.Vector2:new_local() local parentBonePos = LM.Vector2:new_local() parentBonePos:Set(0,0) bonePos = myBone.fAnimPos:GetValue(0) local boneAngle = myBone.fAnimAngle:GetValue(0) local boneAngleExtra = myBone.fAnimAngle:GetValue(self.refKey) table.insert(self.boneOrigPosList, myBone.fAnimPos:GetValue(0)) local boneParentAngle = 0 if myBone.fParent >= 0 then boneParentAngle = parentBone.fAnimAngle:GetValue(0) parentBonePos:Set(parentBone.fAnimPos:GetValue(0)) if parentBone.fParent >= 0 then local parentForParent = skel:Bone(parentBone.fParent) parentForParent.fRestMatrix:Transform(parentBonePos) end parentBone.fRestMatrix:Transform(bonePos) end table.insert(self.boneParentsPosList, parentBonePos) table.insert(self.boneOrigGlobalPosList, bonePos) table.insert(self.boneAngleList, boneAngle) table.insert(self.boneAngleExtraList, boneAngleExtra) table.insert(self.boneParentAngleList, boneParentAngle) end end local layerName = moho.layer:Name() if bonesFound > 0 then self.boneLayerId = moho.layer:UUID() if bonesFound == 1 then self.status = 'Original transformation collected for 1 bone on layer ' .. layerName..'.' else self.status = 'Original transformation collected for ' .. bonesFound.. ' bones on layer ' .. layerName..'.' end else self.boneLayerId = nil self.status = 'No bones was selected. Please select bones first.' end moho.view:DrawMe() moho:UpdateUI() moho.layer:UpdateCurFrame() self:UpdateWidgets(moho) end function MR_SmartBoneFixer:SwapTwoArrayKeys(array, key1, key2) local keyTmp1 = array[key1] local keyTmp2 = array[key2] return keyTmp2, keyTmp1 end function MR_SmartBoneFixer:ApplyBoneTransformation(moho) self.status = 'Correction in progress. Please wait...' self:UpdateWidgets(moho) for q in pairs(self.fixedBonesList) do self.fixedBonesList[q] = nil end for m in pairs(self.fixedActionsList) do self.fixedActionsList[m] = nil end self.fixedBones = 0 self.fixedActions = 0 local curFrame = moho.frame local skel = moho:Skeleton() if (skel == nil) then return elseif (self.boneList[1] == nil) then return end for b in pairs(self.boneList) do local bone = skel:Bone(self.boneList[b]) if bone == nil then self.status = 'Correction was canceled.' self:UpdateWidgets(moho) moho.document:PrepUndo(moho.layer) moho.document:SetDirty() self:ClearFrame(moho, self.refKey) if self.refKey > 1 then self:ClearFrame(moho, self.refKey-1) end local markerChannels = moho.layer.fTimelineMarkers markerChannels:DeleteKey(self.refKey) self:HardUpdate(moho) return LM.GUI.Alert(LM.GUI.ALERT_WARNING, "Some bones are missing. The skeleton structure was changed.", "", "", "EXIT") end end local totalCurBones = 0 for i=0, skel:CountBones()-1 do totalCurBones = totalCurBones + 1 local myBone = skel:Bone(i) local channelAngle = myBone.fAnimAngle local channelPos = myBone.fAnimPos local channelScale = myBone.fAnimScale local isRefKeysOk = true local isPosChannelSplit = channelPos:AreDimensionsSplit() if isPosChannelSplit then local subChannel1 = channelPos:DimensionChannel(0) local subChannel2 = channelPos:DimensionChannel(1) if not subChannel1:HasKey(self.refKey) or not subChannel2:HasKey(self.refKey) then isRefKeysOk = false end elseif not channelPos:HasKey(self.refKey) then isRefKeysOk = false end if not channelAngle:HasKey(self.refKey) or not channelScale:HasKey(self.refKey) then isRefKeysOk = false end if not isRefKeysOk and self.boneRefList[i + 1] ~= nil then self.status = 'Correction was canceled.' self:UpdateWidgets(moho) moho.document:PrepUndo(moho.layer) moho.document:SetDirty() self:ClearFrame(moho, self.refKey) if self.refKey > 1 then self:ClearFrame(moho, self.refKey-1) end local markerChannels = moho.layer.fTimelineMarkers markerChannels:DeleteKey(self.refKey) self:HardUpdate(moho) return LM.GUI.Alert(LM.GUI.ALERT_WARNING, "Some reference keys are missing. Please do not remove or edit any reference keys.", "", "", "EXIT") end end if totalCurBones < self.totalBones then self.status = 'Correction was canceled.' self:UpdateWidgets(moho) moho.document:PrepUndo(moho.layer) moho.document:SetDirty() self:ClearFrame(moho, self.refKey) if self.refKey > 1 then self:ClearFrame(moho, self.refKey-1) end local markerChannels = moho.layer.fTimelineMarkers markerChannels:DeleteKey(self.refKey) self:HardUpdate(moho) return LM.GUI.Alert(LM.GUI.ALERT_WARNING, "The original number of bones is different.", "", "", "EXIT") end local isChanges = false for r in pairs(self.boneList) do local myBone = skel:Bone(self.boneList[r]) local extraBoneAngle = self.boneAngleExtraList[r] local extraBoneAngleTmp = myBone.fAnimAngle:GetValue(self.refKey) if myBone.fParent > -1 then local origParentMatrix = LM.Matrix:new_local() origParentMatrix:Set(self.boneParentMatrixList[r]) extraBoneAngle = self:GetGlobalBoneAngle(moho, origParentMatrix, extraBoneAngle, 0, false) extraBoneAngleTmp = self:GetGlobalBoneAngle(moho, origParentMatrix, extraBoneAngleTmp, self.refKey, false) end local reparentOffset = extraBoneAngle - extraBoneAngleTmp table.insert(self.boneAngleOffsetList, reparentOffset) end repeat isChanges = false for y in pairs(self.boneList) do local myBone = skel:Bone(self.boneList[y]) if myBone.fParent > -1 then local newParentId = myBone.fParent for t in pairs(self.boneList) do if newParentId == self.boneList[t] then if y < t then isChanges = true local key2 = self.boneList[t] self.boneList[y], self.boneList[t] = self:SwapTwoArrayKeys(self.boneList, y, t) self.boneOrigPosList[y], self.boneOrigPosList[t] = self:SwapTwoArrayKeys(self.boneOrigPosList, y, t) self.boneOrigGlobalPosList[y], self.boneOrigGlobalPosList[t] = self:SwapTwoArrayKeys(self.boneOrigGlobalPosList, y, t) self.boneAngleList[y], self.boneAngleList[t] = self:SwapTwoArrayKeys(self.boneAngleList, y, t) self.boneAngleExtraList[y], self.boneAngleExtraList[t] = self:SwapTwoArrayKeys(self.boneAngleExtraList, y, t) self.boneAngleOffsetList[y], self.boneAngleOffsetList[t] = self:SwapTwoArrayKeys(self.boneAngleOffsetList, y, t) self.boneParentAngleList[y], self.boneParentAngleList[t] = self:SwapTwoArrayKeys(self.boneParentAngleList, y, t) self.boneParentsPosList[y], self.boneParentsPosList[t] = self:SwapTwoArrayKeys(self.boneParentsPosList, y, t) self.boneParentList[y], self.boneParentList[t] = self:SwapTwoArrayKeys(self.boneParentList, y, t) self.boneParentMatrixList[y], self.boneParentMatrixList[t] = self:SwapTwoArrayKeys(self.boneParentMatrixList, y, t) break end else local nextBone = skel:Bone(newParentId) repeat local prevBone = nextBone for g in pairs(self.boneList) do if prevBone.fParent == self.boneList[g] then if y < g then isChanges = true local key2 = self.boneList[g] self.boneList[y], self.boneList[g] = self:SwapTwoArrayKeys(self.boneList, y, g) self.boneOrigPosList[y], self.boneOrigPosList[g] = self:SwapTwoArrayKeys(self.boneOrigPosList, y, g) self.boneOrigGlobalPosList[y], self.boneOrigGlobalPosList[g] = self:SwapTwoArrayKeys(self.boneOrigGlobalPosList, y, g) self.boneAngleList[y], self.boneAngleList[g] = self:SwapTwoArrayKeys(self.boneAngleList, y, g) self.boneAngleExtraList[y], self.boneAngleExtraList[g] = self:SwapTwoArrayKeys(self.boneAngleExtraList, y, g) self.boneAngleOffsetList[y], self.boneAngleOffsetList[g] = self:SwapTwoArrayKeys(self.boneAngleOffsetList, y, g) self.boneParentAngleList[y], self.boneParentAngleList[g] = self:SwapTwoArrayKeys(self.boneParentAngleList, y, g) self.boneParentsPosList[y], self.boneParentsPosList[g] = self:SwapTwoArrayKeys(self.boneParentsPosList, y, g) self.boneParentList[y], self.boneParentList[g] = self:SwapTwoArrayKeys(self.boneParentList, y, g) self.boneParentMatrixList[y], self.boneParentMatrixList[g] = self:SwapTwoArrayKeys(self.boneParentMatrixList, y, g) end end end if nextBone.fParent > -1 then nextBone = skel:Bone(nextBone.fParent) end until nextBone == prevBone end end end end until isChanges == false moho.document:PrepUndo(moho.layer) moho.document:SetDirty() for i in pairs(self.boneList) do local myBone = skel:Bone(self.boneList[i]) for act=0, moho.layer:CountActions()-1 do local actName = moho.layer:ActionName(act) local actionChannel = myBone.fAnimAngle:ActionByName(actName) local found = false local isSmartBone = moho.layer:IsSmartBoneAction(actName) if actionChannel and actionChannel:Duration()>0 and isSmartBone then self:ScanAction(moho, skel, myBone, actName, i) found = true end if found == false and isSmartBone then actionChannel = myBone.fAnimPos:ActionByName(actName) if actionChannel and actionChannel:Duration()>0 then self:ScanAction(moho, skel, myBone, actName, i) found = true end end if found == true then self.fixedActions = self.fixedActions + 1 end end end moho.layer:ActivateAction(nil) self:ClearFrame(moho, self.refKey) local markerChannels = moho.layer.fTimelineMarkers markerChannels:DeleteKey(self.refKey) if self.refKey > 1 then self:ClearFrame(moho, self.refKey-1) end local totalBones = #self.fixedBonesList local totalActions = #self.fixedActionsList local actionsStr = ' actions.' local bonesStr = ' bones were' if totalBones == 1 then bonesStr = ' bone was' end if totalActions == 1 then actionsStr = ' action.' end self.status = 'Correction completed! ' .. totalBones.. bonesStr..' fixed in '.. totalActions.. actionsStr if totalBones == 0 or totalActions == 0 then self.status = 'Could not find any actions that need to be fixed.' end self:UpdateWidgets(moho) moho:SetCurFrame(curFrame) self:HardUpdate(moho) end function MR_SmartBoneFixer:HardUpdate(moho) moho.document:PrepUndo(moho.layer) moho.document:SetDirty() moho:UpdateSelectedChannels() moho.document:Undo() moho.view:DrawMe() moho:UpdateUI() moho.layer:UpdateCurFrame() end function MR_SmartBoneFixer:ClearFrame(moho, frame) local skel = moho:Skeleton() for i=0, skel:CountBones()-1 do local myBone = skel:Bone(i) myBone.fAnimPos:DeleteKey(frame) myBone.fAnimAngle:DeleteKey(frame) myBone.fAnimScale:DeleteKey(frame) end end function MR_SmartBoneFixer:SetKey(moho, frame, valueFromFrame) local keyInterp = MOHO.InterpSetting:new_local() keyInterp.tags = 1 local skel = moho:Skeleton() for i=0, skel:CountBones()-1 do local myBone = skel:Bone(i) local channelPos = myBone.fAnimPos local pos = channelPos:GetValue(valueFromFrame) local angle = myBone.fAnimAngle:GetValue(valueFromFrame) local scale = myBone.fAnimScale:GetValue(valueFromFrame) local isPosChannelSplit = channelPos:AreDimensionsSplit() if isPosChannelSplit then local subChannel1 = channelPos:DimensionChannel(0) local subChannel2 = channelPos:DimensionChannel(1) if subChannel1 then subChannel1:SetValue(frame, pos.x) subChannel1:SetKeyInterp(frame, keyInterp) end if subChannel2 then subChannel2:SetValue(frame, pos.y) subChannel2:SetKeyInterp(frame, keyInterp) end else myBone.fAnimPos:SetValue(frame, pos) myBone.fAnimPos:SetKeyInterp(frame, keyInterp) end myBone.fAnimAngle:SetValue(frame, angle) myBone.fAnimAngle:SetKeyInterp(frame, keyInterp) if valueFromFrame == 0 then myBone.fAnimScale:SetValue(frame, 1) myBone.fAnimScale:SetKeyInterp(frame, keyInterp) else myBone.fAnimScale:SetValue(frame, scale) myBone.fAnimScale:SetKeyInterp(frame, keyInterp) end end end function MR_SmartBoneFixer:ScanAction(moho, skel, bone, actionName, boneNum) moho.layer:ActivateAction(actionName) local actionChannelPos = bone.fAnimPos:ActionByName(actionName) local actionChannelAngle = bone.fAnimAngle:ActionByName(actionName) for i=1, actionChannelPos:CountKeys()-1 do local keyTime = actionChannelPos:GetKeyWhen(i) if keyTime > 0 then self:ApplyPosition(moho, skel, bone, keyTime, boneNum, actionName) end end for i=1, actionChannelAngle:CountKeys()-1 do local keyTime = actionChannelAngle:GetKeyWhen(i) if keyTime > 0 then self:ApplyAngle(moho, skel, bone, keyTime, boneNum, actionName) end end end function MR_SmartBoneFixer:ApplyPosition(moho, skel, bone, frame, boneNum, actionName) moho:SetCurFrame(frame) local myBone = bone local parentBone = myBone.fParent local oldParentBone = self.boneParentList[boneNum] local origBonePos = LM.Vector2:new_local() origBonePos:Set(self.boneOrigPosList[boneNum]) local localOrigBonePos = LM.Vector2:new_local() localOrigBonePos:Set(self.boneOrigPosList[boneNum]) local origBoneGlobalPos = LM.Vector2:new_local() origBoneGlobalPos:Set(self.boneOrigGlobalPosList[boneNum]) local bonePosFrameZero = LM.Vector2:new_local() local oldMatrix = LM.Matrix:new_local() oldMatrix:Set(self.boneParentMatrixList[boneNum]) local parent = skel:Bone(parentBone) local parentOffset = LM.Vector2:new_local() local parentOffsetZero = LM.Vector2:new_local() parentOffset:Set(0,0) parentOffsetZero:Set(0,0) local oldParentBonePos = self.boneParentsPosList[boneNum] if parent ~= nil then parent.fMovedMatrix:Transform(parentOffset) parent.fRestMatrix:Transform(parentOffsetZero) end parentOffset = parentOffsetZero - parentOffset local curBonePos = LM.Vector2:new_local() curBonePos:Set(myBone.fAnimPos:GetValue(frame)) oldMatrix:Transform(origBonePos) bonePosFrameZero:Set(myBone.fAnimPos:GetValue(0)) local oldParent = skel:Bone(oldParentBone) if parent ~= nil then parent.fRestMatrix:Transform(bonePosFrameZero) end local bonePos = LM.Vector2:new_local() bonePos:Set(myBone.fAnimPos:GetValue(frame)) local bonePosDifFrameZero = LM.Vector2:new_local() bonePosDifFrameZero = origBoneGlobalPos - bonePosFrameZero local localBonePosFrameZero = LM.Vector2:new_local() localBonePosFrameZero:Set(myBone.fAnimPos:GetValue(0)) oldMatrix:Transform(bonePos) bonePos = bonePos - bonePosDifFrameZero - parentOffset if parent ~= nil then local inverseM = LM.Matrix:new_local() inverseM:Set(parent.fMovedMatrix) inverseM:Invert() inverseM:Transform(bonePos) end if localOrigBonePos.x ~= localBonePosFrameZero.x or localOrigBonePos.y ~= localBonePosFrameZero.y or oldParentBone ~= parentBone then myBone.fAnimPos:SetValue(frame, bonePos) self:CollectLog(myBone:Name(), actionName) end end function MR_SmartBoneFixer:CollectLog(boneName, actionName) local isBoneNew = true local isActionNew = true for z in pairs(self.fixedBonesList) do if self.fixedBonesList[z] == boneName then isBoneNew = false break end end if isBoneNew then table.insert(self.fixedBonesList, boneName) end if actionName ~= '' then for a in pairs(self.fixedActionsList) do if self.fixedActionsList[a] == actionName then isActionNew = false break end end if isActionNew then table.insert(self.fixedActionsList, actionName) end end end function MR_SmartBoneFixer:GetGlobalBoneAngle(moho, matrix, boneAngle, frame, round) local v1 = LM.Vector2:new_local() local v2 = LM.Vector2:new_local() v1:Set(0,0) v2:Set(1,0) local newAngle = 0 local invMatrix = LM.Matrix:new_local() invMatrix:Set(matrix) invMatrix:Invert() invMatrix:Transform(v1) invMatrix:Transform(v2) v2 = v2-v1 newAngle = math.atan2(v2.y, v2.x) if round == true then while newAngle > 2 * math.pi do newAngle = newAngle - 2 * math.pi end while newAngle < - 2 * math.pi do newAngle = newAngle + 2 * math.pi end end newAngle = boneAngle - newAngle return newAngle end function MR_SmartBoneFixer:ApplyAngle(moho, skel, bone, frame, boneNum, actionName) moho:SetCurFrame(frame) local myBone = bone local oldBoneAngle = self.boneAngleList[boneNum] local newBoneAngle = myBone.fAnimAngle:GetValue(frame) local newBoneAngleFrameZero = myBone.fAnimAngle:GetValue(0) local newParentBoneId = myBone.fParent local oldParentBoneId = self.boneParentList[boneNum] local boneAngleDifFrameZero = 0 local parentAngleOffset = 0 local oldParentAngleOffset = 0 if oldBoneAngle ~= newBoneAngleFrameZero and oldParentBoneId == newParentBoneId then local oldParentBone = skel:Bone(oldParentBoneId) boneAngleDifFrameZero = oldBoneAngle - newBoneAngleFrameZero newBoneAngle = (newBoneAngle - boneAngleDifFrameZero) + self.boneAngleOffsetList[boneNum] self:CollectLog(myBone:Name(), actionName) elseif oldBoneAngle ~= newBoneAngleFrameZero and oldParentBoneId ~= newParentBoneId and oldParentBoneId >= 0 then local oldParentBone = skel:Bone(oldParentBoneId) local oldMatrix = LM.Matrix:new_local() oldMatrix = oldParentBone.fMovedMatrix if newParentBoneId > -1 then local parentBone = skel:Bone(newParentBoneId) if self.considerNewParentRotation then local parentAngleZero = parentBone.fAnimAngle:GetValue(0) local parentAngle = parentBone.fAnimAngle:GetValue(frame) if parentBone.fParent > -1 then local gandPaBone = skel:Bone(parentBone.fParent) parentAngleZero = self:GetGlobalBoneAngle(moho, gandPaBone.fRestMatrix, parentAngleZero, 0, false) parentAngle = self:GetGlobalBoneAngle(moho, gandPaBone.fMovedMatrix, parentAngle, frame, false) end parentAngleOffset = parentAngleZero - parentAngle end end oldBoneAngle = oldBoneAngle - self.boneAngleOffsetList[boneNum] if oldParentBoneId > -1 then local oldParentBone = skel:Bone(oldParentBoneId) if self.considerOldParentRotation then local oldParentAngleZero = oldParentBone.fAnimAngle:GetValue(0) local oldParentAngle = oldParentBone.fAnimAngle:GetValue(frame) if oldParentBone.fParent > -1 then local oldGandPaBone = skel:Bone(oldParentBone.fParent) oldParentAngleZero = self:GetGlobalBoneAngle(moho, oldGandPaBone.fRestMatrix, oldParentAngleZero, 0, false) oldParentAngle = self:GetGlobalBoneAngle(moho, oldGandPaBone.fMovedMatrix, oldParentAngle, frame, false) end oldParentAngleOffset = oldParentAngleZero - oldParentAngle end end boneAngleDifFrameZero = oldBoneAngle - newBoneAngleFrameZero newBoneAngle = (newBoneAngle - boneAngleDifFrameZero) + parentAngleOffset - oldParentAngleOffset self:CollectLog(myBone:Name(), actionName) elseif newParentBoneId > -1 and oldParentBoneId < 0 then local parentBone = skel:Bone(newParentBoneId) local newMatrix = LM.Matrix:new_local() newMatrix = parentBone.fMovedMatrix if self.considerNewParentRotation then local parentAngleZero = parentBone.fAnimAngle:GetValue(0) local parentAngle = parentBone.fAnimAngle:GetValue(frame) if parentBone.fParent > -1 then local gandPaBone = skel:Bone(parentBone.fParent) parentAngleZero = self:GetGlobalBoneAngle(moho, gandPaBone.fRestMatrix, parentAngleZero, 0, false) parentAngle = self:GetGlobalBoneAngle(moho, gandPaBone.fMovedMatrix, parentAngle, frame, false) end parentAngleOffset = parentAngleZero - parentAngle end newBoneAngleFrameZero = newBoneAngleFrameZero + self.boneAngleOffsetList[boneNum] boneAngleDifFrameZero = oldBoneAngle - newBoneAngleFrameZero newBoneAngle = (newBoneAngle - boneAngleDifFrameZero) + parentAngleOffset self:CollectLog(myBone:Name(), actionName) end myBone.fAnimAngle:SetValue(frame,newBoneAngle) end -- ************************************************** -- Localization -- ************************************************** function MR_SmartBoneFixer:Localize(text) local phrase = {} phrase['Description'] = 'Fix smart bone actions after editing bones (For rigging only)' phrase['UILabel'] = 'Smart Bone Fixer' phrase['Get original transformation'] = 'Get original transformation' phrase['Fix Actions'] = 'Fix actions' phrase['Clear'] = 'Clear' phrase['Consider new parent rotation'] = 'Consider new parent rotation' phrase['Consider old parent rotation'] = 'Consider old parent rotation' phrase['Close'] = 'Close' phrase['Settings'] = 'Settings' phrase['Status:'] = 'Status: '.. self.status local fileWord = MOHO.Localize("/Menus/File/File=File") if fileWord == "Файл" then phrase['Description'] = 'Fix smart bone actions after editing bones (For rigging only)' phrase['UILabel'] = 'Smart Bone Fixer' phrase['Get original transformation'] = 'Get original transformation' phrase['Fix Actions'] = 'Fix actions' phrase['Clear'] = 'Clear' phrase['Consider new parent rotation'] = 'Consider new parent rotation' phrase['Consider old parent rotation'] = 'Consider old parent rotation' phrase['Close'] = 'Close' phrase['Settings'] = 'Settings' phrase['Status:'] = 'Status: ' .. self.status end return phrase[text] end
Smartbone Fixer
Listed
Author: eugenebabich
View Script
Script type: Tool
Uploaded: Dec 30 2020, 14:11
Last modified: Dec 30 2020, 14:13
Script Version: 1.0
This script allows you to correct actions after insignificant bone transformations.
1. Select bones that you want to transform, activate the Smart Bone Fixer tool and click the Get original transformation button.
2. Transform the bones. You can change the position, rotation, scale, and also change the bone's parent.
3. Activate the Smart Bone Fixer again and click the Fix actions button.
Currently the script only compatible with Moho 12 due to some undocumented breaking API changes in version 13.
This script is no longer supported, please download MR Transform Rig Tool instead.
https://mohoscripts.com/script/mr_transform_rig_tool MR Transform Rig Tool has the functionality of this tool and even more.
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: 1163
Smartbone Fixer
Listed
Author: eugenebabich
View Script
Script type: Tool
Uploaded: Dec 30 2020, 14:11
Last modified: Dec 30 2020, 14:13
Script Version: 1.0
This script allows you to correct actions after insignificant bone transformations.
1. Select bones that you want to transform, activate the Smart Bone Fixer tool and click the Get original transformation button.
2. Transform the bones. You can change the position, rotation, scale, and also change the bone's parent.
3. Activate the Smart Bone Fixer again and click the Fix actions button.
Currently the script only compatible with Moho 12 due to some undocumented breaking API changes in version 13.
This script is no longer supported, please download MR Transform Rig Tool instead.
https://mohoscripts.com/script/mr_transform_rig_tool MR Transform Rig Tool has the functionality of this tool and even more.
2. Transform the bones. You can change the position, rotation, scale, and also change the bone's parent.
3. Activate the Smart Bone Fixer again and click the Fix actions button.
Currently the script only compatible with Moho 12 due to some undocumented breaking API changes in version 13.
This script is no longer supported, please download MR Transform Rig Tool instead.
https://mohoscripts.com/script/mr_transform_rig_tool MR Transform Rig Tool has the functionality of this tool and even more.
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: 1163