ScriptName = 'WP_spineImport' -- ************************************************** -- Import images from Spine Json files -- Created by Maarten de Haas, Wigglepixel -- ************************************************** --[[ ***** Licence & Warranty ***** Copyright 2023 - Maarten de Haas / Wigglepixel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 Conditions require preservation of copyright and license notices. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works. You can: Use - use/reuse freely, even commercially Adapt - remix, transform, and build upon for any purpose Share - redistribute the material in any medium or format Adapt / Share under the following terms: Attribution - You must give appropriate credit, provide a link to the Apache 2.0 license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. Licensed works, modifications and larger works may be distributed under different License terms and without source code. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. The Developer Maarten de Haas, Wigglepixel will not be liable for any direct, indirect or consequential loss of actual or anticipated - data, revenue, profits, business, trade or goodwill that is suffered as a result of the use of the software provided. ]] --[[ ***** SPECIAL THANKS to: * Lua JSON parser: https://github.com/rxi/json.lua (rxi) ]] -- ************************************************** -- General information about this script -- ************************************************** WP_spineImport = {} local json WP_spineImport.WP_UTILS_REQUIRED_VERSION = { 1, 3, 0 } function WP_spineImport:Name() return 'Spine Import' end function WP_spineImport:Version() return '1.1.3' end function WP_spineImport:UILabel() return 'Spine.json Images Import' end function WP_spineImport:Description() return 'Import assets from default skin in Spine json file' end function WP_spineImport:Creator() return 'Maarten de Haas, Wigglepixel' end function WP_spineImport:ColorizeIcon() return false end -- ************************************************** -- Is Relevant / Is Enabled -- ************************************************** function WP_spineImport:IsRelevant(moho) if (not _did_my_resources_package_path) then package.path = package.path .. ";" .. moho:UserAppDir() .. "/scripts/ScriptResources/?.lua;" _did_my_resources_package_path = true end json = json or require('json.json') return true end function WP_spineImport:IsEnabled(moho) return (moho.document:CurrentDocAction() == '') end -- ************************************************** -- GO -- ************************************************** function WP_spineImport:Run(moho) local mohoDoc = moho.document local alertOnWarnings = false local warningsList = {} local imgExtSearchOrder = { 'png', 'jpg', 'jpeg' } local OSPathsUseForwardSlashes -- will be set after browsing path local spineObj -- CHECK UTILS VERSION local utilsVersionCheck = WP_utils:compareVersion(WP_spineImport.WP_UTILS_REQUIRED_VERSION, WP_utils.VERSION) if (utilsVersionCheck < 0) then WP_utils:alert("Halted. This script requires at least version "..WP_utils:ver2str(WP_spineImport.WP_UTILS_REQUIRED_VERSION).." of 'wp_utils'") return elseif (utilsVersionCheck > 0) then WP_utils:alert("Caution! This loaded 'wp_utils' is newer than the expected version "..WP_utils:ver2str(WP_spineImport.WP_UTILS_REQUIRED_VERSION)) end -- ADD WARNING TO THE LIST local function raiseWarning(msg) table.insert(warningsList, '- '..msg) if (alertOnWarnings) then WP_utils:alert(newMsg) end end -- SEARCHES BONE IN BONES OBJECT. RETURNS BONE IF FOUND, OR nil IF NOT FOUND local function findBone(name) if (spineObj and spineObj.bones) then for _, bone in ipairs(spineObj.bones) do if (bone.name == name) then return bone end end end return nil end -- CALCULATE TRANSFORMS AND SET PARENT BONE REFERENCES local function processBones() if (spineObj and spineObj.bones) then local bones = spineObj.bones local parentBone for _, bone in ipairs(bones) do bone.parentObj = nil -- will be set below -- TRANSLATION IN LOCAL SPACE if (bone.x == nil) then bone.x = 0 end if (bone.y == nil) then bone.y = 0 end -- POSITION IN WORLD COORDINATES (USED FOR CALCULATING POSITION OF IMAGES) bone.worldX = bone.x bone.worldY = bone.y -- LENGTH (UNDEPENDENT OF TRANSFORMATIONS) if (bone.length == nil) then bone.length = 0 end -- RELATIVE/LOCAL ROTATION (IN DEGREES) if (bone.rotation == nil) then bone.rotation = 0 end -- WORLD/ABSOLUTE ROTATION (IN DEGREES) bone.worldRotation = bone.rotation -- LOCAL SCALE (1.0 = SAME SCALING AS PARENT) if (bone.scaleX == nil) then bone.scaleX = 1 end if (bone.scaleY == nil) then bone.scaleY = 1 end if (bone.scaleX ~= bone.scaleY) then raiseWarning("Independent X/Y bone scaling is not supported. Using scaleX as scale for bone '"..bone.name.."'.") end bone.scaleSingle = bone.scaleX bone.scaleY = bone.scaleX -- TEMP SOLUTION FOR IMAGE CALCULATIONS BECAUSE INDEPENDENT SCALING IS NOT SUPPORTED ON BONES -- WORLD/ABSOLUTE SCALE bone.worldScaleX = bone.scaleX -- will be set below bone.worldScaleY = bone.scaleY -- will be set below -- LOCAL SHEAR (NOT SUPPORTED YET) if (bone.shearX == nil) then bone.shearX = 0 end if (bone.shearY == nil) then bone.shearY = 0 end if (bone.shearX ~= 0 or bone.shearY ~= 0) then raiseWarning("Shear on bones is not supported. Ignoring Shear on bone '"..bone.name.."'.") end -- LOCAL TRANSFORMATION VECTOR (POSITION, ROTATION, SCALE) bone.locVec2 = LM.Vector2:new_local() bone.locVec2.x = bone.x -- will be set below bone.locVec2.y = bone.y -- will be set below -- WORLD TRANSFORMATION VECTOR (POSITION, ROTATION, SCALE) bone.worldVec2 = LM.Vector2:new_local() bone.worldVec2.x = bone.x -- will be set below bone.worldVec2.y = bone.y -- will be set below if (bone.parent) then parentBone = findBone(bone.parent) if (parentBone) then -- SET PARENT BONE OBJECT bone.parentObj = parentBone -- CALC LOCAL TRANSFORMATION VECTOR if (parentBone.worldRotation ~= 0) then bone.locVec2:Rotate(WP_utils:deg2rad(parentBone.worldRotation)) end bone.locVec2.x = bone.locVec2.x * parentBone.scaleSingle bone.locVec2.y = bone.locVec2.y * parentBone.scaleSingle -- WORLD TRANSFORMATION VECTOR AND WORLD POSITION bone.worldVec2:Set(parentBone.worldVec2 + bone.locVec2) bone.worldX = bone.worldVec2.x bone.worldY = bone.worldVec2.y -- CALC WORLD ROTATION bone.worldRotation = parentBone.worldRotation + bone.rotation -- CALC WORLD SCALE bone.worldScaleX = parentBone.worldScaleX * bone.worldScaleX bone.worldScaleY = parentBone.worldScaleY * bone.worldScaleY end end end end end -- CREATES LAYER WITH IMAGE local function addImgAsLayer(parentLayer, name, imgFile, posPx, rotDeg, scale, tintColStr) local layer = moho:LayerAsImage(moho:CreateNewLayer(MOHO.LT_IMAGE, false)) layer:SetName(name) -- CONVERT FORWARD SLASHES TO BACKWARDS SLASHES ON WINDOWS, OTHERWISE 'REVEAL SOURCE IMAGE' ISN'T WORKING IN WINDOWS if (OSPathsUseForwardSlashes == false) then imgFile = imgFile:gsub('/', '\\') end layer:SetSourceImage(imgFile) if (posPx) then local layerTransVec3 = WP_utils:getMohoRelLayerTransVec3(moho, posPx.x, posPx.y) layer.fTranslation:SetValue(0, layerTransVec3) end if (rotDeg) then layer.fRotationZ:SetValue(0, WP_utils:deg2rad(rotDeg)) end if (scale) then local scaleVec3 = LM.Vector3:new_local() scaleVec3:Set(scale.x, scale.y, 1) layer.fScale:SetValue(0, scaleVec3) end if (parentLayer) then moho:PlaceLayerInGroup(layer, parentLayer, true, false) end -- ADD TINT LAYER if (tintColStr) then local tintLayer = moho:DuplicateLayer(layer, true) tintLayer:SetName(name..'_tint') -- COLORIZE local colVec = spineHexColToMohoColVec(tintColStr) tintLayer.fLayerColor:SetValue(0, colVec) tintLayer.fLayerColorOn:SetValue(0, true) -- BLENDING MODE tintLayer:SetBlendingMode(MOHO.BM_MULTIPLY) end end -- RETURN SKIN OBJECT FROM SKINS ARRAY (ONLY FOR ARRAY FORMATTED SKINS) local function getSkinObjFromSkinsArr(skinsArr, skinName) for _, value in ipairs(skinsArr) do if (value.name ~= nil and value.name == skinName) then return value end end return nil end -- CONVERTS SPINE COLOR STRING TO MOHO COLOR VECTOR local function spineHexColToMohoColVec(hexStr) local rgbVec = LM.rgb_color:new_local() if (hexStr:len() == 8) then rgbVec.r = tonumber('0x'..hexStr:sub(1,2)) rgbVec.g = tonumber('0x'..hexStr:sub(3,4)) rgbVec.b = tonumber('0x'..hexStr:sub(5,6)) rgbVec.a = tonumber('0x'..hexStr:sub(7,8)) return rgbVec else return nil end end -- ========================================================================= -- SETTINGS local debugMode = true local spineFileExt = '.json' local importScale = 1 -- set scale to scale up or down complete group local importBones = true -- set to true to import bones too local expandBoneGroupLayerAfterCreation = true -- if bone layer is expanded after creation local importImagesFromMeshes = false -- set to true to import images from meshes (caution! locations wont work because mesh UV's don't get imported) alertOnWarnings = true local showBoneLabels = false -- true to show label on each bone local cancelOnImageNotFound = false -- if set to true importer will cancel import when an image is not found. if false only a warning will be registered local alwaysCreateUniqueLayerNames = false -- checks through all layers if there is already a layer with that name. If so adds a postfix number -- GET SPINE FILE local spineFile = LM.GUI.OpenFile('Please Spine JSON File... (Wigglepixel Spine Import v'..WP_spineImport:Version()..')') if (spineFile ~= '') then local fileExt = string.match(spineFile, spineFileExt..'$') if (fileExt ~= '.json') then WP_utils:alert('Only Spine files with extension .json are alowed') return end else return end if (string.find(spineFile, '/')) then OSPathsUseForwardSlashes = true else OSPathsUseForwardSlashes = false end local absSpineFileBasePath = WP_utils:getPath(spineFile) local spineFilenameWithoutPath = WP_utils:getFilenameFromPath(spineFile) local spineFileBasename = WP_utils:trim(string.sub(spineFilenameWithoutPath, 1, -(string.len(spineFileExt) + 1))) spineObj = json.decode(WP_utils:readFile(spineFile)) -- GET IMAGES PATH local absImagesPath = absSpineFileBasePath if (spineObj.skeleton and type(spineObj.skeleton.images) == 'string') then local imgPath = WP_utils:trim(spineObj.skeleton.images) if (imgPath ~= '') then if (imgPath:sub(-1, -1) == '/') then imgPath = imgPath:sub(1, -2) end if (imgPath:sub(1,1) == '.') then -- relative absImagesPath = (absImagesPath..'/'..imgPath):gsub('/./', '/') -- remove /./ same dir dot halfway path. throws issues on windows else -- absolute absImagesPath = imgPath end end end absImagesPath = absImagesPath:gsub('\\', '/')-- all forward slash -- GET BONES + SET REFERENCE TO PARENT BONES AND CALCULATE WORLD TRANSFORMS processBones() -- GET SKINS if (spineObj.skins == nil or type(spineObj.skins) ~= 'table') then WP_utils:alert('Halted. No or unrecognized skins object found in spine file. Didn\'t make any changes to your Moho file yet.') return end local skinsCount = WP_utils:arrLen(spineObj.skins) if (skinCount == 0) then WP_utils:alert('Halted. No skins found inside skins object in spine file. Didn\'t make any changes to your Moho file yet.') return end -- GET SKIN local skinName = 'default' local attachmentsObj local skinsIsArrayFormatted = spineObj.skins[1] ~= nil -- new spine formatting for skins object if (skinsIsArrayFormatted) then local skinObj = getSkinObjFromSkinsArr(spineObj.skins, skinName) if (skinObj == nil) then WP_utils:alert('Halted. Skin not found inside skins object in spine file. Didn\'t make any changes to your Moho file yet.') do return end end attachmentsObj = skinObj.attachments if (attachmentsObj == nil) then WP_utils:alert('Halted. Skin is missing attachments object. Didn\'t make any changes to your Moho file yet.') do return end end else attachmentsObj = spineObj.skins[skinName] end -- GET SLOT NAMES AND SKIN DATA local assetsData = {} local layerName -- = slot local assetName -- = attachment local boneObj -- = bound bone object local assetType -- = spine attachment type local localRelImgPath -- = relative path and basename for image file local assetIndex = 0 local imgWorldVec2 = LM.Vector2:new_local() -- temp vector to calculate world position for i, value in ipairs(spineObj.slots) do -- SET BONE AS REFERENCE value.boneObj = findBone(value.bone) boneObj = value.boneObj -- GET SLOT NAME AS LAYER NAME layerName = value.name if (type(layerName) ~= 'string' or layerName:len() == 0) then WP_utils:alert('Halted. Name for Layer/slot at position '..tostring(i)..' (one-based) is missing in spine file. Didn\'t make any changes to your Moho file yet.') return end for i, value in ipairs(assetsData) do if (assetsData[i].layerName == layerName) then WP_utils:alert("Halted. Layer/slot '"..layerName.."' has duplicates in spine file. Didn't make any changes to your Moho file yet.") return end end -- GET ATTACHMENT NAME AS ASSETNAME (SPINE.JSON DOCUMENTATION SAYS: IF MISSING WE SHOULD ASSUME THERE IS NO ATTACHMENT) assetName = value.attachment if (assetName == nil) then raiseWarning("Skipping Layer/slot '"..layerName.."'. Attachment/image is missing in spine file. Either this slot is corrupted or is missing an attachment.") end -- IMAGE FILE (+ CHECK IF IT EXISTS) if (attachmentsObj[layerName] and attachmentsObj[layerName][assetName] and assetName ~= nil) then -- GET ATTACHMENT TYPE assetType = attachmentsObj[layerName][assetName].type if (assetType == nil) then assetType = 'region' end if (assetType == 'region' or (importImagesFromMeshes and assetType == 'mesh')) then assetIndex = assetIndex + 1 localRelImgPath = assetName -- CREATE NEW ASSET ITEM assetsData[assetIndex] = {} assetsData[assetIndex].index = assetIndex assetsData[assetIndex].layerName = layerName assetsData[assetIndex].assetName = assetName assetsData[assetIndex].imgOrgSize = { width = 0, height = 0 } -- original image size (unscaled and undeformed) assetsData[assetIndex].imgFile = '' assetsData[assetIndex].parentBoneObj = nil -- will be set later assetsData[assetIndex].boneObj = boneObj assetsData[assetIndex].imgPosPx = { x = 0, y = 0 } -- local position ofset of image in relation to bone assetsData[assetIndex].imgPosPxWorld = { x = 0, y = 0 } -- world position of image assetsData[assetIndex].boneRotDegWorld = 0 -- rotation of bound bone in world coordinates assetsData[assetIndex].imgRotDeg = 0 -- local rotation in relation to bone assetsData[assetIndex].imgRotDegWorld = 0 -- image rotation in total assetsData[assetIndex].boneScaleWorld = { x = 1, y = 1 } -- bone world scale assetsData[assetIndex].imgScale = { x = 1, y = 1 } -- local image scale assetsData[assetIndex].imgScaleWorld = { x = 1, y = 1 } -- world image scale (total scale) local props = attachmentsObj[layerName][assetName] -- GET BONE AND UPDATE POS, ROT, SCALE OF IMAGE if (boneObj) then assetsData[assetIndex].parentBoneObj = boneObj.parentObj assetsData[assetIndex].imgPosPxWorld.x = boneObj.worldX assetsData[assetIndex].imgPosPxWorld.y = boneObj.worldY assetsData[assetIndex].boneRotDegWorld = boneObj.worldRotation assetsData[assetIndex].imgRotDegWorld = boneObj.worldRotation assetsData[assetIndex].boneScaleWorld.x = boneObj.worldScaleX assetsData[assetIndex].boneScaleWorld.y = boneObj.worldScaleY assetsData[assetIndex].imgScaleWorld.x = boneObj.worldScaleX assetsData[assetIndex].imgScaleWorld.y = boneObj.worldScaleY end if (props ~= nil) then -- GET POSITION AND SIZE DATA if (props.x ~= nil and type(props.x) == 'number') then assetsData[assetIndex].imgPosPx.x = props.x end if (props.y ~= nil and type(props.y) == 'number') then assetsData[assetIndex].imgPosPx.y = props.y end if (props.width ~= nil and type(props.width) == 'number') then assetsData[assetIndex].imgOrgSize.width = props.width end if (props.height ~= nil and type(props.height) == 'number') then assetsData[assetIndex].imgOrgSize.height = props.height end if (props.rotation ~= nil and type(props.rotation) == 'number') then assetsData[assetIndex].imgRotDeg = props.rotation assetsData[assetIndex].imgRotDegWorld = assetsData[assetIndex].imgRotDegWorld + props.rotation end if (props.scaleX ~= nil and type(props.scaleX) == 'number') then assetsData[assetIndex].imgScale.x = props.scaleX assetsData[assetIndex].imgScaleWorld.x = assetsData[assetIndex].imgScaleWorld.x * props.scaleX end if (props.scaleY ~= nil and type(props.scaleY) == 'number') then assetsData[assetIndex].imgScale.y = props.scaleY assetsData[assetIndex].imgScaleWorld.y = assetsData[assetIndex].imgScaleWorld.y * props.scaleY end if (props.color ~= nil and type(props.color) == 'string') then assetsData[assetIndex].tintColStr = props.color end if (props.path ~= nil and type(props.path) == 'string') then localRelImgPath = props.path end -- CALC WORLD POS (USING ROTATED AND SCALED AXIS) imgWorldVec2.x = props.x imgWorldVec2.y = props.y if (boneObj.worldRotation ~= 0) then imgWorldVec2:Rotate(WP_utils:deg2rad(boneObj.worldRotation)) end imgWorldVec2.x = imgWorldVec2.x * boneObj.scaleX imgWorldVec2.y = imgWorldVec2.y * boneObj.scaleY imgWorldVec2:Set(boneObj.worldVec2 + imgWorldVec2) assetsData[assetIndex].imgPosPxWorld.x = imgWorldVec2.x assetsData[assetIndex].imgPosPxWorld.y = imgWorldVec2.y -- GET IMAGE FILE AND CHECK IF IT EXISTS if (props.sequence ~= nil) then raiseWarning("Image sequences aren't supported. Only the first frame will be loaded for asset '"..assetName.."' (layer '"..layerName.."').") -- GET IMAGE SEQUENCE FIRST FRAME INDEX if (type(props.sequence.start) ~= 'number') then WP_utils:alert('Halted. Missing image sequence \'Start\' property on asset "'..assetName..'". Didn\'t make any changes to your Moho file yet.') return end local imgSeqStartIndex = props.sequence.start -- GET IMAGE SEQUENCE DIGITS COUNT if (type(props.sequence.digits) ~= 'number') then WP_utils:alert('Halted. Missing image sequence \'Digits\' property on asset "'..assetName..'". Didn\'t make any changes to your Moho file yet.') return end local imgSeqDigits = props.sequence.digits if (imgSeqDigits < 1) then imgSeqDigits = 1 end local imgSeqFrameNrStr = string.format('%0'..imgSeqDigits..'d', imgSeqStartIndex) localRelImgPath = localRelImgPath..imgSeqFrameNrStr end assetsData[assetIndex].imgFile = absImagesPath .. '/'..localRelImgPath -- SEARCH IMAGE FILE local imgIsFound = false for _, imgExt in ipairs(imgExtSearchOrder) do imgIsFound = WP_utils:fileExists(assetsData[assetIndex].imgFile..'.'..imgExt) if (imgIsFound) then assetsData[assetIndex].imgFile = (assetsData[assetIndex].imgFile..'.'..imgExt) break end end if (imgIsFound == false) then local msg = "File '"..assetsData[assetIndex].imgFile.."' (+ .png/jpg/jpeg) not found (layer '"..layerName.."')." if (cancelOnImageNotFound == true) then WP_utils:alert("Halted. "..msg.." Didn\'t make any changes to your Moho file yet.") return else raiseWarning(msg) end end else WP_utils:alert('Halted. Missing props for attachment '..i..'. Didn\'t make any changes to your Moho file yet.') return end else raiseWarning("Skipping asset '"..assetName.."', because currently '"..assetType.."' assets aren't supported.") end end end -- -------------------------------------------------------------------------------------------------------------------- -- ADD IMAGES AS LAYERS TO MOHO -- -------------------------------------------------------------------------------------------------------------------- -- GOTO FRAME ZERO if (moho.frame ~= 0) then moho:SetCurFrame(0) end -- PREP MULTI UNDO AND RAISE DIRTY FLAG mohoDoc:PrepMultiUndo() mohoDoc:SetDirty() -- SELECT TOP LAYER moho:SetSelLayer(mohoDoc:Layer(mohoDoc:CountLayers() -1), false, true) -- CREATE BONES LAYER local boneGroupLayer = moho:CreateNewLayer(MOHO.LT_BONE, true) local boneGroupLayerName = spineFileBasename.." ('"..skinName.."' skin)" if (WP_utils:layerExists(moho, boneGroupLayerName)) then boneGroupLayerName = WP_utils:findNextFreeLayerName(moho, boneGroupLayerName) end boneGroupLayer:SetName(boneGroupLayerName) boneGroupLayer = moho:LayerAsBone(boneGroupLayer) boneGroupLayer:Expand(expandBoneGroupLayerAfterCreation) local skeleton = boneGroupLayer:Skeleton() -- IMPORT SCALING if (importScale ~= 1) then local importScaleVec3 = LM.Vector3:new_local() importScaleVec3:Set(importScale, importScale, 1) boneGroupLayer.fScale:SetValue(0, importScaleVec3) end -- CREATE IMAGE LAYERS local layerName local autoRenameLayers = nil local docIsModified = false for i in ipairs(assetsData) do layerName = assetsData[i].layerName -- CHECK IF LAYERNAME EXISTS (ONLY IF SETTING IS ENABLED) if (alwaysCreateUniqueLayerNames) then if (WP_utils:layerExists(moho, layerName)) then if (autoRenameLayers == nil) then autoRenameLayers = WP_utils:alert('Layer "'..layerName..'" already exists. Do you want to continue with auto-renaming new duplicate layers? (Hit Cancel to exit without making changes to your moho project).', true) if (autoRenameLayers == 1) then return nil end end layerName = WP_utils:findNextFreeLayerName(moho, layerName) end end -- CREATE LAYER addImgAsLayer( boneGroupLayer, layerName, assetsData[i].imgFile, assetsData[i].imgPosPxWorld, assetsData[i].imgRotDegWorld, assetsData[i].imgScaleWorld, assetsData[i].tintColStr) end -- SELECT GROUP LAYER moho:SetSelLayer(boneGroupLayer) -- -------------------------------------------------------------------------------------------------------------------- -- ADD BONES TO MOHO -- -------------------------------------------------------------------------------------------------------------------- if (importBones and spineObj and spineObj.bones) then local newBone local hasParent = false for i, bone in ipairs(spineObj.bones) do hasParent = bone.parentObj ~= nil -- =============================== -- ADD BONE -- =============================== -- CREATE BONE newBone = skeleton:AddBone(0) newBone:SetName(bone.name) newBone:ShowLabel(showBoneLabels) -- UPDATE BONE DATA FOR CHILDREN bone.mohoBoneObj = newBone bone.mohoBoneId = skeleton:BoneID(newBone) if (hasParent == false) then --===================================================== -- NO PARENT --===================================================== -- SET PARENT newBone.fParent = -1 newBone.fAnimParent:SetValue(0, -1) -- SET POSITION local bonePosMohoVec2 = WP_utils:getMohoRelLayerTransVec2(moho, bone.x, bone.y) newBone.fPos = bonePosMohoVec2 newBone.fAnimPos:SetValue(0, bonePosMohoVec2) -- SET ROTATION local boneRotationMohoRad = WP_utils:deg2rad(bone.rotation) newBone.fAngle = boneRotationMohoRad newBone.fAnimAngle:SetValue(0, boneRotationMohoRad) -- CALCULATE MOHO SCALE newBone.fScale = bone.scaleSingle newBone.fAnimScale:SetValue(0, bone.scaleSingle) -- SET BONE LENGTH local boneLengthVec2 = WP_utils:getMohoLayerSizeVec2(moho, bone.length, 0) boneLengthVec2:Rotate(WP_utils:deg2rad(bone.rotation)) local boneLengthMoho = boneLengthVec2:Mag() newBone.fLength = boneLengthMoho * bone.scaleSingle else --===================================================== -- WITH PARENT --===================================================== -- SET PARENT newBone.fParent = bone.parentObj.mohoBoneId newBone.fAnimParent:SetValue(0, bone.parentObj.mohoBoneId) -- GET DATA FROM MOHO PARENT BONE local mohoParentBone = bone.parentObj.mohoBoneObj local parentBonePosVec2Moho = mohoParentBone.fAnimPos:GetValue(0) local parentBoneScale = mohoParentBone.fAnimScale:GetValue(0) -- SET BONE LENGTH local boneLengthVec2 = WP_utils:getMohoLayerSizeVec2(moho, bone.length, 0) boneLengthVec2:Rotate(WP_utils:deg2rad(bone.rotation)) local boneLengthMoho = boneLengthVec2:Mag() newBone.fLength = boneLengthMoho * parentBoneScale * bone.scaleSingle -- SET POSITION OF CHILD BONE local bonePosMohoVec2 = WP_utils:getMohoLayerSizeVec2(moho, bone.x * parentBoneScale, bone.y * parentBoneScale) newBone.fPos = bonePosMohoVec2 newBone.fAnimPos:SetValue(0, bonePosMohoVec2) -- SET ROTATION OF CHILD BONE local boneRotationMohoRad = WP_utils:deg2rad(bone.rotation) newBone.fAngle = boneRotationMohoRad newBone.fAnimAngle:SetValue(0, boneRotationMohoRad) -- CALCULATE MOHO SCALE newBone.fScale = parentBoneScale * bone.scaleSingle newBone.fAnimScale:SetValue(0, parentBoneScale * bone.scaleSingle) end -- UPDATE BONE TRANSFORM MATRICES (IS THIS NEEDED?) skeleton:UpdateBoneMatrix(bone.mohoBoneId) end end -- FINISHED local warningsCount = WP_utils:arrLen(warningsList) if (warningsCount == 0) then if (debugMode == false) then WP_utils:alert('Successfully Finished importing!') end else print('----------------------------------------------------------------------------------------------') print('WIGGLEPIXEL SPINE IMPORT v'..WP_spineImport:Version()) print('') print('Completed with '..warningsCount..' warnings:') print('') for _, msg in ipairs(warningsList) do print(msg) end print('----------------------------------------------------------------------------------------------') print('') if (debugMode == false) then WP_utils:alert('Completed with '..warningsCount..' warnings (see Lua Console for details)') end end end
WP Import from Spine JSON
Listed
Author: wigglepixel
View Script
Script type: Tool
Uploaded: Oct 16 2023, 08:04
Last modified: Dec 10 2023, 07:28
Script Version: 1.1.3
Import Assets from Spine JSON (To import from Affinity, Krita, Spine, Photoshop, AfterEffects, Gimp etc.)
IMPORT FROM AFFINITY DESIGNER OR PHOTO
Tutorial video on how to use the script to import Affinity (Photo/Designer) Layers as Image Layers with cropped images in Moho:
IMPORT FROM KRITA
Tutorial video on how to use the script to import Krita Layers as Image Layers with cropped images in Moho:
IMPORT FROM SPINE
Short tutorial video on how to use the script to import Region Attachments (images) and their transformations at the setup pose from Spine as Moho 14 Layers:
IMPORT FROM PHOTOSHOP, AFTEREFFECTS, GIMP ETC.
There are spine JSON export scripts for many programs. Look here for export scripts made by Esoteric Software (creator of Spine) for at least Photoshop, AfterEffects, Gimp etc. : https://github.com/EsotericSoftware/spine-scripts
----------------------------------------------------------------------------------------------------------
DISCLAIMER
----------------------------------------------------------------------------------------------------------
I've tried my best to make this plugin as stable and useful as possible. But please bare in mind; I'm sharing this script, which I've created for doing my own animation work, for others to use for free as I think this could help others too. But this script/this plugin is 'as is'. There might be an update in the future, but that's not garanteed and there is no active support. Nor a garantee (always backup first!). That said; I hope you enjoy it and it helps you with your animation work!
----------------------------------------------------------------------------------------------------------
VERSION HISTORY
----------------------------------------------------------------------------------------------------------
1.1.3 [current]
- Changed: Made compatible with newer utils file
1.1.2
- Changed: Made compatible with newer utils file
- Added: Retina icon
1.1.1
- Fixed: Typo
1.1.0
- Added: If there is bone data available it now imports bones and creates the same skeleton in Moho, complete with transforms translation, rotation and scale*. Bone shearing will be ignored tho. *: Different scaling for X and Y isn't supported. If a bone has a different scale value for x and y then the x-value will be used as scale in Moho.
- Changed: Improve warnings output
- Changed: different scaleX and scaleY values in bones aren't supported. The image transformspath to calculate images from bones in the tree is now calculated accordingly to keep image transforms the same as the bone transformations.
1.0.0
- Initial release. Imports and transforms images.
----------------------------------------------------------------------------------------------------------
ABOUT THE CURRENT VERSION
----------------------------------------------------------------------------------------------------------
How to install the script in Moho?
* Click on the 'Download for Install Script command''-button below to download the zip-file
* Unzip the downloaded file
* Start Moho
* Open the Scripts menu --> choose 'Install Script' --> Browse through the unziped folder and choose the folder named 'wp_spine_import_for-moho-install-script-command' --> It should now install the script
* Once installation is done Moho shows a dialog saying 'Installation Complete!'
* Restart Moho (only after restarting the tool will become available)
* You should now see the tool with the spine logo in the tools panel. Ready to use!
Why I created this script?
1. Make exporting layers from Affinity way easier and quicker
I started this script because Affinity (Mainly Photo and Designer) (Affinity website) offers a great way to export all layers to individual cropped images on disc, together with a spine.json file containing all layer data, like positions of all images.
This makes it extremely easy and fast to import all layers and images directly into Spine. However, Moho so far couldn't open these spine.json files. So it took a long time to import all these individual images to moho layers and position all these layers in the right spot. By using this script this now is as easy in Moho as it is in Spine and it's now importing and positioning all images/layers via the spine.json file with a single click so we don't have to spend lots of time importing images anymore and can start rigging right away.
2. Make exporting layers from Krita way easier and quicker
There is also a very useful script for Krita (Krita Website) around to export Krita layers to spine.json. Although I didn't tested the outputed spine.json with that script yet, it should work just fine too. You can find it here: Krita-to-spine script download
Not shown in the tutorial video above, because I wonder if it's useful in Spine, but it's even possible to add special naming to Krita group layers that the export script recognises to automatically create bones etc.. See the krita-to-spine script readme (follow the download link) for more information.
3. Make importing images from spine.json possible too?
After having the import for Affinity-generated spine.json files working I wondered how far I could take this to import spine.json files generated by Spine itself (Esoteric Software/Spine website). These files are more complicated because Spine also has a complete skeleton system, like Mohos bones, and images inside spine files have a hierarchical tree structure with bones. But also all bones can have translations/offsets, rotations, scales and shear values. Even images bound to bones can have these transformations applied. And they all influence each other via parenting.
Besides this in Spine it's possible to tint images, which isn't currently possible in Moho.
Spine has more features, like multiple skeletons in a file, multiple skins per skeleton, meshes, several other types of attachments besides images and transforms. But these aren't supported by this script (yet?) and is way over the original use case for the script, which was to import all layers as images from Affinity.
All images are imported now and get the right transformations (except shears) in Moho, just like in Spine. And when it finds tinted images/slots in the spine.json file it generates extra reference tint-layers in Moho to have a similar effect. At the moment these aren't having exactly the same result/blending, so these will probably be tweaked later. But at least it imports. And I consider this a feature that won't be used by many people.
4. Importing from other software (Photoshop, After Effects, Gimp etc.)?
There are Spine JSON exporters around for Photoshop, Gimp and other software too. So basically any software you have and has an export script to export to Spine JSON fileformat is with this script now able to export to Moho. Esoteric Software, the creators of Spine, have a repository with some exporter scripts for several programs here: https://github.com/EsotericSoftware/spine-scripts
What does the script do?
This first version generates a group with image layers. The group is transformed to a bone layer in moho, but doesn't contain bones yet. A later version will probably add an option to load the bones too, if the spine.json contains bone data.
The bone data in the spine.json is however used to determine the transformations of the images to position/rotate/scale them on the right spots in Moho.
How to use the script?
This is a new script! Backup your moho file before use just to be sure.
Hit the tool button, browse the spine.json file (only json files are supported)
How to export Affinity or Krita layers, or Spine content to spine.json?
See tutorial videos above!
Supported Moho version
Tested in Moho 14.x.
About support of spine.json features
Everything imports fine from files generated by Affinity
About more advanced spine features as generated by Spine:
- Only the default skin (skin named 'default' will currently be imported. Multi-skin is not supported.
- Imports region attachments (= image without a mesh). Images having a mesh will not be imported.
- Like spine the valid supported image file formats are png, jpg and jpeg (will search for these formats in that order, like Spine).
- Positions, rotations and scaling* of images and bones are calculated hierarchical through the bones to their final position.
- *) Different/Individual scaling for X and Y on bones isn't supported. If a bone has a unequal values for scaleX and scaleY then the scaleX value will be used as scale for that bone in Moho.
- Shear on bones aren't currently supported.
- Constraints aren't supported.
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: 621
WP Import from Spine JSON
Listed
Author: wigglepixel
View Script
Script type: Tool
Uploaded: Oct 16 2023, 08:04
Last modified: Dec 10 2023, 07:28
Script Version: 1.1.3
Import Assets from Spine JSON (To import from Affinity, Krita, Spine, Photoshop, AfterEffects, Gimp etc.)
IMPORT FROM AFFINITY DESIGNER OR PHOTO
Tutorial video on how to use the script to import Affinity (Photo/Designer) Layers as Image Layers with cropped images in Moho:
IMPORT FROM KRITA
Tutorial video on how to use the script to import Krita Layers as Image Layers with cropped images in Moho:
IMPORT FROM SPINE
Short tutorial video on how to use the script to import Region Attachments (images) and their transformations at the setup pose from Spine as Moho 14 Layers:
IMPORT FROM PHOTOSHOP, AFTEREFFECTS, GIMP ETC.
There are spine JSON export scripts for many programs. Look here for export scripts made by Esoteric Software (creator of Spine) for at least Photoshop, AfterEffects, Gimp etc. : https://github.com/EsotericSoftware/spine-scripts
----------------------------------------------------------------------------------------------------------
DISCLAIMER
----------------------------------------------------------------------------------------------------------
I've tried my best to make this plugin as stable and useful as possible. But please bare in mind; I'm sharing this script, which I've created for doing my own animation work, for others to use for free as I think this could help others too. But this script/this plugin is 'as is'. There might be an update in the future, but that's not garanteed and there is no active support. Nor a garantee (always backup first!). That said; I hope you enjoy it and it helps you with your animation work!
----------------------------------------------------------------------------------------------------------
VERSION HISTORY
----------------------------------------------------------------------------------------------------------
1.1.3 [current]
- Changed: Made compatible with newer utils file
1.1.2
- Changed: Made compatible with newer utils file
- Added: Retina icon
1.1.1
- Fixed: Typo
1.1.0
- Added: If there is bone data available it now imports bones and creates the same skeleton in Moho, complete with transforms translation, rotation and scale*. Bone shearing will be ignored tho. *: Different scaling for X and Y isn't supported. If a bone has a different scale value for x and y then the x-value will be used as scale in Moho.
- Changed: Improve warnings output
- Changed: different scaleX and scaleY values in bones aren't supported. The image transformspath to calculate images from bones in the tree is now calculated accordingly to keep image transforms the same as the bone transformations.
1.0.0
- Initial release. Imports and transforms images.
----------------------------------------------------------------------------------------------------------
ABOUT THE CURRENT VERSION
----------------------------------------------------------------------------------------------------------
How to install the script in Moho?
* Click on the 'Download for Install Script command''-button below to download the zip-file
* Unzip the downloaded file
* Start Moho
* Open the Scripts menu --> choose 'Install Script' --> Browse through the unziped folder and choose the folder named 'wp_spine_import_for-moho-install-script-command' --> It should now install the script
* Once installation is done Moho shows a dialog saying 'Installation Complete!'
* Restart Moho (only after restarting the tool will become available)
* You should now see the tool with the spine logo in the tools panel. Ready to use!
Why I created this script?
1. Make exporting layers from Affinity way easier and quicker
I started this script because Affinity (Mainly Photo and Designer) (Affinity website) offers a great way to export all layers to individual cropped images on disc, together with a spine.json file containing all layer data, like positions of all images.
This makes it extremely easy and fast to import all layers and images directly into Spine. However, Moho so far couldn't open these spine.json files. So it took a long time to import all these individual images to moho layers and position all these layers in the right spot. By using this script this now is as easy in Moho as it is in Spine and it's now importing and positioning all images/layers via the spine.json file with a single click so we don't have to spend lots of time importing images anymore and can start rigging right away.
2. Make exporting layers from Krita way easier and quicker
There is also a very useful script for Krita (Krita Website) around to export Krita layers to spine.json. Although I didn't tested the outputed spine.json with that script yet, it should work just fine too. You can find it here: Krita-to-spine script download
Not shown in the tutorial video above, because I wonder if it's useful in Spine, but it's even possible to add special naming to Krita group layers that the export script recognises to automatically create bones etc.. See the krita-to-spine script readme (follow the download link) for more information.
3. Make importing images from spine.json possible too?
After having the import for Affinity-generated spine.json files working I wondered how far I could take this to import spine.json files generated by Spine itself (Esoteric Software/Spine website). These files are more complicated because Spine also has a complete skeleton system, like Mohos bones, and images inside spine files have a hierarchical tree structure with bones. But also all bones can have translations/offsets, rotations, scales and shear values. Even images bound to bones can have these transformations applied. And they all influence each other via parenting.
Besides this in Spine it's possible to tint images, which isn't currently possible in Moho.
Spine has more features, like multiple skeletons in a file, multiple skins per skeleton, meshes, several other types of attachments besides images and transforms. But these aren't supported by this script (yet?) and is way over the original use case for the script, which was to import all layers as images from Affinity.
All images are imported now and get the right transformations (except shears) in Moho, just like in Spine. And when it finds tinted images/slots in the spine.json file it generates extra reference tint-layers in Moho to have a similar effect. At the moment these aren't having exactly the same result/blending, so these will probably be tweaked later. But at least it imports. And I consider this a feature that won't be used by many people.
4. Importing from other software (Photoshop, After Effects, Gimp etc.)?
There are Spine JSON exporters around for Photoshop, Gimp and other software too. So basically any software you have and has an export script to export to Spine JSON fileformat is with this script now able to export to Moho. Esoteric Software, the creators of Spine, have a repository with some exporter scripts for several programs here: https://github.com/EsotericSoftware/spine-scripts
What does the script do?
This first version generates a group with image layers. The group is transformed to a bone layer in moho, but doesn't contain bones yet. A later version will probably add an option to load the bones too, if the spine.json contains bone data.
The bone data in the spine.json is however used to determine the transformations of the images to position/rotate/scale them on the right spots in Moho.
How to use the script?
This is a new script! Backup your moho file before use just to be sure.
Hit the tool button, browse the spine.json file (only json files are supported)
How to export Affinity or Krita layers, or Spine content to spine.json?
See tutorial videos above!
Supported Moho version
Tested in Moho 14.x.
About support of spine.json features
Everything imports fine from files generated by Affinity
About more advanced spine features as generated by Spine:
- Only the default skin (skin named 'default' will currently be imported. Multi-skin is not supported.
- Imports region attachments (= image without a mesh). Images having a mesh will not be imported.
- Like spine the valid supported image file formats are png, jpg and jpeg (will search for these formats in that order, like Spine).
- Positions, rotations and scaling* of images and bones are calculated hierarchical through the bones to their final position.
- *) Different/Individual scaling for X and Y on bones isn't supported. If a bone has a unequal values for scaleX and scaleY then the scaleX value will be used as scale for that bone in Moho.
- Shear on bones aren't currently supported.
- Constraints aren't supported.
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: 621