Image
-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************

ScriptName = "LK_SetOrigin"

-- **************************************************
-- The original tool LM_Set_Origin can't be disabled (because it doesn't have an IsRelevant function perhaps?)
-- "LK_Set_Origin's filename must be "LM_SetOrigin.lua"
-- **************************************************

-- **************************************************
-- General information about this script
-- **************************************************

LK_SetOrigin = {}

LK_SetOrigin.BASE_STR = 2325

function LK_SetOrigin:Name()
	return "Set Origin"
end

function LK_SetOrigin:Version()
	return "1.0"
end

function LK_SetOrigin:Description()
	return (MOHO.Localize("/Scripts/Tool/SetOrigin/Description=Click to set the origin of this layer (the point the layer rotates around)").." (also works with layerscripts LK_Origin_Switch.lua and LK_Origin_Bone.lua) (<,/.> to go to previous/next FBF-frame)")
end

function LK_SetOrigin:Creator()
	return "Smith Micro Software, Inc. - Modified by Lukas Krepel, Frame Order"
end

function LK_SetOrigin:UILabel()
	return(MOHO.Localize("/Scripts/Tool/SetOrigin/SetOrigin=Set Origin"))
end

function LK_SetOrigin:IsRelevant(moho)
	if MohoMode ~= nil then
		if MohoMode.vanilla then
			return false
		end
	end
	return true
end

function LK_SetOrigin:ColorizeIcon()
	return true
end

-- **************************************************
-- Disable original tool
-- **************************************************
function LM_SetOrigin:IsRelevant(moho)
	if LK_SetOrigin:IsRelevant(moho) then
		return false
	end
	-- * Copy of original IsRelevant function:
	return true
end


-- **************************************************
-- Recurring values
-- **************************************************

LK_SetOrigin.matrix = LM.Matrix:new_local()
LK_SetOrigin.layerSettingsWnd = nil

-- **************************************************
-- The guts of this script
-- **************************************************

function LK_SetOrigin:OnInputDeviceEvent(moho, deviceEvent)
	return LM_TransformLayer:OnInputDeviceEvent(moho, deviceEvent)
end

function LK_SetOrigin:OnMouseDown(moho, mouseEvent)
	moho.document:PrepUndo(moho.layer, true)
	moho.document:SetDirty()

	self:SetOrigin(moho, mouseEvent)
	mouseEvent.view:DrawMe()
end

function LK_SetOrigin:OnMouseMoved(moho, mouseEvent)
	self:SetOrigin(moho, mouseEvent)
	mouseEvent.view:DrawMe()
end

function LK_SetOrigin:SetOrigin(moho, mouseEvent, newVal)
	local matrix = LM.Matrix:new_local()
	local beforeVec = LM.Vector3:new_local()
	local afterVec = LM.Vector3:new_local()
	-- *
	local layer = moho.layer
	local embeddedscript = string.gsub(tostring(layer:LayerScript()), '\\', '/')
	if embeddedscript ~= "" then
		local lastslashpos = (embeddedscript:reverse()):find("%/") -- find last slash
		embeddedscript = (embeddedscript:sub(-lastslashpos+1)) -- filename only
		if embeddedscript == "LK_Origin_Switch.lua" then
			-- * Script is embedded, set origin of active child:
			if moho.layer:LayerType() == MOHO.LT_SWITCH then
				local switchLayer = moho:LayerAsSwitch(moho.layer)
				local activeChildName = switchLayer:GetValue(moho.frame)
				for i = switchLayer:CountLayers()-1, 0, -1 do
					local childLayer = switchLayer:Layer(i)
					if childLayer:Name() == activeChildName then
						layer = childLayer
					end
				end
			end
		elseif embeddedscript == "LK_Origin_Bone.lua" then
			local skel = moho:Skeleton()
			local OriginTag = "origin"
			-- * Find Origin Bone:
			for i=0, skel:CountBones()-1 do
				local bone = skel:Bone(i)
				local boneName = string.lower(bone:Name())
				if string.match (boneName, OriginTag) then
					OriginBone = bone
					layer:GetFullTransform(moho.frame, matrix, moho.document)
					if mouseEvent ~= nil then
						OriginBone.fAnimPos:SetValue (moho.frame, mouseEvent.view:Point2Vec(mouseEvent.pt, matrix))
					else
						OriginBone.fAnimPos:SetValue (moho.frame, newVal)
					end
					moho.layer:UpdateCurFrame() -- * Updates position in viewport.
					moho:UpdateUI() -- * Updates timeline.
					return
				end
			end
		end
	end
	-- *
	layer:GetFullTransform(moho.frame, matrix, nil)
	matrix:Transform(beforeVec)

	if mouseEvent ~= nil then
		layer:GetFullTransform(moho.frame, matrix, moho.document)
		layer:SetOrigin(mouseEvent.view:Point2Vec(mouseEvent.pt, matrix))
	else
		layer:SetOrigin(newVal)
	end

	layer:GetFullTransform(moho.frame, matrix, nil)
	matrix:Transform(afterVec)

	local newLayerPos = layer.fTranslation.value + beforeVec - afterVec
	local v = newLayerPos - layer.fTranslation.value
	if (v:Mag() > 0.000001) then
		layer.fTranslation:SetValue(moho.layerFrame, newLayerPos)
		moho:NewKeyframe(CHANNEL_LAYER_T)
	end

	moho.document:DepthSort()
end

-- **************************************************
-- Tool options - create and respond to tool's UI
-- **************************************************

LK_SetOrigin.CHANGE =				MOHO.MSG_BASE
LK_SetOrigin.RESET =				MOHO.MSG_BASE + 1
LK_SetOrigin.FLIP_H =				MOHO.MSG_BASE + 2
LK_SetOrigin.FLIP_V =				MOHO.MSG_BASE + 3
LK_SetOrigin.RESET_ORIGIN_X =		MOHO.MSG_BASE + 4
LK_SetOrigin.RESET_ORIGIN_Y =		MOHO.MSG_BASE + 5
LK_SetOrigin.BONE_LAYERSCRIPT =		MOHO.MSG_BASE + 6
LK_SetOrigin.SWITCH_LAYERSCRIPT =	MOHO.MSG_BASE + 7

LK_SetOrigin.layerscript_bone = "LK_Origin_Bone.lua"
LK_SetOrigin.layerscript_switch = "LK_Origin_Switch.lua"

function LK_SetOrigin:DoLayout(moho, layout)
	FO_Utilities.tinyUI = false
	-- *******************
	-- *** Origin X/Y: ***
	-- *******************
	FO_Utilities:Divider(layout, "Origin", true)
	self.xButton = LM.GUI.ImageButton("ScriptResources/FO_icons/channels_X", "Reset Origin's Horizontal Position", false, self.RESET_ORIGIN_X, true)
	layout:AddChild(self.xButton)
	layout:AddPadding(-15)
	self.textX = LM.GUI.TextControl(0, "00.0000", self.CHANGE, LM.GUI.FIELD_FLOAT)
	layout:AddChild(self.textX)
	layout:AddPadding(-15)
	self.yButton = LM.GUI.ImageButton("ScriptResources/FO_icons/channels_Y", "Reset Origin's Vertical Position", false, self.RESET_ORIGIN_Y, true)
	layout:AddChild(self.yButton)
	-- **************
	-- *** Reset: ***
	-- **************
	layout:AddPadding(-15)
	self.textY = LM.GUI.TextControl(0, "00.0000", self.CHANGE, LM.GUI.FIELD_FLOAT)
	layout:AddChild(self.textY)
	layout:AddChild(LM.GUI.Button(MOHO.Localize("/Scripts/Tool/SetOrigin/Reset=Reset"), self.RESET))
	-- *******************
	-- *** Flip Layer: ***
	-- *******************
	FO_Utilities:Divider(layout, "Flip Layer")
	self.flipLayerXButton = (LM.GUI.ImageButton("ScriptResources/flip_layer_h", MOHO.Localize("/Scripts/Tool/SetOrigin/FlipH=Flip Layer Horizontally"), true, self.FLIP_H, true))
	layout:AddChild(self.flipLayerXButton)
	self.flipLayerYButton = (LM.GUI.ImageButton("ScriptResources/flip_layer_v", MOHO.Localize("/Scripts/Tool/SetOrigin/FlipV=Flip Layer Vertically"), true, self.FLIP_V, true))
	layout:AddChild(self.flipLayerYButton)
	-- ***********************
	-- *** Animate Origin: ***
	-- ***********************
	FO_Utilities:Divider(layout, "Embed Animate Origin Layerscripts")
	self.boneButton = (LM.GUI.ImageButton("ScriptResources/FO_icons/channels_bone_channels", "Embed layerscript: '"..self.layerscript_bone.."'", true, self.BONE_LAYERSCRIPT, true))
	layout:AddChild(self.boneButton)
	self.switchButton = (LM.GUI.ImageButton("ScriptResources/FO_icons/channels_switch_channels", "Embed layerscript: '"..self.layerscript_switch.."'", true, self.SWITCH_LAYERSCRIPT, true))
	layout:AddChild(self.switchButton)
end

function LK_SetOrigin:UpdateWidgets(moho)
	local origin = moho.layer:Origin()
	self.textX:SetValue(origin.x)
	self.textY:SetValue(origin.y)
	if (self.layerSettingsWnd) then
		self.layerSettingsWnd:Update(moho)
	end
	self.boneButton:Enable(moho.layer:IsBoneType())
	self.switchButton:Enable(moho.layer:LayerType() == MOHO.LT_SWITCH)

	local layerscript = moho.layer:LayerScript()
	self.boneButton:SetValue(string.match(layerscript, self.layerscript_bone))
	self.switchButton:SetValue(string.match(layerscript, self.layerscript_switch))

	self.flipLayerXButton:SetValue(moho.layer.fFlipH.value)
	self.flipLayerYButton:SetValue(moho.layer.fFlipV.value)
end

function LK_SetOrigin:HandleMessage(moho, view, msg)
	local newVal = LM.Vector2:new_local()
	local selCount = moho.document:CountSelectedLayers()

	if (msg == self.BONE_LAYERSCRIPT) then
		moho.document:PrepUndo(moho.layer, true)
		moho.document:SetDirty()
		if not self.boneButton:Value() then -- * Value is false, because we just clicked the button!
			-- * Remove layerscript:
			moho.layer:SetLayerScript()
			local skel = moho:Skeleton()
			for i=0, skel:CountBones()-1 do
				local bone = skel:Bone(i)
				local boneName = string.lower(bone:Name())
				if string.match (boneName, FO_Utilities.originTag) then
					if FO_Utilities:YesNoQuestion("Delete 'Origin' bone?") then
						skel:DeleteBone(i)
					end
				end
			end
		else
			-- * Embed bone script:
			FO_Utilities:EmbedLayerScript(moho, moho.layer, self.layerscript_bone)
		end
	elseif (msg == self.SWITCH_LAYERSCRIPT) then
		moho.document:PrepUndo(moho.layer, true)
		moho.document:SetDirty()
		if not self.switchButton:Value() then -- * Value is false, because we just clicked the button!
			-- * Remove layerscript:
			moho.layer:SetLayerScript()
		else
			-- * Embed switch script:
			FO_Utilities:EmbedLayerScript(moho, moho.layer, self.layerscript_switch)
		end
	elseif (msg == self.RESET) then
		-- moho.document:PrepMultiUndo(true) -- * Ditched multi layer support?
		moho.document:PrepUndo(moho.layer, true)
		moho.document:SetDirty()
		newVal:Set(0, 0)
		--[[ -- * Ditched multi layer support?
		for i = 0, selCount - 1 do 
			local layer = moho.document:GetSelectedLayer(i)
			self:SetOrigin(moho, nil, newVal)
		end 
		--]] -- * Ditched multi layer support?
		self:SetOrigin(moho, nil, newVal)
	elseif (msg == self.RESET_ORIGIN_X) then
		moho.document:PrepUndo(moho.layer, true)
		moho.document:SetDirty()
		newVal:Set(0, moho.layer:Origin().y)
		self:SetOrigin(moho, nil, newVal)
	elseif (msg == self.RESET_ORIGIN_Y) then
		moho.document:PrepUndo(moho.layer, true)
		moho.document:SetDirty()
		newVal:Set(moho.layer:Origin().x, 0)
		self:SetOrigin(moho, nil, newVal)
	elseif (msg == self.CHANGE) then
		moho.document:PrepUndo(moho.layer, true)
		moho.document:SetDirty()

		newVal.x = self.textX:FloatValue()
		newVal.y = self.textY:FloatValue()
		self:SetOrigin(moho, nil, newVal)
	elseif (msg == self.FLIP_H) then
		moho.document:PrepMultiUndo(true)
		moho.document:SetDirty()
		for i = 0, selCount - 1 do
			local layer = moho.document:GetSelectedLayer(i)
			layer.fFlipH:SetValue(moho.frame + layer:TotalTimingOffset(), not layer.fFlipH.value)
			moho:NewKeyframe(CHANNEL_LAYER_FLIP_H)
		end
	elseif (msg == self.FLIP_V) then
		moho.document:PrepMultiUndo(true)
		moho.document:SetDirty()
		for i = 0, selCount - 1 do
			local layer = moho.document:GetSelectedLayer(i)
			layer.fFlipV:SetValue(moho.frame + layer:TotalTimingOffset(), not layer.fFlipV.value)
			moho:NewKeyframe(CHANNEL_LAYER_FLIP_V)
		end
	end
	self:UpdateWidgets(moho)
end

function LK_SetOrigin:OnKeyDown(moho, keyEvent)
	if keyEvent.key == "." then
		FO_Utilities:GoToNextSwitchKey(moho, false)
		return
	elseif keyEvent.key == "," then
		FO_Utilities:GoToNextSwitchKey(moho, true)
		return
	end
	if LK_SelectPoints ~= nil then
		LK_SelectPoints:OnKeyDown(moho, keyEvent) -- * Contains "," "." "/" FBF shortcuts!
	end
end

Icon
Set Origin
Listed

Author: Lukas View Script
Script type: Tool

Uploaded: Jun 24 2022, 05:25

Last modified: May 26 2023, 07:11

Script Version: 1.0

Allows the Origin to be animated.
Image

Allows the Origin to be animated.

On a bone layer, it is controlled by a pinbone called 'Origin'. Which can be animated like any other bone or simply with this tool.

On a switch or FBF layer the Origin is set per child layer, set the child layers' Origins from the switch level with this tool. This way you can scale and rotate on the Switch/FBF level, while each instance having their own Origin.

Put these files:
- LK_Origin_Bone.lua
- LK_Origin_Switch.lua

in this folder:
/YourCustomContentFolder/Shared Resources/Layerscripts/

For FBF purposes, the LK_SelectPoints shortcuts will also work in this tool if you've got that one installed.
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: 1523