Skip to content

Instantly share code, notes, and snippets.

@rraallvv
Last active January 29, 2019 11:04
Show Gist options
  • Save rraallvv/5963246 to your computer and use it in GitHub Desktop.
Save rraallvv/5963246 to your computer and use it in GitHub Desktop.
Enable turntable rotation with any axis up, the axis that is most aligned vertically is the new up axis of the turntable rotation until a new mouse gesture is initiated by either releasing the mouse button or the modifier keyboard key
'''
Enable turntable rotation with any axis up, the axis that is most aligned vertically is the new up axis of the turntable rotation until a new mouse gesture is initiated by either releasing the mouse button or the modifier keyboard key
'''
bl_info = {
'name': 'Rotate Turntable ANY-axis Up',
'author': '',
'version': (0, 0, 1),
'blender': (2, 6, 7),
'location': '3d view > Ctrl + LMB-drag',
'description': 'Enable turntable rotation with any axis up, the axis that is most aligned verticaly is the new up axis.',
'wiki_url': '',
'tracker_url': '',
'category': '3D View'}
import bpy
from mathutils import *
from math import *
class RotateTurntableANYUp(bpy.types.Operator):
'''Turntable rotation ANY-axis up.'''
bl_idname = "view3d.turntable_any_up"
bl_label = "Turntable rotation ANY-axis up"
angle_yaw=0
angle_pitch=0
axis_selected=0
do_alignment=True
def execute(self, context):
region_3d = context.space_data.region_3d
# world axis in world space
xa=Vector((1,0,0))
ya=Vector((0,1,0))
za=Vector((0,0,1))
# camera axis in world space
cxa=xa.copy()
cxa.rotate(region_3d.view_rotation.inverted())
cya=ya.copy()
cya.rotate(region_3d.view_rotation.inverted())
cza=za.copy()
cza.rotate(region_3d.view_rotation.inverted())
# relative distance rotation of the camera axes to the world Y axis, if two vectors are parallel the cross product magnitud is zero
rdx = cxa.cross(ya).length
rdy = cya.cross(ya).length
rdz = cza.cross(ya).length
# select both, the closest camera axis to the world up axis, and the world up axis
if self.axis_selected == 0:
if rdx < rdy and rdx < rdz:
self.axis_selected=1
elif rdy < rdx and rdy < rdz:
self.axis_selected=2
else:
self.axis_selected=3
if self.axis_selected == 1:
camera_up_axis=cxa
world_up_axis=xa
elif self.axis_selected == 2:
camera_up_axis=cya
world_up_axis=ya
else:
camera_up_axis=cza
world_up_axis=za
# find rotation yaw in camera space, rotation yaw in world space, separation angle between them, and the rotation needed to align the camera axis up in the world space
if camera_up_axis.y > 0:
camera_rotation_yaw=Quaternion(camera_up_axis,self.angle_yaw)
world_rotation_yaw=Quaternion(world_up_axis,self.angle_yaw)
separation_angle=atan2(camera_up_axis.y,camera_up_axis.x)-pi*0.5
rotation_alignment=Quaternion(za,separation_angle)
elif camera_up_axis.y < 0:
camera_rotation_yaw=Quaternion(camera_up_axis,-self.angle_yaw)
world_rotation_yaw=Quaternion(world_up_axis,-self.angle_yaw)
separation_angle=atan2(camera_up_axis.y,camera_up_axis.x)+pi*0.5
rotation_alignment=Quaternion(za,separation_angle)
else:
camera_rotation_yaw=Quaternion((0,0,0,1))
world_rotation_yaw=Quaternion((0,0,0,1))
separation_angle = 0
rotation_alignment=Quaternion((0,0,0,1))
# check if there are selected objects in case of the user preference to rotate around the selection pivot
selected_objects = len(bpy.context.selected_objects) > 0 and bpy.context.user_preferences.view.use_rotate_around_active
# find the pivot point location
if selected_objects:
saved_location = bpy.context.scene.cursor_location.copy()
bpy.ops.view3d.snap_cursor_to_selected()
pivot_location = bpy.context.scene.cursor_location.copy()
bpy.context.scene.cursor_location = saved_location
# rotate the view_location and view_rotation to perform the turn table rotation
if self.do_alignment:
# on invocation rotate the view_location to avoid the origin or the pivot to jump abruptly
if selected_objects:
region_3d.view_location=region_3d.view_location-pivot_location
region_3d.view_rotation=region_3d.view_rotation*Quaternion(za, separation_angle)
camera_normal_axis=za
camera_normal_axis.rotate(region_3d.view_rotation)
region_3d.view_location.rotate(Quaternion(camera_normal_axis, separation_angle))
if selected_objects:
region_3d.view_location=region_3d.view_location+pivot_location
self.do_alignment=False
else:
# oter wise do the alignment
region_3d.view_rotation=region_3d.view_rotation*rotation_alignment
# compensate the rotation is there is objects selected, to rotate around the pivot point
if selected_objects:
camera_horizontal_axis=xa.copy()
camera_horizontal_axis.rotate(region_3d.view_rotation)
world_rotation_pitch=Quaternion(camera_horizontal_axis,self.angle_pitch)
pivot_to_camera = region_3d.view_location - pivot_location
pivot_to_camera.rotate(world_rotation_yaw*world_rotation_pitch)
region_3d.view_location = pivot_location + pivot_to_camera
# find rotation pitch in camera space
camera_rotation_pitch=Quaternion(xa,self.angle_pitch)
region_3d.view_rotation=region_3d.view_rotation*camera_rotation_yaw*camera_rotation_pitch
return {'FINISHED'}
def modal(self, context, event):
self.report({'INFO'}, event.value)
if event.type == 'TRACKPADPAN':
region_3d = context.space_data.region_3d
#find the yaw and pitch angles from the mouse position variation
mouse_pos=Vector((event.mouse_x-event.mouse_prev_x,event.mouse_y - event.mouse_prev_y))
self.angle_yaw=-mouse_pos.x/200.0
self.angle_pitch=mouse_pos.y/200.0
self.execute(context)
elif event.type in {'LEFTMOUSE', 'MIDDLEMOUSE', 'RIGHTMOUSE', 'ESC'} or event.value == 'RELEASE':
return {'FINISHED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
if context.space_data.type == 'VIEW_3D':
region_3d = context.space_data.region_3d
mouse_pos=Vector((event.mouse_x-event.mouse_prev_x,event.mouse_y - event.mouse_prev_y))
self.axis_selected=0
self.do_alignment=True
context.window_manager.modal_handler_add(self)
if region_3d.view_perspective == 'CAMERA':
region_3d.view_perspective = 'PERSP'
return {'RUNNING_MODAL'}
else:
return {'CANCELLED'}
def register():
bpy.utils.register_class(RotateTurntableANYUp)
wm = bpy.context.window_manager
km = wm.keyconfigs.addon.keymaps.new(name="3D View", space_type='VIEW_3D')
#kmi = km.keymap_items.new('view3d.turntable_any_up', 'EVT_TWEAK_L', 'ANY', shift=False, ctrl=True, alt=False)
kmi = km.keymap_items.new('view3d.turntable_any_up', 'TRACKPADPAN', 'ANY', shift=False, ctrl=False, alt=True, oskey=False)
def unregister():
bpy.utils.unregister_class(RotateTurntableANYUp)
wm = bpy.context.window_manager
km = wm.keyconfigs.addon.keymaps['3D View']
for kmi in km.keymap_items:
if kmi.idname == 'view3d.turntable_any_up':
km.keymap_items.remove(kmi)
if __name__ == "__main__":
register()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment