-- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** ScriptName = "MR_PoseTool" -- ************************************************** -- General information about this script -- ************************************************** MR_PoseTool = {} function MR_PoseTool:Name() return self:Localize('UILabel') end function MR_PoseTool:Version() return '1.0.4' end function MR_PoseTool:UILabel() return self:Localize('UILabel') end function MR_PoseTool:Creator() return 'Eugene Babich' end function MR_PoseTool:Description() return self:Localize('Description') end -- ************************************************** -- Is Relevant / Is Enabled -- ************************************************** function MR_PoseTool:IsRelevant(moho) local skel = moho:Skeleton() if (skel == nil) then return false end local v1, v2, v3 = self:GetMohoVersion(moho) self.mohoVersion = v1 return true end function MR_PoseTool:IsEnabled(moho) return true end -- ************************************************** -- Recurring Values -- ************************************************** MR_PoseTool.keepSelection = false MR_PoseTool.bakeAdjacentFrames = false MR_PoseTool.firstBoneID = -1 MR_PoseTool.firstBonePos = LM.Vector2:new_local() MR_PoseTool.firstBoneScalePercent = 50 MR_PoseTool.clickOffset = LM.Vector2:new_local() MR_PoseTool.secondBoneID = -1 MR_PoseTool.isActive = true MR_PoseTool.firstBoneOffset = 0 MR_PoseTool.secondBoneOffset = 0 MR_PoseTool.interval = 1 MR_PoseTool.showPath = false MR_PoseTool.range = false MR_PoseTool.rangeFrames = 10 MR_PoseTool.secondBonePos = LM.Vector2:new_local() MR_PoseTool.secondBoneTipPos = LM.Vector2:new_local() MR_PoseTool.firstDist = 0 MR_PoseTool.secondBoneDist = 0 MR_PoseTool.firstBoneParentID = -1 MR_PoseTool.firstBoneParentIBIK = false MR_PoseTool.firstBoneSizeDelta = 0 MR_PoseTool.secondBoneSizeDelta = 0 MR_PoseTool.firstBoneAngleDelta = 0 MR_PoseTool.secondBoneAngleDelta = 0 MR_PoseTool.selectedBonesList = {} MR_PoseTool.mousePickedID = -1 MR_PoseTool.dragging = false MR_PoseTool.lastVec = LM.Vector2:new_local() MR_PoseTool.lastVec2 = LM.Vector2:new_local() MR_PoseTool.handlesDist = 0.075 MR_PoseTool.handlesDistY = 0.026 MR_PoseTool.handlesDistYMultiplier = 1 MR_PoseTool.additionmarkerDist = 0.05 MR_PoseTool.markerR = 6 MR_PoseTool.markerR2 = 6 MR_PoseTool.additionHandles = false MR_PoseTool.startBonePos = LM.Vector2:new_local() MR_PoseTool.startBoneAngleDelta = 0 MR_PoseTool.startBoneDist = 0 MR_PoseTool.TOLERANCE = 10 MR_PoseTool.transformPath = false MR_PoseTool.drawMode = -1 MR_PoseTool.keepHandles = false MR_PoseTool.height = 1080 -- ************************************************** -- Prefs -- ************************************************** function MR_PoseTool:LoadPrefs(prefs) self.keepSelection = prefs:GetBool("MR_PoseTool.keepSelection", false) self.bakeAdjacentFrames = prefs:GetBool("MR_PoseTool.bakeAdjacentFrames", false) self.interval = prefs:GetInt("MR_PoseTool.interval", 1) self.showPath = prefs:GetBool("MR_PoseTool.showPath", false) self.range = prefs:GetBool("MR_PoseTool.range", false) self.rangeFrames = prefs:GetInt("MR_PoseTool.rangeFrames", 10) end function MR_PoseTool:SavePrefs(prefs) prefs:SetBool("MR_PoseTool.keepSelection", self.keepSelection) prefs:SetBool("MR_PoseTool.bakeAdjacentFrames", self.bakeAdjacentFrames) prefs:SetInt("MR_PoseTool.interval", self.interval) prefs:SetBool("MR_PoseTool.showPath", self.showPath) prefs:SetBool("MR_PoseTool.range", self.range) prefs:SetInt("MR_PoseTool.rangeFrames", self.rangeFrames) end function MR_PoseTool:ResetPrefs() self.keepSelection = false self.bakeAdjacentFrames = false self.interval = 1 self.showPath = false self.range = false self.rangeFrames = 10 end function MR_PoseTool:NonDragMouseMove() return true -- Call MouseMoved() even if the mouse button is not down end -- ************************************************** -- Keyboard/Mouse Control -- ************************************************** function MR_PoseTool:OnMouseDown(moho, mouseEvent) local skel = moho:Skeleton() if skel == nil or moho.frame == 0 then return end if moho:CountSelectedBones() == 1 then selBoneID = skel:SelectedBoneID() end self.translationFrame = moho.frame if self.keepSelection then self.selectedBonesList = {} for i=0, skel:CountBones()-1 do local bone = skel:Bone(i) if bone.fSelected then table.insert(self.selectedBonesList, i) end end end self.trPathBone = nil self.keepHandles = false self.dragging = true self.lastVec:Set(mouseEvent.vec) local id = -1 if self.handlesDistYMultiplier >= 1 then id = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, moho.layer, true, 8 * self.handlesDistYMultiplier) else id = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, moho.layer, true, 8) end local firstBone local secondBone local firstBoneParent self.isActive = false self.transformPath = false if not (mouseEvent.ctrlKey) then self.mousePickedID = id else id = self.mousePickedID end self.secondBoneID = id self.mode = self:TestMousePoint(moho, mouseEvent, selBoneID) if id == -1 and self.mode ~= 5 then skel:SelectNone() return end moho.document:PrepUndo(moho.layer, true) moho.document:SetDirty() if mouseEvent.doubleClick and self.secondBoneID > -1 then local bone = skel:Bone(self.secondBoneID) if self.mode == 0 then if mouseEvent.altKey then bone.fTempScale = bone.fScale self.childBones = {} self.childBonesAngelP = {} self.childBonesAngelN = {} for i=0, skel:CountBones()-1 do local childBone = skel:Bone(i) if childBone.fParent == self.secondBoneID and not childBone.fFixedAngle and not childBone.fIgnoredByIK then local angle = childBone.fAnimAngle:GetValue(moho.layerFrame) childBone.fTempAngle = angle table.insert(self.childBones, i) if moho.layerFrame - self.interval > 0 then table.insert(self.childBonesAngelP, childBone.fAnimAngle:GetValue(moho.layerFrame - self.interval)) else table.insert(self.childBonesAngelP, 0) end if moho.layerFrame + self.interval > 0 then table.insert(self.childBonesAngelN, childBone.fAnimAngle:GetValue(moho.layerFrame + self.interval)) else table.insert(self.childBonesAngelN, 0) end end end local secondBoneSizeDelta = bone.fScale - bone.fAnimScale:GetValue(moho.layerFrame) local secondBoneAngleDelta = bone.fAngle - bone.fAnimAngle:GetValue(moho.layerFrame) local secondTipVec = LM.Vector2:new_local() secondTipVec:Set(bone.fLength, 0) bone.fMovedMatrix:Transform(secondTipVec) local secondVec = LM.Vector2:new_local() secondVec:Set(0, 0) bone.fMovedMatrix:Transform(secondVec) local boneDist = self:GetDistance(secondVec, secondTipVec) bone.fAnimPos:SetValue(moho.layerFrame, bone.fAnimPos:GetValue(0)) skel:UpdateBoneMatrix() moho.layer:UpdateCurFrame() secondVec:Set(0, 0) bone.fMovedMatrix:Transform(secondVec) local NewboneDist = self:GetDistance(secondVec, secondTipVec) local secondDelta = (NewboneDist / boneDist) local newScale = (bone.fTempScale * secondDelta) - secondBoneSizeDelta bone.fAnimScale:SetValue(moho.layerFrame, newScale) local isIgnoredByIK = bone.fIgnoredByIK bone.fIgnoredByIK = false moho.layer:UpdateCurFrame() skel:IKAngleSolver(self.secondBoneID, secondTipVec, 1, true, true) bone.fAnimAngle:SetValue(moho.layerFrame, bone.fAngle - secondBoneAngleDelta) bone.fAnimAngle.value = bone.fAngle local angleSign = 1 if (bone.fFlipH.value and not bone.fFlipV.value) or (not bone.fFlipH.value and bone.fFlipV.value) then angleSign = -1 end local angleDelta = bone.fAngle - bone.fTempAngle for i, q in ipairs(self.childBones) do local childBone = skel:Bone(q) childBone.fAnimAngle:SetValue(moho.layerFrame, childBone.fTempAngle - (angleDelta * angleSign)) end bone.fIgnoredByIK = isIgnoredByIK else bone.fAnimPos:SetValue(moho.layerFrame, bone.fAnimPos:GetValue(0)) end moho.layer:UpdateCurFrame() moho:NewKeyframe(CHANNEL_BONE_T) self:UpdateWidgets(moho) return elseif self.mode == 1 then bone.fAnimAngle:SetValue(moho.layerFrame, bone.fAnimAngle:GetValue(0)) moho.layer:UpdateCurFrame() moho:NewKeyframe(CHANNEL_BONE) self:UpdateWidgets(moho) return elseif self.mode == 2 then bone.fAnimScale:SetValue(moho.layerFrame, bone.fAnimScale:GetValue(0)) moho.layer:UpdateCurFrame() moho:NewKeyframe(CHANNEL_BONE_S) self:UpdateWidgets(moho) return end end skel:SelectNone() self.startAngle = 0 self.startAngle2 = 0 secondBone = skel:Bone(self.secondBoneID) self.jointChain = false self.firstBoneID = -1 if self.mode == 5 then local bone = skel:Bone(selBoneID) bone.fSelected = true self.selID = skel:BoneID(self.trPathBone) self.secondBoneID = selBoneID local boneVec = LM.Vector2:new_local() boneVec:Set(0, 0) bone.fRestMatrix:Transform(boneVec) boneVec = boneVec - mouseEvent.startVec local d = boneVec:Mag() self.boneEnd = 0 boneVec:Set(bone.fLength, 0) bone.fRestMatrix:Transform(boneVec) boneVec = boneVec - mouseEvent.startVec if (boneVec:Mag() < d) then self.boneEnd = 1 end if (self.translationFrame ~= 0 and bone.fSelected) then self.boneChanged = true bone.fTempPos = bone.fAnimPos:GetValue(self.translationFrame) bone.fAnimPos:SetValue(self.translationFrame, bone.fTempPos) end self.isActive = true return elseif self.mode == 4 then -- manipulate bones self.selID = id self.boneBakedA = false self.boneBakedP = false self.boneBakedS = false if self.bakeAdjacentFrames then self.boneSAngleP = secondBone.fAnimAngle:GetValue(moho.layerFrame - self.interval) self.boneSAngleN = secondBone.fAnimAngle:GetValue(moho.layerFrame + self.interval) self.boneSScaleP = secondBone.fAnimScale:GetValue(moho.layerFrame - self.interval) self.boneSScaleN = secondBone.fAnimScale:GetValue(moho.layerFrame + self.interval) end local bone = skel:Bone(id) bone.fTempPos:Set(bone.fAnimPos:GetValue(moho.layerFrame)) bone.fTempAngle = bone.fAnimAngle:GetValue(moho.layerFrame) bone.fTempScale = bone.fAnimScale:GetValue(moho.layerFrame) self.secondTarget = skel:TargetOfBoneChain(id, moho.layerFrame) if self.secondTarget >= 0 then self.isChainTargeted = true local targetBone = skel:Bone(self.secondTarget) targetBone.fTempPos = targetBone.fAnimPos:GetValue(moho.layerFrame) self.targetBonePosP = targetBone.fAnimPos:GetValue(moho.layerFrame - self.interval) self.targetBonePosN = targetBone.fAnimPos:GetValue(moho.layerFrame + self.interval) self.isTargetBonePosPBaked = false self.isTargetBonePosNBaked = false else self.isChainTargeted = false end bone.fSelected = true local isSmartBone = moho.layer:IsSmartBoneAction(bone:Name()) self.isParentBonesBaked = false self.lastVec:Set(mouseEvent.vec) self.parentBones = {} self.parentBonesAngleP = {} self.parentBonesAngleN = {} self.parentBonesScaleP = {} self.parentBonesScaleN = {} self.startAngle = 0 self.boneStartAngles = {} self.boneStartActualAngles = {} table.insert(self.boneStartAngles, bone.fAnimAngle:GetValue(moho.layerFrame)) table.insert(self.boneStartActualAngles, bone.fAnimAngle.value) while bone.fParent >= 0 do local parentBone = skel:Bone(bone.fParent) parentBone.fTempAngle = parentBone.fAnimAngle:GetValue(moho.layerFrame) parentBone.fTempScale = parentBone.fAnimScale:GetValue(moho.layerFrame) if (self:CountBoneChildren(skel, bone.fParent, true) > 1) or parentBone.fIgnoredByIK or parentBone.fFixedAngle then break end local parenBoneID = bone.fParent bone = skel:Bone(parenBoneID) bone.fTempScale = bone.fAnimScale:GetValue(moho.layerFrame) table.insert(self.parentBones, parenBoneID) if moho.layerFrame - self.interval > 0 then table.insert(self.parentBonesAngleP, bone.fAnimAngle:GetValue(moho.layerFrame - self.interval)) table.insert(self.parentBonesScaleP, bone.fAnimScale:GetValue(moho.layerFrame - self.interval)) else table.insert(self.parentBonesAngleP, 0) table.insert(self.parentBonesScaleP, 0) end if moho.layerFrame + self.interval > 0 then table.insert(self.parentBonesAngleN, bone.fAnimAngle:GetValue(moho.layerFrame + self.interval)) table.insert(self.parentBonesScaleN, bone.fAnimScale:GetValue(moho.layerFrame + self.interval)) else table.insert(self.parentBonesAngleN, 0) table.insert(self.parentBonesScaleN, 0) end table.insert(self.boneStartAngles, bone.fAnimAngle:GetValue(moho.layerFrame)) table.insert(self.boneStartActualAngles, bone.fAnimAngle.value) end local startBoneID = self.parentBones[#self.parentBones] local startBone = skel:Bone(startBoneID) startBone.fTempAngle = startBone.fAnimAngle:GetValue(moho.layerFrame) local startBonePos = LM.Vector2:new_local() startBonePos:Set(0, 0) startBone.fMovedMatrix:Transform(startBonePos) self.startBonePos:Set(startBonePos) self.startBoneDist = self:GetDistance(startBonePos, mouseEvent.vec) self.isChildBonesBaked = false self.childBones = {} self.childBonesAngelP = {} self.childBonesAngelN = {} for i=0, skel:CountBones()-1 do local childBone = skel:Bone(i) if childBone.fParent == id and not childBone.fFixedAngle and not childBone.fIgnoredByIK then local angle = childBone.fAnimAngle:GetValue(moho.layerFrame) childBone.fTempAngle = angle table.insert(self.childBones, i) if moho.layerFrame - self.interval > 0 then table.insert(self.childBonesAngelP, childBone.fAnimAngle:GetValue(moho.layerFrame - self.interval)) else table.insert(self.childBonesAngelP, 0) end if moho.layerFrame + self.interval > 0 then table.insert(self.childBonesAngelN, childBone.fAnimAngle:GetValue(moho.layerFrame + self.interval)) else table.insert(self.childBonesAngelN, 0) end end end if self.mode == 4 then self.isActive = true moho:UpdateBonePointSelection() mouseEvent.view:DrawMe() moho:UpdateSelectedChannels() self.lastVec:Set(mouseEvent.vec) return end end if secondBone.fParent > -1 and not secondBone:IsZeroLength() then self.firstBoneID = secondBone.fParent firstBone = skel:Bone(self.firstBoneID) local firstBoneChilds = skel:CountBoneChildren(self.firstBoneID, true) local secondBonePos = LM.Vector2:new_local() secondBonePos:Set(secondBone.fAnimPos:GetValue(moho.layerFrame)) if firstBone.fIgnoredByIK == false and not firstBone.fFixedAngle and not secondBone.fFixedAngle and secondBone.fIgnoredByIK == false and firstBoneChilds == 1 and self:Round(secondBone.fPos.y) == 0 and not self:IsEqual(secondBonePos.x, 0, 0.0001) and not firstBone:IsZeroLength() then self.jointChain = true end end self.secondBoneID = id secondBone.fSelected = true local useFirstBone = self.jointChain and (self.mode == 0 or self.mode == 3) if self.bakeAdjacentFrames then self.boneBakedA = false self.boneBakedP = false self.boneBakedS = false if useFirstBone then self.boneFAngleP = firstBone.fAnimAngle:GetValue(moho.layerFrame - self.interval) self.boneFAngleN = firstBone.fAnimAngle:GetValue(moho.layerFrame + self.interval) self.boneFScaleP = firstBone.fAnimScale:GetValue(moho.layerFrame - self.interval) self.boneFScaleN = firstBone.fAnimScale:GetValue(moho.layerFrame + self.interval) end self.boneSAngleP = secondBone.fAnimAngle:GetValue(moho.layerFrame - self.interval) self.boneSAngleN = secondBone.fAnimAngle:GetValue(moho.layerFrame + self.interval) self.boneSPosP = secondBone.fAnimPos:GetValue(moho.layerFrame - self.interval) self.boneSPosN = secondBone.fAnimPos:GetValue(moho.layerFrame + self.interval) self.boneSScaleP = secondBone.fAnimScale:GetValue(moho.layerFrame - self.interval) self.boneSScaleN = secondBone.fAnimScale:GetValue(moho.layerFrame + self.interval) end moho.layer:UpdateCurFrame() self.secondTarget = skel:TargetOfBoneChain(self.secondBoneID, moho.layerFrame) if self.secondTarget >= 0 then self.isChainTargeted = true local targetBone = skel:Bone(self.secondTarget) targetBone.fTempPos = targetBone.fAnimPos:GetValue(moho.layerFrame) self.targetBonePosP = targetBone.fAnimPos:GetValue(moho.layerFrame - self.interval) self.targetBonePosN = targetBone.fAnimPos:GetValue(moho.layerFrame + self.interval) self.isTargetBonePosPBaked = false self.isTargetBonePosNBaked = false else self.isChainTargeted = false end local firstBoneAngleDelta = 0 local secondBoneAngleDelta = 0 if self.isChainTargeted then local firstBoneStretchDelta local firstBoneIKStratching if useFirstBone then firstBoneStretchDelta = 0 firstBoneIKStratching = firstBone.fMaxAutoScaling firstBone.fMaxAutoScaling = 10000 end local secondBoneStretchDelta = 0 local secondBoneIKStratching = secondBone.fMaxAutoScaling secondBone.fMaxAutoScaling = 10000 moho.layer:UpdateCurFrame() local firstBoneFSize local firstBoneFAngle local isFirstBoneIgnoredByIK if useFirstBone then firstBoneFSize = firstBone.fScale firstBoneFAngle = firstBone.fAngle isFirstBoneIgnoredByIK = firstBone.fIgnoredByIK end local secondBoneFSize = secondBone.fScale local secondBoneFAngle = secondBone.fAngle local isSecondBoneIgnoredByIK = secondBone.fIgnoredByIK if useFirstBone then firstBone.fIgnoredByIK = true end secondBone.fIgnoredByIK = true moho.layer:UpdateCurFrame() if useFirstBone then firstBoneStretchDelta = firstBoneFSize - firstBone.fScale firstBoneAngleDelta = firstBoneFAngle - firstBone.fAngle end secondBoneStretchDelta = secondBoneFSize - secondBone.fScale secondBoneAngleDelta = secondBoneFAngle - secondBone.fAngle if useFirstBone then firstBone.fIgnoredByIK = isFirstBoneIgnoredByIK firstBone.fMaxAutoScaling = firstBoneIKStratching end secondBone.fIgnoredByIK = isSecondBoneIgnoredByIK secondBone.fMaxAutoScaling = secondBoneIKStratching if useFirstBone then firstBone.fAnimScale:SetValue(moho.layerFrame, firstBone.fAnimScale:GetValue(moho.layerFrame) + firstBoneStretchDelta) firstBone.fAnimAngle:SetValue(moho.layerFrame, firstBone.fAnimAngle:GetValue(moho.layerFrame) + firstBoneAngleDelta) end secondBone.fAnimScale:SetValue(moho.layerFrame, secondBone.fAnimScale:GetValue(moho.layerFrame) + secondBoneStretchDelta) secondBone.fAnimAngle:SetValue(moho.layerFrame, secondBone.fAnimAngle:GetValue(moho.layerFrame) + secondBoneAngleDelta) moho.layer:UpdateCurFrame() end self.isTargetMoved = false local secondTipVec = LM.Vector2:new_local() local firstVec = LM.Vector2:new_local() local secondVec = LM.Vector2:new_local() secondTipVec:Set(secondBone.fLength, 0) firstVec:Set(0, 0) secondVec:Set(0, 0) secondBone.fMovedMatrix:Transform(secondTipVec) secondBone.fMovedMatrix:Transform(secondVec) self.secondBonePos:Set(secondVec) if useFirstBone then firstBone.fMovedMatrix:Transform(firstVec) self.firstBonePos:Set(firstVec) self.firstDist = self:GetDistance(self.firstBonePos, self.secondBonePos) end self.secondBoneTipPos:Set(secondTipVec) self.secondBoneDist = self:GetDistance(self.secondBonePos, self.secondBoneTipPos) local mousePos = mouseEvent.vec self.clickOffset = self.secondBonePos - mousePos if self.mode == 2 then self.clickOffset = self.secondBoneTipPos - mousePos end if useFirstBone then self.firstBoneSizeDelta = firstBone.fScale - firstBone.fAnimScale:GetValue(moho.layerFrame) end self.secondBoneSizeDelta = secondBone.fScale - secondBone.fAnimScale:GetValue(moho.layerFrame) self.isChildBonesBaked = false self.childBones = {} self.childBonesAngelP = {} self.childBonesAngelN = {} for i=0, skel:CountBones()-1 do local bone = skel:Bone(i) if bone.fParent == self.secondBoneID and not bone.fFixedAngle and not bone.fIgnoredByIK then local angle = bone.fAnimAngle:GetValue(moho.layerFrame) bone.fTempAngle = angle table.insert(self.childBones, i) if moho.layerFrame - self.interval > 0 then table.insert(self.childBonesAngelP, bone.fAnimAngle:GetValue(moho.layerFrame - self.interval)) else table.insert(self.childBonesAngelP, 0) end if moho.layerFrame + self.interval > 0 then table.insert(self.childBonesAngelN, bone.fAnimAngle:GetValue(moho.layerFrame + self.interval)) else table.insert(self.childBonesAngelN, 0) end end end local firstBoneActualAngle if useFirstBone then firstBoneActualAngle = firstBone.fAngle self.firstBoneAngleDelta = firstBoneActualAngle - firstBone.fAnimAngle:GetValue(moho.layerFrame) end local secondBoneActualAngle = secondBone.fAngle self.secondBoneAngleDelta = secondBoneActualAngle - secondBone.fAnimAngle:GetValue(moho.layerFrame) self.lastVec2:Set(0, 0) skel:UpdateBoneMatrix() secondBone.fMovedMatrix:Transform(self.lastVec2) if self.mode > 1 then if useFirstBone then firstBone.fAnimAngle:SetValue(moho.layerFrame, firstBone.fAnimAngle:GetValue(moho.layerFrame)) firstBone.fAnimAngle.value = firstBoneActualAngle end secondBone.fAnimAngle:SetValue(moho.layerFrame, secondBone.fAnimAngle:GetValue(moho.layerFrame)) secondBone.fAnimAngle.value = secondBoneActualAngle end if useFirstBone then firstBone.fTempScale = firstBone.fScale firstBone.fTempAngle = firstBone.fAngle end secondBone.fTempScale = secondBone.fScale secondBone.fTempAngle = secondBone.fAngle if self.mode == 0 then secondBone.fTempPos:Set(secondBone.fAnimPos:GetValue(moho.layerFrame)) end self.isActive = true if useFirstBone then self.firstBoneParentID = firstBone.fParent if self.firstBoneParentID > -1 then firstBoneParent = skel:Bone(self.firstBoneParentID) self.firstBoneParentIBIK = firstBoneParent.fIgnoredByIK firstBoneParent.fIgnoredByIK = true end end local boneFirstLenght local boneFirstScale if useFirstBone then boneFirstLenght = secondBone.fPos.x boneFirstScale = firstBone.fScale end local boneSecondLenght = secondBone.fLength local boneSecondScale = secondBone.fScale if useFirstBone then self.firstBoneScalePercent = ((boneFirstLenght * boneFirstScale) / ((boneFirstLenght * boneFirstScale) + (boneSecondLenght * boneSecondScale))) * 100 self.firstBoneOffset = firstBone.fScale - firstBone.fAnimScale:GetValue(moho.layerFrame) end self.secondBoneOffset = secondBone.fScale - secondBone.fAnimScale:GetValue(moho.layerFrame) moho.layer:UpdateCurFrame() moho:UpdateBonePointSelection() mouseEvent.view:DrawMe() moho:UpdateSelectedChannels() end function MR_PoseTool:OnMouseMoved(moho, mouseEvent) local skel = moho:Skeleton() if skel == nil or moho.frame == 0 then mouseEvent.view:SetCursor(MOHO.disabledCursor) return end local boneID local bone if moho:CountSelectedBones() == 1 then boneID = skel:SelectedBoneID() bone = skel:Bone(boneID) end if (not self.dragging) then if not (mouseEvent.ctrlKey) then if self.handlesDistYMultiplier >= 1 then self.mousePickedID = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, moho.layer, true, 8 * self.handlesDistYMultiplier) else self.mousePickedID = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, moho.layer, true, 8) end self.additionHandles = self:CheckBone(moho, self.mousePickedID) end if self.mousePickedID > -1 or bone ~= nil then local mode = self:TestMousePoint(moho, mouseEvent, boneID) self.drawMode = mode if not (self.mousePickedID < 0 and mode ~= 5) then if mode == 0 or mode == 5 then mouseEvent.view:SetCursor(MOHO.moveCursor) elseif (mode == 1) then mouseEvent.view:SetCursor(MOHO.rotateCursor) elseif (mode == 2) then mouseEvent.view:SetCursor(MOHO.scaleCursor) elseif (mode == 3) then mouseEvent.view:SetCursor(self.mjCursor) elseif (mode == 4) then mouseEvent.view:SetCursor(self.mbCursor) end else mouseEvent.view:SetCursor(self.mainCursor) end else mouseEvent.view:SetCursor(self.mainCursor) end mouseEvent.view:DrawMe() return end if self.isActive == false then return end if self.mode == 0 or self.mode == 5 then -- move self:OnMouseMovedT(moho, mouseEvent) elseif self.mode == 1 then -- rotate self:OnMouseMovedR(moho, mouseEvent) elseif self.mode == 2 then -- scale rotate self:OnMouseMovedS(moho, mouseEvent) elseif self.mode == 3 then -- move joint if self.jointChain then self:OnMouseMovedJ(moho, mouseEvent) end elseif self.mode == 4 then -- manipulate bones self:OnMouseMovedM(moho, mouseEvent) end end function MR_PoseTool:OnMouseMovedJ(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end local firstBone = skel:Bone(self.firstBoneID) local secondBone = skel:Bone(self.secondBoneID) if self.bakeAdjacentFrames and not self.boneBakedA then self:BakeFrames(moho, secondBone, firstBone, true, false, true) self.boneBakedA = true end local vectorMousePos = (mouseEvent.vec + self.clickOffset) local firstCursorDist = self:GetDistance(vectorMousePos, self.firstBonePos) local firstDelta = (firstCursorDist / self.firstDist) firstBone.fAnimScale:SetValue(moho.layerFrame, (firstBone.fTempScale * firstDelta) - self.firstBoneSizeDelta) if self.isChainTargeted then local secondCursorDist = self:GetDistance(vectorMousePos, self.secondBoneTipPos) local secondDelta = (secondCursorDist / self.secondBoneDist) secondBone.fAnimScale:SetValue(moho.layerFrame, (secondBone.fTempScale * secondDelta) - self.secondBoneSizeDelta) if mouseEvent.shiftKey then local totalDist = firstCursorDist + secondCursorDist local distP = (totalDist / 100) * self.firstBoneScalePercent distP = distP / firstBone.fLength local secondDistP = (totalDist / 100) * (100 - self.firstBoneScalePercent) secondDistP = secondDistP / secondBone.fLength firstBone.fAnimScale:SetValue(moho.layerFrame, distP - self.firstBoneOffset) secondBone.fAnimScale:SetValue(moho.layerFrame, secondDistP - self.secondBoneOffset) end skel:IKAngleSolver(self.firstBoneID, vectorMousePos, 1, true, false) local firstBoneNewAngle = firstBone.fAngle - self.firstBoneAngleDelta firstBone.fAnimAngle:SetValue(moho.layerFrame, firstBoneNewAngle) firstBone.fAnimAngle.value = firstBone.fAngle if not secondBone.fFixedAngle then firstBone.fIgnoredByIK = true skel:IKAngleSolver(self.secondBoneID, self.secondBoneTipPos, 1, true, true) secondBone.fAnimAngle:SetValue(moho.layerFrame, secondBone.fAngle - self.secondBoneAngleDelta) secondBone.fAnimAngle.value = secondBone.fAngle firstBone.fIgnoredByIK = false moho.layer:UpdateCurFrame() skel:IKAngleSolver(self.secondBoneID, self.secondBoneTipPos, 1, true, true) local secondBoneNewAngle = secondBone.fAngle - self.secondBoneAngleDelta secondBone.fAnimAngle:SetValue(moho.layerFrame, secondBoneNewAngle) secondBone.fAnimAngle.value = secondBone.fAngle firstBone.fAnimAngle:SetValue(moho.layerFrame, firstBone.fAngle - self.firstBoneAngleDelta) firstBone.fAnimAngle.value = firstBone.fAngle firstBone.fIgnoredByIK = currentIgnoredByIK end else if mouseEvent.shiftKey then local firstCursorDist = self:GetDistance(vectorMousePos, self.firstBonePos) local secondCursorDist = self:GetDistance(vectorMousePos, self.secondBoneTipPos) local firstDelta = (firstCursorDist / self.firstDist) local secondDelta = (secondCursorDist / self.secondBoneDist) firstBone.fAnimScale:SetValue(moho.layerFrame, (firstBone.fTempScale * firstDelta) - self.firstBoneSizeDelta) secondBone.fAnimScale:SetValue(moho.layerFrame, (secondBone.fTempScale * secondDelta) - self.secondBoneSizeDelta) local totalDist = firstCursorDist + secondCursorDist local distP = (totalDist / 100) * self.firstBoneScalePercent distP = distP / firstBone.fLength local secondDistP = (totalDist / 100) * (100 - self.firstBoneScalePercent) secondDistP = secondDistP / secondBone.fLength firstBone.fAnimScale:SetValue(moho.layerFrame, distP - self.firstBoneOffset) secondBone.fAnimScale:SetValue(moho.layerFrame, secondDistP - self.secondBoneOffset) skel:IKAngleSolver(self.firstBoneID, vectorMousePos, 1, true, false) local originalAngle = firstBone.fTempAngle local correctedAngle = firstBone.fAngle local delta = originalAngle - correctedAngle while math.abs(delta) > math.rad(180) do if delta > math.rad(180) then correctedAngle = correctedAngle + math.rad(360) elseif delta < math.rad(-180) then correctedAngle = correctedAngle - math.rad(360) end delta = originalAngle - correctedAngle end local restoredAngle = correctedAngle - self.firstBoneAngleDelta firstBone.fAnimAngle:SetValue(moho.layerFrame, restoredAngle) firstBone.fAnimAngle.value = firstBone.fAngle secondBone.fAnimScale.value = secondBone.fScale local currentIgnoredByIK = firstBone.fIgnoredByIK if not secondBone.fFixedAngle then firstBone.fIgnoredByIK = true skel:IKAngleSolver(self.secondBoneID, self.secondBoneTipPos, 10, true, true) secondBone.fAnimAngle:SetValue(moho.layerFrame, secondBone.fAngle - self.secondBoneAngleDelta) secondBone.fAnimAngle.value = secondBone.fAngle firstBone.fIgnoredByIK = false moho.layer:UpdateCurFrame() skel:IKAngleSolver(self.secondBoneID, self.secondBoneTipPos, 10, true, true) originalAngle = secondBone.fTempAngle correctedAngle = secondBone.fAngle delta = originalAngle - correctedAngle while math.abs(delta) > math.rad(180) do if delta > math.rad(180) then correctedAngle = correctedAngle + math.rad(360) elseif delta < math.rad(-180) then correctedAngle = correctedAngle - math.rad(360) end delta = originalAngle - correctedAngle end restoredAngle = correctedAngle - self.secondBoneAngleDelta secondBone.fAnimAngle:SetValue(moho.layerFrame, restoredAngle) secondBone.fAnimAngle.value = secondBone.fAngle originalAngle = firstBone.fTempAngle correctedAngle = firstBone.fAngle delta = originalAngle - correctedAngle while math.abs(delta) > math.rad(180) do if delta > math.rad(180) then correctedAngle = correctedAngle + math.rad(360) elseif delta < math.rad(-180) then correctedAngle = correctedAngle - math.rad(360) end delta = originalAngle - correctedAngle end restoredAngle = correctedAngle - self.firstBoneAngleDelta firstBone.fAnimAngle:SetValue(moho.layerFrame, restoredAngle) firstBone.fAnimAngle.value = firstBone.fAngle firstBone.fIgnoredByIK = currentIgnoredByIK end else local bone = firstBone local center = LM.Vector2:new_local() center:Set(0, 0) skel:UpdateBoneMatrix(self.firstBoneID) bone.fMovedMatrix:Transform(center) local mousePos = mouseEvent.vec local angle = self.startAngle local v1 = self.lastVec - center local v2 = mousePos - center v2:Rotate(-math.atan2(v1.y, v1.x)) angle = angle + math.atan2(v2.y, v2.x) self.startAngle = angle self.lastVec:Set(mousePos) local angleSign = 1.0 if (not bone.fFixedAngle) then angleSign = bone:ParentalFlipFactor() end local newAgle = (bone.fTempAngle + (angle * angleSign)) - self.firstBoneAngleDelta if not bone.fFixedAngle then if bone.fConstraints then local min = bone.fAnimAngle:GetValue(0) - self.firstBoneAngleDelta local max = min + bone.fMaxConstraint min = min + bone.fMinConstraint newAgle = LM.Clamp(newAgle, min, max) end end if not self:IsEqual(newAgle, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, newAgle) bone.fAnimAngle.value = bone.fAngle if self.bakeAdjacentFrames and not self.boneBakedA then self:BakeFrames(moho, firstBone, nil, true, false, false) self.boneBakedA = true end end moho.layer:UpdateCurFrame() local bone = secondBone local center = LM.Vector2:new_local() center:Set(0, 0) skel:UpdateBoneMatrix(self.secondBoneID) bone.fMovedMatrix:Transform(center) local angle2 = self.startAngle2 local v1 = self.lastVec2 - self.secondBoneTipPos local v2 = center - self.secondBoneTipPos v2:Rotate(-math.atan2(v1.y, v1.x)) angle2 = angle2 + math.atan2(v2.y, v2.x) self.startAngle2 = angle2 self.lastVec2:Set(center) local angleSign2 = 1.0 if (not bone.fFixedAngle) then angleSign2 = bone:ParentalFlipFactor() end local newAgle2 = (bone.fTempAngle + (angle2 * angleSign2)) - (angle * angleSign2) - self.secondBoneAngleDelta if not bone.fFixedAngle then if bone.fConstraints then local min = bone.fAnimAngle:GetValue(0) - self.secondBoneAngleDelta local max = min + bone.fMaxConstraint min = min + bone.fMinConstraint newAgle2 = LM.Clamp(newAgle2, min, max) end end if not self:IsEqual(newAgle2, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, newAgle2) bone.fAnimAngle.value = bone.fAngle if self.bakeAdjacentFrames and not self.boneBakedA then self:BakeFrames(moho, secondBone, nil, true, false, false) self.boneBakedA = true end end moho.layer:UpdateCurFrame() local center = LM.Vector2:new_local() center:Set(0, 0) skel:UpdateBoneMatrix() secondBone.fMovedMatrix:Transform(center) local secondCursorDist = self:GetDistance(center, self.secondBoneTipPos) local secondDelta = (secondCursorDist / self.secondBoneDist) secondBone.fAnimScale:SetValue(moho.layerFrame, (secondBone.fTempScale * secondDelta) - self.secondBoneSizeDelta) end local angleSign = 1 if (secondBone.fFlipH.value and not secondBone.fFlipV.value) or (not secondBone.fFlipH.value and secondBone.fFlipV.value) then angleSign = -1 end end if self.isChainTargeted and self.isTargetMoved then local targetBone = skel:Bone(self.secondTarget) targetBone.fAnimPos:SetValue(moho.layerFrame, targetBone.fTempPos) end secondBone.fAnimScale.value = secondBone.fScale if mouseEvent.altKey then local angleSign = 1 local angleSignS = 1 if (secondBone.fFlipH.value and not secondBone.fFlipV.value) or (not secondBone.fFlipH.value and secondBone.fFlipV.value) then angleSignS = -1 end local angleSignF = 1 if (firstBone.fFlipH.value and not firstBone.fFlipV.value) or (not firstBone.fFlipH.value and firstBone.fFlipV.value) then angleSignF = -1 end local angleDelta = 0 if angleSignF == 1 and angleSignS == -1 then angleSign = -1 angleDelta = (firstBone.fAngle - firstBone.fTempAngle) + (secondBone.fAngle - secondBone.fTempAngle) elseif angleSignF == 1 and angleSignS == 1 then angleDelta = (firstBone.fAngle - firstBone.fTempAngle) + (secondBone.fAngle - secondBone.fTempAngle) elseif angleSignF == -1 and angleSignS == -1 then angleDelta = (firstBone.fAngle - firstBone.fTempAngle) + ((secondBone.fAngle - secondBone.fTempAngle) * angleSignF) elseif angleSignF == -1 and angleSignS == 1 then angleSign = -1 angleDelta = (firstBone.fAngle - firstBone.fTempAngle) + ((secondBone.fAngle - secondBone.fTempAngle) * angleSignF) end while angleDelta > math.rad(180) do angleDelta = angleDelta - math.rad(360) end while angleDelta < math.rad(-180) do angleDelta = angleDelta + math.rad(360) end if not self.isChildBonesBaked and self.bakeAdjacentFrames then for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) if not self.isChildBonesBaked then if moho.layerFrame - self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame - self.interval, self.childBonesAngelP[i]) end end if not self.isChildBonesBaked then if moho.layerFrame + self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame + self.interval, self.childBonesAngelN[i]) end end end self.isChildBonesBaked = true end for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) local newAngle = bone.fTempAngle - (angleDelta * angleSign) bone.fAnimAngle:SetValue(moho.layerFrame, newAngle) end else for i, q in ipairs(self.childBones) do local firstBone = skel:Bone(q) if not self:IsEqual(firstBone.fTempAngle, firstBone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then firstBone.fAnimAngle:SetValue(moho.layerFrame, firstBone.fTempAngle) end end end moho.layer:UpdateCurFrame() mouseEvent.view:DrawMe() end function MR_PoseTool:OnMouseMovedT(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end local bone = skel:Bone(self.secondBoneID) bone.fPos:Set(bone.fTempPos) local offset = mouseEvent.vec - mouseEvent.startVec local boneVec = LM.Vector2:new_local() local inverseM = LM.Matrix:new_local() local parent = nil boneVec:Set(0, 0) if (bone.fParent >= 0) then parent = skel:Bone(bone.fParent) parent.fMovedMatrix:Transform(boneVec) end boneVec = boneVec + offset if (parent) then inverseM:Set(parent.fMovedMatrix) inverseM:Invert() inverseM:Transform(boneVec) end if (mouseEvent.shiftKey) then if (math.abs(boneVec.x) > math.abs(boneVec.y)) then boneVec.y = 0 else boneVec.x = 0 end end local v = nil v = bone.fPos + boneVec if self.mode == 5 then bone.fAnimPos:SetValue(self.translationFrame, v) elseif self.mode == 0 then local bonePos = bone.fAnimPos:GetValue(moho.layerFrame) if not self:IsEqual(v.x, bonePos.x, 0.0001) or not self:IsEqual(v.y, bonePos.y, 0.0001) then bone.fAnimPos:SetValue(moho.layerFrame, v) if self.bakeAdjacentFrames and not self.boneBakedP then self:BakeFrames(moho, bone, nil, false, true, false) self.boneBakedP = true end end end if self.mode == 0 then if mouseEvent.altKey and bone.fLength > 0 then local vectorMousePos = mouseEvent.vec + self.clickOffset local secondCursorDist = self:GetDistance(vectorMousePos, self.secondBoneTipPos) local secondDelta = (secondCursorDist / self.secondBoneDist) local newScale = (bone.fTempScale * secondDelta) - self.secondBoneSizeDelta if not self:IsEqual(newScale, bone.fAnimScale:GetValue(moho.layerFrame), 0.0001) then bone.fAnimScale:SetValue(moho.layerFrame, newScale) if self.bakeAdjacentFrames and not self.boneBakedS then self:BakeFrames(moho, bone, nil, false, false, true) self.boneBakedS = true end end moho.layer:UpdateCurFrame() local center = LM.Vector2:new_local() center:Set(0, 0) skel:UpdateBoneMatrix(self.secondBoneID) bone.fMovedMatrix:Transform(center) local angle = self.startAngle2 local v1 = self.lastVec2 - self.secondBoneTipPos local v2 = center - self.secondBoneTipPos v2:Rotate(-math.atan2(v1.y, v1.x)) angle = angle + math.atan2(v2.y, v2.x) self.startAngle2 = angle self.lastVec2:Set(center) local angleSign = 1.0 if (not bone.fFixedAngle) then angleSign = bone:ParentalFlipFactor() end local newAgle = (bone.fTempAngle + (angle * angleSign)) - self.secondBoneAngleDelta if not bone.fFixedAngle then if bone.fConstraints then local min = bone.fAnimAngle:GetValue(0) - self.secondBoneAngleDelta local max = min + bone.fMaxConstraint min = min + bone.fMinConstraint newAgle = LM.Clamp(newAgle, min, max) end end if not self:IsEqual(newAgle, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, newAgle) bone.fAnimAngle.value = bone.fAngle end if self.bakeAdjacentFrames and not self.boneBakedA then self:BakeFrames(moho, bone, nil, true, false, false) self.boneBakedA = true if not self.isChildBonesBaked then for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) if moho.layerFrame - self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame - self.interval, self.childBonesAngelP[i]) bone.fAnimAngle.value = bone.fAngle end if moho.layerFrame + self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame + self.interval, self.childBonesAngelN[i]) bone.fAnimAngle.value = bone.fAngle end end self.isChildBonesBaked = true end end local angleSign = 1 if (bone.fFlipH.value and not bone.fFlipV.value) or (not bone.fFlipH.value and bone.fFlipV.value) then angleSign = -1 end moho.layer:UpdateCurFrame() local angleDelta = bone.fAngle - bone.fTempAngle for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) bone.fAnimAngle:SetValue(moho.layerFrame, bone.fTempAngle - (angleDelta * angleSign)) bone.fAnimAngle.value = bone.fAngle end else if not self:IsEqual(bone.fTempScale - self.secondBoneSizeDelta, bone.fAnimScale:GetValue(moho.layerFrame), 0.0001) then bone.fAnimScale:SetValue(moho.layerFrame, bone.fTempScale) end local newAngle = bone.fTempAngle - self.secondBoneAngleDelta if not self:IsEqual(newAngle, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, newAngle) end for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) if not self:IsEqual(bone.fTempAngle, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, bone.fTempAngle) end end end end moho.layer:UpdateCurFrame() mouseEvent.view:DrawMe() end function MR_PoseTool:OnMouseMovedR(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end local secondBone = skel:Bone(self.secondBoneID) local bone = secondBone local center = LM.Vector2:new_local() center:Set(0, 0) skel:UpdateBoneMatrix() bone.fMovedMatrix:Transform(center) local layerMatrix = LM.Matrix:new_local() local mousePos = mouseEvent.vec local angle = self.startAngle local v1 = self.lastVec - center local v2 = mousePos - center v2:Rotate(-math.atan2(v1.y, v1.x)) angle = angle + math.atan2(v2.y, v2.x) self.startAngle = angle self.lastVec:Set(mousePos) local angleSign = 1.0 if (not bone.fFixedAngle) then angleSign = bone:ParentalFlipFactor() end local newAgle = (bone.fTempAngle + (angle * angleSign)) - self.secondBoneAngleDelta if not bone.fFixedAngle then if bone.fConstraints then local min = bone.fAnimAngle:GetValue(0) - self.secondBoneAngleDelta local max = min + bone.fMaxConstraint min = min + bone.fMinConstraint newAgle = LM.Clamp(newAgle, min, max) end end if (mouseEvent.shiftKey) then newAgle = newAgle / (math.pi / 4) newAgle = (math.pi / 4) * LM.Round(newAgle) end if not self:IsEqual(newAgle, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, newAgle) if self.bakeAdjacentFrames and not self.boneBakedA then self:BakeFrames(moho, secondBone, nil, true, false, false) self.boneBakedA = true end end local angleSign = 1 if (bone.fFlipH.value and not bone.fFlipV.value) or (not bone.fFlipH.value and bone.fFlipV.value) then angleSign = -1 end if self.isChainTargeted then local targetBone = skel:Bone(self.secondTarget) local newTargetPos = targetBone.fTempPos - (mouseEvent.startVec - mouseEvent.vec) targetBone.fAnimPos:SetValue(moho.layerFrame, newTargetPos) self.isTargetMoved = true if self.bakeAdjacentFrames then if not self.isTargetBonePosPBaked then if moho.layerFrame - self.interval > 0 then targetBone.fAnimPos:SetValue(moho.layerFrame - self.interval, self.targetBonePosP) end self.isTargetBonePosPBaked = true end if not self.isTargetBonePosNBaked then if moho.layerFrame + self.interval > 0 then targetBone.fAnimPos:SetValue(moho.layerFrame + self.interval, self.targetBonePosN) end self.isTargetBonePosNBaked = true end end end if mouseEvent.altKey then moho.layer:UpdateCurFrame() local angleDelta = secondBone.fAngle - secondBone.fTempAngle if self.bakeAdjacentFrames and not self.isChildBonesBaked then for i, q in ipairs(self.childBones) do local childBone = skel:Bone(q) if moho.layerFrame - self.interval > 0 then childBone.fAnimAngle:SetValue(moho.layerFrame - self.interval, self.childBonesAngelP[i]) end if moho.layerFrame + self.interval > 0 then childBone.fAnimAngle:SetValue(moho.layerFrame + self.interval, self.childBonesAngelN[i]) end end self.isChildBonesBaked = true end for i, q in ipairs(self.childBones) do local childBone = skel:Bone(q) childBone.fAnimAngle:SetValue(moho.layerFrame, childBone.fTempAngle - (angleDelta * angleSign)) end else for i, q in ipairs(self.childBones) do local childBone = skel:Bone(q) if not self:IsEqual(childBone.fTempAngle, childBone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then childBone.fAnimAngle:SetValue(moho.layerFrame, childBone.fTempAngle) end end end moho.layer:UpdateCurFrame() mouseEvent.view:DrawMe() end function MR_PoseTool:OnMouseMovedS(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end local secondBone = skel:Bone(self.secondBoneID) if secondBone:IsZeroLength() then local scaleFactor = (mouseEvent.pt.x - mouseEvent.startPt.x) / 100 if (scaleFactor < 0) then scaleFactor = 1 / (-scaleFactor + 1) else scaleFactor = scaleFactor + 1 end local newScale = (secondBone.fTempScale * scaleFactor) - self.secondBoneSizeDelta if not self:IsEqual(newScale, secondBone.fAnimScale:GetValue(moho.layerFrame), 0.0001) then secondBone.fAnimScale:SetValue(moho.layerFrame, newScale) if self.bakeAdjacentFrames and not self.boneBakedS then self:BakeFrames(moho, secondBone, nil, false, false, true) self.boneBakedS = true end end else local vectorMousePos = mouseEvent.vec + self.clickOffset local secondCursorDist = self:GetDistance(vectorMousePos, self.secondBonePos) local secondDelta = (secondCursorDist / self.secondBoneDist) local newScale = (secondBone.fTempScale * secondDelta) - self.secondBoneSizeDelta if not self:IsEqual(newScale, secondBone.fAnimScale:GetValue(moho.layerFrame), 0.0001) then secondBone.fAnimScale:SetValue(moho.layerFrame, newScale) if self.bakeAdjacentFrames and not self.boneBakedS then self:BakeFrames(moho, secondBone, nil, false, false, true) self.boneBakedS = true end end if not mouseEvent.shiftKey then local bone = secondBone local center = LM.Vector2:new_local() center:Set(0, 0) skel:UpdateBoneMatrix() bone.fMovedMatrix:Transform(center) local mousePos = mouseEvent.vec local angle = self.startAngle local v1 = self.lastVec - center local v2 = mousePos - center v2:Rotate(-math.atan2(v1.y, v1.x)) angle = angle + math.atan2(v2.y, v2.x) self.startAngle = angle self.lastVec:Set(mousePos) local angleSign = 1.0 if (not bone.fFixedAngle) then angleSign = bone:ParentalFlipFactor() end local newAgle = (bone.fTempAngle + (angle * angleSign)) - self.secondBoneAngleDelta if not bone.fFixedAngle then if bone.fConstraints then local min = bone.fAnimAngle:GetValue(0) - self.secondBoneAngleDelta local max = min + bone.fMaxConstraint min = min + bone.fMinConstraint newAgle = LM.Clamp(newAgle, min, max) end end if not self:IsEqual(newAgle, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, newAgle) if self.bakeAdjacentFrames and not self.boneBakedA then self:BakeFrames(moho, secondBone, nil, true, false, false) self.boneBakedA = true end end else local bone = secondBone local newAngle = bone.fTempAngle - self.secondBoneAngleDelta if not self:IsEqual(newAngle, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, newAngle) if self.bakeAdjacentFrames and not self.boneBakedA then self:BakeFrames(moho, secondBone, nil, true, false, false) self.boneBakedA = true end end end end local angleSign = 1 if (secondBone.fFlipH.value and not secondBone.fFlipV.value) or (not secondBone.fFlipH.value and secondBone.fFlipV.value) then angleSign = -1 end if self.isChainTargeted then local targetBone = skel:Bone(self.secondTarget) local newTargetPos = targetBone.fTempPos - (mouseEvent.startVec - mouseEvent.vec) targetBone.fAnimPos:SetValue(moho.layerFrame, newTargetPos) self.isTargetMoved = true if self.bakeAdjacentFrames then if not self.isTargetBonePosPBaked then if moho.layerFrame - self.interval > 0 then targetBone.fAnimPos:SetValue(moho.layerFrame - self.interval, self.targetBonePosP) end self.isTargetBonePosPBaked = true end if not self.isTargetBonePosNBaked then if moho.layerFrame + self.interval > 0 then targetBone.fAnimPos:SetValue(moho.layerFrame + self.interval, self.targetBonePosN) end self.isTargetBonePosNBaked = true end end end if mouseEvent.altKey then moho.layer:UpdateCurFrame() if self.bakeAdjacentFrames and not self.isChildBonesBaked then for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) if moho.layerFrame - self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame - self.interval, self.childBonesAngelP[i]) end if moho.layerFrame + self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame + self.interval, self.childBonesAngelN[i]) end end self.isChildBonesBaked = true end local angleDelta = secondBone.fAngle - secondBone.fTempAngle for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) bone.fAnimAngle:SetValue(moho.layerFrame, bone.fTempAngle - (angleDelta * angleSign)) end else for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) if not self:IsEqual(bone.fTempAngle, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, bone.fTempAngle) end end end moho.layer:UpdateCurFrame() mouseEvent.view:DrawMe() end function MR_PoseTool:OnMouseMovedM(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end local riggingFrame = 0 local bone = skel:Bone(self.selID) local secondBone = skel:Bone(self.selID) if self.bakeAdjacentFrames and not self.boneBakedA then self:BakeFrames(moho, secondBone, nil, true, false, false) self.boneBakedA = true end skel:UpdateBoneMatrix(self.selID) local tipVec = LM.Vector2:new_local() tipVec:Set(bone.fLength, 0) bone.fMovedMatrix:Transform(tipVec) tipVec = tipVec + (mouseEvent.vec - self.lastVec) local startBone = skel:Bone(self.parentBones[#self.parentBones]) local center = LM.Vector2:new_local() center:Set(0, 0) skel:UpdateBoneMatrix() startBone.fMovedMatrix:Transform(center) local mousePos = mouseEvent.vec local angle = self.startAngle local v1 = self.lastVec - center local v2 = mousePos - center v2:Rotate(-math.atan2(v1.y, v1.x)) angle = angle + math.atan2(v2.y, v2.x) self.startAngle = angle self.lastVec:Set(mousePos) local angleSign = 1.0 if (not startBone.fFixedAngle) then angleSign = startBone:ParentalFlipFactor() end local newAgle = (startBone.fTempAngle + (angle * angleSign)) if mouseEvent.shiftKey then if self.bakeAdjacentFrames and not self.boneBakedS then self:BakeFrames(moho, secondBone, nil, false, false, true) for i, q in ipairs(self.parentBones) do local bone = skel:Bone(q) if moho.layerFrame - self.interval > 0 then bone.fAnimScale:SetValue(moho.layerFrame - self.interval, self.parentBonesScaleP[i]) end if moho.layerFrame + self.interval > 0 then bone.fAnimScale:SetValue(moho.layerFrame + self.interval, self.parentBonesScaleN[i]) end end self.boneBakedS = true end if self.bakeAdjacentFrames and not self.boneBakedS then self:BakeFrames(moho, secondBone, nil, false, false, true) self.boneBakedS = true end local newStartBoneDist = self:GetDistance(self.startBonePos, mouseEvent.vec) local startBoneDistDelta = newStartBoneDist / self.startBoneDist bone.fAnimScale:SetValue(moho.layerFrame, bone.fTempScale * startBoneDistDelta) if not startBone.fFixedAngle then if startBone.fConstraints then local min = startBone.fAnimAngle:GetValue(0) - self.startBoneAngleDelta local max = min + startBone.fMaxConstraint min = min + startBone.fMinConstraint newAgle = LM.Clamp(newAgle, min, max) end end for i, q in ipairs(self.parentBones) do local parentBone = skel:Bone(q) parentBone.fAnimScale:SetValue(moho.layerFrame, parentBone.fTempScale * startBoneDistDelta) if not self:IsEqual(parentBone.fTempAngle, parentBone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then parentBone.fAnimAngle:SetValue(moho.layerFrame, parentBone.fTempAngle) end end if not self:IsEqual(bone.fTempAngle, bone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then bone.fAnimAngle:SetValue(moho.layerFrame, bone.fTempAngle) end startBone.fAnimAngle:SetValue(moho.layerFrame, newAgle) else skel:IKAngleSolver(self.selID, tipVec) local boneID = 1 bone.fAnimAngle:SetValue(moho.layerFrame, bone.fAngle - (self.boneStartActualAngles[boneID] - self.boneStartAngles[boneID])) bone.fAnimAngle.value = bone.fAngle boneID = boneID + 1 while bone.fParent >= 0 do if (self:CountBoneChildren(skel, bone.fParent, true) > 1) then break end local parentBone = skel:Bone(bone.fParent) if self.mohoVersion >= 14 then if (parentBone.fAngleControlParent >= 0 or parentBone.fPosControlParent >= 0 or parentBone.fScaleControlParent >= 0 or parentBone:AreDynamicsActive() or parentBone.fFixedAngle or parentBone.fIgnoredByIK) then break end else if (parentBone.fAngleControlParent >= 0 or parentBone.fPosControlParent >= 0 or parentBone.fScaleControlParent >= 0 or parentBone.fBoneDynamics.value or parentBone.fFixedAngle or parentBone.fIgnoredByIK) then break end end bone = skel:Bone(bone.fParent) bone.fAnimAngle:SetValue(moho.layerFrame, bone.fAngle - (self.boneStartActualAngles[boneID] - self.boneStartAngles[boneID])) bone.fAnimAngle.value = bone.fAngle boneID = boneID + 1 end end if #self.parentBones == 1 then local firstBone = skel:Bone(self.parentBones[1]) if mouseEvent.altKey then moho.layer:UpdateCurFrame() local angleSign = 1 local angleSignS = 1 if (secondBone.fFlipH.value and not secondBone.fFlipV.value) or (not secondBone.fFlipH.value and secondBone.fFlipV.value) then angleSignS = -1 end local angleSignF = 1 if (firstBone.fFlipH.value and not firstBone.fFlipV.value) or (not firstBone.fFlipH.value and firstBone.fFlipV.value) then angleSignF = -1 end local angleDelta = 0 if angleSignF == 1 and angleSignS == -1 then angleSign = -1 angleDelta = (firstBone.fAngle - firstBone.fTempAngle) + (secondBone.fAngle - secondBone.fTempAngle) elseif angleSignF == 1 and angleSignS == 1 then angleDelta = (firstBone.fAngle - firstBone.fTempAngle) + (secondBone.fAngle - secondBone.fTempAngle) elseif angleSignF == -1 and angleSignS == -1 then angleDelta = (firstBone.fAngle - firstBone.fTempAngle) + ((secondBone.fAngle - secondBone.fTempAngle) * angleSignF) elseif angleSignF == -1 and angleSignS == 1 then angleSign = -1 angleDelta = (firstBone.fAngle - firstBone.fTempAngle) + ((secondBone.fAngle - secondBone.fTempAngle) * angleSignF) end moho.layer:UpdateCurFrame() if not self.isChildBonesBaked and self.bakeAdjacentFrames then for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) if not self.isChildBonesBaked then if moho.layerFrame - self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame - self.interval, self.childBonesAngelP[i]) end end if not self.isChildBonesBaked then if moho.layerFrame + self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame + self.interval, self.childBonesAngelN[i]) end end end self.isChildBonesBaked = true end for i, q in ipairs(self.childBones) do local bone = skel:Bone(q) local newAngle = bone.fTempAngle - (angleDelta * angleSign) bone.fAnimAngle:SetValue(moho.layerFrame, newAngle) end else for i, q in ipairs(self.childBones) do local firstBone = skel:Bone(q) if not self:IsEqual(firstBone.fTempAngle, firstBone.fAnimAngle:GetValue(moho.layerFrame), 0.0001) then firstBone.fAnimAngle:SetValue(moho.layerFrame, firstBone.fTempAngle) end end end end if self.bakeAdjacentFrames and not self.isParentBonesBaked then for i, q in ipairs(self.parentBones) do local bone = skel:Bone(q) if moho.layerFrame - self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame - self.interval, self.parentBonesAngleP[i]) end if moho.layerFrame + self.interval > 0 then bone.fAnimAngle:SetValue(moho.layerFrame + self.interval, self.parentBonesAngleN[i]) end end self.isParentBonesBaked = true end if self.isChainTargeted then local targetBone = skel:Bone(self.secondTarget) local newTargetPos = targetBone.fTempPos - (mouseEvent.startVec - mouseEvent.vec) targetBone.fAnimPos:SetValue(moho.layerFrame, newTargetPos) self.isTargetMoved = true if self.bakeAdjacentFrames then if not self.isTargetBonePosPBaked then if moho.layerFrame - self.interval > 0 then targetBone.fAnimPos:SetValue(moho.layerFrame - self.interval, self.targetBonePosP) end self.isTargetBonePosPBaked = true end if not self.isTargetBonePosNBaked then if moho.layerFrame + self.interval > 0 then targetBone.fAnimPos:SetValue(moho.layerFrame + self.interval, self.targetBonePosN) end self.isTargetBonePosNBaked = true end end end skel:UpdateBoneMatrix() moho.layer:UpdateCurFrame() mouseEvent.view:DrawMe() self.lastVec:Set(mouseEvent.vec) end function MR_PoseTool:OnMouseUp(moho, mouseEvent) self.dragging = false local skel = moho:Skeleton() if skel == nil or moho.frame == 0 then return end if self.isActive == false then return end local bone if self.firstBoneID > -1 then bone = skel:Bone(self.firstBoneID) end local secondBoneID = skel:Bone(self.secondBoneID) if self.firstBoneParentID > -1 then local firstBoneParent = skel:Bone(self.firstBoneParentID) if firstBoneParent then firstBoneParent.fIgnoredByIK = self.firstBoneParentIBIK end end if self.keepSelection then skel:SelectNone() for i, boneID in ipairs(self.selectedBonesList) do local bone = skel:Bone(boneID) bone.fSelected = true end end moho.layer:UpdateCurFrame() moho:UpdateUI() mouseEvent.view:DrawMe() moho:UpdateSelectedChannels() end function MR_PoseTool:DrawMe(moho, view) local skel = moho:Skeleton() if (skel == nil) then return end local markerR = self.markerR if self.additionHandles then markerR = self.markerR2 end local v = LM.Vector2:new_local() local g = view:Graphics() local layerMatrix = LM.Matrix:new_local() local vc1 = LM.ColorVector:new_local() local vc2 = LM.ColorVector:new_local() local interp = MOHO.InterpSetting:new_local() local center = LM.Vector2:new_local() local tip = LM.Vector2:new_local() local offset = LM.Vector2:new_local() local angle = 0 local newAngle = 0 local offsetAngle = 0 vc1:Set(MOHO.MohoGlobals.SelCol) vc2:Set(MOHO.MohoGlobals.BackCol) local colorExtraHandle = LM.rgb_color:new_local() colorExtraHandle.r = 255 colorExtraHandle.g = 189 colorExtraHandle.b = 46 colorExtraHandle.a = 255 local whiteColor = LM.rgb_color:new_local() whiteColor.r = 255 whiteColor.g = 255 whiteColor.b = 255 whiteColor.a = 255 vc1 = (vc1 + vc2) / 2 local fillCol = vc1:AsColorStruct() local colorExtraHandleHL = LM.rgb_color:new_local() colorExtraHandleHL.r = ((colorExtraHandle.r * 3) + whiteColor.r) / 4 colorExtraHandleHL.g = ((colorExtraHandle.g * 3) + whiteColor.g) / 4 colorExtraHandleHL.b = ((colorExtraHandle.b * 3) + whiteColor.b) / 4 colorExtraHandleHL.a = 255 local colorExtraHandleOutlineHL = LM.rgb_color:new_local() colorExtraHandleOutlineHL.r = (colorExtraHandleHL.r + (MOHO.MohoGlobals.SelCol.r * 2)) / 3 colorExtraHandleOutlineHL.g = (colorExtraHandleHL.g + (MOHO.MohoGlobals.SelCol.g * 2)) / 3 colorExtraHandleOutlineHL.b = (colorExtraHandleHL.b + (MOHO.MohoGlobals.SelCol.b * 2)) / 3 colorExtraHandleOutlineHL.a = 255 local fillColHL = LM.rgb_color:new_local() fillColHL.r = ((fillCol.r * 3) + whiteColor.r) / 4 fillColHL.g = ((fillCol.g * 3) + whiteColor.g) / 4 fillColHL.b = ((fillCol.b * 3) + whiteColor.b) / 4 fillColHL.a = 255 local hlDelta = 1 moho.layer:GetFullTransform(moho.frame, layerMatrix, moho.document) g:Push() g:ApplyMatrix(layerMatrix) local height = g:Height() / moho.document:Height() height = g:Height() / self.height local markerMultiplier = 0.002 local additionMarker1 = self.additionHandles local currentScale = g:CurrentScale(false) g:SetSmoothing(true) g:SetBezierTolerance(2) if self.mousePickedID > -1 and not (self.dragging or moho:IsPlaying()) then local bone = skel:Bone(self.mousePickedID) if bone ~= nil then local isPin = bone:IsZeroLength() if not bone.fHidden and bone:IsGroupVisible() then v:Set(bone.fLength - bone.fLength * self.handlesDist, 0) bone.fMovedMatrix:Transform(v) if additionMarker1 then center:Set(0, 0) tip:Set(1, 0) bone.fMovedMatrix:Transform(center) bone.fMovedMatrix:Transform(tip) offset:Set(v.x, v.y - ((markerR * markerMultiplier) / currentScale / height)) angle = math.atan2(tip.y - center.y, tip.x - center.x) offsetAngle = math.atan2(offset.y - center.y, offset.x - center.x) angle = offsetAngle + (angle - offsetAngle) v:Set(self:RotateVector2(moho, offset, v, angle)) end if self.drawMode == 2 then if isPin then g:SetColor(colorExtraHandleHL) else g:SetColor(fillColHL) end markerR = self.markerR + hlDelta else if isPin then g:SetColor(colorExtraHandle) else g:SetColor(fillCol) end markerR = self.markerR end if bone.fLength == 0 and self.keepHandles then markerR = markerR * 3 end g:FillCirclePixelRadius(v, markerR) g:SetColor(MOHO.MohoGlobals.SelCol) g:FrameCirclePixelRadius(v, markerR) markerR = self.markerR if additionMarker1 then v:Set(bone.fLength - (bone.fLength * self.handlesDist), 0) bone.fMovedMatrix:Transform(v) offset:Set(v.x, v.y + ((markerR * markerMultiplier) / currentScale / height)) offsetAngle = math.atan2(offset.y - center.y, offset.x - center.x) newAngle = offsetAngle + (angle - offsetAngle) v:Set(self:RotateVector2(moho, offset, v, newAngle)) if self.drawMode == 4 then g:SetColor(colorExtraHandleHL) markerR = self.markerR + hlDelta else g:SetColor(colorExtraHandle) markerR = self.markerR end g:FillCirclePixelRadius(v, markerR) g:SetColor(colorExtraHandleOutlineHL) g:FrameCirclePixelRadius(v, markerR) markerR = self.markerR end v:Set(bone.fLength * self.handlesDist, 0) bone.fMovedMatrix:Transform(v) if additionMarker1 then offset:Set(v.x, v.y - ((markerR * markerMultiplier) / currentScale / height)) offsetAngle = math.atan2(offset.y - center.y, offset.x - center.x) newAngle = offsetAngle + (angle - offsetAngle) v:Set(self:RotateVector2(moho, offset, v, newAngle)) end if self.drawMode == 0 or self.drawMode == 5 then g:SetColor(fillColHL) markerR = self.markerR + hlDelta else g:SetColor(fillCol) markerR = self.markerR end g:FillCirclePixelRadius(v, markerR) g:SetColor(MOHO.MohoGlobals.SelCol) g:FrameCirclePixelRadius(v, markerR) markerR = self.markerR if not isPin then if additionMarker1 then v:Set(bone.fLength * self.handlesDist, 0) bone.fMovedMatrix:Transform(v) offset:Set(v.x, v.y + ((markerR * markerMultiplier) / currentScale / height)) offsetAngle = math.atan2(offset.y - center.y, offset.x - center.x) newAngle = offsetAngle + (angle - offsetAngle) v:Set(self:RotateVector2(moho, offset, v, newAngle)) if self.drawMode == 3 then g:SetColor(colorExtraHandleHL) markerR = self.markerR + hlDelta else g:SetColor(colorExtraHandle) markerR = self.markerR end g:FillCirclePixelRadius(v, markerR) g:SetColor(colorExtraHandleOutlineHL) g:FrameCirclePixelRadius(v, markerR) markerR = self.markerR end end end end end if moho:CountSelectedBones() == 1 then local bone = skel:Bone(skel:SelectedBoneID()) if bone ~= nil then if (bone.fSelected and self.showPath and bone.fParent < 0) then -- draw path local channelPos = bone.fAnimPos local startFrame = channelPos:GetKeyWhen(0) local channelDuration = channelPos:Duration() local endFrame = channelDuration local totalTimingOffset = moho.layer:TotalTimingOffset() if (startFrame - totalTimingOffset < 0) then startFrame = totalTimingOffset end channelPos:GetKeyInterp(endFrame, interp) if (interp:IsAdditiveCycle()) then endFrame = moho.document:EndFrame() + totalTimingOffset end if (endFrame > startFrame) then local vec = LM.Vector2:new_local() local oldVec = LM.Vector2:new_local() if self.range then startFrame = LM.Clamp(moho.layerFrame - self.rangeFrames, 1, channelDuration) endFrame = LM.Clamp(moho.layerFrame + self.rangeFrames, 1, channelDuration) end g:SetColor(102, 152, 203) for frame = startFrame, endFrame do vec = channelPos:GetValue(frame) if (frame > startFrame) then g:DrawLine(oldVec.x, oldVec.y, vec.x, vec.y) end if (channelPos:HasKey(frame)) then g:DrawFatMarker(vec.x, vec.y, 5) else g:DrawMarker(vec.x, vec.y) end oldVec:Set(vec) end end end end end g:Pop() end function MR_PoseTool:Round(x, n) n = 10 ^ (n or 3) x = x * n if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end return x / n end function MR_PoseTool:GetDistance(Pos1, Pos2) return math.sqrt((Pos2.x-Pos1.x)^2+(Pos2.y-Pos1.y)^2) end function MR_PoseTool:GetMohoVersion(moho) local numVers = {} local vers = moho:AppVersion() for n in string.gmatch (vers, "%d+") do table.insert(numVers, tonumber(n)) end return numVers[1], numVers[2], numVers[3] end -- ************************************************** -- Tool Panel Layout -- ************************************************** MR_PoseTool.KEEP_SELECTION = MOHO.MSG_BASE MR_PoseTool.BAKE_ADJACENT_FRAMES = MOHO.MSG_BASE + 1 MR_PoseTool.INTERVAL_1 = MOHO.MSG_BASE + 2 MR_PoseTool.INTERVAL_2 = MOHO.MSG_BASE + 3 MR_PoseTool.INTERVAL_3 = MOHO.MSG_BASE + 4 MR_PoseTool.INTERVAL_4 = MOHO.MSG_BASE + 5 MR_PoseTool.SHOW_PATH = MOHO.MSG_BASE + 6 MR_PoseTool.RANGE = MOHO.MSG_BASE + 7 MR_PoseTool.RANGE_FRAMES = MOHO.MSG_BASE + 8 MR_PoseTool.FLIP_H = MOHO.MSG_BASE + 9 MR_PoseTool.FLIP_V = MOHO.MSG_BASE + 10 function MR_PoseTool:DoLayout(moho, layout) self.keepSelectionCheckbox = LM.GUI.CheckBox(self:Localize('Keep selection'), self.KEEP_SELECTION) layout:AddChild(self.keepSelectionCheckbox, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.bakeAdjacentFramesCheckbox = LM.GUI.CheckBox(self:Localize('Bake Adjacent Frames'), self.BAKE_ADJACENT_FRAMES) layout:AddChild(self.bakeAdjacentFramesCheckbox, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.StaticText(self:Localize("Interval"))) 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) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.showPathCheckbox = LM.GUI.CheckBox(self:Localize('Show path'), self.SHOW_PATH) layout:AddChild(self.showPathCheckbox, LM.GUI.ALIGN_LEFT, 0) self.rangeCheckbox = LM.GUI.CheckBox(self:Localize('Range'), self.RANGE) layout:AddChild(self.rangeCheckbox, LM.GUI.ALIGN_LEFT, 0) self.rangeFramesInput = LM.GUI.TextControl(0, '1000', self.RANGE_FRAMES, LM.GUI.FIELD_INT, self:Localize('Range frames:')) layout:AddChild(self.rangeFramesInput, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) layout:AddChild(LM.GUI.ImageButton("ScriptResources/flip_bone_h", self:Localize('End flip'), false, self.FLIP_H, true)) layout:AddChild(LM.GUI.ImageButton("ScriptResources/flip_bone_v", self:Localize('Side flip'), false, self.FLIP_V, true)) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) end function MR_PoseTool:UpdateWidgets(moho) if self.mainCursor == nil then self.mainCursor = LM.GUI.Cursor(moho:UserContentDir()..'/Scripts/Tool/mr_pose_tool_cursor', 1, 1) end if self.mjCursor == nil then self.mjCursor = LM.GUI.Cursor(moho:UserContentDir()..'/Scripts/ScriptResources/mr_pose_tool/mr_mj_cursor', 1, 1) end if self.mbCursor == nil then self.mbCursor = LM.GUI.Cursor(moho:UserContentDir()..'/Scripts/ScriptResources/mr_pose_tool/mr_mb_cursor', 1, 1) end self.keepSelectionCheckbox:SetValue(self.keepSelection) self.bakeAdjacentFramesCheckbox:SetValue(self.bakeAdjacentFrames) if self.interval < 1 or self.interval > 4 or self.interval == nil then self.interval = 1 end self.intervalMenu:SetChecked(self.INTERVAL_1, false) self.intervalMenu:SetChecked(self.INTERVAL_2, false) self.intervalMenu:SetChecked(self.INTERVAL_3, false) self.intervalMenu:SetChecked(self.INTERVAL_4, false) 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:Enable(self.bakeAdjacentFrames) self.intervalPopup:Redraw() self.showPathCheckbox:SetValue(self.showPath) self.rangeCheckbox:SetValue(self.range) self.rangeFramesInput:SetValue(self.rangeFrames) self.rangeCheckbox:Enable(self.showPath) self.rangeFramesInput:Enable(self.range and self.showPath) end function MR_PoseTool:HandleMessage(moho, view, msg) if msg == self.KEEP_SELECTION then self.keepSelection = self.keepSelectionCheckbox:Value() elseif msg == self.BAKE_ADJACENT_FRAMES then self.bakeAdjacentFrames = self.bakeAdjacentFramesCheckbox:Value() self.intervalPopup:Enable(self.bakeAdjacentFrames) 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:UpdateWidgets(moho) elseif msg == self.SHOW_PATH then self.showPath = self.showPathCheckbox:Value() self.rangeCheckbox:Enable(self.showPath) self.rangeFramesInput:Enable(self.range and self.showPath) elseif msg == self.RANGE then self.range = self.rangeCheckbox:Value() self.rangeCheckbox:Enable(self.showPath) self.rangeFramesInput:Enable(self.range and self.showPath) elseif msg == self.RANGE_FRAMES then self.rangeFrames = LM.Clamp(self.rangeFramesInput:Value(), 1, 1000) self.rangeFramesInput:SetValue(self.rangeFrames) elseif (msg == self.FLIP_H) then self:FlipBones(moho, true) elseif (msg == self.FLIP_V) then self:FlipBones(moho, false) end end function MR_PoseTool:IsEqual(a, b, epsilon) local absA = math.abs(a) local absB = math.abs(b) local diff = math.abs(a - b) if a == b then return true elseif diff < epsilon then return true else return false end end function MR_PoseTool:CheckBone(moho, id) local skel = moho:Skeleton() if (skel == nil) then return false end if id < 0 or not id then return false end local secondBone = skel:Bone(id) if secondBone then if secondBone.fParent > -1 and not secondBone:IsZeroLength() and not secondBone.fFixedAngle then local firstBoneID = secondBone.fParent local firstBone = skel:Bone(firstBoneID) local firstBoneChilds = skel:CountBoneChildren(firstBoneID, true) local secondBonePos = LM.Vector2:new_local() secondBonePos:Set(secondBone.fAnimPos:GetValue(moho.layerFrame)) if firstBone.fIgnoredByIK == false and secondBone.fIgnoredByIK == false and firstBoneChilds == 1 and self:Round(secondBone.fPos.y) == 0 and not self:IsEqual(secondBonePos.x, 0, 0.0001) and not firstBone:IsZeroLength() and not firstBone.fFixedAngle then return true end end end return false end function MR_PoseTool:TestMousePoint(moho, mouseEvent, id) local skel = moho:Skeleton() if (skel == nil) then return 1 end local markerR = self.markerR if self.additionHandles then markerR = self.markerR2 end local v = LM.Vector2:new_local() local pt = LM.Point:new_local() local m = LM.Matrix:new_local() local center = LM.Vector2:new_local() local tip = LM.Vector2:new_local() local offset = LM.Vector2:new_local() local angle = 0 local newAngle = 0 local offsetAngle = 0 moho.layer:GetFullTransform(moho.frame, m, moho.document) local g = mouseEvent.view:Graphics() g:Push() g:ApplyMatrix(m) local height = g:Height() / moho.document:Height() height = g:Height() / self.height local markerMultiplier = 0.002 local additionMarker1 = self.additionHandles local currentScale = g:CurrentScale(false) g:Pop() self.keepHandles = mouseEvent.ctrlKey if self.mousePickedID > -1 then local bone = skel:Bone(self.mousePickedID) if bone ~= nil then if not bone.fHidden and bone:IsGroupVisible() then v:Set(bone.fLength * self.handlesDist, 0) bone.fMovedMatrix:Transform(v) if additionMarker1 then center:Set(0, 0) tip:Set(1, 0) bone.fMovedMatrix:Transform(center) bone.fMovedMatrix:Transform(tip) offset:Set(v.x, v.y - ((markerR * markerMultiplier) / currentScale / height)) angle = math.atan2(tip.y - center.y, tip.x - center.x) offsetAngle = math.atan2(offset.y - center.y, offset.x - center.x) newAngle = offsetAngle + (angle - offsetAngle) v:Set(self:RotateVector2(moho, offset, v, newAngle)) end m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then return 0 end if additionMarker1 then v:Set(bone.fLength * self.handlesDist, 0) bone.fMovedMatrix:Transform(v) offset:Set(v.x, v.y + ((markerR * markerMultiplier) / currentScale / height)) angle = math.atan2(tip.y - center.y, tip.x - center.x) offsetAngle = math.atan2(offset.y - center.y, offset.x - center.x) newAngle = offsetAngle + (angle - offsetAngle) v:Set(self:RotateVector2(moho, offset, v, newAngle)) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then return 3 end end end if not bone.fHidden and bone:IsGroupVisible() then v:Set(bone.fLength - bone.fLength * self.handlesDist, 0) bone.fMovedMatrix:Transform(v) if additionMarker1 then offset:Set(v.x, v.y - ((markerR * markerMultiplier) / currentScale / height)) angle = math.atan2(tip.y - center.y, tip.x - center.x) offsetAngle = math.atan2(offset.y - center.y, offset.x - center.x) newAngle = offsetAngle + (angle - offsetAngle) v:Set(self:RotateVector2(moho, offset, v, newAngle)) end m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if bone:IsZeroLength() and mouseEvent.ctrlKey then if (math.abs(pt.x - mouseEvent.pt.x) < markerR * 3 and math.abs(pt.y - mouseEvent.pt.y) < markerR * 3) then return 2 end else if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then return 2 end end if additionMarker1 then v:Set(bone.fLength - (bone.fLength * self.handlesDist), 0) bone.fMovedMatrix:Transform(v) offset:Set(v.x, v.y + ((markerR * markerMultiplier) / currentScale / height)) offsetAngle = math.atan2(offset.y - center.y, offset.x - center.x) newAngle = offsetAngle + (angle - offsetAngle) v:Set(self:RotateVector2(moho, offset, v, newAngle)) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then return 4 end end end end end if id then if id > -1 and self.showPath then local bone = skel:Bone(id) if bone ~= nil then local channelPos = bone.fAnimPos local translationWhen = -20000 local g = mouseEvent.view:Graphics() local m = LM.Matrix:new_local() local vec = LM.Vector2:new_local() local pt = LM.Point:new_local() local totalTimingOffset = moho.layer:TotalTimingOffset() moho.layer:GetFullTransform(moho.frame, m, moho.document) -- First see if any keyframes were picked for i = 0, bone.fAnimPos:CountKeys() - 1 do local frame = bone.fAnimPos:GetKeyWhen(i) if frame > 0 then vec = bone.fAnimPos:GetValue(frame) m:Transform(vec) g:WorldToScreen(vec, pt) if (math.abs(pt.x - mouseEvent.pt.x) < self.TOLERANCE and math.abs(pt.y - mouseEvent.pt.y) < self.TOLERANCE) then translationWhen = frame self.trPathBone = bone break end end end -- If no keyframes were picked, try picking a random point along the curve. if (translationWhen <= -10000) then local startFrame = channelPos:GetKeyWhen(0) local channelDuration = channelPos:Duration() local endFrame = channelDuration if self.range then startFrame = LM.Clamp(moho.layerFrame - self.rangeFrames, 1, channelDuration) endFrame = LM.Clamp(moho.layerFrame + self.rangeFrames, 1, channelDuration) end if (endFrame > startFrame) then local oldVec = LM.Vector2:new_local() g:Clear(0, 0, 0, 0) g:SetColor(255, 255, 255) g:BeginPicking(mouseEvent.pt, 4) for frame = startFrame, endFrame do vec = channelPos:GetValue(frame) m:Transform(vec) if (frame > startFrame) then g:DrawLine(oldVec.x, oldVec.y, vec.x, vec.y) end if (g:Pick()) then translationWhen = frame self.trPathBone = bone break end oldVec:Set(vec) end end end if (translationWhen > -10000) then self.translationFrame = translationWhen return 5 end end end end return 1 end function MR_PoseTool:FlipBones(moho, horizontal) local skel = moho:Skeleton() if (skel == nil) then return 1 end if (moho:CountSelectedBones(true) < 1) then return end moho.document:PrepUndo(moho.layer, true) moho.document:SetDirty() for i = 0, skel:CountBones() - 1 do local bone = skel:Bone(i) if (bone.fSelected) then if (horizontal) then bone.fFlipH:SetValue(moho.layerFrame, not bone.fFlipH.value) else bone.fFlipV:SetValue(moho.layerFrame, not bone.fFlipV.value) end end end moho.layer:UpdateCurFrame() if (horizontal) then moho:NewKeyframe(CHANNEL_BONE_FLIPH) else moho:NewKeyframe(CHANNEL_BONE_FLIPV) end end function MR_PoseTool:BakeFrames(moho, secondBone, firstBone, angle, pos, scale) if moho.layerFrame - self.interval > 0 then if firstBone then if angle and self.boneFAngleP then firstBone.fAnimAngle:SetValue(moho.layerFrame - self.interval, self.boneFAngleP) end if pos and self.boneFPosP then local channel = firstBone.fAnimPos if channel:AreDimensionsSplit() then local channelX = channel:DimensionChannel(0) local channelY = channel:DimensionChannel(1) channelX:SetValue(moho.layerFrame - self.interval, self.boneFPosP.x) channelY:SetValue(moho.layerFrame - self.interval, self.boneFPosP.y) else firstBone.fAnimPos:SetValue(moho.layerFrame - self.interval, self.boneFPosP) end end if scale and self.boneFScaleP then firstBone.fAnimScale:SetValue(moho.layerFrame - self.interval, self.boneFScaleP) end end if secondBone then if angle and self.boneSAngleP then secondBone.fAnimAngle:SetValue(moho.layerFrame - self.interval, self.boneSAngleP) end if pos and self.boneSPosP then local channel = secondBone.fAnimPos if channel:AreDimensionsSplit() then local channelX = channel:DimensionChannel(0) local channelY = channel:DimensionChannel(1) channelX:SetValue(moho.layerFrame - self.interval, self.boneSPosP.x) channelY:SetValue(moho.layerFrame - self.interval, self.boneSPosP.y) else secondBone.fAnimPos:SetValue(moho.layerFrame - self.interval, self.boneSPosP) end end if scale and self.boneSScaleP then secondBone.fAnimScale:SetValue(moho.layerFrame - self.interval, self.boneSScaleP) end end end if moho.layerFrame + self.interval > 0 then if firstBone then if angle and self.boneFAngleN then firstBone.fAnimAngle:SetValue(moho.layerFrame + self.interval, self.boneFAngleN) end if pos and self.boneFPosN then local channel = firstBone.fAnimPos if channel:AreDimensionsSplit() then local channelX = channel:DimensionChannel(0) local channelY = channel:DimensionChannel(1) channelX:SetValue(moho.layerFrame + self.interval, self.boneFPosN.x) channelY:SetValue(moho.layerFrame + self.interval, self.boneFPosN.y) else firstBone.fAnimPos:SetValue(moho.layerFrame + self.interval, self.boneFPosN) end end if scale and self.boneFScaleN then firstBone.fAnimScale:SetValue(moho.layerFrame + self.interval, self.boneFScaleN) end end if secondBone then if angle and self.boneSAngleN then secondBone.fAnimAngle:SetValue(moho.layerFrame + self.interval, self.boneSAngleN) end if pos and self.boneSPosN then local channel = secondBone.fAnimPos if channel:AreDimensionsSplit() then local channelX = channel:DimensionChannel(0) local channelY = channel:DimensionChannel(1) channelX:SetValue(moho.layerFrame + self.interval, self.boneSPosN.x) channelY:SetValue(moho.layerFrame + self.interval, self.boneSPosN.y) else secondBone.fAnimPos:SetValue(moho.layerFrame + self.interval, self.boneSPosN) end end if scale and self.boneSScaleN then secondBone.fAnimScale:SetValue(moho.layerFrame + self.interval, self.boneSScaleN) end end end end function MR_PoseTool:CountBoneChildren(skel, boneID, ignoreControlledBones) local n = 0 for i = 0, skel:CountBones() - 1 do local bone = skel:Bone(i) if (bone.fParent == boneID) then n = n + 1 if (ignoreControlledBones) then if self.mohoVersion >= 14 then if (bone.fAngleControlParent >= 0 or bone.fPosControlParent >= 0 or bone.fScaleControlParent >= 0 or (bone.fBoneDynamics.value and (bone.fAngleDynamics or bone.fPosDynamics or bone.fScaleDynamics)) or bone.fIgnoredByIK) then n = n - 1 -- ignore this bone, as it is not free to move by itself end else if (bone.fAngleControlParent >= 0 or bone.fPosControlParent >= 0 or bone.fScaleControlParent >= 0 or bone.fBoneDynamics.value or bone.fIgnoredByIK) then n = n - 1 -- ignore this bone, as it is not free to move by itself end end end end end return n end function MR_PoseTool:RotateVector2(moho, vector2, center, angle) local centerVec = LM.Vector2:new_local() local newVector2 = LM.Vector2:new_local() local dif = LM.Vector2:new_local() centerVec:Set(center) dif:Set(vector2 - centerVec) local px = dif.x * math.cos(angle) - dif.y * math.sin(angle) local py = dif.x * math.sin(angle) + dif.y * math.cos(angle) newVector2:Set(px + centerVec.x, py + centerVec.y) return newVector2 end -- ************************************************** -- Localization -- ************************************************** function MR_PoseTool:Localize(text) local phrase = {} phrase['Description'] = 'Set up the pose of your character (hold <ctrl/cmd> to keep active handles visible, <alt> for compensation of child bone transformation, <shift> for additional transformation variations)' phrase['UILabel'] = 'MR Pose Tool 1.0' phrase['Keep selection'] = 'Keep selection' phrase['Hide bones while moving'] = 'Hide' phrase['Keep previous selection'] = 'Keep previous selection' phrase['Bake Adjacent Frames'] = 'Smart bake' phrase['Interval'] = 'Interval:' phrase['Show path'] = 'Show path' phrase['Range'] = 'Range' phrase['Range frames:'] = 'Range frames:' phrase['End flip'] = 'End flip' phrase['Side flip'] = 'Side flip' return phrase[text] end
MR Pose Tool
Listed
Author: eugenebabich
View Script
Script type: Tool
Uploaded: Aug 14 2023, 06:38
Last modified: Sep 25 2023, 11:14
Plan and polish your animation with ease!
MR Pose Tool allows you to minimize the routine and spend more effort on creativity. This script combines the tools Transform Bone, Manipulate Bones, MR Move Targeted Joint, and even more.
To work correctly the lower bone (the shin or the forearm) must not have any Y-axis translation.
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: 857

MR Pose Tool
Listed
Author: eugenebabich
View Script
Script type: Tool
Uploaded: Aug 14 2023, 06:38
Last modified: Sep 25 2023, 11:14
Plan and polish your animation with ease!
MR Pose Tool allows you to minimize the routine and spend more effort on creativity. This script combines the tools Transform Bone, Manipulate Bones, MR Move Targeted Joint, and even more.
To work correctly the lower bone (the shin or the forearm) must not have any Y-axis translation.
To work correctly the lower bone (the shin or the forearm) must not have any Y-axis translation.
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: 857