A blender addon that contains a number of custom operators designed to address Graph Editor and animation tool limitations in Blender.
Saner-Tools: Graph Editor Extensions
A Python module that extends the native Graph Editor with some additional quality of life animation functionality.
- Modal Cage Transform: Implements a custom modal operator (`GRAPH_OT_custom_cage_transform`) that intercepts mouse events to draw a 2D bounding box using the `gpu` and `gpu_extras` modules. Allows for Maya-style scaling and pivoting of keyframes in an overlay.
- Interactive Retiming: A drag-operator that calculates pivots (Start/End/Center) and scales keyframe selection relative to the mouse delta. It handles `handle_left` and `handle_right` vectors explicitly to maintain curve tangency during time-dilation.
- Gimbal Guide: A Maya style rotation gizmo supporting local and gimbal style rotation axes (drag one, children follow, to help with visualizing gimbal lock).
Turntabler: Render Context Isolation
A state-managed automation tool for generating asset reviews. The primary challenge was allowing users to preview assets without modifying the source scene data.
The tool implements a `SceneStateBackup` class to cache current engine settings. It creates a temporary "Preview_Collection", injects standardized lighting rigs (Studio/Sunset presets via Python definition), and isolates selected geometry. Upon completion, a robust cleanup routine restores the original scene state and unlinks temporary data blocks to prevent file bloat.
Lightmaker: Vectorized Map Compositing
A utility for baking and combining lightmaps for glTF export. Standard Python loops are too slow for pixel-level manipulation of 4k textures.
This tool bakes Ambient Occlusion and Shadow passes to internal memory, converts the image buffers to NumPy arrays, and performs vectorized mixing. The resulting buffer is written back to Blender's image block, reducing processing time for large assets. It automatically generates and links the required glTF shader node tree.
Vectorized Mixing Logic
def bake_process(self, context, obj):
# ... (Safety checks & Context Manager setup) ...
# Optimization: Zero-copy data transfer using foreach_get
# avoiding slow Python list allocation for 4k+ textures
count = len(ao_img.pixels)
ao_arr = np.empty(count, dtype=np.float32)
sh_arr = np.empty(count, dtype=np.float32)
ao_img.pixels.foreach_get(ao_arr)
shadow_img.pixels.foreach_get(sh_arr)
# Vectorized mixing (C-speed math)
combined = (ao_arr + sh_arr) * 0.5
# Direct memory write back to Blender
final_img.pixels.foreach_set(combined)