Skip to content
Snippets Groups Projects
Commit f4cf9b00 authored by Damien Picard's avatar Damien Picard
Browse files

Add camera rigs: add 2D camera rig

This rig is mostly useful for 2D shots, when the camera is static and
the action happens in front of it (like a theatre stage).

In a 2D production (and some shots in 3D as well), you sometimes need
to rotate the camera while zooming, effectively "cropping" the field,
just as you would using a rostrum camera. This is tedious and
error-prone if animating built-in basic transforms, so this rig
implements a more intuitive way to do that, by just animating the two
lower corners of the camera's field.

Also improved other stuff in the add-on:
- add the GPL license block to create_widgets.py;
- rename "arm[ature]" to "rig" in some functions, for consistency and
  to avoid confusion with the crane's arm;
- changes to the UI panel:
  - remove the boxes,
  - put focal length at the top of the panel,
  - group related properties using aligned columns,
  - change the Make Camera Active operator's poll method, so that it
    is always visible in the UI, but greyed out when the camera is
    already active.
parent cebfa3b6
No related branches found
No related tags found
No related merge requests found
......@@ -18,8 +18,8 @@
bl_info = {
"name": "Add Camera Rigs",
"author": "Wayne Dixon, Brian Raschko, Kris Wittig, Damien Picard",
"version": (1, 4, 2),
"author": "Wayne Dixon, Brian Raschko, Kris Wittig, Damien Picard, Flavio Perez",
"version": (1, 4, 3),
"blender": (2, 80, 0),
"location": "View3D > Add > Camera > Dolly or Crane Rig",
"description": "Adds a Camera Rig with UI",
......
......@@ -17,18 +17,33 @@
# ##### END GPL LICENSE BLOCK #####
import bpy
from bpy_extras import object_utils
from bpy.types import Operator
from math import radians, pi
from bpy_extras import object_utils
from mathutils import Vector
from rna_prop_ui import rna_idprop_ui_prop_get
from math import pi
from .create_widgets import (create_root_widget,
create_widget,
create_camera_widget,
create_aim_widget,
)
create_camera_widget, create_aim_widget,
create_circle_widget, create_corner_widget)
def create_prop_driver(rig, cam, prop_from, prop_to):
"""Create driver to a property on the rig"""
driver = cam.data.driver_add(prop_to)
driver.driver.type = 'SCRIPTED'
var = driver.driver.variables.new()
var.name = 'var'
var.type = 'SINGLE_PROP'
# Target the custom bone property
var.targets[0].id = rig
var.targets[0].data_path = 'pose.bones["Camera"]["%s"]' % prop_from
driver.driver.expression = 'var'
def create_dolly_bones(rig, bone_layers):
def create_dolly_bones(rig):
"""Create bones for the dolly camera rig"""
bones = rig.data.edit_bones
# Add new bones
......@@ -39,7 +54,7 @@ def create_dolly_bones(rig, bone_layers):
ctrl_aim_child = bones.new("Aim_shape_rotation-MCH")
ctrl_aim_child.head = (0.0, 10.0, 1.7)
ctrl_aim_child.tail = (0.0, 11.0, 1.7)
ctrl_aim_child.layers = bone_layers
ctrl_aim_child.layers = tuple(i == 1 for i in range(32))
ctrl_aim = bones.new("Aim")
ctrl_aim.head = (0.0, 10.0, 1.7)
......@@ -57,7 +72,8 @@ def create_dolly_bones(rig, bone_layers):
ctrl_aim_child.parent = ctrl_aim
def create_crane_bones(rig, bone_layers):
def create_crane_bones(rig):
"""Create bones for the crane camera rig"""
bones = rig.data.edit_bones
# Add new bones
......@@ -68,7 +84,7 @@ def create_crane_bones(rig, bone_layers):
ctrl_aim_child = bones.new("Aim_shape_rotation-MCH")
ctrl_aim_child.head = (0.0, 10.0, 1.7)
ctrl_aim_child.tail = (0.0, 11.0, 1.7)
ctrl_aim_child.layers = bone_layers
ctrl_aim_child.layers = tuple(i == 1 for i in range(32))
ctrl_aim = bones.new("Aim")
ctrl_aim.head = (0.0, 10.0, 1.7)
......@@ -112,34 +128,13 @@ def create_crane_bones(rig, bone_layers):
pose_bones["Crane_height"].lock_scale = (True, False, True)
def build_camera_rig(context, mode):
bone_layers = tuple(i == 1 for i in range(32))
view_layer = bpy.context.view_layer
rig_name = mode.capitalize() + "_Rig"
rig_data = bpy.data.armatures.new(rig_name)
rig = object_utils.object_data_add(context, rig_data, name=rig_name)
rig["rig_id"] = "%s" % rig_name
view_layer.objects.active = rig
rig.location = context.scene.cursor.location
bpy.ops.object.mode_set(mode='EDIT')
# Add new bones
if mode == "DOLLY":
create_dolly_bones(rig, bone_layers)
elif mode == "CRANE":
create_crane_bones(rig, bone_layers)
def setup_3d_rig(rig, cam):
"""Finish setting up Dolly and Crane rigs"""
# Jump into object mode and change bones to euler
bpy.ops.object.mode_set(mode='OBJECT')
pose_bones = rig.pose.bones
for b in pose_bones:
b.rotation_mode = 'XYZ'
# Add custom properties to the armature’s Camera bone,
# so that all properties may be animated in a single action
# Add driver after the camera is created
for bone in pose_bones:
bone.rotation_mode = 'XYZ'
# Lens property
pb = pose_bones['Camera']
......@@ -150,22 +145,6 @@ def build_camera_rig(context, mode):
prop["max"] = 1000000.0
prop["soft_max"] = 5000.0
# DOF Focus Distance property
pb = pose_bones['Camera']
pb["focus_distance"] = 10.0
prop = rna_idprop_ui_prop_get(pb, "focus_distance", create=True)
prop["default"] = 10.0
prop["min"] = 0.0
# DOF F-Stop property
pb = pose_bones['Camera']
pb["aperture_fstop"] = 2.8
prop = rna_idprop_ui_prop_get(pb, "aperture_fstop", create=True)
prop["default"] = 2.8
prop["min"] = 0.0
prop["soft_min"] = 0.1
prop["soft_max"] = 128.0
# Build the widgets
root_widget = create_root_widget("Camera_Root")
camera_widget = create_camera_widget("Camera")
......@@ -176,7 +155,7 @@ def build_camera_rig(context, mode):
pose_bones["Aim"].custom_shape = aim_widget
pose_bones["Camera"].custom_shape = camera_widget
# Set the "At" field to the child
# Set the "At" field to the shape mecanism
pose_bones["Aim"].custom_shape_transform = pose_bones["Aim_shape_rotation-MCH"]
# Add constraints to bones
......@@ -189,55 +168,359 @@ def build_camera_rig(context, mode):
con.subtarget = "Aim"
con.use_target_z = True
# Change display to BBone: it just looks nicer
bpy.context.object.data.display_type = 'BBONE'
# Change display to wire for object
bpy.context.object.display_type = 'WIRE'
cam.data.display_size = 1.0
cam.rotation_euler[0] = pi / 2.0 # Rotate the camera 90 degrees in x
create_prop_driver(rig, cam, "lens", "lens")
def create_2d_bones(context, rig, cam):
"""Create bones for the 2D camera rig"""
scene = context.scene
bones = rig.data.edit_bones
# Add new bones
bones = rig.data.edit_bones
root = bones.new("Root")
root.tail = Vector((0.0, 0.0, 1.0))
root.show_wire = True
ctrl = bones.new('Camera')
ctrl.tail = Vector((0.0, 0.0, 1.0))
ctrl.show_wire = True
left_corner = bones.new("Left_corner")
left_corner.head = (-3, 10, -2)
left_corner.tail = left_corner.head + Vector((0.0, 0.0, 1.0))
left_corner.show_wire = True
right_corner = bones.new("Right_corner")
right_corner.head = (3, 10, -2)
right_corner.tail = right_corner.head + Vector((0.0, 0.0, 1.0))
right_corner.show_wire = True
corner_distance_x = (left_corner.head - right_corner.head).length
corner_distance_y = -left_corner.head.z
corner_distance_z = left_corner.head.y
center = bones.new("Center-MCH")
center.head = ((right_corner.head + left_corner.head) / 2.0)
center.tail = center.head + Vector((0.0, 0.0, 1.0))
center.layers = tuple(i == 1 for i in range(32))
center.show_wire = True
# Setup hierarchy
ctrl.parent = root
left_corner.parent = root
right_corner.parent = root
center.parent = root
# Jump into object mode and change bones to euler
bpy.ops.object.mode_set(mode='OBJECT')
pose_bones = rig.pose.bones
for bone in pose_bones:
bone.rotation_mode = 'XYZ'
# Bone drivers
center_drivers = pose_bones["Center-MCH"].driver_add("location")
# Center X driver
driver = center_drivers[0].driver
driver.type = 'AVERAGE'
for corner in ('left', 'right'):
var = driver.variables.new()
var.name = corner
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner'
var.targets[0].transform_type = 'LOC_X'
var.targets[0].transform_space = 'TRANSFORM_SPACE'
# Center Y driver
driver = center_drivers[1].driver
driver.type = 'SCRIPTED'
driver.expression = '({distance_x} - (left_x-right_x))*(res_y/res_x)/2 + (left_y + right_y)/2'.format(distance_x=corner_distance_x)
for direction in ('x', 'y'):
for corner in ('left', 'right'):
var = driver.variables.new()
var.name = '%s_%s' % (corner, direction)
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner'
var.targets[0].transform_type = 'LOC_' + direction.upper()
var.targets[0].transform_space = 'TRANSFORM_SPACE'
var = driver.variables.new()
var.name = 'res_' + direction
var.type = 'SINGLE_PROP'
var.targets[0].id_type = 'SCENE'
var.targets[0].id = scene
var.targets[0].data_path = 'render.resolution_' + direction
# Center Z driver
driver = center_drivers[2].driver
driver.type = 'AVERAGE'
for corner in ('left', 'right'):
var = driver.variables.new()
var.name = corner
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner'
var.targets[0].transform_type = 'LOC_Z'
var.targets[0].transform_space = 'TRANSFORM_SPACE'
# Bone constraints
con = pose_bones["Camera"].constraints.new('DAMPED_TRACK')
con.target = rig
con.subtarget = "Center-MCH"
con.track_axis = 'TRACK_NEGATIVE_Z'
# Build the widgets
left_widget = create_corner_widget("Left_corner", reverse=True)
right_widget = create_corner_widget("Right_corner")
parent_widget = create_circle_widget("Root", radius=0.5)
camera_widget = create_circle_widget("Camera_2D", radius=0.3)
# Add the custom bone shapes
pose_bones["Left_corner"].custom_shape = left_widget
pose_bones["Right_corner"].custom_shape = right_widget
pose_bones["Root"].custom_shape = parent_widget
pose_bones["Camera"].custom_shape = camera_widget
# Lock the relevant loc, rot and scale
pose_bones["Left_corner"].lock_rotation = (True,) * 3
pose_bones["Right_corner"].lock_rotation = (True,) * 3
pose_bones["Camera"].lock_rotation = (True,) * 3
pose_bones["Camera"].lock_scale = (True,) * 3
# Camera settings
cam.data.sensor_fit = "HORIZONTAL" # Avoids distortion in portrait format
# Property to switch between rotation and switch mode
pose_bones["Camera"]['rotation_shift'] = 0.0
prop = rna_idprop_ui_prop_get(pose_bones["Camera"], 'rotation_shift', create=True)
prop["min"] = 0.0
prop["max"] = 1.0
prop["soft_min"] = 0.0
prop["soft_max"] = 1.0
prop["description"] = 'rotation_shift'
# Rotation / shift switch driver
driver = con.driver_add('influence').driver
driver.expression = '1 - rotation_shift'
var = driver.variables.new()
var.name = 'rotation_shift'
var.type = 'SINGLE_PROP'
var.targets[0].id = rig
var.targets[0].data_path = 'pose.bones["Camera"]["rotation_shift"]'
# Focal length driver
driver = cam.data.driver_add('lens').driver
driver.expression = 'abs({distance_z} - (left_z + right_z)/2 + cam_z) * 36 / frame_width'.format(distance_z=corner_distance_z)
var = driver.variables.new()
var.name = 'frame_width'
var.type = 'LOC_DIFF'
var.targets[0].id = rig
var.targets[0].bone_target = "Left_corner"
var.targets[0].transform_space = 'WORLD_SPACE'
var.targets[1].id = rig
var.targets[1].bone_target = "Right_corner"
var.targets[1].transform_space = 'WORLD_SPACE'
for corner in ('left', 'right'):
var = driver.variables.new()
var.name = corner + '_z'
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner'
var.targets[0].transform_type = 'LOC_Z'
var.targets[0].transform_space = 'TRANSFORM_SPACE'
var = driver.variables.new()
var.name = 'cam_z'
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = "Camera"
var.targets[0].transform_type = 'LOC_Z'
var.targets[0].transform_space = 'TRANSFORM_SPACE'
# Orthographic scale driver
driver = cam.data.driver_add('ortho_scale').driver
driver.expression = 'abs({distance_x} - (left_x - right_x))'.format(distance_x=corner_distance_x)
for corner in ('left', 'right'):
var = driver.variables.new()
var.name = corner + '_x'
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner'
var.targets[0].transform_type = 'LOC_X'
var.targets[0].transform_space = 'TRANSFORM_SPACE'
# Shift driver X
driver = cam.data.driver_add('shift_x').driver
driver.expression = 'rotation_shift * (((left_x + right_x)/2 - cam_x) * lens / abs({distance_z} - (left_z + right_z)/2 + cam_z) / 36)'.format(distance_z=corner_distance_z)
var = driver.variables.new()
var.name = 'rotation_shift'
var.type = 'SINGLE_PROP'
var.targets[0].id = rig
var.targets[0].data_path = 'pose.bones["Camera"]["rotation_shift"]'
for direction in ('x', 'z'):
for corner in ('left', 'right'):
var = driver.variables.new()
var.name = '%s_%s' % (corner, direction)
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner'
var.targets[0].transform_type = 'LOC_' + direction.upper()
var.targets[0].transform_space = 'TRANSFORM_SPACE'
var = driver.variables.new()
var.name = 'cam_' + direction
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = "Camera"
var.targets[0].transform_type = 'LOC_' + direction.upper()
var.targets[0].transform_space = 'TRANSFORM_SPACE'
var = driver.variables.new()
var.name = 'lens'
var.type = 'SINGLE_PROP'
var.targets[0].id_type = 'CAMERA'
var.targets[0].id = cam.data
var.targets[0].data_path = 'lens'
# Shift driver Y
driver = cam.data.driver_add('shift_y').driver
driver.expression = 'rotation_shift * -(({distance_y} - (left_y + right_y)/2 + cam_y) * lens / abs({distance_z} - (left_z + right_z)/2 + cam_z) / 36 - (res_y/res_x)/2)'.format(distance_y=corner_distance_y, distance_z=corner_distance_z)
var = driver.variables.new()
var.name = 'rotation_shift'
var.type = 'SINGLE_PROP'
var.targets[0].id = rig
var.targets[0].data_path = 'pose.bones["Camera"]["rotation_shift"]'
for direction in ('y', 'z'):
for corner in ('left', 'right'):
var = driver.variables.new()
var.name = '%s_%s' % (corner, direction)
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner'
var.targets[0].transform_type = 'LOC_' + direction.upper()
var.targets[0].transform_space = 'TRANSFORM_SPACE'
var = driver.variables.new()
var.name = 'cam_' + direction
var.type = 'TRANSFORMS'
var.targets[0].id = rig
var.targets[0].bone_target = "Camera"
var.targets[0].transform_type = 'LOC_' + direction.upper()
var.targets[0].transform_space = 'TRANSFORM_SPACE'
for direction in ('x', 'y'):
var = driver.variables.new()
var.name = 'res_' + direction
var.type = 'SINGLE_PROP'
var.targets[0].id_type = 'SCENE'
var.targets[0].id = scene
var.targets[0].data_path = 'render.resolution_' + direction
var = driver.variables.new()
var.name = 'lens'
var.type = 'SINGLE_PROP'
var.targets[0].id_type = 'CAMERA'
var.targets[0].id = cam.data
var.targets[0].data_path = 'lens'
def build_camera_rig(context, mode):
"""Create stuff common to all camera rigs."""
# Add the camera object
cam_name = "%s_Camera" % mode.capitalize()
cam_data = bpy.data.cameras.new(cam_name)
cam = object_utils.object_data_add(context, cam_data, name=cam_name)
view_layer.objects.active = cam
context.scene.camera = cam
cam.data.display_size = 1.0
cam.rotation_euler[0] = pi / 2.0 # Rotate the camera 90 degrees in x
# Add the rig object
rig_name = mode.capitalize() + "_Rig"
rig_data = bpy.data.armatures.new(rig_name)
rig = object_utils.object_data_add(context, rig_data, name=rig_name)
rig["rig_id"] = "%s" % rig_name
rig.location = context.scene.cursor.location
bpy.ops.object.mode_set(mode='EDIT')
# Add new bones and setup specific rigs
if mode == "DOLLY":
create_dolly_bones(rig)
setup_3d_rig(rig, cam)
elif mode == "CRANE":
create_crane_bones(rig)
setup_3d_rig(rig, cam)
elif mode == "2D":
create_2d_bones(context, rig, cam)
# Parent the camera to the rig
cam.location = (0.0, -1.0, 0.0) # Move the camera to the correct position
cam.parent = rig
cam.parent_type = "BONE"
cam.parent_bone = "Camera"
# Change display to BBone: it just looks nicer
rig.data.display_type = 'BBONE'
# Change display to wire for object
rig.display_type = 'WIRE'
# Lock camera transforms
cam.lock_location = (True,) * 3
cam.lock_rotation = (True,) * 3
cam.lock_scale = (True,) * 3
# Add custom properties to the armature’s Camera bone,
# so that all properties may be animated in a single action
pose_bones = rig.pose.bones
# DOF Focus Distance property
pb = pose_bones['Camera']
pb["focus_distance"] = 10.0
prop = rna_idprop_ui_prop_get(pb, "focus_distance", create=True)
prop["default"] = 10.0
prop["min"] = 0.0
# DOF F-Stop property
pb = pose_bones['Camera']
pb["aperture_fstop"] = 2.8
prop = rna_idprop_ui_prop_get(pb, "aperture_fstop", create=True)
prop["default"] = 2.8
prop["min"] = 0.0
prop["soft_min"] = 0.1
prop["soft_max"] = 128.0
# Add drivers to link the camera properties to the custom props
# on the armature
for prop_from, prop_to in (("lens", "lens"),
("focus_distance", "dof.focus_distance"),
("aperture_fstop", "dof.aperture_fstop")):
driver = cam.data.driver_add(prop_to)
driver.driver.type = 'SCRIPTED'
var = driver.driver.variables.new()
var.name = 'var'
var.type = 'SINGLE_PROP'
# Target the custom bone property
var.targets[0].id = rig
var.targets[0].data_path = 'pose.bones["Camera"]["%s"]' % prop_from
driver.driver.expression = 'var'
create_prop_driver(rig, cam, "focus_distance", "dof.focus_distance")
create_prop_driver(rig, cam, "aperture_fstop", "dof.aperture_fstop")
# Make the rig the active object
for ob in view_layer.objects:
ob.select_set(False)
view_layer = context.view_layer
for obj in view_layer.objects:
obj.select_set(False)
rig.select_set(True)
view_layer.objects.active = rig
return rig
class OBJECT_OT_build_camera_rig(Operator):
bl_idname = "object.build_camera_rig"
......@@ -245,11 +528,12 @@ class OBJECT_OT_build_camera_rig(Operator):
bl_description = "Build a Camera Rig"
bl_options = {'REGISTER', 'UNDO'}
mode: bpy.props.EnumProperty(items=
(('DOLLY',) * 3,
('CRANE',) * 3,),
mode: bpy.props.EnumProperty(items=(('DOLLY', 'Dolly', 'Dolly rig'),
('CRANE', 'Crane', 'Crane rig',),
('2D', '2D', '2D rig')),
name="mode",
description="", default="DOLLY")
description="Type of camera to create",
default="DOLLY")
def execute(self, context):
# Build the rig
......@@ -260,18 +544,23 @@ class OBJECT_OT_build_camera_rig(Operator):
def add_dolly_crane_buttons(self, context):
"""Dolly and crane entries in the Add Object > Camera Menu"""
if context.mode == 'OBJECT':
op = self.layout.operator(
self.layout.operator(
OBJECT_OT_build_camera_rig.bl_idname,
text="Dolly Camera Rig",
icon='CAMERA_DATA'
)
op.mode = "DOLLY"
op = self.layout.operator(
icon='VIEW_CAMERA'
).mode = "DOLLY"
self.layout.operator(
OBJECT_OT_build_camera_rig.bl_idname,
text="Crane Camera Rig",
icon='CAMERA_DATA'
)
op.mode = "CRANE"
icon='VIEW_CAMERA'
).mode = "CRANE"
self.layout.operator(
OBJECT_OT_build_camera_rig.bl_idname,
text="2D Camera Rig",
icon='PIVOT_BOUNDBOX'
).mode = "2D"
classes = (
......
......@@ -19,7 +19,7 @@
import bpy
from bpy.types import Panel
from .operators import get_arm_and_cam
from .operators import get_rig_and_cam
class ADD_CAMERA_RIGS_PT_composition_guides(Panel):
bl_label = "Composition Guides"
......@@ -29,7 +29,7 @@ class ADD_CAMERA_RIGS_PT_composition_guides(Panel):
def draw(self, context):
layout = self.layout
arm, cam = get_arm_and_cam(context.active_object)
rig, cam = get_rig_and_cam(context.active_object)
cam = cam.data
layout.prop(cam, "show_safe_areas")
......
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
from mathutils import Vector
from math import cos, sin, pi
def create_widget(name):
......@@ -29,6 +49,44 @@ def create_widget(name):
return obj
def create_corner_widget(name, reverse=False):
"""Create a wedge-shaped widget"""
obj = create_widget(name)
if not obj.data.vertices:
reverse = -1 if reverse else 1
verts = (Vector((reverse * 0.0, 0.0, 0.0)),
Vector((reverse * 0.0, 1.0, 0.0)),
Vector((reverse * -0.1, 1.0, 0.0)),
Vector((reverse * -0.1, 0.1, 0.0)),
Vector((reverse * -1.0, 0.1, 0.0)),
Vector((reverse * -1.0, 0.0, 0.0)),
)
edges = [(n, (n+1) % len(verts)) for n in range(len(verts))]
mesh = obj.data
mesh.from_pydata(verts, edges, ())
mesh.update()
return obj
def create_circle_widget(name, radius=1.0):
"""Create a circle-shaped widget"""
obj = create_widget(name)
if not obj.data.vertices:
vert_n = 16
verts = []
for n in range(vert_n):
angle = n / vert_n * 2*pi
verts.append(Vector((cos(angle) * radius,
sin(angle) * radius, 0.0)))
edges = [(n, (n+1) % len(verts)) for n in range(len(verts))]
mesh = obj.data
mesh.from_pydata(verts, edges, ())
mesh.update()
return obj
def create_root_widget(name):
"""Create a compass-shaped widget"""
obj = create_widget(name)
......
......@@ -20,7 +20,7 @@ import bpy
from bpy.types import Operator
def get_arm_and_cam(obj):
def get_rig_and_cam(obj):
if obj.type == 'ARMATURE':
cam = None
for child in obj.children:
......@@ -32,7 +32,8 @@ def get_arm_and_cam(obj):
elif (obj.type == 'CAMERA'
and obj.parent is not None
and "rig_id" in obj.parent
and obj.parent["rig_id"].lower() in {"dolly_rig", "crane_rig"}):
and obj.parent["rig_id"].lower() in {"dolly_rig",
"crane_rig", "2d_rig"}):
return obj.parent, obj
return None, None
......@@ -41,25 +42,31 @@ class CameraRigMixin():
@classmethod
def poll(cls, context):
if context.active_object is not None:
return get_arm_and_cam(context.active_object) != (None, None)
return get_rig_and_cam(context.active_object) != (None, None)
return False
class ADD_CAMERA_RIGS_OT_set_scene_camera(Operator, CameraRigMixin):
class ADD_CAMERA_RIGS_OT_set_scene_camera(Operator):
bl_idname = "add_camera_rigs.set_scene_camera"
bl_label = "Make Camera Active"
bl_description = "Makes the camera parented to this rig the active scene camera"
@classmethod
def poll(cls, context):
if context.active_object is not None:
rig, cam = get_rig_and_cam(context.active_object)
if cam is not None:
return cam is not context.scene.camera
return False
def execute(self, context):
arm, cam = get_arm_and_cam(context.active_object)
rig, cam = get_rig_and_cam(context.active_object)
scene_cam = context.scene.camera
if cam is not None and cam is not scene_cam:
context.scene.camera = cam
return {'FINISHED'}
return {'CANCELLED'}
context.scene.camera = cam
return {'FINISHED'}
class ADD_CAMERA_RIGS_OT_add_marker_bind(Operator, CameraRigMixin):
......@@ -68,7 +75,7 @@ class ADD_CAMERA_RIGS_OT_add_marker_bind(Operator, CameraRigMixin):
bl_description = "Add marker to current frame then bind rig camera to it (for camera switching)"
def execute(self, context):
arm, cam = get_arm_and_cam(context.active_object)
rig, cam = get_rig_and_cam(context.active_object)
marker = context.scene.timeline_markers.new(
"cam_" + str(context.scene.frame_current),
......@@ -85,15 +92,15 @@ class ADD_CAMERA_RIGS_OT_add_dof_object(Operator, CameraRigMixin):
bl_description = "Create Empty and add as DOF Object"
def execute(self, context):
arm, cam = get_arm_and_cam(context.active_object)
bone = arm.data.bones['Aim_shape_rotation-MCH']
rig, cam = get_rig_and_cam(context.active_object)
bone = rig.data.bones['Aim_shape_rotation-MCH']
# Add Empty
empty_obj = bpy.data.objects.new("EmptyDOF", None)
context.scene.collection.objects.link(empty_obj)
# Parent to Aim Child bone
empty_obj.parent = arm
empty_obj.parent = rig
empty_obj.parent_type = "BONE"
empty_obj.parent_bone = "Aim_shape_rotation-MCH"
......
......@@ -19,7 +19,7 @@
import bpy
from bpy.types import Panel
from .operators import get_arm_and_cam, CameraRigMixin
from .operators import get_rig_and_cam, CameraRigMixin
class ADD_CAMERA_RIGS_PT_camera_rig_ui(Panel, CameraRigMixin):
......@@ -30,66 +30,87 @@ class ADD_CAMERA_RIGS_PT_camera_rig_ui(Panel, CameraRigMixin):
def draw(self, context):
active_object = context.active_object
arm, cam = get_arm_and_cam(context.active_object)
pose_bones = arm.pose.bones
rig, cam = get_rig_and_cam(context.active_object)
pose_bones = rig.pose.bones
cam_data = cam.data
layout = self.layout
# Camera lens
if rig["rig_id"].lower() in ("dolly_rig", "crane_rig"):
layout.prop(pose_bones["Camera"], '["lens"]',
text="Focal Length (mm)")
col = layout.column(align=True)
col.label(text="Clipping:")
col.prop(cam_data, "clip_start", text="Start")
col.prop(cam_data, "clip_end", text="End")
layout = self.layout.box().column()
layout.label(text="Clipping:")
layout.prop(cam_data, "clip_start", text="Start")
layout.prop(cam_data, "clip_end", text="End")
layout.prop(cam_data, "type")
layout.prop(cam_data.dof, "use_dof")
if cam_data.dof.use_dof:
if cam_data.dof.focus_object is None:
layout.operator("add_camera_rigs.add_dof_object",
text="Add DOF Empty", icon="OUTLINER_OB_EMPTY")
layout.prop(pose_bones["Camera"],
'["focus_distance"]', text="Focus Distance")
layout.prop(pose_bones["Camera"],
'["aperture_fstop"]', text="F-Stop")
# DoF
col = layout.column(align=True)
col.prop(cam_data.dof, "use_dof")
if cam_data.dof.use_dof:
if rig["rig_id"].lower() in ("crane_rig", "dolly_rig"):
if cam_data.dof.focus_object is None:
col.operator("add_camera_rigs.add_dof_object",
text="Add DOF Empty", icon="OUTLINER_OB_EMPTY")
else:
col.prop(cam_data.dof, "focus_object")
row = col.row(align=True)
row.active = cam_data.dof.focus_object is None
row.prop(pose_bones["Camera"],
'["focus_distance"]', text="Focus Distance")
col.prop(pose_bones["Camera"],
'["aperture_fstop"]', text="F-Stop")
# Viewport display
layout.prop(active_object, 'show_in_front',
toggle=False, text='Show in Front')
layout.prop(cam_data, "show_limits")
layout.prop(cam_data, "show_passepartout")
col = layout.column(align=True)
col.prop(cam_data, "show_passepartout")
if cam_data.show_passepartout:
layout.prop(cam_data, "passepartout_alpha")
col.prop(cam_data, "passepartout_alpha")
layout.row().separator()
# Added the comp guides here
# Composition guides
layout.popover(
panel="ADD_CAMERA_RIGS_PT_composition_guides",
text="Composition Guides",)
layout.row().separator()
layout.prop(cam,
# Props and operators
col = layout.column(align=True)
col.prop(cam,
"hide_select", text="Make Camera Unselectable")
layout.operator("add_camera_rigs.add_marker_bind",
text="Add Marker and Bind", icon="MARKER_HLT")
if context.scene.camera is not cam:
layout.operator("add_camera_rigs.set_scene_camera",
text="Make Camera Active", icon='CAMERA_DATA')
# Camera lens
layout.separator()
layout.prop(pose_bones["Camera"], '["lens"]', text="Focal Length (mm)")
# Track to Constraint
layout.label(text="Tracking:")
layout.prop(pose_bones["Camera"].constraints["Track To"],
'influence', text="Aim Lock", slider=True)
if arm["rig_id"].lower() == "crane_rig":
col = layout.box().column()
col.operator("add_camera_rigs.add_marker_bind",
text="Add Marker and Bind", icon="MARKER_HLT")
col.operator("add_camera_rigs.set_scene_camera",
text="Make Camera Active", icon='CAMERA_DATA')
if rig["rig_id"].lower() in ("dolly_rig", "crane_rig"):
# Track to Constraint
col = layout.column(align=True)
col.label(text="Tracking:")
col.prop(pose_bones["Camera"].constraints["Track To"],
'influence', text="Aim Lock", slider=True)
# Crane arm stuff
col.label(text="Crane Arm:")
col.prop(pose_bones["Crane_height"],
'scale', index=1, text="Arm Height")
col.prop(pose_bones["Crane_arm"],
'scale', index=1, text="Arm Length")
if rig["rig_id"].lower() == "crane_rig":
col = layout.column(align=True)
col.label(text="Crane Arm:")
col.prop(pose_bones["Crane_height"],
'scale', index=1, text="Arm Height")
col.prop(pose_bones["Crane_arm"],
'scale', index=1, text="Arm Length")
# 2D rig stuff
elif rig["rig_id"].lower() == "2d_rig":
col = layout.column(align=True)
col.label(text="2D Rig:")
col.prop(pose_bones["Camera"], '["rotation_shift"]',
text="Rotation/Shift")
if cam.data.sensor_width != 36:
col.label(text="Please set Camera Sensor Width to 36", icon="ERROR")
def register():
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment