object_collection_manager: Remove import globbing
[blender-addons.git] / camera_turnaround.py
blob8711a93067e5c1899e29ba74e9921be0bf087c1c
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 #####
19 bl_info = {
20 "name": "Turnaround Camera",
21 "author": "Antonio Vazquez (antonioya)",
22 "version": (0, 3, 0),
23 "blender": (2, 80, 0),
24 "location": "View3D > Sidebar > View Tab > Turnaround Camera",
25 "description": "Add a camera rotation around selected object",
26 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
27 "animation/turnaround_camera.html",
28 "category": "Animation"}
31 import bpy
32 from math import pi
33 from bpy.props import (
34 BoolProperty,
35 EnumProperty,
36 FloatProperty,
37 PointerProperty,
39 from bpy.types import (
40 Operator,
41 Panel,
42 PropertyGroup,
46 # ------------------------------------------------------
47 # Action class
48 # ------------------------------------------------------
49 class CAMERATURN_OT_RunAction(Operator):
50 bl_idname = "object.rotate_around"
51 bl_label = "Turnaround"
52 bl_description = "Create camera rotation around selected object"
54 def execute(self, context):
55 # ----------------------
56 # Save old data
57 # ----------------------
58 scene = context.scene
59 turn_camera = scene.turn_camera
60 selectobject = context.active_object
61 camera = context.scene.camera
62 savedcursor = bpy.context.scene.cursor.location.copy() # cursor position
63 savedframe = scene.frame_current
64 if turn_camera.use_cursor is False:
65 bpy.ops.view3d.snap_cursor_to_selected()
67 # -------------------------
68 # Create empty and parent
69 # -------------------------
70 bpy.ops.object.empty_add(type='PLAIN_AXES')
71 myempty = context.active_object
73 myempty.location = selectobject.location
74 savedstate = myempty.matrix_world
75 myempty.parent = selectobject
76 myempty.name = 'MCH_Rotation_target'
77 myempty.matrix_world = savedstate
79 # -------------------------
80 # Parent camera to empty
81 # -------------------------
82 savedstate = camera.matrix_world
83 camera.parent = myempty
84 camera.matrix_world = savedstate
86 # -------------------------
87 # Now add revolutions
88 # (make empty active object)
89 # -------------------------
90 bpy.ops.object.select_all(False)
91 myempty.select_set(True)
92 context.view_layer.objects.active = myempty
93 # save current configuration
94 savedinterpolation = context.preferences.edit.keyframe_new_interpolation_type
95 # change interpolation mode
96 context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR'
97 # create first frame
98 myempty.rotation_euler = (0, 0, 0)
99 myempty.empty_display_size = 0.1
100 context.scene.frame_set(scene.frame_start)
101 myempty.keyframe_insert(data_path='rotation_euler', frame=scene.frame_start)
103 # Clear the Camera Animations if the option is checked
104 if turn_camera.reset_cam_anim:
105 try:
106 if bpy.data.cameras[camera.name].animation_data:
107 bpy.data.cameras[camera.name].animation_data_clear()
108 except Exception as e:
109 print("\n[Camera Turnaround]\nWarning: {}\n".format(e))
111 # Dolly zoom
112 if turn_camera.dolly_zoom != "0":
113 bpy.data.cameras[camera.name].lens = turn_camera.camera_from_lens
114 bpy.data.cameras[camera.name].keyframe_insert('lens', frame=scene.frame_start)
116 # Calculate rotation XYZ
117 ix = -1 if turn_camera.inverse_x else 1
118 iy = -1 if turn_camera.inverse_y else 1
119 iz = -1 if turn_camera.inverse_z else 1
121 xrot = (pi * 2) * turn_camera.camera_revol_x * ix
122 yrot = (pi * 2) * turn_camera.camera_revol_y * iy
123 zrot = (pi * 2) * turn_camera.camera_revol_z * iz
125 # create middle frame
126 if turn_camera.back_forw is True:
127 myempty.rotation_euler = (xrot, yrot, zrot)
128 myempty.keyframe_insert(
129 data_path='rotation_euler',
130 frame=((scene.frame_end - scene.frame_start) / 2)
132 # reverse
133 xrot *= -1
134 yrot *= -1
135 zrot = 0
137 # Dolly zoom
138 if turn_camera.dolly_zoom == "2":
139 bpy.data.cameras[camera.name].lens = turn_camera.camera_to_lens
140 bpy.data.cameras[camera.name].keyframe_insert(
141 'lens',
142 frame=((scene.frame_end - scene.frame_start) / 2)
145 # create last frame
146 myempty.rotation_euler = (xrot, yrot, zrot)
147 myempty.keyframe_insert(data_path='rotation_euler', frame=scene.frame_end)
148 # Dolly zoom
149 if turn_camera.dolly_zoom != "0":
150 if turn_camera.dolly_zoom == "1":
151 bpy.data.cameras[camera.name].lens = turn_camera.camera_to_lens # final
152 else:
153 bpy.data.cameras[camera.name].lens = turn_camera.camera_from_lens # back to init
155 bpy.data.cameras[camera.name].keyframe_insert(
156 'lens', frame=scene.frame_end
159 # Track constraint
160 if turn_camera.track is True:
161 bpy.context.view_layer.objects.active = camera
162 bpy.ops.object.constraint_add(type='TRACK_TO')
163 bpy.context.object.constraints[-1].track_axis = 'TRACK_NEGATIVE_Z'
164 bpy.context.object.constraints[-1].up_axis = 'UP_Y'
165 bpy.context.object.constraints[-1].target = bpy.data.objects[myempty.name]
167 # back previous configuration
168 context.preferences.edit.keyframe_new_interpolation_type = savedinterpolation
169 bpy.context.scene.cursor.location = savedcursor
171 # -------------------------
172 # Back to old selection
173 # -------------------------
174 bpy.ops.object.select_all(False)
175 selectobject.select_set(True)
176 bpy.context.view_layer.objects.active = selectobject
177 bpy.context.scene.frame_set(savedframe)
179 return {'FINISHED'}
182 # ------------------------------------------------------
183 # Define Properties
184 # ------------------------------------------------------
185 class CAMERATURN_Props(PropertyGroup):
187 camera_revol_x: FloatProperty(
188 name='X', min=0, max=25,
189 default=0, precision=2,
190 description='Number total of revolutions in X axis'
192 camera_revol_y: FloatProperty(
193 name='Y', min=0, max=25,
194 default=0, precision=2,
195 description='Number total of revolutions in Y axis'
197 camera_revol_z: FloatProperty(
198 name='Z', min=0, max=25,
199 default=1, precision=2,
200 description='Number total of revolutions in Z axis'
202 inverse_x: BoolProperty(
203 name="-X",
204 description="Inverse rotation",
205 default=False
207 inverse_y: BoolProperty(
208 name="-Y",
209 description="Inverse rotation",
210 default=False
212 inverse_z: BoolProperty(
213 name="-Z",
214 description="Inverse rotation",
215 default=False
217 use_cursor: BoolProperty(
218 name="Use cursor position",
219 description="Use cursor position instead of object origin",
220 default=False
222 back_forw: BoolProperty(
223 name="Back and forward",
224 description="Create back and forward animation",
225 default=False
227 dolly_zoom: EnumProperty(
228 items=(
229 ('0', "None", ""),
230 ('1', "Dolly zoom", ""),
231 ('2', "Dolly zoom B/F", "")
233 name="Lens Effects",
234 description="Create a camera lens movement"
236 camera_from_lens: FloatProperty(
237 name="From",
238 min=1, max=500, default=35,
239 precision=3,
240 description="Start lens value"
242 camera_to_lens: FloatProperty(
243 name="To",
244 min=1, max=500,
245 default=35, precision=3,
246 description="End lens value"
248 track: BoolProperty(
249 name="Create track constraint",
250 description="Add a track constraint to the camera",
251 default=False
253 reset_cam_anim: BoolProperty(
254 name="Clear Camera",
255 description="Clear previous camera animations if there are any\n"
256 "(For instance, previous Dolly Zoom)",
257 default=False
261 # ------------------------------------------------------
262 # UI Class
263 # ------------------------------------------------------
264 class CAMERATURN_PT_ui(Panel):
265 bl_idname = "CAMERA_TURN_PT_main"
266 bl_label = "Turnaround Camera"
267 bl_space_type = "VIEW_3D"
268 bl_region_type = "UI"
269 bl_category = "Animate"
270 bl_context = "objectmode"
271 bl_options = {'DEFAULT_CLOSED'}
273 def draw(self, context):
274 layout = self.layout
275 scene = context.scene
276 turn_camera = scene.turn_camera
278 try:
279 bpy.context.scene.camera.name
280 except AttributeError:
281 row = layout.row(align=False)
282 row.label(text="No defined camera for scene", icon="INFO")
283 return
285 if context.active_object is not None:
286 if context.active_object.type != 'CAMERA':
287 buf = context.active_object.name
288 row = layout.row(align=True)
289 row.operator("object.rotate_around", icon='OUTLINER_DATA_CAMERA')
290 box = row.box()
291 box.scale_y = 0.5
292 box.label(text=buf, icon='MESH_DATA')
293 row = layout.row(align=False)
294 row.prop(scene, "camera")
296 layout.label(text="Rotation:")
297 row = layout.row(align=True)
298 row.prop(scene, "frame_start")
299 row.prop(scene, "frame_end")
301 col = layout.column(align=True)
302 split = col.split(factor=0.85, align=True)
303 split.prop(turn_camera, "camera_revol_x")
304 split.prop(turn_camera, "inverse_x", toggle=True)
305 split = col.split(factor=0.85, align=True)
306 split.prop(turn_camera, "camera_revol_y")
307 split.prop(turn_camera, "inverse_y", toggle=True)
308 split = col.split(factor=0.85, align=True)
309 split.prop(turn_camera, "camera_revol_z")
310 split.prop(turn_camera, "inverse_z", toggle=True)
312 col = layout.column(align=True)
313 col.label(text="Options:")
314 row = col.row(align=True)
315 row.prop(turn_camera, "back_forw", toggle=True)
316 row.prop(turn_camera, "reset_cam_anim", toggle=True)
317 col.prop(turn_camera, "track", toggle=True)
318 col.prop(turn_camera, "use_cursor", toggle=True)
320 row = layout.row()
321 row.prop(turn_camera, "dolly_zoom")
322 if turn_camera.dolly_zoom != "0":
323 row = layout.row(align=True)
324 row.prop(turn_camera, "camera_from_lens")
325 row.prop(turn_camera, "camera_to_lens")
327 else:
328 buf = "No valid object selected"
329 layout.label(text=buf, icon='MESH_DATA')
332 # ------------------------------------------------------
333 # Registration
334 # ------------------------------------------------------
335 classes = (
336 CAMERATURN_OT_RunAction,
337 CAMERATURN_PT_ui,
338 CAMERATURN_Props
341 def register():
342 from bpy.utils import register_class
343 for cls in classes:
344 register_class(cls)
346 bpy.types.Scene.turn_camera = PointerProperty(type=CAMERATURN_Props)
348 def unregister():
349 from bpy.utils import unregister_class
350 for cls in reversed(classes):
351 unregister_class(cls)
353 del bpy.types.Scene.turn_camera
356 if __name__ == "__main__":
357 register()