Forgot all about vitruvian bones.
Code: Select all
-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************
ScriptName = "LM_ReparentBone"
-- **************************************************
-- General information about this script
-- **************************************************
LM_ReparentBone = {}
LM_ReparentBone.BASE_STR = 2230
function LM_ReparentBone:Name()
return "Reparent Bone"
end
function LM_ReparentBone:Version()
return "6.0"
end
function LM_ReparentBone:Description()
return MOHO.Localize("/Scripts/Tool/ReparentBone/Description=Click to attach bone to a new parent (hold <alt> to select a new bone, <ctrl/cmd> to select a target bone)")
end
function LM_ReparentBone:Creator()
return "Lost Marble LLC"
end
function LM_ReparentBone:UILabel()
return(MOHO.Localize("/Scripts/Tool/ReparentBone/ReparentBone=Reparent Bone"))
end
-- **************************************************
-- The guts of this script
-- **************************************************
function LM_ReparentBone:IsEnabled(moho)
if (moho:CountBones() < 2) then
return false
end
if (moho.frame > 0 and not MOHO.IsMohoPro()) then
return false
end
return true
end
function LM_ReparentBone:IsRelevant(moho)
local skel = moho:Skeleton()
if (skel == nil) then
return false
end
if (moho.frame > 0 and not MOHO.IsMohoPro()) then
return false
end
return true
end
function LM_ReparentBone:OnMouseDown(moho, mouseEvent)
moho.document:PrepUndo(moho.layer, true)
moho.document:SetDirty()
self:OnReparent(moho, mouseEvent)
end
function LM_ReparentBone:OnMouseMoved(moho, mouseEvent)
self:OnReparent(moho, mouseEvent)
end
function LM_ReparentBone:OnMouseUp(moho, mouseEvent)
moho:UpdateUI()
end
function LM_ReparentBone:OnKeyDown(moho, keyEvent)
LM_SelectBone:OnKeyDown(moho, keyEvent)
end
function LM_ReparentBone:DrawMe(moho, view)
local skel = moho:Skeleton()
if (skel == nil) then
return
end
local g = view:Graphics()
local matrix = LM.Matrix:new_local()
local bonePt = LM.Vector2:new_local()
local parentPt = LM.Vector2:new_local()
local arrowPt1 = LM.Vector2:new_local()
local arrowPt2 = LM.Vector2:new_local()
moho.layer:GetFullTransform(moho.frame, matrix, moho.document)
g:Push()
g:ApplyMatrix(matrix)
g:SetSmoothing(true)
for i = 0, skel:CountBones() - 1 do
local bone = skel:Bone(i)
--[syn] if (bone.fParent >= 0) then
--[[syn]] if (bone.fParent >= 0) and (not bone.fHidden --[[and not bone.fShy]]) and (bone:IsGroupVisible()) then
local parentBone = skel:Bone(bone.fParent)
bonePt:Set(bone.fLength / 2, 0)
bone.fMovedMatrix:Transform(bonePt)
parentPt:Set(parentBone.fLength / 2, 0)
parentBone.fMovedMatrix:Transform(parentPt)
local v = parentPt - bonePt
local mag = v:Mag()
local f = mag
if (f > 0.2) then
f = 0.2
end
arrowPt1:Set(mag - f / 4, -f / 16)
arrowPt2:Set(mag - f / 4, f / 16)
f = math.atan2(v.y, v.x)
arrowPt1:Rotate(f)
arrowPt2:Rotate(f)
arrowPt1.x = arrowPt1.x + bonePt.x
arrowPt1.y = arrowPt1.y + bonePt.y
arrowPt2.x = arrowPt2.x + bonePt.x
arrowPt2.y = arrowPt2.y + bonePt.y
if (bone.fSelected) then
g:SetColor(MOHO.MohoGlobals.SelCol)
else
g:SetColor(MOHO.MohoGlobals.ElemCol)
end
g:DrawLine(bonePt.x, bonePt.y, parentPt.x, parentPt.y)
g:BeginShape()
g:AddLine(parentPt, arrowPt1)
g:AddLine(arrowPt1, arrowPt2)
g:AddLine(arrowPt2, parentPt)
g:EndShape()
end
end
g:Pop()
end
function LM_ReparentBone:OnReparent(moho, mouseEvent)
if (mouseEvent.altKey) then
LM_SelectBone:Select(moho, mouseEvent.pt, mouseEvent.vec, mouseEvent.view, mouseEvent.shiftKey, mouseEvent.ctrlKey)
return
end
local skel = moho:Skeleton()
if (skel == nil) then
return
end
if (moho:CountSelectedBones() < 1) then
return
end
-- let the user pick another bone - this will be the new parent
local parentID = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, moho.layer, true)
local parentFrame = moho.layerFrame -- 0
if (mouseEvent.ctrlKey) then
local targetChanged = false
for i = 0, skel:CountBones() - 1 do
local bone = skel:Bone(i)
if (bone.fSelected and parentID ~= bone.fTargetBone:GetValue(moho.layerFrame)) then
bone.fTargetBone:SetValue(moho.layerFrame, parentID)
targetChanged = true
end
end
if (targetChanged) then
moho:NewKeyframe(CHANNEL_BONE_TARGET)
end
else
for i = 0, skel:CountBones() - 1 do
local bone = skel:Bone(i)
if (bone.fSelected) then
if (not skel:IsBoneChild(i, parentID)) then
-- new parent chosen
local v1 = LM.Vector2:new_local()
local v2 = LM.Vector2:new_local()
v1:Set(0, 0)
if (bone:IsZeroLength()) then
v2:Set(0.1, 0)
else
v2:Set(bone.fLength, 0)
end
bone.fMovedMatrix:Transform(v1)
bone.fMovedMatrix:Transform(v2)
if (parentID >= 0) then
local invMatrix = LM.Matrix:new_local()
local parent = skel:Bone(parentID)
invMatrix:Set(parent.fMovedMatrix)
invMatrix:Invert()
invMatrix:Transform(v1)
invMatrix:Transform(v2)
end
if (parentFrame ~= 0) then
bone.fAnimPos:AddKey(parentFrame - 1)
if (bone:ParentalFlipFactor() < 0.0) then
bone.fFlipV:SetValue(parentFrame, not bone.fFlipV:GetValue(parentFrame))
end
end
bone.fAnimPos:SetValue(parentFrame, v1)
v2 = v2 - v1
local angle = math.atan2(v2.y, v2.x)
while angle > 2 * math.pi do
angle = angle - 2 * math.pi
end
while angle < 0 do
angle = angle + 2 * math.pi
end
if (bone.fFixedAngle) then
if (parentFrame == 0) then
bone.fAnimAngle:SetValue(parentFrame, angle)
end
else
local angleDiff = angle - bone.fAnimAngle:GetValue(parentFrame)
if (parentFrame ~= 0) then
bone.fAnimAngle:AddKey(parentFrame - 1)
end
bone.fAnimAngle:SetValue(parentFrame, angle)
-- update future angle keyframes for relative offsets just like this frame
for keyID = 0, bone.fAnimAngle:CountKeys() - 1 do
local angleFrame = bone.fAnimAngle:GetKeyWhen(keyID)
if (angleFrame > parentFrame) then
local newAngle = bone.fAnimAngle:GetValue(angleFrame) + angleDiff
bone.fAnimAngle:SetValue(angleFrame, newAngle)
end
end
if (parentFrame == 0) then -- update action keyframes for relative offsets just like this frame
for actionID = 0, bone.fAnimAngle:CountActions() - 1 do
local action = moho:ChannelAsAnimVal(bone.fAnimAngle:Action(actionID))
for keyID = 0, action:CountKeys() - 1 do
local angleFrame = action:GetKeyWhen(keyID)
if (angleFrame > parentFrame) then
local newAngle = action:GetValue(angleFrame) + angleDiff
action:SetValue(angleFrame, newAngle)
end
end
end
end
end
if (bone.fAnimParent:CountKeys() < 2) then
bone.fAnimParent:SetValue(0, bone.fParent)
end
bone.fParent = parentID
bone.fAnimParent:SetValue(parentFrame, parentID)
if (parentFrame ~= 0) then
if (bone:ParentalFlipFactor() < 0.0) then
bone.fFlipV:SetValue(parentFrame, not bone.fFlipV:GetValue(parentFrame))
end
end
end
end
end
moho:NewKeyframe(CHANNEL_BONE)
moho:NewKeyframe(CHANNEL_BONE_T)
moho:NewKeyframe(CHANNEL_BONE_PARENT)
end
moho.layer:UpdateCurFrame()
mouseEvent.view:DrawMe()
moho:UpdateSelectedChannels()
end
-- **************************************************
-- Tool options - create and respond to tool's UI
-- **************************************************
LM_ReparentBone.CHANGE = MOHO.MSG_BASE
LM_ReparentBone.DUMMY = MOHO.MSG_BASE + 1
LM_ReparentBone.SELECTITEM = MOHO.MSG_BASE + 2
function LM_ReparentBone:DoLayout(moho, layout)
self.menu = LM.GUI.Menu(MOHO.Localize("/Scripts/Tool/ReparentBone/SelectBone=Select Bone"))
self.popup = LM.GUI.PopupMenu(128, false)
self.popup:SetMenu(self.menu)
layout:AddChild(self.popup)
end
function LM_ReparentBone:UpdateWidgets(moho)
local skel = moho:Skeleton()
if (skel == nil) then
return
end
MOHO.BuildBoneMenu(self.menu, skel, self.SELECTITEM, self.DUMMY)
end
function LM_ReparentBone:HandleMessage(moho, view, msg)
local skel = moho:Skeleton()
if (skel == nil) then
return
end
if (msg >= self.SELECTITEM) then
for i = 0, skel:CountBones() - 1 do
skel:Bone(i).fSelected = (i == msg - self.SELECTITEM)
end
moho:UpdateUI()
end
end