Pose Library: update for rename of asset_library to asset_library_ref
[blender-addons.git] / camera_turnaround.py
blob8d0fa417106e0cfd56b8f49c442bed51229261e0
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 "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/turnaround_camera.html",
27 "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"
53 bl_options = {'REGISTER', 'UNDO'}
55 def execute(self, context):
56 # ----------------------
57 # Save old data
58 # ----------------------
59 scene = context.scene
60 turn_camera = scene.turn_camera
61 selectobject = context.active_object
62 camera = scene.camera
63 savedcursor = scene.cursor.location.copy() # cursor position
64 savedframe = scene.frame_current
65 if turn_camera.use_cursor is False:
66 bpy.ops.view3d.snap_cursor_to_selected()
68 # -------------------------
69 # Create empty and parent
70 # -------------------------
71 bpy.ops.object.empty_add(type='PLAIN_AXES')
72 myempty = context.active_object
74 myempty.location = selectobject.location
75 savedstate = myempty.matrix_world
76 myempty.parent = selectobject
77 myempty.name = "MCH_Rotation_target"
78 myempty.matrix_world = savedstate
80 # -------------------------
81 # Parent camera to empty
82 # -------------------------
83 savedstate = camera.matrix_world
84 camera.parent = myempty
85 camera.matrix_world = savedstate
87 # -------------------------
88 # Now add revolutions
89 # (make empty active object)
90 # -------------------------
91 bpy.ops.object.select_all(False)
92 myempty.select_set(True)
93 context.view_layer.objects.active = myempty
94 # save current configuration
95 savedinterpolation = context.preferences.edit.keyframe_new_interpolation_type
96 # change interpolation mode
97 context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR'
98 # create first frame
99 myempty.rotation_euler = (0, 0, 0)
100 myempty.empty_display_size = 0.1
101 scene.frame_set(scene.frame_start)
102 myempty.keyframe_insert(data_path="rotation_euler", frame=scene.frame_start)
104 # Clear the Camera Animations if the option is checked
105 if turn_camera.reset_cam_anim:
106 try:
107 if bpy.data.cameras[camera.name].animation_data:
108 bpy.data.cameras[camera.name].animation_data_clear()
109 except Exception as e:
110 print("\n[Camera Turnaround]\nWarning: {}\n".format(e))
112 # Dolly zoom
113 if turn_camera.dolly_zoom != "0":
114 bpy.data.cameras[camera.name].lens = turn_camera.camera_from_lens
115 bpy.data.cameras[camera.name].keyframe_insert("lens", frame=scene.frame_start)
117 # Calculate rotation XYZ
118 ix = -1 if turn_camera.inverse_x else 1
119 iy = -1 if turn_camera.inverse_y else 1
120 iz = -1 if turn_camera.inverse_z else 1
122 xrot = (pi * 2) * turn_camera.camera_revol_x * ix
123 yrot = (pi * 2) * turn_camera.camera_revol_y * iy
124 zrot = (pi * 2) * turn_camera.camera_revol_z * iz
126 # create middle frame
127 if turn_camera.back_forw is True:
128 myempty.rotation_euler = (xrot, yrot, zrot)
129 myempty.keyframe_insert(
130 data_path="rotation_euler",
131 frame=((scene.frame_end - scene.frame_start) / 2)
133 # reverse
134 xrot *= -1
135 yrot *= -1
136 zrot = 0
138 # Dolly zoom
139 if turn_camera.dolly_zoom == "2":
140 bpy.data.cameras[camera.name].lens = turn_camera.camera_to_lens
141 bpy.data.cameras[camera.name].keyframe_insert(
142 "lens",
143 frame=((scene.frame_end - scene.frame_start) / 2)
146 # create last frame
147 myempty.rotation_euler = (xrot, yrot, zrot)
148 myempty.keyframe_insert(data_path="rotation_euler", frame=scene.frame_end)
149 # Dolly zoom
150 if turn_camera.dolly_zoom != "0":
151 if turn_camera.dolly_zoom == "1":
152 bpy.data.cameras[camera.name].lens = turn_camera.camera_to_lens # final
153 else:
154 bpy.data.cameras[camera.name].lens = turn_camera.camera_from_lens # back to init
156 bpy.data.cameras[camera.name].keyframe_insert(
157 "lens", frame=scene.frame_end
160 # Track constraint
161 if turn_camera.track is True:
162 bpy.context.view_layer.objects.active = camera
163 bpy.ops.object.constraint_add(type='TRACK_TO')
164 bpy.context.object.constraints[-1].track_axis = 'TRACK_NEGATIVE_Z'
165 bpy.context.object.constraints[-1].up_axis = 'UP_Y'
166 bpy.context.object.constraints[-1].target = myempty
168 # back previous configuration
169 context.preferences.edit.keyframe_new_interpolation_type = savedinterpolation
170 scene.cursor.location = savedcursor
172 # -------------------------
173 # Back to old selection
174 # -------------------------
175 bpy.ops.object.select_all(False)
176 selectobject.select_set(True)
177 bpy.context.view_layer.objects.active = selectobject
178 scene.frame_set(savedframe)
180 return {'FINISHED'}
183 # ------------------------------------------------------
184 # Define Properties
185 # ------------------------------------------------------
186 class CAMERATURN_Props(PropertyGroup):
188 camera_revol_x: FloatProperty(
189 name="X", min=0, max=25,
190 default=0, precision=2,
191 description="Number total of revolutions in X axis"
193 camera_revol_y: FloatProperty(
194 name="Y", min=0, max=25,
195 default=0, precision=2,
196 description="Number total of revolutions in Y axis"
198 camera_revol_z: FloatProperty(
199 name="Z", min=0, max=25,
200 default=1, precision=2,
201 description="Number total of revolutions in Z axis"
203 inverse_x: BoolProperty(
204 name="-X",
205 description="Inverse rotation",
206 default=False
208 inverse_y: BoolProperty(
209 name="-Y",
210 description="Inverse rotation",
211 default=False
213 inverse_z: BoolProperty(
214 name="-Z",
215 description="Inverse rotation",
216 default=False
218 use_cursor: BoolProperty(
219 name="Use cursor position",
220 description="Use cursor position instead of object origin",
221 default=False
223 back_forw: BoolProperty(
224 name="Back and forward",
225 description="Create back and forward animation",
226 default=False
228 dolly_zoom: EnumProperty(
229 items=(
230 ('0', "None", ""),
231 ('1', "Dolly zoom", ""),
232 ('2', "Dolly zoom B/F", "")
234 name="Lens Effects",
235 description="Create a camera lens movement"
237 camera_from_lens: FloatProperty(
238 name="From",
239 min=1, max=500, default=35,
240 precision=3,
241 description="Start lens value"
243 camera_to_lens: FloatProperty(
244 name="To",
245 min=1, max=500,
246 default=35, precision=3,
247 description="End lens value"
249 track: BoolProperty(
250 name="Create track constraint",
251 description="Add a track constraint to the camera",
252 default=False
254 reset_cam_anim: BoolProperty(
255 name="Clear Camera",
256 description="Clear previous camera animations if there are any\n"
257 "(For instance, previous Dolly Zoom)",
258 default=False
262 # ------------------------------------------------------
263 # UI Class
264 # ------------------------------------------------------
265 class CAMERATURN_PT_ui(Panel):
266 bl_idname = "CAMERA_TURN_PT_main"
267 bl_label = "Turnaround Camera"
268 bl_space_type = "VIEW_3D"
269 bl_region_type = "UI"
270 bl_category = "Animate"
271 bl_context = "objectmode"
272 bl_options = {'DEFAULT_CLOSED'}
274 def draw(self, context):
275 layout = self.layout
276 scene = context.scene
277 turn_camera = scene.turn_camera
279 try:
280 scene.camera.name
281 except AttributeError:
282 row = layout.row(align=False)
283 row.label(text="No defined camera for scene", icon="INFO")
284 return
286 if context.active_object is not None:
287 if context.active_object.type != 'CAMERA':
288 buf = context.active_object.name
289 row = layout.row(align=True)
290 row.operator("object.rotate_around", icon='OUTLINER_DATA_CAMERA')
291 box = row.box()
292 box.scale_y = 0.5
293 box.label(text=buf, icon='MESH_DATA')
294 row = layout.row(align=False)
295 row.prop(scene, "camera")
297 layout.label(text="Rotation:")
298 row = layout.row(align=True)
299 row.prop(scene, "frame_start")
300 row.prop(scene, "frame_end")
302 col = layout.column(align=True)
303 split = col.split(factor=0.85, align=True)
304 split.prop(turn_camera, "camera_revol_x")
305 split.prop(turn_camera, "inverse_x", toggle=True)
306 split = col.split(factor=0.85, align=True)
307 split.prop(turn_camera, "camera_revol_y")
308 split.prop(turn_camera, "inverse_y", toggle=True)
309 split = col.split(factor=0.85, align=True)
310 split.prop(turn_camera, "camera_revol_z")
311 split.prop(turn_camera, "inverse_z", toggle=True)
313 col = layout.column(align=True)
314 col.label(text="Options:")
315 row = col.row(align=True)
316 row.prop(turn_camera, "back_forw", toggle=True)
317 row.prop(turn_camera, "reset_cam_anim", toggle=True)
318 col.prop(turn_camera, "track", toggle=True)
319 col.prop(turn_camera, "use_cursor", toggle=True)
321 row = layout.row()
322 row.prop(turn_camera, "dolly_zoom")
323 if turn_camera.dolly_zoom != "0":
324 row = layout.row(align=True)
325 row.prop(turn_camera, "camera_from_lens")
326 row.prop(turn_camera, "camera_to_lens")
328 else:
329 buf = "No valid object selected"
330 layout.label(text=buf, icon='MESH_DATA')
333 # ------------------------------------------------------
334 # Registration
335 # ------------------------------------------------------
336 classes = (
337 CAMERATURN_OT_RunAction,
338 CAMERATURN_PT_ui,
339 CAMERATURN_Props
343 def register():
344 from bpy.utils import register_class
345 for cls in classes:
346 register_class(cls)
348 bpy.types.Scene.turn_camera = PointerProperty(type=CAMERATURN_Props)
351 def unregister():
352 from bpy.utils import unregister_class
353 for cls in reversed(classes):
354 unregister_class(cls)
356 del bpy.types.Scene.turn_camera
359 if __name__ == "__main__":
360 register()