Two scripts for you: "Maintain pose" and "Toggle visibility"
Moderators: Víctor Paredes, Belgarath, slowtiger
-
- Posts: 107
- Joined: Mon Jan 14, 2013 3:18 pm
Two scripts for you: "Maintain pose" and "Toggle visibility"
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!
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.
- hayasidist
- Posts: 3518
- Joined: Wed Feb 16, 2011 8:12 pm
- Location: Kent, England
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
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??!! )
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!!
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??!! )
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!!
-
- Posts: 107
- Joined: Mon Jan 14, 2013 3:18 pm
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
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!
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!
- synthsin75
- Posts: 9972
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
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.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.
Just give it frame-1 to make sure you get past keyframes.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?
- Wes
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
-
- Posts: 107
- Joined: Mon Jan 14, 2013 3:18 pm
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
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?
- hayasidist
- Posts: 3518
- Joined: Wed Feb 16, 2011 8:12 pm
- Location: Kent, England
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
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
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
- synthsin75
- Posts: 9972
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
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.
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.
- Wes
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
-
- Posts: 107
- Joined: Mon Jan 14, 2013 3:18 pm
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
Ah thanks for the clarification. So KeyID is something different, that explains it. I'll give it another go!
- hayasidist
- Posts: 3518
- Joined: Wed Feb 16, 2011 8:12 pm
- Location: Kent, England
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
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]
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.
-
- Posts: 107
- Joined: Mon Jan 14, 2013 3:18 pm
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
Thanks, now it works!
- synthsin75
- Posts: 9972
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
Keyframe ID is always in timeline order. So if you count through them, you are counting them chronologically.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
- Wes
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
- hayasidist
- Posts: 3518
- Joined: Wed Feb 16, 2011 8:12 pm
- Location: Kent, England
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
yeah - you're right! sorry. key id is in order chronologically. (I'll edit to my post to correct that.)
-
- Posts: 107
- Joined: Mon Jan 14, 2013 3:18 pm
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
I tried this and it doesn't seem to work this way for some reason.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.
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?
- synthsin75
- Posts: 9972
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
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.
This may not be exact...just off the top of my head.
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)
- Wes
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
- synthsin75
- Posts: 9972
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Re: Two scripts for you: "Maintain pose" and "Toggle visibil
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)
- Wes
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/
Donations: https://www.paypal.com/paypalme/synthsin75 (Thx, everyone.)
https://www.youtube.com/user/synthsin75
Scripting reference: https://mohoscripting.com/