Hi folks!
Recently, I've been learning KineFX and APEX by following this tutorial:
Rig & Animate a Stylized Crab [www.sidefx.com]
I'm wondering how to expose only specific animation channels on a control, similar to the Lock & Hide workflow in Maya or Softimage.
For example, let's say I have a control that should only rotate around the X axis. As a rigger, I'd like to expose only Rotate X to the animator, while Rotate Y/Z and all Translate/Scale channels remain hidden or unavailable.
Ideally, when the animator selects this control, they would see only the intended animatable channels in the Animation Editor.
I can see that APEX allows us to Promote T, R, and S, but is there a way to go more granular and promote only a specific component, such as RX, rather than the entire Rotate vector?
What is the recommended workflow for this in Houdini 22?
Thanks!
Promote specyfic channel to animation.
227 3 1-
- Chris Gryzka
- Member
- 2 posts
- Joined: April 2021
- Offline
-
- AslakKS
- Member
- 454 posts
- Joined: Feb. 2016
- Offline
-
- Chris Gryzka
- Member
- 2 posts
- Joined: April 2021
- Offline
Thank you for your answer. Yes, I'm already aware of the limits, but unfortunately they don't solve what I'm trying to achieve.
Even when the limits are set, selecting the controller still shows all of its channels in the Animation Editor.
What I'm looking for is a way to expose only the channels that are actually intended for animation. In other DCCs, such as Maya or Softimage, this can be done by locking and hiding unnecessary channels.
This keeps the Animation Editor clean, so the animator sees only the channels that are relevant for a particular control.
For example, if a controller should only rotate around the X axis, I'd like the animator to see only Rotate X, rather than X, Y, and Z with Y and Z simply limited or locked.
Even when the limits are set, selecting the controller still shows all of its channels in the Animation Editor.
What I'm looking for is a way to expose only the channels that are actually intended for animation. In other DCCs, such as Maya or Softimage, this can be done by locking and hiding unnecessary channels.
This keeps the Animation Editor clean, so the animator sees only the channels that are relevant for a particular control.
For example, if a controller should only rotate around the X axis, I'd like the animator to see only Rotate X, rather than X, Y, and Z with Y and Z simply limited or locked.
-
- AslakKS
- Member
- 454 posts
- Joined: Feb. 2016
- Offline
Sorry, I completely jumped over the part where you mentioned the animation editor.
This does sound like a very good RFE to send to support, I would also love to see this easily done without having to dive deep into apex.
I did manage to create a monkey patch that does this with shelf tool that you click when an apex animate state is active.
Slop code, but I tested that it works 🫣
edit: made the code more precise by filtering the scoped channels
This does sound like a very good RFE to send to support, I would also love to see this easily done without having to dive deep into apex.
I did manage to create a monkey patch that does this with shelf tool that you click when an apex animate state is active.
Slop code, but I tested that it works 🫣
import stateutils; import types viewer = stateutils.findSceneViewer() if viewer is None: hou.ui.displayMessage("No scene viewer found") else: args = {} viewer.runStateCommand("getState", args) state = args.get("state") if state is None or not hasattr(state, "getScopedChannels"): viewer.setPromptMessage("Not in APEX Animate state") elif hasattr(state, "_apex_non_locked_scoped_original"): state.getScopedChannels = state._apex_non_locked_scoped_original del state._apex_non_locked_scoped_original viewer.setPromptMessage("Scope non-locked channels: disabled") else: original_scoped_channels = state.getScopedChannels def locked_axes(control_path, transform): try: control = state.control_manager.controls[control_path] minimum = ( getattr(control, f"{transform}_min_lock") if getattr(control, f"{transform}_use_min_lock") else getattr(control, f"{transform}_min") if getattr(control, f"{transform}_use_min") else None ) maximum = ( getattr(control, f"{transform}_max_lock") if getattr(control, f"{transform}_use_max_lock") else getattr(control, f"{transform}_max") if getattr(control, f"{transform}_use_max") else None ) if minimum is None or maximum is None: return set() return { axis for axis in "xyz" if abs(getattr(minimum, axis)() - getattr(maximum, axis)()) < 1e-6 } except (AttributeError, KeyError, TypeError): return set() def filter_channels(scoped_channels): return { rig_path: [ channel for channel in channels if not ( (control_name := channel.rpartition("_")[0]) and len(component := channel.rpartition("_")[2]) == 2 and component[0] in "rts" and component[1] in locked_axes(f"{rig_path}/{control_name}", component[0]) ) ] for rig_path, channels in scoped_channels.items() } def get_scoped_channels(_state): return filter_channels(original_scoped_channels()) state._apex_non_locked_scoped_original = original_scoped_channels state.getScopedChannels = types.MethodType(get_scoped_channels, state) viewer.setPromptMessage("Scope non-locked channels: enabled")
Edited by AslakKS - Aug. 29, 2026 06:45:12
-
- Quick Links

