-- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** ScriptName = "AE_SeamlessRotationSmartMaker" -- ************************************************** -- General information about this script -- ************************************************** AE_SeamlessRotationSmartMaker = {} function AE_SeamlessRotationSmartMaker:Name() return 'Seamlessly rotating smartbone on/off' end function AE_SeamlessRotationSmartMaker:Version() return '1.0' end function AE_SeamlessRotationSmartMaker:UILabel() return 'Seamlessly rotating smartbone on/off' end function AE_SeamlessRotationSmartMaker:Creator() return 'Alexandra Evseeva' end function AE_SeamlessRotationSmartMaker:Description() return 'Create or discard a bone system to make smartbone joints rotaing seamlessly' end -- ************************************************** -- Is Relevant / Is Enabled -- ************************************************** function AE_SeamlessRotationSmartMaker:IsRelevant(moho) return true end function AE_SeamlessRotationSmartMaker:IsEnabled(moho) return true end -- ************************************************** -- The guts of this script -- ************************************************** function AE_SeamlessRotationSmartMaker:ReparentPoints(moho, layer, sourceBone, targetBone) if moho:LayerAsGroup(layer) then local group = moho:LayerAsGroup(layer) for i=0, group:CountLayers()-1 do self:ReparentPoints(moho, group:Layer(i), sourceBone, targetBone) end elseif moho:LayerAsVector(layer) then local skel = moho:Skeleton() local sourceID = skel:BoneID(sourceBone) local targetID = skel:BoneID(targetBone) local mesh = moho:LayerAsVector(layer):Mesh() for p = 0, mesh:CountPoints()-1 do local point = mesh:Point(p) if point.fParent == sourceID then point.fParent = targetID end end end end function AE_SeamlessRotationSmartMaker:MoveAngleAnimation(moho, sourceBone, targetBone) --copy animation from source to target in any of sources's smart actions (removing previously existing in target) local actionNames = {sourceBone:Name(), (sourceBone:Name().." 2")} for i, actionName in pairs(actionNames) do if moho.layer:HasAction(actionName) then local oldSmartRotation = sourceBone.fAnimAngle:ActionByName(actionName) if not oldSmartRotation then return string.format("Something went wrong: %s is not animated in its own action %s", sourceBone:Name(), actionName) end oldSmartRotation = moho:ChannelAsAnimVal(oldSmartRotation) if not targetBone.fAnimAngle:ActionByName(actionName) then targetBone.fAnimAngle:ActivateAction(actionName) end local newSmartRotation = moho:ChannelAsAnimVal(targetBone.fAnimAngle:ActionByName(actionName)) newSmartRotation:Clear() for k=0, oldSmartRotation:CountKeys()-1 do newSmartRotation:SetValue(oldSmartRotation:GetKeyWhen(k), oldSmartRotation:GetValueByID(k)) local interp = MOHO.InterpSetting:new_local() oldSmartRotation:GetKeyInterpByID(k, interp) newSmartRotation:SetKeyInterp(oldSmartRotation:GetKeyWhen(k), interp) end oldSmartRotation:Clear() end end --rename actions for i, actionName in pairs(actionNames) do local newName = targetBone:Name() .. string.sub(actionName, #sourceBone:Name()+1) moho.layer:RenameAction(actionName, newName) end --rebind angle-dependent bones local skel = moho:Skeleton() local sourceID = skel:BoneID(sourceBone) local targetID = skel:BoneID(targetBone) for b=0, skel:CountBones()-1 do local nextBone = skel:Bone(b) if nextBone.fAngleControlParent == sourceID then nextBone.fAngleControlParent = targetID end end --[[ --if arm has points bind to it, lets rebind them self:ReparentPoints(moho, moho.layer, sourceBone, targetBone) --]] return nil end function AE_SeamlessRotationSmartMaker:CreateSeamlessSystem(moho, armBone) local skel = moho:Skeleton() local baseName = armBone:Name() armBone.fConstraints = false --find or create target bone local targetName = baseName .. "_smartTarget" local targetBone = skel:BoneByName(targetName) if not targetBone then targetBone = skel:AddBone(0) targetBone:SetName(targetName) targetBone.fAnimParent:SetValue(0, skel:BoneID(armBone)) local tipVector = LM.Vector2:new_local() tipVector:Set(armBone.fLength, 0) targetBone.fAnimPos:SetValue(0, tipVector) targetBone.fLength = 0.0001 targetBone.fShy = true targetBone.fHidden = true end --find or create joint bone local jointName = baseName .. "_smartElbow" local jointBone = skel:BoneByName(jointName) if not jointBone then jointBone = skel:AddBone(0) jointBone:SetName(jointName) jointBone.fAnimParent:SetValue(0, armBone.fAnimParent:GetValue(0)) jointBone.fAnimPos:SetValue(0, armBone.fAnimPos:GetValue(0)) jointBone.fAnimAngle:SetValue(0, 0) jointBone.fLength = 0 jointBone.fPosControlParent = skel:BoneID(armBone) jointBone.fMaxConstraint = 0 jointBone.fMinConstraint = 0 jointBone.fShy = true jointBone.fHidden = true end --find or create smartBone local smartName = baseName .. "_smart" local smartBone = skel:BoneByName(smartName) if not smartBone then smartBone = skel:AddBone(0) smartBone:SetName(smartName) smartBone.fAnimParent:SetValue(0, skel:BoneID(jointBone)) --[[]] --if no points to rebind smartBone.fLength = 0.0001 -- --[[ --if arm has points bind to it, lets rebind them smartBone.fLength = armBone.fLength smartBone.fMaxAutoScaling = 1000 smartBone.fSquashStretchScaling = armBone.fSquashStretchScaling --]] local zeroVector = LM.Vector2:new_local() smartBone.fAnimPos:SetValue(0, zeroVector) smartBone.fAnimAngle:SetValue(0, armBone.fAnimAngle:GetValue(0)) smartBone.fTargetBone:SetValue(0, skel:BoneID(targetBone)) smartBone.fShy = true smartBone.fHidden = true end --create smartBone animation for any of arm's smart actions (removing previously existing) return self:MoveAngleAnimation(moho, armBone, smartBone) end function AE_SeamlessRotationSmartMaker:DiscardSeamlessSystem(moho, armBone, smartBone) local skel = moho:Skeleton() local baseName = armBone:Name() local smartName = baseName .. "_smart" --if smartBone provided, test if this is the bone user wanted. Then rename it and all its friends. if smartBone then local userReturn = LM.GUI.Alert(LM.GUI.ALERT_INFO, "There are no "..smartName.." bone in skeleton. But "..smartBone:Name().." seems to be the fake smartBone to turn off. It will be renamed.", "", "", "OK", "Cancel") if userReturn > 0 then return end --find joint and target bones and rename them first local targetBone = skel:Bone(smartBone.fTargetBone:GetValue(0)) if targetBone then targetBone:SetName(baseName .. "_smartTarget") else return LM.GUI.Alert(LM.GUI.ALERT_WARNING, smartBone:Name().." has no target. Aborting.") end local jointBone = skel:Bone(smartBone.fAnimParent:GetValue(0)) if jointBone then jointBone:SetName(baseName .. "_smartElbow") end else smartBone = skel:BoneByName(smartName) end --create animation for armBone in smartBone actions local errorText = self:MoveAngleAnimation(moho, smartBone, armBone) if not errorText then smartBone:SetName(smartName) else return LM.GUI.Alert(LM.GUI.ALERT_WARNING, errorText) end end function AE_SeamlessRotationSmartMaker:Run(moho) moho.document:SetDirty() moho.document:PrepUndo(nil) --test if selected bone has smart actions and call create or discard the system respecively local skel = moho:Skeleton() if not skel then return LM.GUI.Alert(LM.GUI.ALERT_WARNING, "Please select a bone layer") end local selBone = skel:Bone(skel:SelectedBoneID()) if not selBone then return LM.GUI.Alert(LM.GUI.ALERT_WARNING, "Please select any bone") end local selName = selBone:Name() if moho.layer:HasAction(selName) then return self:CreateSeamlessSystem(moho, selBone) end local smartName = selName .. "_smart" if moho.layer:HasAction(smartName) then return self:DiscardSeamlessSystem(moho, selBone) end if skel:BoneByName(smartName) then return LM.GUI.Alert(LM.GUI.ALERT_WARNING, string.format("Something went wrong: You have %s bone, but no action for it", smartName)) end local armParentID = selBone.fAnimParent:GetValue(moho.frame) for b=0, skel:CountBones()-1 do if b ~= skel:SelectedBoneID() and skel:Bone(b).fAnimParent:GetValue(moho.frame) == armParentID then for bb =0, skel:CountBones()-1 do if skel:Bone(bb).fAnimParent:GetValue(moho.frame) == b then local targetID = skel:Bone(bb).fTargetBone:GetValue(moho.frame) if targetID >= 0 and skel:Bone(targetID).fAnimParent:GetValue(moho.frame) == skel:SelectedBoneID() then local smartBone = skel:Bone(bb) if moho.layer:HasAction(smartBone:Name()) then return self:DiscardSeamlessSystem(moho, selBone, smartBone) end end end end end local targetID = skel:Bone(b).fTargetBone:GetValue(moho.frame) if targetID >= 0 and skel:Bone(targetID).fAnimParent:GetValue(moho.frame) == skel:SelectedBoneID() then local smartBone = skel:Bone(b) if moho.layer:HasAction(smartBone:Name()) then return self:DiscardSeamlessSystem(moho, selBone, smartBone) end end end return LM.GUI.Alert(LM.GUI.ALERT_WARNING, "Something went wrong: did not find any action related to selected bone, neither seamless, nor common") end