"""Rig of the sliding pressure door. Three sliders on PressureDoorSliding_Root: open 0 shut .. 1 open: 0.00 - 0.30 the lock ring's light goes red -> amber -> green (material M_PDS_Lock) 0.36 - 1.00 the two leaves slide 0.72 m apart into the plate's pockets (eased) arm_swing -1 .. 1: the console (monitor, arm and tray) yaws +-25 deg about its post cam_yaw -1 .. 1: the camera yaws +-30 deg on its ball Drivers are simple expressions (this Blender runs with Python auto-run off; simple expressions need no Python). Timeline, as the video: shut at frame 1, open at 60, held, shut again at 180 (30 fps). Action "PressureDoorSliding | OPEN_CLOSE". Each moving part keeps its shut transform in ["closed_location"] / ["closed_rotation"] (the project's convention). """ import bpy, math ROOT = "PressureDoorSliding_Root" K0, K1 = 0.00, 0.30 # lock light S0, S1 = 0.36, 1.00 # slide ARM_DEG, CAM_DEG = 25.0, 30.0 def ramp(a, b, var="open"): return "min(max((%s-%.4f)/%.4f,0),1)" % (var, a, b - a) def eased(a, b, var="open"): r = ramp(a, b, var) return "%s*%s*(3-2*%s)" % (r, r, r) def drive(idb, path, index, expr, root, var="open"): try: idb.driver_remove(path, index) if index is not None else idb.driver_remove(path) except TypeError: pass fc = idb.driver_add(path, index) if index is not None else idb.driver_add(path) d = fc.driver d.type = 'SCRIPTED' v = d.variables.new() v.name = var v.type = 'SINGLE_PROP' v.targets[0].id_type = 'OBJECT' v.targets[0].id = root v.targets[0].data_path = '["%s"]' % var d.expression = expr return d def prop(root, name, lo, hi, default, desc): root[name] = default ui = root.id_properties_ui(name) ui.update(min=lo, max=hi, soft_min=lo, soft_max=hi, default=default, description=desc) def rig(): root = bpy.data.objects[ROOT] prop(root, "open", 0.0, 1.0, 0.0, "0 shut, 1 open: lock light, then the leaves slide") prop(root, "arm_swing", -1.0, 1.0, 0.0, "console yaw, -1 .. 1 = -25 .. 25 deg") prop(root, "cam_yaw", -1.0, 1.0, 0.0, "camera yaw, -1 .. 1 = -30 .. 30 deg") report = [] for name in ("SM_PDSliding_LeafL", "SM_PDSliding_LeafR"): ob = bpy.data.objects[name] cx = ob["closed_location"][0] ob.location = ob["closed_location"] d = drive(ob, "location", 0, "%.6f+%.6f*%s" % (cx, ob["slide_x"], eased(S0, S1)), root) report.append((name, d.is_simple_expression, d.is_valid)) mon = bpy.data.objects["SM_PDSliding_Monitor"] d = drive(mon, "rotation_euler", 2, "arm_swing*%.6f" % math.radians(ARM_DEG), root, "arm_swing") report.append((mon.name, d.is_simple_expression, d.is_valid)) cam = bpy.data.objects["SM_PDSliding_Camera"] d = drive(cam, "rotation_euler", 2, "cam_yaw*%.6f" % math.radians(CAM_DEG), root, "cam_yaw") report.append((cam.name, d.is_simple_expression, d.is_valid)) report.append(("M_PDS_Lock", *rig_lock_material(root))) # timeline sc = bpy.context.scene sc.render.fps = 30 sc.frame_start, sc.frame_end = 1, 200 root.animation_data_clear() for f, v in ((1, 0.0), (60, 1.0), (120, 1.0), (180, 0.0)): root["open"] = v root.keyframe_insert('["open"]', frame=f) act = root.animation_data.action act.name = "PressureDoorSliding | OPEN_CLOSE" try: from bpy_extras.anim_utils import action_get_channelbag_for_slot for fc in action_get_channelbag_for_slot(act, root.animation_data.action_slot).fcurves: for kp in fc.keyframe_points: kp.interpolation = 'LINEAR' except Exception as ex: report.append(("interpolation", str(ex), False)) root["open"] = 0.0 sc.frame_set(1) return report def rig_lock_material(root=None): """the ring light's ramp Fac follows the slider's first 30 %; works on the preview nodes and on the real ones""" root = root or bpy.data.objects[ROOT] m = bpy.data.materials.get("M_PDS_Lock") node = m.node_tree.nodes.get("LockRamp") if node is None: return (False, False) d = drive(m.node_tree, 'nodes["LockRamp"].inputs[0].default_value', None, ramp(K0, K1), root) return (d.is_simple_expression, d.is_valid) def set_open(v): root = bpy.data.objects[ROOT] root["open"] = float(v) _tag(root) def set_prop(name, v): root = bpy.data.objects[ROOT] root[name] = float(v) _tag(root) def _tag(root): root.update_tag() for o in bpy.data.objects: if o.name.startswith("SM_PDSliding_") and o.animation_data: o.update_tag() m = bpy.data.materials.get("M_PDS_Lock") if m and m.node_tree.animation_data: m.node_tree.update_tag() bpy.context.view_layer.update()