image
https://prnt.sc/r-s10uVgUttx
Code: Select all
-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************
ScriptName = "CM_Moho2AE"
-- **************************************************
-- General information about this script
-- **************************************************
CM_Moho2AE = {}
function CM_Moho2AE:Name()
return 'Export to After Effects jsx'
end
function CM_Moho2AE:Version()
return '1.0'
end
function CM_Moho2AE:UILabel()
return 'Export to After Effects jsx'
end
function CM_Moho2AE:Creator()
return 'Community test'
end
function CM_Moho2AE:Description()
return 'Create .jsx script which been called from AE imports all the bitmap images of moho bitmap layers and (optionaly) rendered sequences of moho vector layers, placing it into new composition in correct order and position (optionaly with layer animation too)'
end
-- **************************************************
-- Is Relevant / Is Enabled
-- **************************************************
function CM_Moho2AE:IsRelevant(moho)
return true
end
function CM_Moho2AE:IsEnabled(moho)
return true
end
-- **************************************************
-- CM_Moho2AEDialog
-- **************************************************
local CM_Moho2AEDialog = {}
function CM_Moho2AEDialog:new()
local d = LM.GUI.SimpleDialog('Export to After Effects jsx', CM_Moho2AEDialog)
local l = d:GetLayout()
return d
end
-- **************************************************
-- The guts of this script
-- **************************************************
-- map moho blending mode constants to AE blending mode constant names
CM_Moho2AE.blendingModesMap = {
[MOHO.BM_ADD] = "BlendingMode.ADD",
[MOHO.BM_COLOR] = "BlendingMode.COLOR",
[MOHO.BM_DIFFERENCE] = "BlendingMode.DIFFERENCE",
[MOHO.BM_HUE] = "BlendingMode.HUE",
[MOHO.BM_LUMINOSITY] = "BlendingMode.LUMINOSITY",
[MOHO.BM_MULTIPLY] = "BlendingMode.MULTIPLY",
[MOHO.BM_OVERLAY] = "BlendingMode.OVERLAY",
[MOHO.BM_SATURATION] = "BlendingMode.SATURATION",
[MOHO.BM_SCREEN] = "BlendingMode.SCREEN"
}
function CM_Moho2AE:ResourcePath()
local str = debug.getinfo(2, "S").source:sub(2)
local _, slashPos = string.find(string.lower(str), "scripts")
local slash = string.sub(str, slashPos+1, slashPos+1)
local pattern = "(.*\\)[mM]enu\\.*"
if slash == "/" then pattern = "(.*/)[mM]enu/.*" end
return (str:match(pattern) .. "ScriptResources".. slash .. "moho2ae" .. slash), slash
end
function CM_Moho2AE:Run(moho)
--dialog call to uncomment later
--[[
local dlog = CM_Moho2AEDialog:new(moho)
if (dlog:DoModal() == LM.GUI.MSG_CANCEL) then
return
end
--]]
moho.document:SetDirty()
moho.document:PrepUndo(nil)
-- read some common functions for jsx
local jsxCommonFunctionsPath = self.ResourcePath().."common.jsx"
local file = io.open(jsxCommonFunctionsPath, "r")
if not file then
return LM.GUI.Alert(LM.GUI.ALERT_WARNING, "No " .. jsxCommonFunctionsPath)
end
local content = file:read "*a" -- *a or *all reads the whole file
io.close(file)
-- ask user for output file
local path = LM.GUI.SaveFile("Select JSX File")
if (path == "") then
return
end
if string.sub(path, -4) ~= ".jsx" then path = path..".jsx" end
local f = io.open(path, "w")
if (f == nil) then
return
end
io.output(f)
-- write some common functions for jsx
io.write(content)
-- write creation of main comp with this comp size and duration
local name = string.sub(moho.document:Name(), 1, -6)
self.w = moho.document:Width()
self.h = moho.document:Height()
self.fps = moho.document:Fps()
self.dur = moho.document:EndFrame() - moho.document:StartFrame() + 1
self.seconds = self.dur/self.fps
io.write(string.format('var mainComp = app.project.items.addComp("%s", %s, %s, 1, %s, %s);\n', name, self.w, self.h, self.seconds, self.fps))
io.write(string.format('var mainFolder = app.project.items.addFolder("%s");\n', name))
io.write('mainComp.parentFolder = mainFolder;\n')
self.globalVarNameIndex = 0
io.write('var cameraNull = mainComp.layers.addNull();\ncameraNull.name = "camera";\n')
io.write('var persNull = mainComp.layers.addNull();\npersNull.name = "pers";\n')
self:BakeCamera(moho)
for i = 0, moho.document:CountLayers() - 1 do
local nextLayer = moho.document:Layer(i)
local nextCompLayerName = self:ProcessAnyLayer(moho, nextLayer, 'mainComp')
end
io.close(f)
end
function CM_Moho2AE:BakeCamera(moho)
--TODO: set animation for persNull and cameraNull layers, baking camera animation from Moho (see ae_export_2d_camera.lua for baking formulas)
end
function CM_Moho2AE:ProcessAnyLayer(moho, layer, parentCompVarName, folderName)
if not folderName then folderName = "mainFolder" end
local nextIndex = self.globalVarNameIndex + 1
local name = 'layer' .. nextIndex
self.globalVarNameIndex = self.globalVarNameIndex + 1
local layerType = layer:LayerType()
local newFootageItemVarName = nil
if layerType == MOHO.LT_GROUP then
--create folder for footage
local newFolderName = 'folder' .. nextIndex
io.write(string.format('var %s = %s.items.addFolder("%s");\n', newFolderName, folderName, layer:Name()))
newFootageItemVarName = self:CreateGroupFootage(moho, layer)
io.write(string.format('%s.parentFolder = %s;\n', newFootageItemVarName, folderName))
for i=0, moho:LayerAsGroup(layer):CountLayers() - 1 do
local nextLayer = moho:LayerAsGroup(layer):Layer(i)
self:ProcessAnyLayer(moho, nextLayer, newFootageItemVarName, newFolderName)
end
io.write(string.format('if (%s.items.length == 0) %s.remove();\n', newFolderName, newFolderName))
elseif layerType == MOHO.LT_IMAGE then
newFootageItemVarName = self:CreateBitmapFootage(moho, layer)
elseif layerType == MOHO.LT_BONE then
newFootageItemVarName = self:CreateSequenceFootage(moho, layer)
end
if not newFootageItemVarName then return nil end
-- place resulting footage (composition, image or sequence) into parent composition
if layerType == MOHO.LT_GROUP or layerType == MOHO.LT_IMAGE then
io.write(string.format('var %s = %s.layers.add(%s);\n', name, parentCompVarName, newFootageItemVarName))
self:ApplyLayerTransform(moho, layer, name)
if not layer:IsImmuneToCamera() then
io.write(string.format('%s.parent = cameraNull;\n', name))
end
elseif layerType == MOHO.LT_BONE and #newFootageItemVarName > 0 then
for k, v in pairs(newFootageItemVarName) do
io.write(string.format('var %s = %s.layers.add(%s);\n', name..v, parentCompVarName, v))
io.write(string.format('%s.parent = persNull;\n'), name..v)
end
end
self:ApplyLayerBlending(moho, layer, name)
if layerType == MOHO.LT_GROUP then
-- set collapse transformations on
io.write(string.format('%s.collapseTransformation = true;\n', name))
end
return name
end
function CM_Moho2AE:CreateGroupFootage(moho, layer)
local nextIndex = self.globalVarNameIndex + 1
local name = 'comp' .. nextIndex
self.globalVarNameIndex = self.globalVarNameIndex + 1
-- create new composition with same parameters as main one
io.write(string.format('var %s = app.project.items.addComp("%s", %s, %s, 1, %s, %s);\n', name, layer:Name(), self.w, self.h, self.seconds, self.fps))
return name
end
function CM_Moho2AE:CreateBitmapFootage(moho, layer)
local nextIndex = self.globalVarNameIndex + 1
local name = 'img' .. nextIndex
self.globalVarNameIndex = self.globalVarNameIndex + 1
-- import bitmap
local imageLayer = moho:LayerAsImage(layer)
local path = string.gsub(imageLayer:SourceImage(), "\\", "\\\\")
local args = string.format('"%s"', path)
if imageLayer:IsPSDImage() then
args = args .. string.format(', %s', imageLayer:PSDLayerOrderID() + 1)
end
-- call jsx function with these args (to import single bitmap or PSD file or use PSD layer allways imported for another layer of same moho project
io.write(string.format('var %s = FindOrImport(%s);\n', name, args));
return name
end
function CM_Moho2AE:CreateSequenceFootage(moho, layer)
local characters = {}
--TODO: find all the layercomps which include this layer
--TODO: compute path to rendered images (ask user in first dialog and save to script preferences)
--TODO: import sequence (if exists, or offline dummy if does not) to AE via jsx
-- return array of new layers' names
return characters
end
function CM_Moho2AE:ApplyLayerTransform(moho, layer, layerVarName)
-- set ae anchor and position to moho values
local aeAnchor = layer:Origin()
aeAnchor.x = aeAnchor.x * self.h/2
aeAnchor.y = -aeAnchor.y * self.h/2
local aePos = layer.fTranslation:GetValue(1)
aePos.x = aePos.x * self.h/2 + self.w/2 + aeAnchor.x
aePos.y = - aePos.y * self.h/2 + self.h/2 + aeAnchor.y
if layer:LayerType() == MOHO.LT_GROUP then
aeAnchor.x = aeAnchor.x + self.w/2
aeAnchor.y = aeAnchor.y + self.h/2
elseif layer:LayerType() == MOHO.LT_IMAGE then
local imageLayer = moho:LayerAsImage(layer)
aeAnchor.x = aeAnchor.x + imageLayer:PixelWidth()/2
aeAnchor.y = aeAnchor.y + imageLayer:PixelHeight()/2
end
io.write(string.format('%s.position.setValue([%s, %s]);\n', layerVarName, aePos.x, aePos.y))
io.write(string.format('%s.property("Anchor Point").setValue([%s, %s]);\n', layerVarName, aeAnchor.x, aeAnchor.y))
-- set ae rotation to moho values
local aeAngle = -180 * layer.fRotationZ:GetValue(1)/math.pi
io.write(string.format('%s.rotation.setValue(%s);\n', layerVarName, aeAngle))
-- set ae scale to moho scale and flip values
local aeScale = layer.fScale:GetValue(1) * 100
if layer.fFlipH:GetValue(1) then aeScale.x = -aeScale.x end
if layer.fFlipV:GetValue(1) then aeScale.y = -aeScale.y end
io.write(string.format('%s.scale.setValue([%s, %s, %s]);\n', layerVarName, aeScale.x, aeScale.y, aeScale.z))
end
function CM_Moho2AE:ApplyLayerBlending(moho, layer, layerVarName)
--TODO: set blending mode, transparency and visibility from moho layer to AE
end