1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
21 "name": "Add Camera Rigs",
22 "author": "Wayne Dixon, Kris Wittig",
24 "blender": (2, 80, 0),
25 "location": "View3D > Add > Camera > Dolly or Crane Rig",
26 "description": "Adds a Camera Rig with UI",
27 "warning": "Enable Auto Run Python Scripts in User Preferences > Save & Load",
28 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
29 "Py/Scripts/Rigging/Add_Camera_Rigs",
34 from bpy
.types
import (
38 from rna_prop_ui
import rna_idprop_ui_prop_get
39 from math
import radians
42 # =========================================================================
43 # Define the functions to build the Widgets
44 # =========================================================================
45 def create_widget(self
, name
):
46 """ Creates an empty widget object for a bone, and returns the object."""
47 obj_name
= "WDGT_" + name
48 scene
= bpy
.context
.scene
50 mesh
= bpy
.data
.meshes
.new(obj_name
)
51 obj
= bpy
.data
.objects
.new(obj_name
, mesh
)
53 # create a new collection for the wigets
54 collection_name
= "camera_widgets"
55 c
= bpy
.data
.collections
.get(collection_name
)
59 c
= bpy
.data
.collections
.new(collection_name
)
60 c
.hide_viewport
= True
64 scene
.collection
.children
.link(c
)
70 def create_root_widget(self
, name
):
71 # Creates a compass-shaped widget.
73 obj
= create_widget(self
, name
)
75 verts
= [(0.2102552056312561, -0.0012103617191314697, 0.21025514602661133),
76 (0.11378927528858185, -0.001210339367389679, 0.274711549282074),
77 (-3.070153553608179e-08, -0.0012103626504540443, 0.29734566807746887),
78 (-0.11378933489322662, -0.0012103542685508728, 0.27471157908439636),
79 (-0.2102552056312561, -0.0012103617191314697, 0.21025516092777252),
80 (-0.27471160888671875, -0.0012103617191314697, 0.11378928273916245),
81 (-0.29734569787979126, -0.0012103617191314697, -1.6809221392577456e-07),
82 (0.29734572768211365, -0.001210331916809082, -1.0901101177296368e-07),
83 (0.2747114598751068, -0.0012103617191314697, 0.11378948390483856),
84 (0.07152898609638214, -0.0012103691697120667, 0.5070746541023254),
85 (-0.07152895629405975, -0.0012103617191314697, 0.5070746541023254),
86 (-0.07152898609638214, -0.0012103915214538574, 0.38030144572257996),
87 (0.07152898609638214, -0.0012103691697120667, 0.38030144572257996),
88 (-0.1325872540473938, -0.0012103617191314697, 0.5070746541023254),
89 (0.13258719444274902, -0.0012103617191314697, 0.5070746541023254),
90 (-3.070154264150915e-08, -0.0012104818597435951, 0.6688110828399658),
91 (-0.274711549282074, -0.0012103617191314697, -0.11378948390483856),
92 (0.274711549282074, -0.001210331916809082, -0.1137893795967102),
93 (0.21025514602661133, -0.001210331916809082, -0.21025525033473969),
94 (0.11378927528858185, -0.001210339367389679, -0.27471160888671875),
95 (-9.030617320604506e-08, -0.0012103328481316566, -0.29734572768211365),
96 (-0.11378933489322662, -0.0012103542685508728, -0.27471157908439636),
97 (-0.2102552056312561, -0.001210331916809082, -0.21025516092777252),
98 (-0.6688110828399658, -0.0012103915214538574, 5.982118267411352e-08),
99 (-0.5070747137069702, -0.0012103915214538574, 0.13258729875087738),
100 (-0.5070747137069702, -0.001210331916809082, -0.1325872540473938),
101 (-0.38030147552490234, -0.0012103617191314697, 0.07152903825044632),
102 (-0.38030147552490234, -0.0012103617191314697, -0.07152897119522095),
103 (-0.5070747137069702, -0.001210331916809082, -0.07152896374464035),
104 (-0.5070747137069702, -0.0012103915214538574, 0.07152900844812393),
105 (0.5070745944976807, -0.001210331916809082, -0.07152891904115677),
106 (0.5070745944976807, -0.001210331916809082, 0.07152905315160751),
107 (0.38030144572257996, -0.0012103617191314697, 0.07152903825044632),
108 (0.38030141592025757, -0.001210331916809082, -0.07152897119522095),
109 (0.5070745944976807, -0.001210331916809082, 0.13258734345436096),
110 (0.5070745944976807, -0.001210331916809082, -0.13258720934391022),
111 (0.6688110828399658, -0.001210331916809082, 5.279173720396102e-08),
112 (1.4811239168466273e-07, -0.001210303045809269, -0.6688110828399658),
113 (-0.13258716464042664, -0.0012103021144866943, -0.5070746541023254),
114 (0.13258737325668335, -0.0012103021144866943, -0.5070746541023254),
115 (-0.07152889668941498, -0.0012103617191314697, -0.38030150532722473),
116 (0.07152910530567169, -0.0012103095650672913, -0.38030150532722473),
117 (0.07152910530567169, -0.0012103095650672913, -0.5070746541023254),
118 (-0.07152886688709259, -0.0012103021144866943, -0.5070746541023254)]
120 edges
= [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (7, 8), (0, 8),
121 (10, 11), (9, 12), (11, 12), (10, 13), (9, 14), (13, 15), (14, 15),
122 (16, 22), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (7, 17),
123 (6, 16), (23, 24), (23, 25), (24, 29), (25, 28), (26, 27), (26, 29),
124 (27, 28), (31, 32), (30, 33), (32, 33), (31, 34), (30, 35), (34, 36),
125 (35, 36), (37, 38), (37, 39), (38, 43), (39, 42), (40, 41), (40, 43), (41, 42)]
127 mesh
.from_pydata(verts
, edges
, [])
131 def create_camera_widget(self
, name
):
132 # Creates a camera ctrl widget
134 obj
= create_widget(self
, name
)
136 verts
= [(0.13756819069385529, 1.0706068032106941e-08, -0.13756819069385529),
137 (0.1797415018081665, 5.353034016053471e-09, -0.07445136457681656),
138 (0.19455081224441528, -6.381313819948996e-16, 8.504085435845354e-09),
139 (0.1797415018081665, -5.353034016053471e-09, 0.07445138692855835),
140 (0.13756819069385529, -1.0706068032106941e-08, 0.13756819069385529),
141 (0.07445137947797775, -2.1412136064213882e-08, 0.1797415018081665),
142 (-9.740904971522468e-08, -2.1412136064213882e-08, 0.19455081224441528),
143 (-5.87527146933553e-08, 2.1412136064213882e-08, -0.19455081224441528),
144 (0.0744515135884285, 2.1412136064213882e-08, -0.17974145710468292),
145 (0.3317747414112091, 5.353034016053471e-09, -0.04680081456899643),
146 (0.3317747414112091, -5.353034016053471e-09, 0.04680081456899643),
147 (0.24882805347442627, -5.353034016053471e-09, 0.04680081456899643),
148 (0.24882805347442627, 5.353034016053471e-09, -0.04680084437131882),
149 (0.3317747414112091, -5.353034016053471e-09, 0.08675074577331543),
150 (0.3317747414112091, 5.353034016053471e-09, -0.08675074577331543),
151 (0.43759751319885254, 0.0, 0.0), (-0.07445148378610611, -2.1412136064213882e-08, 0.17974145710468292),
152 (-0.07445141673088074, 2.1412136064213882e-08, -0.1797415018081665),
153 (-0.13756820559501648, 1.0706068032106941e-08, -0.1375681608915329),
154 (-0.1797415018081665, 5.353034016053471e-09, -0.07445136457681656),
155 (-0.19455081224441528, -1.2762627639897992e-15, 2.0872269246297037e-08),
156 (-0.1797415018081665, -5.353034016053471e-09, 0.07445140182971954),
157 (-0.1375681608915329, -1.0706068032106941e-08, 0.13756820559501648),
158 (5.1712785165136665e-08, -4.2824272128427765e-08, 0.43759751319885254),
159 (0.08675077557563782, -2.1412136064213882e-08, 0.3317747414112091),
160 (-0.08675073087215424, -2.1412136064213882e-08, 0.3317747414112091),
161 (0.046800870448350906, -2.1412136064213882e-08, 0.24882805347442627),
162 (-0.04680079594254494, -2.1412136064213882e-08, 0.24882805347442627),
163 (-0.04680079594254494, -2.1412136064213882e-08, 0.3317747414112091),
164 (0.04680084437131882, -2.1412136064213882e-08, 0.3317747414112091),
165 (-0.04680076241493225, 2.1412136064213882e-08, -0.3317747414112091),
166 (0.046800874173641205, 2.1412136064213882e-08, -0.3317747414112091),
167 (0.04680086299777031, 2.1412136064213882e-08, -0.24882805347442627),
168 (-0.046800799667835236, 2.1412136064213882e-08, -0.24882805347442627),
169 (0.0867508053779602, 2.1412136064213882e-08, -0.3317747414112091),
170 (-0.08675070106983185, 2.1412136064213882e-08, -0.3317747414112091),
171 (4.711345980012993e-08, 4.2824272128427765e-08, -0.43759751319885254),
172 (-0.43759751319885254, 1.0210102111918393e-14, -9.882624141255292e-08),
173 (-0.3317747414112091, -5.353034016053471e-09, 0.08675065636634827),
174 (-0.3317747414112091, 5.353034016053471e-09, -0.08675083518028259),
175 (-0.24882805347442627, -5.353034016053471e-09, 0.04680076986551285),
176 (-0.24882805347442627, 5.353034016053471e-09, -0.0468008853495121),
177 (-0.3317747414112091, 5.353034016053471e-09, -0.046800896525382996),
178 (-0.3317747414112091, -5.353034016053471e-09, 0.04680073633790016),
179 (-0.08263588696718216, -7.0564780685344886e-09, 0.08263592422008514),
180 (-0.10796899348497391, -3.5282390342672443e-09, 0.04472224414348602),
181 (-0.11686481535434723, -8.411977372806655e-16, 1.2537773486087644e-08),
182 (-0.10796899348497391, 3.5282390342672443e-09, -0.04472222551703453),
183 (-0.08263592422008514, 7.0564780685344886e-09, -0.08263588696718216),
184 (-0.04472225159406662, 7.0564780685344886e-09, -0.10796899348497391),
185 (-0.0447222925722599, -7.0564780685344886e-09, 0.10796897858381271),
186 (0.0447223074734211, 7.0564780685344886e-09, -0.10796897858381271),
187 (-3.529219583242593e-08, 7.0564780685344886e-09, -0.11686481535434723),
188 (-5.8512675593647145e-08, -7.0564780685344886e-09, 0.11686481535434723),
189 (0.04472222924232483, -7.0564780685344886e-09, 0.10796899348497391),
190 (0.08263590186834335, -7.0564780685344886e-09, 0.08263590186834335),
191 (0.10796899348497391, -3.5282390342672443e-09, 0.04472223296761513),
192 (0.11686481535434723, -4.2059886864033273e-16, 5.108323541946902e-09),
193 (0.10796899348497391, 3.5282390342672443e-09, -0.04472222924232483),
194 (0.08263590186834335, 7.0564780685344886e-09, -0.08263590186834335),
195 (3.725290298461914e-08, -2.1412136064213882e-08, 0.24882805347442627)]
196 edges
= [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (7, 8), (0, 8),
197 (10, 11), (9, 12), (11, 12), (10, 13), (9, 14), (13, 15), (14, 15), (16, 22),
198 (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (7, 17), (6, 16), (23, 24),
199 (23, 25), (24, 29), (25, 28), (26, 29), (27, 28), (31, 32), (30, 33), (32, 33),
200 (31, 34), (30, 35), (34, 36), (35, 36), (37, 38), (37, 39), (38, 43), (39, 42),
201 (40, 41), (40, 43), (41, 42), (50, 53), (49, 52), (44, 45), (45, 46), (46, 47),
202 (47, 48), (48, 49), (44, 50), (51, 59), (51, 52), (53, 54), (54, 55), (55, 56),
203 (56, 57), (57, 58), (58, 59), (26, 60), (27, 60), (23, 60)]
205 mesh
.from_pydata(verts
, edges
, [])
209 def create_aim_widget(self
, name
):
210 """ Creates a camera aim widget."""
212 obj
= create_widget(self
, name
)
214 verts
= [(0.15504144132137299, 1.4901161193847656e-08, 0.15504144132137299),
215 (0.20257140696048737, 7.450580596923828e-09, 0.0839078277349472),
216 (0.21926172077655792, -8.881784197001252e-16, -9.584233851001045e-09),
217 (0.20257140696048737, -7.450580596923828e-09, -0.0839078426361084),
218 (0.15504144132137299, -1.4901161193847656e-08, -0.15504144132137299),
219 (0.0839078351855278, -1.4901161193847656e-08, -0.20257140696048737),
220 (-1.0978147457763043e-07, -1.4901161193847656e-08, -0.21926172077655792),
221 (-6.621520043381679e-08, 1.4901161193847656e-08, 0.21926172077655792),
222 (0.08390798419713974, 1.4901161193847656e-08, 0.2025713473558426),
223 (0.39969685673713684, 3.725290298461914e-09, 0.05274524539709091),
224 (0.39969685673713684, -3.725290298461914e-09, -0.05274524167180061),
225 (0.4931790232658386, -3.725290298461914e-09, -0.05274524167180061),
226 (0.4931790232658386, 3.725290298461914e-09, 0.052745271474123),
227 (0.39969685673713684, -7.450580596923828e-09, -0.09776943176984787),
228 (0.39969685673713684, 7.450580596923828e-09, 0.09776943176984787),
229 (0.28043296933174133, 6.226862126577502e-17, -6.226862788321993e-17),
230 (-0.08390796184539795, -1.4901161193847656e-08, -0.2025713473558426),
231 (-0.08390787988901138, 1.4901161193847656e-08, 0.20257140696048737),
232 (-0.15504147112369537, 1.4901161193847656e-08, 0.1550414115190506),
233 (-0.20257140696048737, 7.450580596923828e-09, 0.08390782028436661),
234 (-0.21926172077655792, -1.7763568394002505e-15, -2.352336458955051e-08),
235 (-0.20257140696048737, -7.450580596923828e-09, -0.08390786498785019),
236 (-0.1550414115190506, -1.4901161193847656e-08, -0.15504147112369537),
237 (2.9140544199890428e-08, 2.9802322387695312e-08, 0.2804329991340637),
238 (-0.09776944667100906, 2.9802322387695312e-08, 0.3996969163417816),
239 (0.09776947647333145, 2.9802322387695312e-08, 0.3996969163417816),
240 (-0.052745264023542404, 2.9802322387695312e-08, 0.4931790828704834),
241 (0.05274529010057449, 2.9802322387695312e-08, 0.4931790828704834),
242 (0.052745264023542404, 2.9802322387695312e-08, 0.3996969163417816),
243 (-0.052745234221220016, 2.9802322387695312e-08, 0.3996969163417816),
244 (0.05274517461657524, -2.9802322387695312e-08, -0.3996969759464264),
245 (-0.052745334804058075, -2.9802322387695312e-08, -0.3996969759464264),
246 (-0.05274537205696106, -2.9802322387695312e-08, -0.49317920207977295),
247 (0.05274519696831703, -2.9802322387695312e-08, -0.49317920207977295),
248 (-0.09776955097913742, -2.9802322387695312e-08, -0.3996969163417816),
249 (0.09776940196752548, -2.9802322387695312e-08, -0.39969703555107117),
250 (-7.148475589247028e-08, -2.9802322387695312e-08, -0.2804329991340637),
251 (-0.2804330289363861, 3.552713678800501e-15, 4.234420103443881e-08),
252 (-0.3996969759464264, -7.450580596923828e-09, -0.09776938706636429),
253 (-0.39969685673713684, 7.450580596923828e-09, 0.09776950627565384),
254 (-0.4931790232658386, -3.725290298461914e-09, -0.05274520441889763),
255 (-0.4931790232658386, 3.725290298461914e-09, 0.05274531990289688),
256 (-0.3996969163417816, 3.725290298461914e-09, 0.052745312452316284),
257 (-0.3996969163417816, -3.725290298461914e-09, -0.05274519324302673),
258 (-0.06401804089546204, -7.450580596923828e-09, -0.06401806324720383),
259 (-0.0836436077952385, -3.725290298461914e-09, -0.03464633598923683),
260 (-0.09053517132997513, -8.881784197001252e-16, -9.713016169143884e-09),
261 (-0.0836436077952385, 3.725290298461914e-09, 0.03464631363749504),
262 (-0.06401806324720383, 7.450580596923828e-09, 0.06401804089546204),
263 (-0.03464633598923683, 7.450580596923828e-09, 0.0836436077952385),
264 (-0.034646373242139816, -7.450580596923828e-09, -0.0836435854434967),
265 (0.03464638441801071, 7.450580596923828e-09, 0.0836435854434967),
266 (-2.734086912425937e-08, 7.450580596923828e-09, 0.09053517132997513),
267 (-4.532979147597871e-08, -7.450580596923828e-09, -0.09053517132997513),
268 (0.034646324813365936, -7.450580596923828e-09, -0.0836436077952385),
269 (0.06401804834604263, -7.450580596923828e-09, -0.06401804834604263),
270 (0.0836436077952385, -3.725290298461914e-09, -0.034646324813365936),
271 (0.09053517132997513, -4.440892098500626e-16, -3.957419281164221e-09),
272 (0.0836436077952385, 3.725290298461914e-09, 0.03464632108807564),
273 (0.06401804834604263, 7.450580596923828e-09, 0.06401804834604263),
274 (1.1175870895385742e-08, 2.9802322387695312e-08, 0.4931790828704834),
275 (-3.3337176574832483e-08, 2.4835267176115394e-09, 0.030178390443325043),
276 (-3.9333485801762436e-08, -2.4835271617007493e-09, -0.030178390443325043),
277 (-0.030178390443325043, -7.40148665436918e-16, -7.794483281031717e-09),
278 (0.030178390443325043, -5.921189111737107e-16, -5.875951281097969e-09)]
279 edges
= [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (7, 8), (0, 8),
280 (10, 11), (9, 12), (11, 12), (10, 13), (9, 14), (13, 15), (14, 15), (16, 22),
281 (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (7, 17), (6, 16), (23, 24),
282 (23, 25), (24, 29), (25, 28), (26, 29), (27, 28), (31, 32), (30, 33), (32, 33),
283 (31, 34), (30, 35), (34, 36), (35, 36), (37, 38), (37, 39), (38, 43), (39, 42),
284 (40, 41), (40, 43), (41, 42), (50, 53), (49, 52), (44, 45), (45, 46), (46, 47),
285 (47, 48), (48, 49), (44, 50), (51, 59), (51, 52), (53, 54), (54, 55), (55, 56),
286 (56, 57), (57, 58), (58, 59), (26, 60), (27, 60), (23, 60), (61, 62), (63, 64)]
288 mesh
.from_pydata(verts
, edges
, [])
292 # =========================================================================
293 # Define the function to make the camera active
294 # =========================================================================
296 ob
= bpy
.context
.active_object
297 # find the children on the rig (the camera name)
298 active_cam
= ob
.children
[0].name
299 # cam = bpy.data.cameras[bpy.data.objects[active_cam]]
300 scene_cam
= bpy
.context
.scene
.camera
302 if active_cam
!= scene_cam
.name
:
303 bpy
.context
.scene
.camera
= bpy
.data
.objects
[active_cam
]
308 class MakeCameraActive(Operator
):
309 bl_idname
= "scene.make_camera_active"
310 bl_label
= "Make Camera Active"
311 bl_description
= "Makes the camera parented to this rig the active scene camera"
314 def poll(cls
, context
):
315 return context
.active_object
is not None
317 def execute(self
, context
):
323 # =========================================================================
324 # Define function to add marker to timeline and bind camera
325 # =========================================================================
327 view_layer
= bpy
.context
.view_layer
328 ob
= bpy
.context
.active_object
# rig object
329 active_cam
= ob
.children
[0] # camera object
331 # switch area to timeline to add marker
332 bpy
.context
.area
.ui_type
= 'TIMELINE'
335 bpy
.ops
.marker
.rename(name
="cam_" + str(bpy
.context
.scene
.frame_current
))
337 view_layer
.objects
.active
= active_cam
338 # bind marker to selected camera
339 bpy
.ops
.marker
.camera_bind()
340 # switch selected object back to the rig
341 view_layer
.objects
.active
= ob
342 # switch back to 3d view
343 bpy
.context
.area
.ui_type
= 'VIEW_3D'
346 class AddMarkerBind(Operator
):
347 bl_idname
= "add.marker_bind"
348 bl_label
= "Add marker and Bind Camera"
349 bl_description
= ("Add marker to current frame then bind "
350 "rig camera to it (for camera switching)")
353 def poll(cls
, context
):
354 return context
.active_object
is not None
356 def execute(self
, context
):
362 # =========================================================================
363 # Define the function to add an Empty as DOF object
364 # =========================================================================
366 view_layer
= bpy
.context
.view_layer
367 smode
= bpy
.context
.mode
368 rig
= bpy
.context
.active_object
369 bone
= rig
.data
.bones
['AIM_child']
370 active_cam
= rig
.children
[0].name
371 cam
= bpy
.data
.cameras
[bpy
.data
.objects
[active_cam
].data
.name
]
373 bpy
.ops
.object.mode_set(mode
='OBJECT', toggle
=False)
375 bpy
.ops
.object.empty_add()
376 obj
= bpy
.context
.active_object
378 obj
.name
= "Empty_DOF"
379 # parent to AIM_Child
381 obj
.parent_type
= "BONE"
382 obj
.parent_bone
= "AIM_child"
384 bpy
.ops
.object.location_clear()
385 bpy
.ops
.object.rotation_clear()
387 obj
.location
= bone
.head
389 # make this new empty the dof_object
390 # cam.dof_object = obj
392 view_layer
.objects
.active
= rig
393 obj
.select_set(False)
396 bpy
.ops
.object.mode_set(mode
=smode
, toggle
=False)
399 class AddDofEmpty(Operator
):
400 bl_idname
= "add.dof_empty"
401 bl_label
= "Add DOF Empty"
402 bl_description
= "Create empty and add as DOF Object"
405 def poll(cls
, context
):
406 return context
.active_object
is not None
408 def execute(self
, context
):
414 # =========================================================================
415 # Define the function to build the Dolly Rig
416 # =========================================================================
417 def build_dolly_rig(context
):
418 view_layer
= bpy
.context
.view_layer
420 # Define some useful variables:
421 boneLayer
= (False, True, False, False, False, False, False, False,
422 False, False, False, False, False, False, False, False,
423 False, False, False, False, False, False, False, False,
424 False, False, False, False, False, False, False, False)
426 # Add the new armature object:
427 bpy
.ops
.object.armature_add()
428 rig
= context
.active_object
430 # it will try to name the rig "Dolly_Rig" but if that name exists it will
431 # add 000 to the name
432 if "Dolly_Rig" not in context
.scene
.objects
:
433 rig
.name
= "Dolly_Rig"
435 rig
.name
= "Dolly_Rig.000"
436 rig
["rig_id"] = "Dolly_Rig"
438 bpy
.ops
.object.mode_set(mode
='EDIT')
440 # Remove default bone:
441 bones
= rig
.data
.edit_bones
442 bones
.remove(bones
[0])
445 root
= bones
.new("Root")
446 root
.tail
= (0.0, 0.0, -5.0)
447 root
.roll
= radians(90)
449 bpy
.ops
.object.mode_set(mode
='EDIT')
450 ctrlAimChild
= bones
.new("AIM_child")
451 ctrlAimChild
.head
= (0.0, 5.0, 3.0)
452 ctrlAimChild
.tail
= (0.0, 7.0, 3.0)
453 ctrlAimChild
.layers
= boneLayer
455 ctrlAim
= bones
.new("AIM")
456 ctrlAim
.head
= (0.0, 5.0, 3.0)
457 ctrlAim
.tail
= (0.0, 7.0, 3.0)
459 ctrl
= bones
.new("CTRL")
460 ctrl
.head
= (0.0, 0.0, 3.0)
461 ctrl
.tail
= (0.0, 2.0, 3.0)
465 ctrlAim
.parent
= root
466 ctrlAimChild
.parent
= ctrlAim
468 # jump into pose mode and change bones to euler
469 bpy
.ops
.object.mode_set(mode
='POSE')
470 for x
in bpy
.context
.object.pose
.bones
:
471 x
.rotation_mode
= 'XYZ'
473 # jump into pose mode and add the custom bone shapes
474 bpy
.ops
.object.mode_set(mode
='POSE')
475 bpy
.context
.object.pose
.bones
["Root"].custom_shape
= \
476 bpy
.data
.objects
["WDGT_Camera_Root"] # add the widget as custom shape
477 # set the wireframe checkbox to true
478 bpy
.context
.object.data
.bones
["Root"].show_wire
= True
479 bpy
.context
.object.pose
.bones
["AIM"].custom_shape
= \
480 bpy
.data
.objects
["WDGT_AIM"]
481 bpy
.context
.object.data
.bones
["AIM"].show_wire
= True
482 bpy
.context
.object.pose
.bones
["AIM"].custom_shape_transform
= \
483 bpy
.data
.objects
[rig
.name
].pose
.bones
["AIM_child"] # sets the "At" field to the child
484 bpy
.context
.object.pose
.bones
["CTRL"].custom_shape
= \
485 bpy
.data
.objects
["WDGT_CTRL"]
486 bpy
.context
.object.data
.bones
["CTRL"].show_wire
= True
488 # jump into object mode
489 bpy
.ops
.object.mode_set(mode
='OBJECT')
491 # Add constraints to bones:
492 con
= rig
.pose
.bones
['AIM_child'].constraints
.new('COPY_ROTATION')
494 con
.subtarget
= "CTRL"
496 con
= rig
.pose
.bones
['CTRL'].constraints
.new('TRACK_TO')
498 con
.subtarget
= "AIM"
499 con
.use_target_z
= True
501 # Add custom Bone property to CTRL bone
502 ob
= bpy
.context
.object.pose
.bones
['CTRL']
503 prop
= rna_idprop_ui_prop_get(ob
, "Lock", create
=True)
505 prop
["soft_min"] = prop
["min"] = 0.0
506 prop
["soft_max"] = prop
["max"] = 1.0
508 # Add Driver to Lock/Unlock Camera from Aim Target
509 rig
= view_layer
.objects
.active
510 pose_bone
= bpy
.data
.objects
[rig
.name
].pose
.bones
['CTRL']
512 constraint
= pose_bone
.constraints
["Track To"]
513 inf_driver
= constraint
.driver_add('influence')
514 inf_driver
.driver
.type = 'SCRIPTED'
515 var
= inf_driver
.driver
.variables
.new()
517 var
.type = 'SINGLE_PROP'
519 # Target the Custom bone property
520 var
.targets
[0].id = bpy
.data
.objects
[rig
.name
]
521 var
.targets
[0].data_path
= 'pose.bones["CTRL"]["Lock"]'
522 inf_driver
.driver
.expression
= 'var'
524 # Add the camera object:
525 bpy
.ops
.object.mode_set(mode
='OBJECT')
527 bpy
.ops
.object.camera_add(
528 align
='WORLD', enter_editmode
=False, location
=(0, 0, 0), rotation
=(0, 0, 0))
529 cam
= bpy
.context
.active_object
531 # this will name the Camera Object
532 if 'Dolly_Camera' not in context
.scene
.objects
:
533 cam
.name
= "Dolly_Camera"
535 cam
.name
= "Dolly_Camera.000"
537 # this will name the camera Data Object
538 if "Dolly_Camera" not in bpy
.context
.scene
.objects
.data
.camera
:
539 cam
.data
.name
= "Dolly_Camera"
541 cam
.data
.name
= "Dolly_Camera.000"
543 cam_data_name
= bpy
.context
.object.data
.name
544 bpy
.data
.cameras
[cam_data_name
].display_size
= 1.0
545 cam
.rotation_euler
[0] = 1.5708 # rotate the camera 90 degrees in x
546 cam
.rotation_euler
[2] = 0.01309
548 cam
.location
= (0.0, -2.0, 0.0) # move the camera to the correct position
550 cam
.parent_type
= "BONE"
551 cam
.parent_bone
= "CTRL"
553 # Add blank drivers to lock the camera loc, rot scale
554 cam
.driver_add('location', 0)
555 cam
.driver_add('location', 1)
556 cam
.driver_add('location', 2)
557 cam
.driver_add('rotation_euler', 0)
558 cam
.driver_add('rotation_euler', 1)
559 cam
.driver_add('rotation_euler', 2)
560 cam
.driver_add('scale', 0)
561 cam
.driver_add('scale', 1)
562 cam
.driver_add('scale', 2)
564 # Set new camera as active camera
565 bpy
.context
.scene
.camera
= cam
567 # make sure the camera is selectable by default (this can be locked in the UI)
568 bpy
.context
.object.hide_select
= False
570 # make the rig the active object before finishing
571 view_layer
.objects
.active
= rig
572 cam
.select_set(False)
578 # =========================================================================
579 # Define the function to build the Crane Rig
580 # =========================================================================
581 def build_crane_rig(context
):
582 # Define some useful variables:
583 boneLayer
= (False, True, False, False, False, False, False, False,
584 False, False, False, False, False, False, False, False,
585 False, False, False, False, False, False, False, False,
586 False, False, False, False, False, False, False, False)
588 # Add the new armature object:
589 view_layer
= bpy
.context
.view_layer
590 bpy
.ops
.object.armature_add()
591 rig
= context
.active_object
593 # it will try to name the rig "Dolly_Rig" but if that name exists it will
594 # add .000 to the name
595 if "Crane_Rig" not in context
.scene
.objects
:
596 rig
.name
= "Crane_Rig"
598 rig
.name
= "Crane_Rig.000"
599 rig
["rig_id"] = "Crane_Rig"
601 bpy
.ops
.object.mode_set(mode
='EDIT')
603 # Remove default bone:
604 bones
= rig
.data
.edit_bones
605 bones
.remove(bones
[0])
608 root
= bones
.new("Root")
609 root
.tail
= (0.0, 0.0, -5.0)
611 ctrlAimChild
= bones
.new("AIM_child")
612 ctrlAimChild
.head
= (0.0, 10.0, 1.0)
613 ctrlAimChild
.tail
= (0.0, 12.0, 1.0)
614 ctrlAimChild
.layers
= boneLayer
616 ctrlAim
= bones
.new("AIM")
617 ctrlAim
.head
= (0.0, 10.0, 1.0)
618 ctrlAim
.tail
= (0.0, 12.0, 1.0)
620 ctrl
= bones
.new("CTRL")
621 ctrl
.head
= (0.0, 1.0, 1.0)
622 ctrl
.tail
= (0.0, 3.0, 1.0)
624 arm
= bones
.new("Crane_Arm")
625 arm
.head
= (0.0, 0.0, 1.0)
626 arm
.tail
= (0.0, 1.0, 1.0)
628 height
= bones
.new("Height")
629 height
.head
= (0.0, 0.0, 0.0)
630 height
.tail
= (0.0, 0.0, 1.0)
634 ctrl
.use_inherit_rotation
= False
635 ctrl
.use_inherit_scale
= False
638 arm
.use_inherit_scale
= False
641 ctrlAim
.parent
= root
642 ctrlAimChild
.parent
= ctrlAim
644 # change display to BBone: it just looks nicer
645 bpy
.context
.object.data
.display_type
= 'BBONE'
646 # change display to wire for object
647 bpy
.context
.object.display_type
= 'WIRE'
649 # jump into pose mode and change bones to euler
650 bpy
.ops
.object.mode_set(mode
='POSE')
651 for x
in bpy
.context
.object.pose
.bones
:
652 x
.rotation_mode
= 'XYZ'
654 # lock the relevant loc, rot and scale
655 bpy
.context
.object.pose
.bones
[
656 "Crane_Arm"].lock_rotation
= [False, True, False]
657 bpy
.context
.object.pose
.bones
["Crane_Arm"].lock_scale
= [True, False, True]
658 bpy
.context
.object.pose
.bones
["Height"].lock_location
= [True, True, True]
659 bpy
.context
.object.pose
.bones
["Height"].lock_rotation
= [True, True, True]
660 bpy
.context
.object.pose
.bones
["Height"].lock_scale
= [True, False, True]
662 # add the custom bone shapes
663 bpy
.context
.object.pose
.bones
["Root"].custom_shape
= bpy
.data
.objects
[
664 "WDGT_Camera_Root"] # add the widget as custom shape
665 # set the wireframe checkbox to true
666 bpy
.context
.object.data
.bones
["Root"].show_wire
= True
667 bpy
.context
.object.pose
.bones
[
668 "AIM"].custom_shape
= bpy
.data
.objects
["WDGT_AIM"]
669 bpy
.context
.object.data
.bones
["AIM"].show_wire
= True
670 bpy
.context
.object.pose
.bones
["AIM"].custom_shape_transform
= bpy
.data
.objects
[
671 rig
.name
].pose
.bones
["AIM_child"] # sets the "At" field to the child
672 bpy
.context
.object.pose
.bones
[
673 "CTRL"].custom_shape
= bpy
.data
.objects
["WDGT_CTRL"]
674 bpy
.context
.object.data
.bones
["CTRL"].show_wire
= True
676 # jump into object mode
677 bpy
.ops
.object.mode_set(mode
='OBJECT')
679 # Add constraints to bones:
680 con
= rig
.pose
.bones
['AIM_child'].constraints
.new('COPY_ROTATION')
682 con
.subtarget
= "CTRL"
684 con
= rig
.pose
.bones
['CTRL'].constraints
.new('TRACK_TO')
686 con
.subtarget
= "AIM"
687 con
.use_target_z
= True
689 # Add custom Bone property to CTRL bone
690 ob
= bpy
.context
.object.pose
.bones
['CTRL']
691 prop
= rna_idprop_ui_prop_get(ob
, "Lock", create
=True)
693 prop
["soft_min"] = prop
["min"] = 0.0
694 prop
["soft_max"] = prop
["max"] = 1.0
696 # Add Driver to Lock/Unlock Camera from Aim Target
697 rig
= view_layer
.objects
.active
698 pose_bone
= bpy
.data
.objects
[rig
.name
].pose
.bones
['CTRL']
700 constraint
= pose_bone
.constraints
["Track To"]
701 inf_driver
= constraint
.driver_add('influence')
702 inf_driver
.driver
.type = 'SCRIPTED'
703 var
= inf_driver
.driver
.variables
.new()
705 var
.type = 'SINGLE_PROP'
707 # Target the Custom bone property
708 var
.targets
[0].id = bpy
.data
.objects
[rig
.name
]
709 var
.targets
[0].data_path
= 'pose.bones["CTRL"]["Lock"]'
710 inf_driver
.driver
.expression
= 'var'
712 # Add the camera object:
713 bpy
.ops
.object.mode_set(mode
='OBJECT')
715 bpy
.ops
.object.camera_add(
716 align
='WORLD', enter_editmode
=False, location
=(0, 0, 0), rotation
=(0, 0, 0))
717 cam
= bpy
.context
.active_object
719 # this will name the Camera Object
720 if 'Crane_Camera' not in context
.scene
.objects
:
721 cam
.name
= "Crane_Camera"
723 cam
.name
= "Crane_Camera.000"
725 # this will name the camera Data Object
726 if "Crane_Camera" not in bpy
.context
.scene
.objects
.data
.camera
:
727 cam
.data
.name
= "Crane_Camera"
729 cam
.data
.name
= "Crane_Camera.000"
731 cam_data_name
= bpy
.context
.object.data
.name
732 bpy
.data
.cameras
[cam_data_name
].display_size
= 1.0
733 cam
.rotation_euler
[0] = 1.5708 # rotate the camera 90 degrees in x
734 cam
.rotation_euler
[2] = 0.01309
735 cam
.location
= (0.0, -2.0, 0.0) # move the camera to the correct position
737 cam
.parent_type
= "BONE"
738 cam
.parent_bone
= "CTRL"
740 # Add blank drivers to lock the camera loc, rot scale
741 cam
.driver_add('location', 0)
742 cam
.driver_add('location', 1)
743 cam
.driver_add('location', 2)
744 cam
.driver_add('rotation_euler', 0)
745 cam
.driver_add('rotation_euler', 1)
746 cam
.driver_add('rotation_euler', 2)
747 cam
.driver_add('scale', 0)
748 cam
.driver_add('scale', 1)
749 cam
.driver_add('scale', 2)
751 # Set new camera as active camera
752 bpy
.context
.scene
.camera
= cam
754 # make sure the camera is selectable by default (this can be locked in the UI)
755 bpy
.context
.object.hide_select
= False
757 # make the rig the active object before finishing
758 view_layer
.objects
.active
= rig
759 cam
.select_set(False)
765 # =========================================================================
766 # This is the UI for the Dolly Camera Rig
767 # =========================================================================
768 class VIEW3D_PT_DollyCameraUI(Panel
):
769 bl_space_type
= 'VIEW_3D'
770 bl_region_type
= 'UI'
771 bl_label
= "Dolly Camera UI"
775 def poll(self
, context
):
777 ob
= bpy
.context
.active_object
778 return (ob
["rig_id"] == "Dolly_Rig")
779 except (AttributeError, KeyError, TypeError):
782 def draw(self
, context
):
784 ob
= bpy
.context
.active_object
785 pose_bones
= context
.active_object
.pose
.bones
786 # find the children on the rig (the camera name)
787 active_cam
= ob
.children
[0].name
789 cam
= bpy
.data
.cameras
[bpy
.data
.objects
[active_cam
].data
.name
]
794 # Display Camera Properties
795 col
.label(text
="Clipping:")
796 col
.prop(cam
, "clip_start", text
="Start")
797 col
.prop(cam
, "clip_end", text
="End")
798 col
.prop(cam
, "type")
799 # col.prop(cam, "dof_object")
801 # if cam.dof_object is None:
802 # col.operator("add.dof_empty", text="Add DOF Empty")
803 # col.prop(cam, "dof_distance")
804 # added the comp guides here
805 # col.prop_menu_enum(cam, "show_guide", text="Compostion Guides")
806 col
.prop(bpy
.data
.objects
[active_cam
],
807 "hide_select", text
="Make Camera Unselectable")
809 col
.operator("add.marker_bind", text
="Add Marker and Bind")
811 if bpy
.context
.scene
.camera
.name
!= active_cam
:
812 col
.operator("scene.make_camera_active",
813 text
="Make Active Camera", icon
='CAMERA_DATA')
815 col
.prop(context
.active_object
,
816 'show_in_front', toggle
=False, text
='X Ray')
817 col
.prop(cam
, "show_limits")
818 col
.prop(cam
, "show_safe_areas")
819 col
.prop(cam
, "show_passepartout")
820 col
.prop(cam
, "passepartout_alpha")
823 col
.label(text
="Focal Length:")
824 col
.prop(cam
, "lens", text
="Angle")
826 # Track to Constraint
827 col
.label(text
="Tracking:")
828 col
.prop(pose_bones
["CTRL"], '["Lock"]', text
="Aim Lock", slider
=True)
831 # =========================================================================
832 # This is the UI for the Crane Rig Camera
833 # =========================================================================
834 class VIEW3D_PT_CraneCameraUI(Panel
):
835 bl_space_type
= 'VIEW_3D'
836 bl_region_type
= 'UI'
837 bl_label
= "Crane Camera UI"
841 def poll(self
, context
):
843 ob
= bpy
.context
.active_object
844 return (ob
["rig_id"] == "Crane_Rig")
845 except (AttributeError, KeyError, TypeError):
848 def draw(self
, context
):
850 ob
= bpy
.context
.active_object
851 pose_bones
= context
.active_object
.pose
.bones
852 # find the children on the rig (camera)
853 active_cam
= ob
.children
[0].name
854 cam
= bpy
.data
.cameras
[bpy
.data
.objects
[active_cam
].data
.name
]
860 # Display Camera Properties
861 col
.label(text
="Clipping:")
862 col
.prop(cam
, "clip_start", text
="Start")
863 col
.prop(cam
, "clip_end", text
="End")
864 col
.prop(cam
, "type")
865 # col.prop(cam, "dof_object")
866 ob
= bpy
.context
.object
867 # if cam.dof_object is None:
868 # col.operator("add.dof_empty", text="Add DOF object")
869 # col.prop(cam, "dof_distance")
870 # added the comp guides here
871 # col.prop_menu_enum(cam, "show_guide", text="Compostion Guides")
872 col
.prop(bpy
.data
.objects
[active_cam
],
873 "hide_select", text
="Make Camera Unselectable")
874 col
.operator("add.marker_bind", text
="Add Marker and Bind")
876 if bpy
.context
.scene
.camera
.name
!= active_cam
:
878 "scene.make_camera_active", text
="Make Active Camera", icon
='CAMERA_DATA')
880 context
.active_object
, 'show_in_front', toggle
=False, text
='X Ray')
881 col
.prop(cam
, "show_limits")
882 col
.prop(cam
, "show_safe_areas")
883 col
.prop(cam
, "show_passepartout")
884 col
.prop(cam
, "passepartout_alpha")
887 col
.label(text
="Focal Length:")
888 col
.prop(cam
, "lens", text
="Angle")
890 # Track to Constraint
891 col
.label(text
="Tracking:")
892 col
.prop(pose_bones
["CTRL"], '["Lock"]', text
="Aim Lock", slider
=True)
894 # make this camera active if more than one camera exists
896 if cam != bpy.context.scene.camera:
897 col.op(, text="Make Active Camera", toggle=True)
905 col
.label(text
="Crane Arm:")
906 col
.prop(pose_bones
["Height"], 'scale', index
=1, text
="Arm Height")
907 col
.prop(pose_bones
["Crane_Arm"], 'scale', index
=1, text
="Arm Length")
910 # =========================================================================
911 # This is the operator that will call all the functions and build the dolly rig
912 # =========================================================================
913 class BuildDollyRig(Operator
):
914 bl_idname
= "object.build_dolly_rig"
915 bl_label
= "Build Dolly Camera Rig"
916 bl_description
= "Build a Camera Dolly Rig"
917 bl_options
= {'REGISTER', 'UNDO'}
919 def execute(self
, context
):
922 create_root_widget(self
, "Camera_Root")
923 create_camera_widget(self
, "CTRL")
924 create_aim_widget(self
, "AIM")
926 # call the function to build the rig
927 build_dolly_rig(context
)
932 # =========================================================================
933 # This is the operator that will call all the functions and build the crane rig
934 # =========================================================================
935 class BuildCraneRig(Operator
):
936 bl_idname
= "object.build_crane_rig"
937 bl_label
= "Build Crane Camera Rig"
938 bl_description
= "Build a Camera Crane Rig"
939 bl_options
= {'REGISTER', 'UNDO'}
941 def execute(self
, context
):
944 create_root_widget(self
, "Camera_Root")
945 create_camera_widget(self
, "CTRL")
946 create_aim_widget(self
, "AIM")
948 # call the function to build the rig
949 build_crane_rig(context
)
954 # =========================================================================
956 # =========================================================================
958 # dolly and crane entries in the Add Object > Camera Menu
959 def add_dolly_crane_buttons(self
, context
):
960 if context
.mode
== 'OBJECT':
961 self
.layout
.operator(
962 BuildDollyRig
.bl_idname
,
963 text
="Dolly Camera Rig",
966 self
.layout
.operator(
967 BuildCraneRig
.bl_idname
,
968 text
="Crane Camera Rig",
974 bpy
.utils
.register_class(BuildDollyRig
)
975 bpy
.utils
.register_class(BuildCraneRig
)
976 bpy
.utils
.register_class(VIEW3D_PT_DollyCameraUI
)
977 bpy
.utils
.register_class(VIEW3D_PT_CraneCameraUI
)
978 bpy
.utils
.register_class(MakeCameraActive
)
979 bpy
.utils
.register_class(AddMarkerBind
)
980 bpy
.utils
.register_class(AddDofEmpty
)
981 bpy
.types
.VIEW3D_MT_camera_add
.append(add_dolly_crane_buttons
)
985 bpy
.utils
.unregister_class(BuildDollyRig
)
986 bpy
.utils
.unregister_class(BuildCraneRig
)
987 bpy
.utils
.unregister_class(VIEW3D_PT_DollyCameraUI
)
988 bpy
.utils
.unregister_class(VIEW3D_PT_CraneCameraUI
)
989 bpy
.utils
.unregister_class(MakeCameraActive
)
990 bpy
.utils
.unregister_class(AddMarkerBind
)
991 bpy
.utils
.unregister_class(AddDofEmpty
)
992 bpy
.types
.VIEW3D_MT_camera_add
.remove(add_dolly_crane_buttons
)
995 if __name__
== "__main__":