Two scripts for you: "Maintain pose" and "Toggle visibility"

Moho allows users to write new tools and plugins. Discuss scripting ideas and problems here.

Moderators: Víctor Paredes, Belgarath, slowtiger

lehtiniemi
Posts: 107
Joined: Mon Jan 14, 2013 3:18 pm

Two scripts for you: "Maintain pose" and "Toggle visibility"

Post by lehtiniemi »

Hi!

Here are two tools for free use that I wrote for myself, they help my workflow incredibly much. I just finished them so let me know if you find errors, I haven't tested them in real life scenarios so much yet.

MAINTAIN POSE
This is like "Freeze pose", but instead of freezing the current pose, it creates a copy of the previous keyframed pose on current playhead position. Activate on a bone layer to fetch previous keyframes for each bone in the layer separately and copy those keyframes to current playhead position.

This is super handy to prevent drifting and find the drifting bones - this makes your character stay where it is until the current keyframe (for the selected skeleton).

Notes:
-Can be used to fetch previous pose without need to go search for keyframes on the timeline.
-The script bypasses shy bones. Selecting bones doesn't affect the result, keyframes are added to each bone. There's a commented line in the script that you can uncomment to enable the script only affect selected bones.

TOGGLE VISIBILITY
The script toggles the current visibility state for the layer: if it's visible, it becomes hidden and vice versa. "Automated layer effects" is automatically enabled for the selected layer. If the new visibility state is the same as in previous frame (=layer visibility state doesn't change), the keyframe is automatically removed.

Makes animating visibility a LOT faster than with the built-in functions (unless there's something I've missed), especially when the "Automated layer effects" isn't enabled and you animate visibility for the first time in the project.

Installation
Copy both .lua and .png files to the tool-folder.
The buttons appear in "Other"-group after restart.

Download both here:
http://www.juhanalehtiniemi.com/tmp/JL_ ... cripts.zip

Thanks to David Sandberg, I used his script "Bone Time Machine" as the groundwork for the "Maintain Pose"-script, since I hadn't scripted with LUA before.

I hope these help someone!
Last edited by lehtiniemi on Sun Mar 06, 2016 10:59 pm, edited 2 times in total.
User avatar
hayasidist
Posts: 3518
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by hayasidist »

Hi and welcome to world of scripting!!

one question: why ignore shy bones in "maintain pose"? If anything I'd have though they're the ones that really need this sort of "automagic" keying especially if they're hidden??

and -- hope you don't mind a couple of observations:

you've used "if boolean == true then ..." a lot -- you only need to say "if boolean then..." (or "if not boolean" instead of "if boolean == false") .. saves time and typos (how many times have I typed ture or flase??!! :roll: )

there's a native routine to get the previous key: Channel:GetClosestKeyID(frame):
see http://aslua.com/index.php?show=method& ... yID&id=426
and to find its frame you could use newFrame = Channel:GetKeyWhen(Channel:GetClosestKeyID(frame)) -- no need for the findPrevKey subroutine

anyway -- enjoy scripting!!
lehtiniemi
Posts: 107
Joined: Mon Jan 14, 2013 3:18 pm

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by lehtiniemi »

Thank you so much for the observations! I most definitely won't mind, happy to get feedback.

Shy bones: I personally use shy bones for physics automation (like hair) so that's why I wrote it that way. They are hidden and I didn't want to have this script affect anything that's not visible. Well, physics bones can't be keyframed anyway, but I also usually hide control bones that are used for head rotation actions, moving whole limbs etc, so I don't want them keyframed. They need to be controllable by the smart bone actions that control them. I hope this makes sense. I'm sure other people animate differently.

I guess it's not possible to make options for a button script? And if I would make this a tool script where I could create my own option checkboxes, then it's not possible to assign a keyboard shortcut for this which I need for fast access. At least when I checked, toolbar buttons of tools didn't show up in the keyboard assignment menu and this script needs to be key-mappable. If it's possible to create options for this kind of script so that it remains keyboard mappable, please let me know! I'd be happy to add some options to this (like bypassing shy bones, hidden bones, freezing all bones vs selected bones etc.

Thanks for the boolean-tip - I'll fix that!

As for the native keyframe search routine, I found that, but it doesn't work because it will also find the keyframes that are ahead. I particularly need to find the first previous keyframe and exclude everything that comes after the given frame. Or is there a way to force it search backwards-only?

Thanks again for your through comments, much appreciated!
User avatar
synthsin75
Posts: 9972
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by synthsin75 »

lehtiniemi wrote:I guess it's not possible to make options for a button script? And if I would make this a tool script where I could create my own option checkboxes, then it's not possible to assign a keyboard shortcut for this which I need for fast access. At least when I checked, toolbar buttons of tools didn't show up in the keyboard assignment menu and this script needs to be key-mappable. If it's possible to create options for this kind of script so that it remains keyboard mappable, please let me know! I'd be happy to add some options to this (like bypassing shy bones, hidden bones, freezing all bones vs selected bones etc.
You can make shortcuts in tools, but then the whole shortcut would be the key for the tool and then like ctrl+key for the option. So a two-step, three-key shortcut. May not be ideal.
As for the native keyframe search routine, I found that, but it doesn't work because it will also find the keyframes that are ahead. I particularly need to find the first previous keyframe and exclude everything that comes after the given frame. Or is there a way to force it search backwards-only?
Just give it frame-1 to make sure you get past keyframes.
lehtiniemi
Posts: 107
Joined: Mon Jan 14, 2013 3:18 pm

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by lehtiniemi »

But if I give it frame-1, doesn't it find the current frame, which is frame+1 (=current frame) which is only one frame away? Or can I give it a searching "direction" as in backwards from frame-1?
User avatar
hayasidist
Posts: 3518
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by hayasidist »

I'm pretty sure getClosest ONLY searches backwards from (including) the frame you give it?!!

so getClosest(10) won't find a key at 11; it will find a key at 10 if there is one; but if all you have is a key at 11, it will come back with frame 1 (best to check that you never give it <1 )

===

another way of having options and a keystroke mapped button:

decouple the button and the options: the tool script to set the options is a "run infrequently" tool that just sets up global variables for the button to use.

IOW the "Set Options" tool has little more than the UI that sets a bunch of globals -- you've already defined such as JL_maintain_pose.doRot as a global (as, BTW, also with variables skel and mesh - it would be more usual to have those as local)

Just to remove any lingering doubts here: a Global is accessible by any script any time.

So the UI script makes calls to LM.GUI. stuff and is responsible for maintaining the globals ...
...and the button script accesses them.

The bad news: a tool script can save and load its options settings, but I don't think a button can even just do the load; so to provide "non-default" values either the tool would have to run once or the active document would have to have a copy saved (e.g. using MohoDoc:Metadata() although I'm not totally convinced that this will be supported in the longer term - I think I read somewhere that this would be superseded by layer:ScriptData()???!!)

However, it's trivial for the button to know if the tool has run: if type(<your global>) == nil then <it hasn't>...

so, e.g. the button has code such as:

local doRot
If type (JL_maintain_pose.doRot) == "boolean" then doRot = JL_maintain_pose.doRot else doRot = true end
User avatar
synthsin75
Posts: 9972
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by synthsin75 »

I don't remember off hand which of these is the right syntax.

Channel:GetKeyWhen(Channel:GetClosestKeyID(frame-1))

or

Channel:GetKeyWhen(Channel:GetClosestKeyID(frame)-1)

The second one would definitely get the next earlier keyframe, and +1 would get the next latter keyframe.
lehtiniemi
Posts: 107
Joined: Mon Jan 14, 2013 3:18 pm

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by lehtiniemi »

Ah thanks for the clarification. So KeyID is something different, that explains it. I'll give it another go!
User avatar
hayasidist
Posts: 3518
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by hayasidist »

Channel:GetClosestKeyID(frame) returns ID -- the keyframe's ***ID***

and Channel:GetKeyWhen(ID) takes ID as its paramerter (http://aslua.com/index.php?show=method& ... hen&id=430)


so Channel:GetKeyWhen( Channel:GetClosestKeyID(frame) -1) is the same as Channel:GetKeyWhen( ID -1) ... IOW takes one off the *** key ID***

ID-1 is before ID and that ID+1 is after it in the timeline.

[edit: corrected error]
Last edited by hayasidist on Mon Mar 07, 2016 12:18 am, edited 2 times in total.
lehtiniemi
Posts: 107
Joined: Mon Jan 14, 2013 3:18 pm

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by lehtiniemi »

Thanks, now it works! :)
User avatar
synthsin75
Posts: 9972
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by synthsin75 »

hayasidist wrote:there is no guarantee that ID-1 is before ID nor that ID+1 is after it in the timeline. I'm pretty sure it's order of creation not order in timeline
Keyframe ID is always in timeline order. So if you count through them, you are counting them chronologically.
User avatar
hayasidist
Posts: 3518
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by hayasidist »

yeah - you're right! sorry. key id is in order chronologically. (I'll edit to my post to correct that.)
lehtiniemi
Posts: 107
Joined: Mon Jan 14, 2013 3:18 pm

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by lehtiniemi »

synthsin75 wrote:I don't remember off hand which of these is the right syntax.

Channel:GetKeyWhen(Channel:GetClosestKeyID(frame-1))

or

Channel:GetKeyWhen(Channel:GetClosestKeyID(frame)-1)

The second one would definitely get the next earlier keyframe, and +1 would get the next latter keyframe.
I tried this and it doesn't seem to work this way for some reason.

Imagine you have a bone pose at frame 1 and another at frame 100. I'm at frame 10 and want to find the next keyframe that occurs after my current cursor position. If I start searching from frame 10+1, this will still return the keyframe at frame 1 because this is the closest (11 frames away, instead of 89 frames away to frame 100). The problem is that GetClosestKeyID doesn't have a parameter for the direction where to look for.

If I give the frame 10+1, it still returns the closest which is at frame 1. Is there some way to make it specifically look forward instead and not both directions?
User avatar
synthsin75
Posts: 9972
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by synthsin75 »

Without a keyframe on the current frame, you will need to test whether it is higher or lower than the current frame and change your arithmetic accordingly.

Code: Select all

local direction

If (Channel:GetKeyWhen(Channel:GetClosestKeyID(frame)) < frame) then
     direction = 1
elseif (Channel:GetKeyWhen(Channel:GetClosestKeyID(frame)) > frame) then
     direction = -1
end

local nextKeyframe = Channel:GetKeyWhen(Channel:GetClosestKeyID(frame)+direction) 
This may not be exact...just off the top of my head.
User avatar
synthsin75
Posts: 9972
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Two scripts for you: "Maintain pose" and "Toggle visibil

Post by synthsin75 »

Here's code that gets both next and last keyframe (point motion here), whether the current frame is keyed or not:

Code: Select all

	local pos = 1
	local neg = -1
	local frame = moho.frame
	local ch = moho.layer:Channel(2, 1, moho.document)

	if (ch:GetKeyWhen(ch:GetClosestKeyID(frame)) ~= frame) then
		neg = 0
	end
	
	local nextKeyframe = ch:GetKeyWhen(ch:GetClosestKeyID(frame)+pos)
	local lastKeyframe = ch:GetKeyWhen(ch:GetClosestKeyID(frame)+neg)
	print("nextKeyframe = "..nextKeyframe.."\n".."lastKeyframe = "..lastKeyframe)
Post Reply