Fix T62695: OBJ mtllib fails when filename contains spaces
[blender-addons.git] / camera_dolly_crane_rigs.py
blob13025675f9f2508a1480f40124c16fc4a5808361
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 #####
20 bl_info = {
21 "name": "Add Camera Rigs",
22 "author": "Wayne Dixon, Kris Wittig",
23 "version": (1, 1, 1),
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 > File",
28 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
29 "Py/Scripts/Rigging/Add_Camera_Rigs",
30 "category": "Camera",
33 import bpy
34 from bpy.types import (
35 Operator,
36 Panel,
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)
56 if c is not None:
57 c.objects.link(obj)
58 else:
59 c = bpy.data.collections.new(collection_name)
60 c.hide_viewport = True
61 c.hide_render = True
63 # link the collection
64 scene.collection.children.link(c)
65 c.objects.link(obj)
67 return obj
70 def create_root_widget(self, name):
71 # Creates a compass-shaped widget.
73 obj = create_widget(self, name)
74 if obj is not None:
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)]
126 mesh = obj.data
127 mesh.from_pydata(verts, edges, [])
128 mesh.update()
131 def create_camera_widget(self, name):
132 # Creates a camera ctrl widget
134 obj = create_widget(self, name)
135 if obj is not None:
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)]
204 mesh = obj.data
205 mesh.from_pydata(verts, edges, [])
206 mesh.update()
209 def create_aim_widget(self, name):
210 """ Creates a camera aim widget."""
212 obj = create_widget(self, name)
213 if obj is not None:
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)]
287 mesh = obj.data
288 mesh.from_pydata(verts, edges, [])
289 mesh.update()
292 # =========================================================================
293 # Define the function to make the camera active
294 # =========================================================================
295 def sceneCamera():
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]
304 else:
305 return None
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"
313 @classmethod
314 def poll(cls, context):
315 return context.active_object is not None
317 def execute(self, context):
318 sceneCamera()
320 return {'FINISHED'}
323 # =========================================================================
324 # Define function to add marker to timeline and bind camera
325 # =========================================================================
326 def markerBind():
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.type = 'TIMELINE'
333 # add marker
334 bpy.ops.marker.add()
335 bpy.ops.marker.rename(name="cam_" + str(bpy.context.scene.frame_current))
336 # select rig camera
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.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)")
352 @classmethod
353 def poll(cls, context):
354 return context.active_object is not None
356 def execute(self, context):
357 markerBind()
359 return {'FINISHED'}
362 # =========================================================================
363 # Define the function to add an Empty as DOF object
364 # =========================================================================
365 def add_DOF_Empty():
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)
374 # Add Empty
375 bpy.ops.object.empty_add()
376 obj = bpy.context.active_object
378 obj.name = "Empty_DOF"
379 # parent to AIM_Child
380 obj.parent = rig
381 obj.parent_type = "BONE"
382 obj.parent_bone = "AIM_child"
383 # clear loc and rot
384 bpy.ops.object.location_clear()
385 bpy.ops.object.rotation_clear()
386 # move to bone head
387 obj.location = bone.head
389 # make this new empty the dof_object
390 cam.dof_object = obj
391 # reselect the rig
392 view_layer.objects.active = rig
393 obj.select_set(False)
394 rig.select_set(True)
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"
404 @classmethod
405 def poll(cls, context):
406 return context.active_object is not None
408 def execute(self, context):
409 add_DOF_Empty()
411 return {'FINISHED'}
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"
434 else:
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])
444 # Add new bones:
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)
463 # Setup hierarchy:
464 ctrl.parent = root
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')
493 con.target = rig
494 con.subtarget = "CTRL"
496 con = rig.pose.bones['CTRL'].constraints.new('TRACK_TO')
497 con.target = rig
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)
504 ob["Lock"] = 1.0
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()
516 var.name = 'var'
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 view_align=False, 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"
534 else:
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"
540 else:
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.location = (0.0, -2.0, 0.0) # move the camera to the correct position
547 cam.parent = rig
548 cam.parent_type = "BONE"
549 cam.parent_bone = "CTRL"
551 # Add blank drivers to lock the camera loc, rot scale
552 cam.driver_add('location', 0)
553 cam.driver_add('location', 1)
554 cam.driver_add('location', 2)
555 cam.driver_add('rotation_euler', 0)
556 cam.driver_add('rotation_euler', 1)
557 cam.driver_add('rotation_euler', 2)
558 cam.driver_add('scale', 0)
559 cam.driver_add('scale', 1)
560 cam.driver_add('scale', 2)
562 # Set new camera as active camera
563 bpy.context.scene.camera = cam
565 # make sure the camera is selectable by default (this can be locked in the UI)
566 bpy.context.object.hide_select = False
568 # make the rig the active object before finishing
569 view_layer.objects.active = rig
570 cam.select_set(False)
571 rig.select_set(True)
573 return rig
576 # =========================================================================
577 # Define the function to build the Crane Rig
578 # =========================================================================
579 def build_crane_rig(context):
580 # Define some useful variables:
581 boneLayer = (False, True, False, False, False, False, False, False,
582 False, False, False, False, False, False, False, False,
583 False, False, False, False, False, False, False, False,
584 False, False, False, False, False, False, False, False)
586 # Add the new armature object:
587 view_layer = bpy.context.view_layer
588 bpy.ops.object.armature_add()
589 rig = context.active_object
591 # it will try to name the rig "Dolly_Rig" but if that name exists it will
592 # add .000 to the name
593 if "Crane_Rig" not in context.scene.objects:
594 rig.name = "Crane_Rig"
595 else:
596 rig.name = "Crane_Rig.000"
597 rig["rig_id"] = "Crane_Rig"
599 bpy.ops.object.mode_set(mode='EDIT')
601 # Remove default bone:
602 bones = rig.data.edit_bones
603 bones.remove(bones[0])
605 # Add new bones:
606 root = bones.new("Root")
607 root.tail = (0.0, 0.0, -5.0)
609 ctrlAimChild = bones.new("AIM_child")
610 ctrlAimChild.head = (0.0, 10.0, 1.0)
611 ctrlAimChild.tail = (0.0, 12.0, 1.0)
612 ctrlAimChild.layers = boneLayer
614 ctrlAim = bones.new("AIM")
615 ctrlAim.head = (0.0, 10.0, 1.0)
616 ctrlAim.tail = (0.0, 12.0, 1.0)
618 ctrl = bones.new("CTRL")
619 ctrl.head = (0.0, 1.0, 1.0)
620 ctrl.tail = (0.0, 3.0, 1.0)
622 arm = bones.new("Crane_Arm")
623 arm.head = (0.0, 0.0, 1.0)
624 arm.tail = (0.0, 1.0, 1.0)
626 height = bones.new("Height")
627 height.head = (0.0, 0.0, 0.0)
628 height.tail = (0.0, 0.0, 1.0)
630 # Setup hierarchy:
631 ctrl.parent = arm
632 ctrl.use_inherit_rotation = False
633 ctrl.use_inherit_scale = False
635 arm.parent = height
636 arm.use_inherit_scale = False
638 height.parent = root
639 ctrlAim.parent = root
640 ctrlAimChild.parent = ctrlAim
642 # change display to BBone: it just looks nicer
643 bpy.context.object.data.display_type = 'BBONE'
644 # change display to wire for object
645 bpy.context.object.display_type = 'WIRE'
647 # jump into pose mode and change bones to euler
648 bpy.ops.object.mode_set(mode='POSE')
649 for x in bpy.context.object.pose.bones:
650 x.rotation_mode = 'XYZ'
652 # lock the relevant loc, rot and scale
653 bpy.context.object.pose.bones[
654 "Crane_Arm"].lock_rotation = [False, True, False]
655 bpy.context.object.pose.bones["Crane_Arm"].lock_scale = [True, False, True]
656 bpy.context.object.pose.bones["Height"].lock_location = [True, True, True]
657 bpy.context.object.pose.bones["Height"].lock_rotation = [True, True, True]
658 bpy.context.object.pose.bones["Height"].lock_scale = [True, False, True]
660 # add the custom bone shapes
661 bpy.context.object.pose.bones["Root"].custom_shape = bpy.data.objects[
662 "WDGT_Camera_Root"] # add the widget as custom shape
663 # set the wireframe checkbox to true
664 bpy.context.object.data.bones["Root"].show_wire = True
665 bpy.context.object.pose.bones[
666 "AIM"].custom_shape = bpy.data.objects["WDGT_AIM"]
667 bpy.context.object.data.bones["AIM"].show_wire = True
668 bpy.context.object.pose.bones["AIM"].custom_shape_transform = bpy.data.objects[
669 rig.name].pose.bones["AIM_child"] # sets the "At" field to the child
670 bpy.context.object.pose.bones[
671 "CTRL"].custom_shape = bpy.data.objects["WDGT_CTRL"]
672 bpy.context.object.data.bones["CTRL"].show_wire = True
674 # jump into object mode
675 bpy.ops.object.mode_set(mode='OBJECT')
677 # Add constraints to bones:
678 con = rig.pose.bones['AIM_child'].constraints.new('COPY_ROTATION')
679 con.target = rig
680 con.subtarget = "CTRL"
682 con = rig.pose.bones['CTRL'].constraints.new('TRACK_TO')
683 con.target = rig
684 con.subtarget = "AIM"
685 con.use_target_z = True
687 # Add custom Bone property to CTRL bone
688 ob = bpy.context.object.pose.bones['CTRL']
689 prop = rna_idprop_ui_prop_get(ob, "Lock", create=True)
690 ob["Lock"] = 1.0
691 prop["soft_min"] = prop["min"] = 0.0
692 prop["soft_max"] = prop["max"] = 1.0
694 # Add Driver to Lock/Unlock Camera from Aim Target
695 rig = view_layer.objects.active
696 pose_bone = bpy.data.objects[rig.name].pose.bones['CTRL']
698 constraint = pose_bone.constraints["Track To"]
699 inf_driver = constraint.driver_add('influence')
700 inf_driver.driver.type = 'SCRIPTED'
701 var = inf_driver.driver.variables.new()
702 var.name = 'var'
703 var.type = 'SINGLE_PROP'
705 # Target the Custom bone property
706 var.targets[0].id = bpy.data.objects[rig.name]
707 var.targets[0].data_path = 'pose.bones["CTRL"]["Lock"]'
708 inf_driver.driver.expression = 'var'
710 # Add the camera object:
711 bpy.ops.object.mode_set(mode='OBJECT')
713 bpy.ops.object.camera_add(
714 view_align=False, enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0))
715 cam = bpy.context.active_object
717 # this will name the Camera Object
718 if 'Crane_Camera' not in context.scene.objects:
719 cam.name = "Crane_Camera"
720 else:
721 cam.name = "Crane_Camera.000"
723 # this will name the camera Data Object
724 if "Crane_Camera" not in bpy.context.scene.objects.data.camera:
725 cam.data.name = "Crane_Camera"
726 else:
727 cam.data.name = "Crane_Camera.000"
729 cam_data_name = bpy.context.object.data.name
730 bpy.data.cameras[cam_data_name].display_size = 1.0
731 cam.rotation_euler[0] = 1.5708 # rotate the camera 90 degrees in x
732 cam.location = (0.0, -2.0, 0.0) # move the camera to the correct position
733 cam.parent = rig
734 cam.parent_type = "BONE"
735 cam.parent_bone = "CTRL"
737 # Add blank drivers to lock the camera loc, rot scale
738 cam.driver_add('location', 0)
739 cam.driver_add('location', 1)
740 cam.driver_add('location', 2)
741 cam.driver_add('rotation_euler', 0)
742 cam.driver_add('rotation_euler', 1)
743 cam.driver_add('rotation_euler', 2)
744 cam.driver_add('scale', 0)
745 cam.driver_add('scale', 1)
746 cam.driver_add('scale', 2)
748 # Set new camera as active camera
749 bpy.context.scene.camera = cam
751 # make sure the camera is selectable by default (this can be locked in the UI)
752 bpy.context.object.hide_select = False
754 # make the rig the active object before finishing
755 view_layer.objects.active = rig
756 cam.select_set(False)
757 rig.select_set(True)
759 return rig
762 # =========================================================================
763 # This is the UI for the Dolly Camera Rig
764 # =========================================================================
765 class DollyCameraUI(Panel):
766 bl_idname = "CAMERA_DOLLY_PT_ui"
767 bl_space_type = 'VIEW_3D'
768 bl_region_type = 'UI'
769 bl_label = "Dolly Camera UI"
771 @classmethod
772 def poll(self, context):
773 try:
774 ob = bpy.context.active_object
775 return (ob["rig_id"] == "Dolly_Rig")
776 except (AttributeError, KeyError, TypeError):
777 return False
779 def draw(self, context):
780 layout = self.layout
781 ob = bpy.context.active_object
782 pose_bones = context.active_object.pose.bones
783 # find the children on the rig (the camera name)
784 active_cam = ob.children[0].name
786 cam = bpy.data.cameras[bpy.data.objects[active_cam].data.name]
787 box = layout.box()
788 col = box.column()
789 col.separator()
791 # Display Camera Properties
792 col.label(text="Clipping:")
793 col.prop(cam, "clip_start", text="Start")
794 col.prop(cam, "clip_end", text="End")
795 col.prop(cam, "type")
796 col.prop(cam, "dof_object")
798 if cam.dof_object is None:
799 col.operator("add.dof_empty", text="Add DOF Empty")
800 col.prop(cam, "dof_distance")
801 # added the comp guides here
802 col.prop_menu_enum(cam, "show_guide", text="Compostion Guides")
803 col.prop(bpy.data.objects[active_cam],
804 "hide_select", text="Make Camera Unselectable")
806 col.operator("add.marker_bind", text="Add Marker and Bind")
808 if bpy.context.scene.camera.name != active_cam:
809 col.operator("scene.make_camera_active",
810 text="Make Active Camera", icon='CAMERA_DATA')
812 col.prop(context.active_object,
813 'show_in_front', toggle=False, text='X Ray')
814 col.prop(cam, "show_limits")
815 col.prop(cam, "show_safe_areas")
816 col.prop(cam, "show_passepartout")
817 col.prop(cam, "passepartout_alpha")
819 # Camera Lens
820 col.label(text="Focal Length:")
821 col.prop(cam, "lens", text="Angle")
823 # Track to Constraint
824 col.label(text="Tracking:")
825 col.prop(pose_bones["CTRL"], '["Lock"]', text="Aim Lock", slider=True)
828 # =========================================================================
829 # This is the UI for the Crane Rig Camera
830 # =========================================================================
831 class CraneCameraUI(Panel):
832 bl_idname = "CAMERA_CRANE_PT_ui"
833 bl_space_type = 'VIEW_3D'
834 bl_region_type = 'UI'
835 bl_label = "Crane Camera UI"
837 @classmethod
838 def poll(self, context):
839 try:
840 ob = bpy.context.active_object
841 return (ob["rig_id"] == "Crane_Rig")
842 except (AttributeError, KeyError, TypeError):
843 return False
845 def draw(self, context):
846 layout = self.layout
847 ob = bpy.context.active_object
848 pose_bones = context.active_object.pose.bones
849 # find the children on the rig (camera)
850 active_cam = ob.children[0].name
851 cam = bpy.data.cameras[bpy.data.objects[active_cam].data.name]
853 box = layout.box()
854 col = box.column()
855 col.separator()
857 # Display Camera Properties
858 col.label(text="Clipping:")
859 col.prop(cam, "clip_start", text="Start")
860 col.prop(cam, "clip_end", text="End")
861 col.prop(cam, "type")
862 col.prop(cam, "dof_object")
864 if cam.dof_object is None:
865 col.operator("add.dof_empty", text="Add DOF object")
866 col.prop(cam, "dof_distance")
867 # added the comp guides here
868 col.prop_menu_enum(cam, "show_guide", text="Compostion Guides")
869 col.prop(bpy.data.objects[active_cam],
870 "hide_select", text="Make Camera Unselectable")
871 col.operator("add.marker_bind", text="Add Marker and Bind")
873 if bpy.context.scene.camera.name != active_cam:
874 col.operator(
875 "scene.make_camera_active", text="Make Active Camera", icon='CAMERA_DATA')
876 col.prop(
877 context.active_object, 'show_in_front', toggle=False, text='X Ray')
878 col.prop(cam, "show_limits")
879 col.prop(cam, "show_safe_areas")
880 col.prop(cam, "show_passepartout")
881 col.prop(cam, "passepartout_alpha")
883 # Camera Lens
884 col.label(text="Focal Length:")
885 col.prop(cam, "lens", text="Angle")
887 # Track to Constraint
888 col.label(text="Tracking:")
889 col.prop(pose_bones["CTRL"], '["Lock"]', text="Aim Lock", slider=True)
891 # make this camera active if more than one camera exists
893 if cam != bpy.context.scene.camera:
894 col.op(, text="Make Active Camera", toggle=True)
897 box = layout.box()
898 col = box.column()
899 col.separator()
901 # Crane arm stuff
902 col.label(text="Crane Arm:")
903 col.prop(pose_bones["Height"], 'scale', index=1, text="Arm Height")
904 col.prop(pose_bones["Crane_Arm"], 'scale', index=1, text="Arm Length")
907 # =========================================================================
908 # This is the operator that will call all the functions and build the dolly rig
909 # =========================================================================
910 class BuildDollyRig(Operator):
911 bl_idname = "object.build_dolly_rig"
912 bl_label = "Build Dolly Camera Rig"
913 bl_description = "Build a Camera Dolly Rig"
914 bl_options = {'REGISTER', 'UNDO'}
916 def execute(self, context):
918 # build the Widgets
919 create_root_widget(self, "Camera_Root")
920 create_camera_widget(self, "CTRL")
921 create_aim_widget(self, "AIM")
923 # call the function to build the rig
924 build_dolly_rig(context)
926 return {'FINISHED'}
929 # =========================================================================
930 # This is the operator that will call all the functions and build the crane rig
931 # =========================================================================
932 class BuildCraneRig(Operator):
933 bl_idname = "object.build_crane_rig"
934 bl_label = "Build Crane Camera Rig"
935 bl_description = "Build a Camera Crane Rig"
936 bl_options = {'REGISTER', 'UNDO'}
938 def execute(self, context):
940 # build the Widgets
941 create_root_widget(self, "Camera_Root")
942 create_camera_widget(self, "CTRL")
943 create_aim_widget(self, "AIM")
945 # call the function to build the rig
946 build_crane_rig(context)
948 return {'FINISHED'}
951 # =========================================================================
952 # Registration:
953 # =========================================================================
955 # dolly and crane entries in the Add Object > Camera Menu
956 def add_dolly_crane_buttons(self, context):
957 if context.mode == 'OBJECT':
958 self.layout.operator(
959 BuildDollyRig.bl_idname,
960 text="Dolly Camera Rig",
961 icon='CAMERA_DATA'
963 self.layout.operator(
964 BuildCraneRig.bl_idname,
965 text="Crane Camera Rig",
966 icon='CAMERA_DATA'
970 def register():
971 bpy.utils.register_class(BuildDollyRig)
972 bpy.utils.register_class(BuildCraneRig)
973 bpy.utils.register_class(DollyCameraUI)
974 bpy.utils.register_class(CraneCameraUI)
975 bpy.utils.register_class(MakeCameraActive)
976 bpy.utils.register_class(AddMarkerBind)
977 bpy.utils.register_class(AddDofEmpty)
978 bpy.types.VIEW3D_MT_camera_add.append(add_dolly_crane_buttons)
981 def unregister():
982 bpy.utils.unregister_class(BuildDollyRig)
983 bpy.utils.unregister_class(BuildCraneRig)
984 bpy.utils.unregister_class(DollyCameraUI)
985 bpy.utils.unregister_class(CraneCameraUI)
986 bpy.utils.unregister_class(MakeCameraActive)
987 bpy.utils.unregister_class(AddMarkerBind)
988 bpy.utils.unregister_class(AddDofEmpty)
989 bpy.types.VIEW3D_MT_camera_add.remove(add_dolly_crane_buttons)
992 if __name__ == "__main__":
993 register()