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 "name": "Turnaround Camera",
21 "author": "Antonio Vazquez (antonioya)",
23 "blender": (2, 68, 0),
24 "location": "View3D > Toolshelf > Turnaround camera",
25 "description": "Add a camera rotation around selected object",
26 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
27 "Scripts/Animation/TurnaroundCamera",
33 from bpy
.props
import (
39 from bpy
.types
import (
46 # ------------------------------------------------------
48 # ------------------------------------------------------
49 class 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 # ----------------------
57 # ----------------------
59 turn_camera
= scene
.turn_camera
60 selectobject
= context
.active_object
61 camera
= bpy
.data
.objects
[bpy
.context
.scene
.camera
.name
]
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
= bpy
.data
.objects
[bpy
.context
.active_object
.name
]
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 # -------------------------
88 # (make empty active object)
89 # -------------------------
90 bpy
.ops
.object.select_all(False)
92 bpy
.context
.scene
.objects
.active
= myempty
93 # save current configuration
94 savedinterpolation
= context
.user_preferences
.edit
.keyframe_new_interpolation_type
95 # change interpolation mode
96 context
.user_preferences
.edit
.keyframe_new_interpolation_type
= 'LINEAR'
98 myempty
.rotation_euler
= (0, 0, 0)
99 myempty
.empty_draw_size
= 0.1
100 bpy
.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
:
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
))
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)
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(
142 frame
=((scene
.frame_end
- scene
.frame_start
) / 2)
146 myempty
.rotation_euler
= (xrot
, yrot
, zrot
)
147 myempty
.keyframe_insert(data_path
='rotation_euler', frame
=scene
.frame_end
)
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
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
160 if turn_camera
.track
is True:
161 bpy
.context
.scene
.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
.user_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
= True
176 bpy
.context
.scene
.objects
.active
= selectobject
177 bpy
.context
.scene
.frame_set(savedframe
)
182 # ------------------------------------------------------
184 # ------------------------------------------------------
185 class CameraTurnProps(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(
204 description
="Inverse rotation",
207 inverse_y
= BoolProperty(
209 description
="Inverse rotation",
212 inverse_z
= BoolProperty(
214 description
="Inverse rotation",
217 use_cursor
= BoolProperty(
218 name
="Use cursor position",
219 description
="Use cursor position instead of object origin",
222 back_forw
= BoolProperty(
223 name
="Back and forward",
224 description
="Create back and forward animation",
227 dolly_zoom
= EnumProperty(
230 ('1', "Dolly zoom", ""),
231 ('2', "Dolly zoom B/F", "")
234 description
="Create a camera lens movement"
236 camera_from_lens
= FloatProperty(
238 min=1, max=500, default
=35,
240 description
="Start lens value"
242 camera_to_lens
= FloatProperty(
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",
253 reset_cam_anim
= BoolProperty(
255 description
="Clear previous camera animations if there are any\n"
256 "(For instance, previous Dolly Zoom)",
261 # ------------------------------------------------------
263 # ------------------------------------------------------
264 class PanelUI(Panel
):
265 bl_idname
= "CAMERA_TURN_PT_main"
266 bl_label
= "Turnaround Camera"
267 bl_space_type
= "VIEW_3D"
268 bl_region_type
= "TOOLS"
269 bl_category
= "Animation"
271 def draw(self
, context
):
273 scene
= context
.scene
274 turn_camera
= scene
.turn_camera
277 bpy
.context
.scene
.camera
.name
278 except AttributeError:
279 row
= layout
.row(align
=False)
280 row
.label("No defined camera for scene", icon
="INFO")
283 if context
.active_object
is not None:
284 if context
.active_object
.type != 'CAMERA':
285 buf
= context
.active_object
.name
286 row
= layout
.row(align
=True)
287 row
.operator("object.rotate_around", icon
='OUTLINER_DATA_CAMERA')
290 box
.label(buf
, icon
='MESH_DATA')
291 row
= layout
.row(align
=False)
292 row
.prop(scene
, "camera")
294 layout
.label("Rotation:")
295 row
= layout
.row(align
=True)
296 row
.prop(scene
, "frame_start")
297 row
.prop(scene
, "frame_end")
299 col
= layout
.column(align
=True)
300 split
= col
.split(percentage
=0.85, align
=True)
301 split
.prop(turn_camera
, "camera_revol_x")
302 split
.prop(turn_camera
, "inverse_x", toggle
=True)
303 split
= col
.split(percentage
=0.85, align
=True)
304 split
.prop(turn_camera
, "camera_revol_y")
305 split
.prop(turn_camera
, "inverse_y", toggle
=True)
306 split
= col
.split(percentage
=0.85, align
=True)
307 split
.prop(turn_camera
, "camera_revol_z")
308 split
.prop(turn_camera
, "inverse_z", toggle
=True)
310 col
= layout
.column(align
=True)
311 col
.label("Options:")
312 row
= col
.row(align
=True)
313 row
.prop(turn_camera
, "back_forw", toggle
=True)
314 row
.prop(turn_camera
, "reset_cam_anim", toggle
=True)
315 col
.prop(turn_camera
, "track", toggle
=True)
316 col
.prop(turn_camera
, "use_cursor", toggle
=True)
319 row
.prop(turn_camera
, "dolly_zoom")
320 if turn_camera
.dolly_zoom
!= "0":
321 row
= layout
.row(align
=True)
322 row
.prop(turn_camera
, "camera_from_lens")
323 row
.prop(turn_camera
, "camera_to_lens")
326 buf
= "No valid object selected"
327 layout
.label(buf
, icon
='MESH_DATA')
330 # ------------------------------------------------------
332 # ------------------------------------------------------
334 bpy
.utils
.register_class(RunAction
)
335 bpy
.utils
.register_class(PanelUI
)
336 bpy
.utils
.register_class(CameraTurnProps
)
337 bpy
.types
.Scene
.turn_camera
= PointerProperty(type=CameraTurnProps
)
341 bpy
.utils
.unregister_class(RunAction
)
342 bpy
.utils
.unregister_class(PanelUI
)
343 bpy
.utils
.unregister_class(CameraTurnProps
)
344 del bpy
.types
.Scene
.turn_camera
347 if __name__
== "__main__":