I got the basics of exporting and importing actions to moho files to work (with the keys in the mainline), during export I ended up simply modifying the current file and saving it as a new file for each action. When reverting back to the original file (because I also delete all layers except for the selected layer and its childlayers) it somehow crashes on the 2nd or 3rd time running the script. No idea why, especially because the first run always goes fine.synthsin75 wrote: ↑Wed Feb 09, 2022 5:12 am Yeah, I think we've talked about Moho not quite updating the moho.document reference before, especially with new, tabbed documents. Have you tried by having the script only have one open document at a time?
Code: Select all
-- **************************************************
-- Provide Moho with the name of this script object
-- ***************************************************
ScriptName = "LK_ExportActions"
-- ***************************************************
-- General information about this script
-- ***************************************************
LK_ExportActions = {}
function LK_ExportActions:Name()
return "LK_ExportActions"
end
function LK_ExportActions:Version()
return "0.1"
end
function LK_ExportActions:Description()
return "Export Actions"
end
function LK_ExportActions:Creator()
return "Lukas Krepel, Frame Order"
end
function LK_ExportActions:UILabel()
return "Export Actions"
end
function LK_ExportActions:ColorizeIcon()
return true
end
-- ***************************************************
-- The guts of this script
-- ***************************************************
function LK_ExportActions:IsRelevant(moho)
if MohoMode ~= nil then
return MohoMode.experimental
else
return true
end
end
function LK_ExportActions:IsEnabled(moho)
if MohoMode ~= nil then
return MohoMode.experimental
else
return true
end
end
function LK_ExportActions:Run(moho)
-- * Directories:
local path = moho.document:Path()
if path == nil then
print ("Save your file before exporting actions!")
return
end
local layer = moho.layer
local rigName = layer:Name()
-- * Remove version from name. For example, if the rig layer is called "CrazyGuy (v003_L)", we are going to remove " (v003_L)" and use "CrazyGuy"
if string.match(rigName, " %(") then
rigName = string.sub(rigName, 0, string.find(rigName, " %(")-1)
end
local actionsDirectory = FO_Utilities:FileDirectory(path).."/Actions" -- * No last Slash here for FileExists check!
if not FO_Utilities:FileExists(moho, actionsDirectory) then
FO_Utilities:Alert({"Please make sure the 'Actions' directory exists:", "'"..actionsDirectory.."/'"})
return
end
-- * Count poses and animations:
local poses = {}
local animations = {}
for a=0, layer:CountActions()-1 do
actionName = layer:ActionName(a)
if not layer:IsSmartBoneAction(actionName) then
local duration = layer:ActionDuration(actionName)
if duration == 1 then
table.insert(poses, actionName)
elseif duration > 1 then
table.insert(animations, actionName)
end
end
end
-- *
if #poses == 0 and #animations == 0 then
FO_Utilities:Alert(layer:Name().." has no Poses / Animations.")
return
end
-- * Are you sure?
moho:FileSave()
local startLayerID = moho.document:LayerID(moho.layer)
-- * Delete all other layers from project:
self:MakeLayerSolo(moho)
FO_Utilities:CreateDirectory(moho, actionsDirectory)
local rigActionsDirectory = actionsDirectory.."/"..rigName.."/"
FO_Utilities:CreateDirectory(moho, rigActionsDirectory)
-- *
local poses = {}
local animations = {}
for a=0, layer:CountActions()-1 do
actionName = layer:ActionName(a)
if not layer:IsSmartBoneAction(actionName) then
local duration = layer:ActionDuration(actionName)
if duration == 1 then
table.insert(poses, actionName)
elseif duration > 1 then
table.insert(animations, actionName)
end
end
end
local posesDirectory = rigActionsDirectory.."Poses/"
local animationsDirectory = rigActionsDirectory.."Animations/"
-- *
local messages = { "Created Moho-files in: '"..rigActionsDirectory.."'"}
-- *
if #poses > 0 then
FO_Utilities:CreateDirectory(moho, posesDirectory)
table.insert(messages, "In 'Pose(s)' subfolder:")
end
-- * First we create the files:
for i = 1, #poses do
local actionName = poses[i]
local fileName = actionName..".moho"
local actionPath = posesDirectory..fileName
moho:FileSaveAs(actionPath)
moho.document:ClearAnimation(0, false)
table.insert(messages, i.." - "..fileName)
moho.layer:InsertAction(actionName, 1, false)
moho:FileSave()
end
-- *
if #animations > 0 then
FO_Utilities:CreateDirectory(moho, animationsDirectory)
table.insert(messages, "In 'Animation(s)' subfolder:")
end
-- *
for i = 1, #animations do
local actionName = animations[i]
local fileName = actionName..".moho"
local actionPath = animationsDirectory..fileName
moho:FileSaveAs(actionPath)
moho.document:ClearAnimation(0, false)
table.insert(messages, i.." - "..fileName)
moho.layer:InsertAction(actionName, 1, false)
moho:FileSave()
end
-- *
local closePath = moho.document:Path()
if closePath ~= path then
moho:FileOpen(path)
moho:FileOpen(closePath)
moho:FileClose()
end
-- *
FO_Utilities:Alert(messages)
end
function LK_ExportActions:MakeLayerSolo(moho)
local layer0 = moho.document:Layer(0)
moho:PlaceLayerBehindAnother(moho.layer, layer0)
for i = moho.document:CountLayers()-1, 1, -1 do
moho:DeleteLayer(moho.document:Layer(i))
end
end
----------
----------
FO_Utilities = {}
-- **********
-- LAYER TAGS
-- **********
FO_Utilities.rigTag = "rig"
FO_Utilities.animTag = "anim"
FO_Utilities.outfitTag = "outfit"
FO_Utilities.embedTag = "embed"
FO_Utilities.keysTag = "keys"
-- *********
-- BONE TAGS
-- *********
-- * Constraining channels
FO_Utilities.constrainRotationTag = "!r"
FO_Utilities.constrainTranslationTag = "!t"
FO_Utilities.constrainScaleTag = "!s"
-- * Constrain individual translation dimensions
FO_Utilities.constrainXTranslationTag = "!x"
FO_Utilities.constrainYTranslationTag = "!y"
-- * Forcing interpolation
FO_Utilities.forceStepTag = "_step"
-- * Specific bones
FO_Utilities.hipTag = ".hip"
FO_Utilities.footTag = ".foot"
-- * Switchbone boneTag
FO_Utilities.switchTag = ".switch"
FO_Utilities.controlTag = ".control"
-- * Switchbone position tags:
FO_Utilities.posTag = ".pos"
-- *** List of all tags (LK_SelectBone needs this)
FO_Utilities.boneTags = { FO_Utilities.constrainTranslationTag, FO_Utilities.constrainXTranslationTag, FO_Utilities.constrainYTranslationTag, FO_Utilities.constrainRotationTag, FO_Utilities.constrainScaleTag, FO_Utilities.forceStepTag, FO_Utilities.hipTag, FO_Utilities.footTag, FO_Utilities.controlTag, FO_Utilities.switchTag.."%d?%d?%d?", FO_Utilities.posTag.."%d?%d?%d?" }
-- * Settings:
FO_Utilities.tinyUI = false
FO_Utilities.tinyUITreshold = 1500
FO_Utilities.reverseBoneColorButtons = true
FO_Utilities.forceBigUI = false
-- * Color names:
FO_Utilities.colorNames = {}
FO_Utilities.colorNames[0] = "Plain"
FO_Utilities.colorNames[1] = "Red"
FO_Utilities.colorNames[2] = "Orange"
FO_Utilities.colorNames[3] = "Yellow"
FO_Utilities.colorNames[4] = "Green"
FO_Utilities.colorNames[5] = "Blue"
FO_Utilities.colorNames[6] = "Purple"
FO_Utilities.colorNames[7] = "Tan"
FO_Utilities.colorNames[8] = "Pink"
FO_Utilities.colorNames[9] = "Turquoise"
FO_Utilities.colorNames[10] = "CadetBlue"
FO_Utilities.colorNames[11] = "Coral"
-- * Color RGB values:
FO_Utilities.colorsR = {}
FO_Utilities.colorsG = {}
FO_Utilities.colorsB = {}
FO_Utilities.colorsR[0], FO_Utilities.colorsG[0], FO_Utilities.colorsB[0] = MOHO.MohoGlobals.ElemCol.r, MOHO.MohoGlobals.ElemCol.g, MOHO.MohoGlobals.ElemCol.b -- No color
FO_Utilities.colorsR[1], FO_Utilities.colorsG[1], FO_Utilities.colorsB[1] = 220, 64, 51 -- Red
FO_Utilities.colorsR[2], FO_Utilities.colorsG[2], FO_Utilities.colorsB[2] = 240, 160, 0 -- Orange
FO_Utilities.colorsR[3], FO_Utilities.colorsG[3], FO_Utilities.colorsB[3] = 240, 240, 0 -- Yellow
FO_Utilities.colorsR[4], FO_Utilities.colorsG[4], FO_Utilities.colorsB[4] = 64, 220, 51 -- Green
FO_Utilities.colorsR[5], FO_Utilities.colorsG[5], FO_Utilities.colorsB[5] = 89, 126, 183 -- Blue
FO_Utilities.colorsR[6], FO_Utilities.colorsG[6], FO_Utilities.colorsB[6] = 192, 96, 191 -- Purple
FO_Utilities.colorsR[7], FO_Utilities.colorsG[7], FO_Utilities.colorsB[7] = 225, 185, 107 -- Tan
FO_Utilities.colorsR[8], FO_Utilities.colorsG[8], FO_Utilities.colorsB[8] = 248, 162, 159 -- Pink
FO_Utilities.colorsR[9], FO_Utilities.colorsG[9], FO_Utilities.colorsB[9] = 109, 223, 210 -- Turquoise
FO_Utilities.colorsR[10], FO_Utilities.colorsG[10], FO_Utilities.colorsB[10] = 129, 200, 187 -- Cadet Blue
FO_Utilities.colorsR[11], FO_Utilities.colorsG[11], FO_Utilities.colorsB[11] = 248, 146, 96 -- Coral
FO_Utilities.colorsR[12], FO_Utilities.colorsG[12], FO_Utilities.colorsB[12] = MOHO.MohoGlobals.InacCol.r, MOHO.MohoGlobals.InacCol.g, MOHO.MohoGlobals.InacCol.b -- Color for hidden bones
FO_Utilities.colorsR[13], FO_Utilities.colorsG[13], FO_Utilities.colorsB[13] = MOHO.MohoGlobals.SelCol.r, MOHO.MohoGlobals.SelCol.g, MOHO.MohoGlobals.SelCol.b -- Color for selected bones
function FO_Utilities:DrawMeTinyUI(moho)
-- * Full screen on MacOS Cintiq 21UX low res mode = 1498
local UIbool = false
if not FO_Utilities.forceBigUI then
UIbool = moho.view:Graphics():Width() < FO_Utilities.tinyUITreshold
end
if FO_Utilities.tinyUI ~= UIbool then
FO_Utilities.tinyUI = UIbool
FO_Utilities:ReloadTools(moho)
end
end
-- **************************************************
-- Reload tools by hopping in and out of frame 0
-- **************************************************
function FO_Utilities:ReloadTools(moho)
local returnFrame = moho.frame
local tempFrame = 0
if returnFrame == tempFrame then
tempFrame = 1
end
moho:SetCurFrame(tempFrame)
moho:SetCurFrame(returnFrame)
end
-- **************************************************
-- Getting the skeleton
-- **************************************************
function FO_Utilities:GetSkel(moho)
local skel = moho:Skeleton()
if (skel == nil) then
if (not moho.layer:IsBoneType()) then
skel = moho:ParentSkeleton()
end
end
return skel
end
function FO_Utilities:Divider(layout, title, first)
if not first then
layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL, 0) --divider
end
if FO_Utilities.tinyUI == false and title ~= nil then
layout:AddChild(LM.GUI.StaticText(title..":"))
layout:AddPadding(-15)
end
end
-- **************************************************
-- Get Layer-matrix without Camera-matrix
-- **************************************************
function FO_Utilities:LayerMatrix(moho, layer, frame) -- actions not processed yet
local prevMatrix = LM.Matrix:new_local()
prevMatrix:Identity()
repeat
local prevLayer = layer
local matrix = LM.Matrix:new_local()
layer:GetLayerTransform(frame, matrix, moho.document)
matrix:Multiply(prevMatrix)
prevMatrix:Set(matrix)
if layer:Parent() then
layer = layer:Parent()
end
until layer == prevLayer
-- * Subtract camera matrix:
local cameraMatrix = LM.Matrix:new_local()
moho.document:GetCameraMatrix(frame, cameraMatrix)
cameraMatrix:Invert()
cameraMatrix:Multiply(prevMatrix)
local layerMatrixWithoutCamera = cameraMatrix
return layerMatrixWithoutCamera
end
-- **************************************************
-- Get Layer-matrix WITH Camera-matrix
-- **************************************************
function FO_Utilities:LayerMatrixWithCamera(moho, layer, frame) -- actions not processed yet
local prevMatrix = LM.Matrix:new_local()
prevMatrix:Identity()
repeat
local prevLayer = layer
local matrix = LM.Matrix:new_local()
layer:GetLayerTransform(frame, matrix, moho.document)
matrix:Multiply(prevMatrix)
prevMatrix:Set(matrix)
if layer:Parent() then
layer = layer:Parent()
end
until layer == prevLayer
return prevMatrix
--[[
-- * Subtract camera matrix:
local cameraMatrix = LM.Matrix:new_local()
moho.document:GetCameraMatrix(frame, cameraMatrix)
cameraMatrix:Invert()
cameraMatrix:Multiply(prevMatrix)
local layerMatrixWithoutCamera = cameraMatrix
return layerMatrixWithoutCamera
--]]
end
-- **************************************************
-- Embed layerscript on layer
-- **************************************************
function FO_Utilities:EmbedLayerScript(moho, layer, script)
local userpath = string.gsub(moho:UserAppDir(), '\\', '/')
local layerscriptsfolder = userpath .. "/Shared Resources/Embedded Scripts/"
if not string.match(script, ".lua") then
script = script..".lua"
end
local scriptfile = script
local script = layerscriptsfolder..scriptfile
layer:SetLayerScript(script)
FO_Utilities:AddTag("embed", layer)
end
-- **************************************************
-- Python Script Command String
-- **************************************************
function FO_Utilities:Python(moho, pythonScript)
-- if not string.match(pythonScript, ".py") then
-- pythonScript = pythonScript..".py"
-- end
local pythonVersion = ""
local pythonScriptDirectory = moho:UserAppDir().."/python/"
pythonScriptPath = pythonScriptDirectory..pythonScript
-- * Fix slashes per OS:
if(FO_Utilities:getOS() == "win") then
pythonVersion = "python"
pythonScriptPath = string.gsub(pythonScriptPath, "/", "\\") -- * Reverse and double slashes for Windows
-- * Add quotes around paths:
pythonScriptPath = "\""..pythonScriptPath.."\""
else
pythonVersion = "python3"
pythonScriptPath = string.gsub(pythonScriptPath, " ", "\\ ") -- * Add backslash to space
end
local command = pythonVersion.." "..pythonScriptPath
return command
end
-- **************************************************
-- Run Powershell Command???
-- **************************************************
function FO_Utilities:Powershell(programWithParams)
print ("FO_Utilities:Powershell(programWithParams = "..programWithParams..")")
local psCmd = '"powershell.exe -Command "Start-Process cmd " -ArgumentList "/c", "\'' .. programWithParams .. '\'" -Verb RunAs -Wait"'
--print (psCmd)
os.execute(psCmd)
end
-- *****************************************************
-- Return a string based on any variables. By hayasidist
-- *****************************************************
function FO_Utilities:ToString(...)
return HS_formatUserData(...)
end
-- **************************************************
-- Execute command line
-- **************************************************
function FO_Utilities:Execute(command, ask, feedback)
Debug:Log(" - Command:")
Debug:Log(command)
Debug:Log("ask = "..tostring(ask))
Debug:Log("feedback = "..tostring(feedback))
if ask then
local messages = string.split(command, " %-")
table.insert(messages, "Are you sure?")
local dlog = FO_Utilities:AreYouSureDialog(moho, "Execute command?", messages)
if (dlog:DoModal() == LM.GUI.MSG_CANCEL) then
return "cancelled"
end
end
Debug:Log("Executing command!")
local output
if self:getOS() == ("unix") then
output = io.popen(command)
else
-- os.execute(' "start "any title" "C:\\Program Files\\Lost Marble\\Moho 13.5\\Moho.exe" -r "w:\\zombies.moho" -f PNG -o "R:\\\\_out\\\\zombies\\\\" " ')
output = os.execute(command)
end
return output -- * returns true if an error is thrown, otherwise nil.
end
-- **************************************************
-- Reveal directory in Finder/Explorer
-- **************************************************
function FO_Utilities:RevealDirectory(path)
local command
if self:getOS() == "win" then
path = string.gsub(path, "/", "\\")
command = "explorer \""..path.."\""
local output = os.execute(command)
Debug:Log("Windows: output = "..tostring(output))
else
command = "open "..string.gsub(path, " ", "\\ ")
local output = io.popen(command)
if output == nil then
FO_Utilities:Alert("Directory doesn't exist (yet), or you are not connected to the server!", path)
end
end
end
-- **************************************************
-- Split String
-- **************************************************
function string.split(s, delimiter)
result = {}
local first = true
for match in (s..string.gsub(delimiter, "%%", "")):gmatch("(.-)"..delimiter) do
if first or delimiter == " " then
table.insert(result, match)
first = false
else
table.insert(result, string.gsub(delimiter, "%%", "")..match)
end
end
return result
end
-- **************************************************
-- Collapse all groups in the layer panel
-- **************************************************
function FO_Utilities:CollapseGroups(moho)
local layers = FO_Utilities:AllLayers(moho)
local i
for i = 1, #layers do
layer = layers[i]
if layer:IsGroupType() == true then
layer:Expand(false)
end
end
end
-- **************************************************
-- Getting the rig layer:
-- **************************************************
function FO_Utilities:RigLayer(moho)
local skel = FO_Utilities:GetSkel(moho)
local layer = moho.layer
local tags = layer:UserTags()
if string.match(tags, FO_Utilities.rigTag) then
return layer
end
--
while (layer ~= nil and not string.match(tags, FO_Utilities.rigTag)) do
layer = layer:Parent()
if (layer ~= nil) then
tags = layer:UserTags()
if string.match(tags, FO_Utilities.rigTag) then
return layer
end
end
end
--
-- * FAILED TO FIND A RIG-TAGGED BONE LAYER!!!")
if (moho.layer:LayerType() == 4 or moho.layer:LayerType() == 5 ) then -- 4 = LT_BONE -- 5 = LT_SWITCH
return moho.layer
end
-- *** Trying again, but this time, we're not going to look for a tag, but any bone layer.
layer = moho.layer
while layer ~= nil and (not (layer:LayerType() == 4) or not (layer:LayerType() == 5)) do
layer = layer:Parent()
if layer ~= nil then
if layer:LayerType() == 4 or layer:LayerType() == 5 then
return layer
end
end
end
-- * ...ALSO FAILED TO FIND AN UN-TAGGED BONE LAYER!!!")
return nil
end
-- *******************************************************
-- Gets color for bone according to comment, returns color
-- *******************************************************
function FO_Utilities:LegacyBoneCommentColor(moho, boneID)
-- print ("bonecommentcolor being called")
local color = 0
if rigLayer == nil then -- * help is dit verstandig?
return color
end
local comment = rigLayer:UserComments()
if comment == nil then
local skel = FO_Utilities:GetSkel(moho)
color = skel:Bone(boneID):Tags()
return color
end
local boneNumber = boneID+1
if string.len(comment) < boneNumber * 3 then
return 0
end
-- *** WAARSCHUWING: comment:sub crashed keihard als hij niks kan vinden ***
color = comment:sub(boneNumber*2-1+boneNumber-1, boneNumber*2-1+boneNumber) -- variant om "01.02.03." te lezen
if string.match(color, "%d%d") then
color = tonumber(color)
else
local skel = FO_Utilities:GetSkel(moho)
local bone = skel:Bone(boneID)
if bone ~= nil then
color = bone:Tags()
end
end
return color
end
-- **************************************************
-- Replaces original function!!!
-- **************************************************
function MOHO.BuildBoneMenu(menu, skel, baseMsg, dummyMsg, moho) -- original doesn't accept the moho argument!
local vanilla = false
if MohoMode ~= nil then
if MohoMode.vanilla then
vanilla = true
end
end
if not vanilla then
FO_Utilities:BuildBoneMenu(menu, skel, baseMsg, dummyMsg, moho)
else
menu:RemoveAllItems()
for i = 0, skel:CountBones() - 1 do
local bone = skel:Bone(i)
if (bone:Name() ~= "") then
menu:AddItem(bone:Name(), 0, baseMsg + i)
end
end
if (menu:CountItems() == 0) then
menu:AddItem(MOHO.Localize("/Scripts/Utility/None=<None>"), 0, dummyMsg)
menu:SetEnabled(dummyMsg, false)
end
end
end
-- ****************************************************************************
-- Returns duration as a formatted string, for example: "6 Minutes, 30 Seconds"
-- ****************************************************************************
function FO_Utilities:DisplayDuration(time)
local days = math.floor(time/86400)
local hours = math.floor(math.fmod(time, 86400)/3600)
local minutes = math.floor(math.fmod(time,3600)/60)
local seconds = math.floor(math.fmod(time,60))
local msg = ""
if days > 0 then
msg = string.format("%d Days, ", days)
end
if hours > 0 then
msg = msg..string.format("%d Hours, ", hours)
end
if minutes > 0 then
msg = msg..string.format("%d Minutes, ", minutes)
end
msg = msg..string.format("%d Seconds", seconds)
return msg
end
-- ********************************************************************************************************
-- Finds all layers in current document and returns 'layers', sets checkmarks for layers that aren't hidden
-- ********************************************************************************************************
function FO_Utilities:BuildBoneMenu(menu, skel, baseMsg, dummyMsg, moho)
menu:RemoveAllItems()
-- * Bone Tables
local boneTable = {}
local smartBoneTable = {}
local shyBoneTable = {}
local unnamedBonesTable = {}
-- * Get Bones
for i = 0, skel:CountBones() - 1 do
local bone = skel:Bone(i)
local nameless = string.match(bone:Name(), "B%d%d?%d?")
if moho ~= nil and FO_Utilities:IsASmartBone(moho, skel, bone) then
table.insert(smartBoneTable, {boneName = bone:Name(), int = 0, msg = baseMsg + i, selected = bone.fSelected, hidden = bone.fHidden})
elseif not bone.fShy and not nameless then
table.insert(boneTable, {boneName = bone:Name(), int = 0, msg = baseMsg + i, selected = bone.fSelected, hidden = bone.fHidden})
elseif nameless then
table.insert(unnamedBonesTable, {boneName = bone:Name(), int = 0, msg = baseMsg + i, selected = bone.fSelected, hidden = bone.fHidden})
else
table.insert(shyBoneTable, {boneName = bone:Name(), int = 0, msg = baseMsg + i, selected = bone.fSelected, hidden = bone.fHidden})
end
end
-- * Sort bones alphabetically
table.sort(smartBoneTable, function(a,b) return a.boneName < b.boneName end)
table.sort(boneTable, function(a,b) return a.boneName < b.boneName end)
table.sort(shyBoneTable, function(a,b) return a.boneName < b.boneName end)
table.sort(unnamedBonesTable, function(a,b) return a.boneName < b.boneName end)
-- * Add Bones to Dropdown Menu
FO_Utilities:AddBoneTableToMenu(smartBoneTable, "SMARTBONES", menu)
FO_Utilities:AddBoneTableToMenu(boneTable, "BONES", menu)
FO_Utilities:AddBoneTableToMenu(shyBoneTable, "SHY BONES", menu)
FO_Utilities:AddBoneTableToMenu(unnamedBonesTable, "NAMELESS BONES", menu)
--
if (menu:CountItems() == 0) then
menu:AddItem(MOHO.Localize("/Scripts/Utility/None=<None>"), 0, dummyMsg)
menu:SetEnabled(dummyMsg, false)
end
end
-- **************************************************
-- Replaces original function!!!
-- **************************************************
function MOHO.BuildBoneChoiceMenu(menu, skel, baseMsg, exclude, moho)
local vanilla = false
if MohoMode ~= nil then
if MohoMode.vanilla then
vanilla = true
end
end
if not vanilla then
-- * new function
menu:RemoveAllItems()
menu:AddItem(MOHO.Localize("/Scripts/Utility/None=<None>"), 0, baseMsg)
-- * Bone Tables
local boneTable = {}
local smartBoneTable = {}
local shyBoneTable = {}
local unnamedBonesTable = {}
-- * Get Bones
for i = 0, skel:CountBones() - 1 do
if i ~= exclude then
local bone = skel:Bone(i)
local nameless = string.match(bone:Name(), "B%d%d?%d?")
if moho ~= nil and FO_Utilities:IsASmartBone(moho, skel, bone) then
table.insert(smartBoneTable, {boneName = bone:Name(), int = 0, msg = baseMsg + 1 + i, selected = bone.fSelected, hidden = false})
elseif not bone.fShy and not nameless then
table.insert(boneTable, {boneName = bone:Name(), int = 0, msg = baseMsg + 1 + i, selected = bone.fSelected, hidden = false})
elseif nameless then
table.insert(unnamedBonesTable, {boneName = bone:Name(), int = 0, msg = baseMsg + 1 + i, selected = bone.fSelected, hidden = false})
else
table.insert(shyBoneTable, {boneName = bone:Name(), int = 0, msg = baseMsg + 1 + i, selected = bone.fSelected, hidden = false})
end
end
end
-- * Sort bones alphabetically
table.sort(smartBoneTable, function(a,b) return a.boneName < b.boneName end)
table.sort(boneTable, function(a,b) return a.boneName < b.boneName end)
table.sort(shyBoneTable, function(a,b) return a.boneName < b.boneName end)
table.sort(unnamedBonesTable, function(a,b) return a.boneName < b.boneName end)
-- * Add Bones to Dropdown Menu
FO_Utilities:AddBoneTableToMenu(smartBoneTable, "SMARTBONES", menu)
FO_Utilities:AddBoneTableToMenu(boneTable, "BONES", menu)
FO_Utilities:AddBoneTableToMenu(shyBoneTable, "SHY BONES", menu)
FO_Utilities:AddBoneTableToMenu(unnamedBonesTable, "NAMELESS BONES", menu)
-- * end new function
else
-- * Original function!
menu:RemoveAllItems()
menu:AddItem(MOHO.Localize("/Scripts/Utility/None=<None>"), 0, baseMsg)
for i = 0, skel:CountBones() - 1 do
local bone = skel:Bone(i)
if (i ~= exclude and bone:Name() ~= "") then
menu:AddItem(bone:Name(), 0, baseMsg + 1 + i)
end
end
-- * End Original function!
end
end
function FO_Utilities:AddBoneTableToMenu(bTable, title, menu)
if #bTable ~= 0 then
local label = "___________[ "..#bTable.." "..title.." ]"
menu:AddItem(label, 0, dummyMsg)
menu:SetEnabled(dummyMsg, false)
--
for i = 1, #bTable do
local boneName = bTable[i].boneName
local int = bTable[i].int
local msg = bTable[i].msg
local selected = bTable[i].selected
local hidden = bTable[i].hidden
if (boneName ~= "") then
menu:AddItem(boneName, int, msg)
menu:SetChecked(msg, selected)
menu:SetEnabled(msg, not hidden)
end
end
--
end
end
-- ******************************************************
-- Return filename of the embedded layerscript of a layer
-- ******************************************************
function FO_Utilities:LayerScript(layer)
local embeddedscript = layer:LayerScript()
if embeddedscript ~= "" then
embeddedscript = self:FileName(embeddedscript)
else
embeddedscript = nil
end
return embeddedscript
end
-- ************************************************************
-- Finds all layercomps in current document and returns 'comps'
-- ************************************************************
function FO_Utilities:AllComps(moho)
local doc = moho.document
local comps = {}
for i = 0, doc:CountLayerComps() do
local comp = doc:GetLayerComp(i)
table.insert(comps, comp)
end
return comps
end
-- *********************************************************
-- Finds all layers in current document and returns 'layers'
-- *********************************************************
function FO_Utilities:AllLayers(moho)
-- print ("FO_Utilities:AllLayers(moho)")
local layers = {}
local stack = {}
local sp = 0
for i=0, moho.document:CountLayers()-1 do
local layer = moho.document:Layer(i)
table.insert(layers, layer)
local group = nil
local layerID = 0
while true do
if (layer:IsGroupType()) then
table.insert(stack, {group, layerID -1})
sp = sp+1
group = moho:LayerAsGroup(layer)
layerID = group:CountLayers()
end
if (layerID > 0) then
layerID = layerID -1
layer = group:Layer--[[ByDepth]](layerID)
table.insert(layers, layer)
else
layerID = -1
while (sp > 0) do
group, layerID = stack[sp][1], stack[sp][2]
table.remove(stack)
sp = sp -1
if (layerID >= 0) then
layer = group:Layer--[[ByDepth]](layerID)
table.insert(layers, layer)
break
end
end
end
if (layerID < 0) then
break
end
end
end
return layers
end
-- *********************************************************
-- Finds all layers in current document and returns 'layers'
-- *********************************************************
function FO_Utilities:LayerInclChildLayers(moho, layer)
local layers = {}
local stack = {}
local sp = 0
table.insert(layers, layer)
if (layer:IsGroupType()) then
moho:LayerAsGroup(layer)
for i=0, layer:CountLayers()-1 do
local layer = layer:Layer(i)
table.insert(layers, layer)
local group = nil
local layerID = 0
while true do
if (layer:IsGroupType()) then
table.insert(stack, {group, layerID -1})
sp = sp+1
group = moho:LayerAsGroup(layer)
layerID = group:CountLayers()
end
if (layerID > 0) then
layerID = layerID -1
layer = group:Layer--[[ByDepth]](layerID)
table.insert(layers, layer)
else
layerID = -1
while (sp > 0) do
group, layerID = stack[sp][1], stack[sp][2]
table.remove(stack)
sp = sp -1
if (layerID >= 0) then
layer = group:Layer--[[ByDepth]](layerID)
table.insert(layers, layer)
break
end
end
end
if (layerID < 0) then
break
end
end
end
end
return layers
end
-- **********************************************************
-- Returns full layerstructure of specified layer as a string
-- For example: "~/Forest/CrazyGuy/Arm/HandSwitch/Open"
-- **********************************************************
function FO_Utilities:LayerStructure(layer)
structure = layer:Name()
while layer:Parent() ~= nil do
layer = layer:Parent()
structure = layer:Name().."/"..structure
end
structure = "~/"..structure
return structure
end
-- **************************************************
-- List files in specific directory, returns 'files'
-- **************************************************
function FO_Utilities:ListFiles(directory, moho)
local files = {}
moho:BeginFileListing(directory, true)
local file = moho:GetNextFile()
while file ~= nil do
if file ~= ".DS_Store" then
table.insert(files, file)
end
file = moho:GetNextFile()
end
table.sort(files, function(a, b) return a:upper() < b:upper() end) -- * Sorts table alphabetically
return files
end
-- **************************************************
-- Check if a certain file exists
-- **************************************************
function FO_Utilities:FileExists(moho, filePath)
-- * Check if file exists:
filePath = string.gsub(filePath, "\\", "/")
local lastslashpos = (filePath:reverse()):find("%/") -- find last slash
local fileName = (filePath:sub(-lastslashpos+1)) -- filename only
local fileDir = string.gsub(filePath, string.gsub(fileName, "%-", "%%-"), "") -- * Catch dash characters
moho:BeginFileListing(fileDir, true)
local fileFound = false
local file = moho:GetNextFile()
while file ~= nil and not fileFound do
if file == fileName then
fileFound = true
else
file = moho:GetNextFile()
end
end
return fileFound
end
-- **************************************************
-- Get file directory
-- **************************************************
function FO_Utilities:FileDirectory(filePath)
filePath = string.gsub(filePath, "\\", "/")
local lastslashpos = (filePath:reverse()):find("%/") -- find last slash
local fileDir = string.sub(filePath, 0, -lastslashpos-1)
return fileDir
end
-- ****************************************************
-- List layers with a specific tag, returns 'taglayers'
-- ****************************************************
function FO_Utilities:ListLayersWithTag(tag, moho) -- todo taglayers was ooit global, returned nu!
local taglayers = {}
local layers = FO_Utilities:AllLayers(moho)
local i
for i = 1, #layers do
layer = layers[i]
local tags = layer:UserTags()
--local tag = "rig"
if string.match(tags, tag) then
table.insert(taglayers, layer)
end
end
return taglayers
end
-- **************************************************
-- Find a layer by name
-- **************************************************
function FO_Utilities:AnyLayerByName(moho, name, types)
local layers = FO_Utilities:AllLayers(moho)
for i = 1, #layers do
local layer = layers[i]
if types ~= nil then
local layerType = layer:LayerType()
for j = 1, #types do
local type = types[j]
if layerType ~= type then
if layer:Name() == name then
return layer
end
end
end
else
if layer:Name() == name then
return layer
end
end
end
return nil
end
-- **************************************************
-- LAYER TYPES
-- **************************************************
-- 0 LT_UNKNOWN Unkown layer type
-- 1 LT_VECTOR Vector layer type
-- 2 LT_IMAGE Image layer type
-- 3 LT_GROUP Group layer type
-- 4 LT_BONE Bone layer type
-- 5 LT_SWITCH Switch layer type
-- 6 LT_PARTICLE Particle layer
-- 7 LT_NOTE Note layer type
-- 8 LT_3D 3D layer type
-- 9 LT_AUDIO Audio layer type
-- 10 LT_PATCH Patch layer type
-- 11 LT_TEXT Text layer type
-- **************************************************
-- **************************************************
-- Toggles a tag on selected layers
-- **************************************************
function FO_Utilities:ToggleTagSelectedLayers(tag, moho)
local selCount = moho.document:CountSelectedLayers()
local layer = moho.layer
local tagMatch = false
local tagNotfound = false
local doAddTag = false
local tags
--Round 1 - Check if any of the selected layers already has the tag but other don't. Because in that case we should add the tag to all layers which don't have it yet.
for i = 0, selCount - 1 do
local layer = moho.document:GetSelectedLayer(i)
tags = layer:UserTags()
-- * Is ["..tag.."] in ["..tags.."]?")
if string.match(tags, tag) then
tagMatch = true
else
tagNotfound = true
end
end
--Decide if we need to add or remove the tag from all layers
if tagNotfound == true then-- and tagMatch == false then
doAddTag = true
end
-- * doAddTag: "..tostring(doAddTag))
--Round 2 - Actually do it per layer
for i = 0, selCount - 1 do
local layer = moho.document:GetSelectedLayer(i)
local tags = layer:UserTags()
if doAddTag == false then -- Remove tag from layer
self:RemoveTag(tag, layer, moho)
elseif doAddTag == true then -- Add tag to each layer
self:AddTag(tag, layer, moho)
end
end
end
-- *****************************************************************************************
-- Check if layer should never be considered an animation layer (particles, references, etc)
-- *****************************************************************************************
function FO_Utilities:NonKeysTagLayer(layer, moho)
local skipLayer = false
-- * Skip Particles:
local parentLayer = layer:Parent() -- * todo, check entire parent hierarchy
if parentLayer ~= nil then
if moho:LayerAsParticle(parentLayer) ~= nil then
skipLayer = true
end
end
return skipLayer
end
-- **************************************************
-- Adds a tag to a layer
-- **************************************************
function FO_Utilities:AddTag(tag, layer)
tags = layer:UserTags()
if tags == "" then
tags = tag
elseif not string.match(tags, tag) then
tags = tags .. ", " .. tag -- Add tag to tags string
tags = self:CleanString(tags)
end
layer:SetUserTags(tags)
end
-- **************************************************
-- Cleans string of unnecessary spaces and commas
-- **************************************************
function FO_Utilities:CleanString(str)
-- * Remove spaces before a comma:
while string.match(str, " ,") do
str = string.gsub(str, " , ", ",")
end
-- * Remove double commas:
while string.match(str, ",,") do
str = string.gsub(str, ",,", ",")
end
-- * Add spaces after comma's:
str = string.gsub(str, ",",", ")
-- *
if string.sub(str,1, 1) == "," then -- * Check if the string starts with a comma
str = string.sub(str, 2) -- * Remove the first character of the string because it's a comma
end
-- * Remove double spaces:
while string.match(str, " ") do
str = string.gsub(str, " ", " ")
end
-- * Remove space at the end of string:
if string.sub(str, 0, 1) == " " then
str = str:sub(2, -1)
end
-- * Remove space at the end of string:
if string.sub(str, -1) == " " then
str = str:sub(1, -2)
end
return str
end
-- **************************************************
-- Adds a tag to all selected layers
-- **************************************************
function FO_Utilities:AddTagToSelectedLayers(tag, moho) --FOUT
-- * FO_Utilities:AddTagToSelectedLayers("..tag..", moho)")
local selCount = moho.document:CountSelectedLayers()
for i = 0, selCount - 1 do
local layer = moho.document:GetSelectedLayer(i)
self:AddLayerTag(tag, layer, moho)
end
end
-- **************************************************
-- Removes a tag to all selected layers
-- **************************************************
function FO_Utilities:RemoveTagFromSelectedLayers(tag, moho) --FOUT
local selCount = moho.document:CountSelectedLayers()
for i = 0, selCount - 1 do
local layer = moho.document:GetSelectedLayer(i)
self:RemoveLayerTag(tag, layer, moho)
end
end
-- **************************************************
-- Removes a tag from a layer
-- **************************************************
function FO_Utilities:RemoveTag(tag, layer, moho)
-- * FO_Utilities:RemoveTag("..tag..", "..layer:Name()..", moho)")
local tags = layer:UserTags()
local commaTag = "," .. tag
local commaSpaceTag = ", " .. tag
tags = tags:gsub(commaSpaceTag, "") -- Remove ", anim" from tags string
tags = tags:gsub(commaTag, "") -- remove ",anim" from string, in case it doesn't have the space
tags = tags:gsub(tag, "") -- remove tag from tags string if it wasn't already deleted with comma and/or space
tags = self:CleanString(tags)
layer:SetUserTags(tags)
end
-- ***********************************************************************
-- Get shot name based on second to last underscore (removes _v000_X.moho)
-- ***********************************************************************
function FO_Utilities:ShotName(moho)
local path = moho.document:Path()
if path == nil then
print ("path == nil")
return nil
end
local fileName = self:FileName(path)
local shotName = string.gsub(fileName, "_v%d%d%d_%u+.moho", "")
return shotName
end
-- ***********************************************************************
-- Strip everything before the last slash of a full filepath and return filename
-- ***********************************************************************
function FO_Utilities:FileName(path)
path = string.gsub(path, "\\", "/")
local lastslashpos = (path:reverse()):find("%/") -- find last slash
local fileName = (path:sub(-lastslashpos+1)) -- filename only
return fileName
end
-- *****************************************************************************************************
-- Filters layerpanel by tag and selects a layer if necessary and optionally set the timeline visibility
-- *****************************************************************************************************
function FO_Utilities:FilterTag(tag, setTimelineVisibility, moho)
if (setTimelineVisibility == false and moho:LayersWindowGetSearchContext() == 8 and moho:LayersWindowGetSearchContextValue() == tag) then
moho:LayersWindowSetSearchContext(0) -- 0 = LAYERWND_SEARCHCONTEXT_ALL
moho:LayersWindowSetSearchContextValue("")
moho:ShowLayerInLayersPalette(moho.layer)
else
moho:LayersWindowSetSearchContext(8) -- 8 = LAYERWND_SEARCHCONTEXT_TAGCONTAIN
moho:LayersWindowSetSearchContextValue(tag)
local layer = moho.layer
first_match = nil
tags = layer:UserTags()
if string.match(tags, tag) then
first_match = layer
end
local layers = FO_Utilities:AllLayers(moho)
local i
for i = 1, #layers do
layer = layers[i]
tags = layer:UserTags()
if string.match(tags, tag) then
if first_match == nil then
first_match = layer
end
if (setTimelineVisibility == true) then
layer:SetShownOnTimeline(true)
end
elseif (setTimelineVisibility == true) then
layer:SetShownOnTimeline(false)
end
end
if first_match ~= nil then
moho:SetSelLayer(first_match, false, false)
end
end
end
-- **************************************************
-- Filters layerpanel by group name
-- **************************************************
function FO_Utilities:FilterGroupname(groupname, moho)
if (moho:LayersWindowGetSearchContext() == 2) then
moho:LayersWindowSetSearchContext(0) -- 0 = LAYERWND_SEARCHCONTEXT_ALL
moho:LayersWindowSetSearchContextValue("")
else
moho:LayersWindowSetSearchContext(2) -- 2 = LAYERWND_SEARCHCONTEXT_GROUPNAMECONTAINS
moho:LayersWindowSetSearchContextValue(groupname)
end
end
-- **************************************************
-- Check whether a table contains an element
-- **************************************************
function table.contains(table, element)
if table ~= nil then
for _, value in pairs(table) do
if value == element then
return true
end
end
end
return false
end
-- **************************************************
-- Deletes an element from a table
-- **************************************************
function table.delete(tbl, element)
for index, value in pairs(tbl) do
if value == element then
table.remove(tbl, index)
end
end
end
-- **************************************************
-- Round a number by x amount of decimals
-- **************************************************
function round(number, decimals)
local power = 10^decimals
return math.floor(number * power) / power
end
-- ***********************************************************************
-- Show all child layers of 'layer' that are tagged "anim" on the timeline
-- ***********************************************************************
function FO_Utilities:ShowAnimChildLayersOnTimeline(layer)
local tags = layer:UserTags()
if (string.match(tags, "rig") and not layer:SecondarySelection()) then
return
end
for i = 0, layer:CountLayers() - 1 do
local layer = layer:Layer(i)
tags = layer:UserTags()
local tags = layer:UserTags()
if layer:LayerType() == 3 or layer:LayerType() == 4 or layer:LayerType() == 5 then
self:ShowAnimChildLayersOnTimeline(layer)
end
if string.match(tags, "anim") and not string.match(tags, "rig") then
layer:SetShownOnTimeline(true)
end
end
end
-- **************************************************
-- Recolor bones (The vanilla way)
-- **************************************************
function FO_Utilities:RecolorizeBones(vanilla, moho)
if moho.layer:UserComments() == "" then
return
end
local skel = FO_Utilities:GetSkel(moho)
if skel == nil then
return
end
local customSkelActive = FO_Draw:WantCustomSkel(moho)
for i = 0, skel:CountBones() - 1 do
local bone = skel:Bone(i)
local boneColorComment = FO_Utilities:LegacyBoneCommentColor(moho, i)
if vanilla and not customSkelActive then
if boneColorComment ~= 0 then
bone:SetTags(boneColorComment)
end
end
end
if rigLayer ~= nil then
Debug:Log("Removed comment from "..moho.layer:Name().." ("..moho.layer:UserComments()..")")
rigLayer:SetUserComments("")
end
-- * Force viewport to redraw itself:
MOHO.Redraw()
-- * Update UI so timeline channels show up and dissapear correctly:
moho:UpdateUI()
end
function FO_Utilities:Alert2(msg1, msg2, msg3)
local type = LM.GUI.ALERT_WARNING
if msg1 == "i" then
type = LM.GUI.ALERT_INFO
elseif msg1 == "!" then
type = LM.GUI.ALERT_WARNING
elseif msg1 == "?" then
type = LM.GUI.ALERT_QUESTION
end
if msg3 ~= nil then
msg3 = "\n"..msg3
end
LM.GUI.Alert(icon, msg1, msg2, msg3, "OK", nil, nil)
end
-- *********************************************************
-- Simple alert dialog, takes many strings.
-- Either as a table with strings, or as multiple arguments.
-- Add a "i"/"?"/"!" to change the type of alertbox.
-- *********************************************************
function FO_Utilities:Alert(...)
local arg = {...}
if (type(...) == "table") then
arg = ...
end
local msg1 = nil
local msg2 = nil
local msg3 = nil
local type = LM.GUI.ALERT_WARNING
for i,v in ipairs(arg) do
if v == "i" then
type = LM.GUI.ALERT_INFO
elseif v == "!" then
type = LM.GUI.ALERT_WARNING
elseif v == "?" then
type = LM.GUI.ALERT_QUESTION
elseif msg1 == nil then
msg1 = v
elseif msg2 == nil then
msg2 = v
elseif msg3 == nil then
msg3 = "\n"..v
else
msg3 = msg3.."\n"..v
end
end
LM.GUI.Alert(type, msg1, msg2, msg3, "OK", nil, nil)
end
-- ****************************************************
-- Create simple question dialog with OK/Cancel buttons
-- ****************************************************
-- * An empty table to set up as a dialog subclass:
FO_Utilities.AreYouSureDialogTable = {}
-- * Dialog function:
function FO_Utilities:AreYouSureDialog(moho, dialogTitle, messages)
local d = LM.GUI.SimpleDialog(dialogTitle, self.AreYouSureDialogTable)
local l = d:GetLayout()
d.moho = moho
local command = ""
local align = LM.GUI.ALIGN_LEFT
if messages == nil then
return d
end
for i = 1, #messages do
msg = messages[i]
if i == #messages then
if (string.match(string.lower(dialogTitle), "command")) then -- * SHOULD WRITE A NEAT SEPARATE FUNCTION...
l:PushH()
d.textBox = LM.GUI.TextControl(0, "Some text for a textbox to set its length!", 0, LM_FIELD_TEXT, "Command:")
d.textBox:SetValue(command)
l:AddChild(d.textBox, align)
local tip = "(Copy/paste command into Windows Command Prompt)"
if FO_Utilities:getOS() == "unix" then
tip = "(Copy/paste command into MacOS Terminal)"
end
l:AddChild(LM.GUI.StaticText(tip))
l:Pop()
end
align = LM.GUI.ALIGN_RIGHT
else
command = command..msg
end
l:AddChild(LM.GUI.StaticText(msg), align)
end
return d
end
-- ****************************************************
-- Create simple question dialog with text input
-- ****************************************************
-- * An empty table to set up as a dialog subclass:
FO_Utilities.InputTextDialogTable = {}
-- * Dialog function:
function FO_Utilities:InputTextDialog(moho, dialogTitle, label, default, messages)
local d = LM.GUI.SimpleDialog(dialogTitle, self.InputTextDialogTable)
local l = d:GetLayout()
d.moho = moho
local align = LM.GUI.ALIGN_LEFT
d.textBox = LM.GUI.TextControl(0, "Room for a loooooooooooooong string", 0, LM_FIELD_TEXT, label)
d.textBox:SetValue(default)
l:AddChild(d.textBox, align)
if messages ~= nil then
for i = 1, #messages do
msg = messages[i]
print (msg)
l:AddChild(LM.GUI.StaticText(msg), align)
end
end
return d
end
function FO_Utilities.InputTextDialogTable:OnOK()
FO_Utilities.input = self.textBox:Value()
end
-- *************************************************
-- Create simple question dialog with Yes/No buttons
-- *************************************************
function FO_Utilities:YesNoQuestion(line1, line2, line3)
local d = LM.GUI.Alert(
LM.GUI.ALERT_QUESTION,
line1,
line2,
line3,
"Yes", -- returns 0
"No", -- returns 1
nil
)
return d
end
-- **************************************************
-- Create directory
-- **************************************************
function FO_Utilities:CreateDirectory(moho, directory)
local directoryExists = false
if moho ~= nil then
directoryExists = FO_Utilities:FileExists(moho, directory)
end
if not directoryExists then
if FO_Utilities:getOS() == "win" then
directory = "\""..directory.."\""
os.execute("mkdir "..directory)
else
directory = string.gsub(directory, " ", "\\ ")
os.execute("mkdir "..directory)
end
end
end
-- **************************************************
-- Get Operating System, returns "win" or "unix"
-- **************************************************
function FO_Utilities:getOS()
if os.getenv("OS") ~= nil
then
local opSys = string.lower(string.sub(os.getenv("OS"), 1, 3))
if opSys == "win" then
return "win"
else
return "unix"
end
else
return "unix"
end
end
-- **************************************************
-- Get Moho Version
-- **************************************************
function FO_Utilities:MohoVersion(moho)
if moho.AppVersion ~= nil then
local sVersion = string.gsub(moho:AppVersion(), "^(%d+)(%.%d+)(%..+)", "%1%2")
version = tonumber(sVersion)
return version
end
end
-- **************************************************
-- Open text file in text editor
-- **************************************************
function FO_Utilities:EditTextFile(path)
local command
local editor = "Sublime Text"
if(FO_Utilities:getOS()=="win") then
local editorpath = "C:\\Program Files\\Sublime Text 3\\sublime_text.exe"
path = string.gsub(path, "/", "\\") -- * Reverse and double slashes for Windows
command = "start \""..editor.."\" \""..editorpath.."\" \""..path.."\""
else
path = string.gsub(path, " ", "\\ ") -- * Add backslash to space
command = "open -a \""..editor.."\" "..path
end
FO_Utilities:Execute(command)
-- ************************************************************************
-- *** In "Sublime Text" config, add: 'open_files_in_new_window": true,'
-- ************************************************************************
end
-- **************************************************
-- Get bone's basename without all the bonetags
-- **************************************************
function FO_Utilities:BaseName(boneName)
local tags = FO_Utilities.boneTags
local baseName = boneName
for word in string.gmatch(boneName, "%S+") do
for i = 1, #tags do
local tag = tags[i]
if string.match(word, tag) then
baseName = string.gsub(baseName, tag, "")
end
end
end
baseName = FO_Utilities:CleanString(baseName)
baseName = string.gsub(baseName, '[ \t]+%f[\r\n%z]', '') -- trim whitespace at start and end of string
return baseName
end
-- **************************************************
-- Get distance between vectors
-- **************************************************
function FO_Utilities:Distance(moho, vector1, vector2)
local distance = vector1 - vector2
distance = math.abs(distance:Mag())
return distance
end
-- **************************************************
-- Check if bone has a smartbone action
-- **************************************************
function FO_Utilities:IsASmartBone(moho, skel, bone)
local name = bone:Name()
if not moho.layer:HasAction(name) then
return false
end
if string.find(name, "|") then -- ?
return false
end
local boneID = skel:BoneID(bone)
local layers = FO_Utilities:AllLayers(moho)
for i = 1, #layers do
local layer = layers[i]
if layer:ControllingSkeleton() == skel then
local parentBone = layer:LayerParentBone()
if parentBone >= 0 then
if parentBone == boneID then
return false
end
else
local meshLayer = moho:LayerAsVector(layer)
if meshLayer then
local mesh = meshLayer:Mesh()
for p=0, mesh:CountPoints()-1 do
if mesh:Point(p).fParent == boneID then
return false
end
end
end
end
end
end
return true
end
--- **************************************************
-- Checks if layer is animated
-- **************************************************
function FO_Utilities:LayerIsAnimated(moho, layer)
if not layer:IsReferencedLayer() then
if not self:NonKeysTagLayer(layer, moho) then
layer:ClearLayerKeyCount()
if (layer:CountLayerKeys() > 2) then
return true
else
return false
end
end
end
end
-- **************************************************
-- Paint keys of bones the same color as the bone
-- **************************************************
function FO_Utilities:PaintKeys(moho)
local c, i
local doc = moho.document
local layer = moho.layer
if layer:IsBoneType() then
local boneLayer = moho:LayerAsBone(layer)
local skel = boneLayer:Skeleton()
if skel:CountBones() > 0 then
local channels = {}
for i = 0, skel:CountBones() - 1 do
bone = skel:Bone(i)
boneColor = bone:Tags()
c = 0
c = c + 1; channels[c] = skel:Bone(i).fAnimPos
c = c + 1; channels[c] = skel:Bone(i).fAnimAngle
c = c + 1; channels[c] = skel:Bone(i).fAnimScale
c = c + 1; channels[c] = skel:Bone(i).fAnimParent
c = c + 1; channels[c] = skel:Bone(i).fTargetBone
c = c + 1; channels[c] = skel:Bone(i).fFlipH
c = c + 1; channels[c] = skel:Bone(i).fFlipV
c = c + 1; channels[c] = skel:Bone(i).fIKGlobalAngle
c = c + 1; channels[c] = skel:Bone(i).fIKLock
c = c + 1; channels[c] = skel:Bone(i).fIKParentTarget
c = c + 1; channels[c] = skel:Bone(i).fPhysicsMotorSpeed
for c = 1, #channels do
channel = channels[c]
local endKey = channel:Duration()
local thisKey = 0
local keyCount = channel:CountKeys()
local keysFound = 0
local frameNum = endKey
while keysFound < keyCount do
thisKey = channel:GetClosestKeyID(frameNum)
keyFrameNum = channel:GetKeyWhen(thisKey)
keysFound = 1 + keysFound
local interp = MOHO.InterpSetting:new_local()
channel:GetKeyInterp(keyFrameNum, interp)
interp.tags = boneColor
channel:SetKeyInterp(keyFrameNum, interp)
frameNum = keyFrameNum - 1
end
end
end
end
end
end
function FO_Utilities:ColorizeBone(moho, bone, boneID, color)
if FO_Draw:DoCustomSkel(moho) then
bone:SetTags(0)
else
bone:SetTags(color)
end
local doubleDigitColor = (string.format("%02d" .. ".",color))
local comment = moho.layer:UserComments()
while string.len(comment) < skel:CountBones()*3 do
comment = comment.."."
end
comment = (comment:sub(0, 3*boneID) .. doubleDigitColor .. (comment:sub(3*boneID+4)))
moho.layer:SetUserComments(comment)
end
-- **************************************************************************
-- Dummy function to make sure no errors are thrown if Debug.lua is not found
-- **************************************************************************
Debug = {}
function Debug:Log()
return
end