1
$\begingroup$

How to rotate Viewport (not object, not camera) around the Z axis only. is there another way to rotate the viewport horizontally beside Numpad 6. the view can be easily rotate to above or below the the object.

Thanks in advance!

$\endgroup$
4
  • $\begingroup$ No, as far as I know only Numpad 4 and Numpad 6 can be used to rotate along Z axis. What do you mean by "the view can be easily rotate to above or below the the object"? $\endgroup$ Dec 14, 2023 at 8:55
  • $\begingroup$ Yes. You can use a Camera for Viewing. Parent the camera to an Empty centered in the world's origin. Now you can rotate the empty along its Z axis and the Camera will follow. $\endgroup$ Dec 14, 2023 at 8:58
  • $\begingroup$ @RumenBelev It is stated clearly in the question to rotate the viewport and not a camera. $\endgroup$ Dec 14, 2023 at 12:31
  • $\begingroup$ @GordonBrinkmann The viewport is not rotated, you just see through a camera that is not visible or selectable - the question is misleading and my answer is still a valid alternative. $\endgroup$ Dec 14, 2023 at 16:24

1 Answer 1

1
$\begingroup$

Try Fly Navigation with Shift + ~ or View > Navigation > Fly Navigation.

Here is an addon.

enter image description here

bl_info = {
    "name": "Viewport Euler Rotation",
    "author": "unwave",
    "version": (1, 0),
    "blender": (2, 83, 0),
    "location": "View3D > N-Panel > View",
    "description": "More viewport controls. A kind of a hack",
    "warning": "",
    "doc_url": "",
    "category": "3D View",
}


import bpy
import mathutils


KEYS = set(bpy.types.RegionView3D.bl_rna.properties.keys())

NOT_USEFUL = {
    'clip_planes',
    'is_orthographic_side_view',
    'is_perspective',
    'perspective_matrix',
    'rna_type',
    'use_box_clip',
    'use_clip_planes',
    'view_matrix',
    'window_matrix',
}

class VIEWPORTEULER_PT_viewport_properties(bpy.types.Panel):
    bl_idname = "TEST_PT_viewport_properties"
    bl_label = "Viewport Euler Rotation"
    bl_category = "View"
    bl_space_type = 'VIEW_3D'
    bl_region_type = "UI"
    bl_options = {'DEFAULT_CLOSED'}

    def draw(self, context):
        column = self.layout.column(align=True)

        column.prop(context.window_manager, 'viewporteuler_viewport_rotation')

        region_3d = context.space_data.region_3d
        for attr in dir(region_3d):
            if attr.startswith('_'):
                continue
            if not attr in KEYS:
                continue
            if attr in NOT_USEFUL:
                continue
            column.prop(context.space_data.region_3d, attr)

        column.prop(context.window_manager, 'viewporteuler_live_update')

view_rotation_euler = mathutils.Euler((0, 0, 0), 'XYZ')
PI_x_2 = 3.141592653589793238462643383279502884 * 2


def get_view_rotation_euler(self):
    global view_rotation_euler
    region_3d = bpy.context.area.spaces.active.region_3d

    return region_3d.view_rotation.to_euler('XYZ', view_rotation_euler)

def set_view_rotation_euler(self, value):
    global view_rotation_euler
    region_3d = bpy.context.area.spaces.active.region_3d

    euler = mathutils.Euler(value, 'XYZ')

    quaternion = euler.to_quaternion()
    quaternion.make_compatible(region_3d.view_rotation)

    region_3d.view_rotation = quaternion

    view_rotation_euler = mathutils.Euler((n % PI_x_2 for n in quaternion.to_euler('XYZ', view_rotation_euler)), 'XYZ')


def update_side_panel_callback():
    for region in bpy.context.area.regions:
        if region.type == 'UI':
            region.tag_redraw()


drawing_handler = None

def get_live_update(self):
    global drawing_handler

    return bool(drawing_handler)

def set_live_update(self, value):
    global drawing_handler

    if drawing_handler is not None:
        bpy.types.SpaceView3D.draw_handler_remove(drawing_handler, 'WINDOW')

    if value:
        drawing_handler = bpy.types.SpaceView3D.draw_handler_add(update_side_panel_callback, tuple(), 'WINDOW', 'POST_PIXEL')
    else:
        drawing_handler = None


def register():
    bpy.types.WindowManager.viewporteuler_viewport_rotation = bpy.props.FloatVectorProperty(name='Viewport Rotation', step=6, subtype='EULER', get=get_view_rotation_euler, set=set_view_rotation_euler)

    bpy.types.WindowManager.viewporteuler_live_update = bpy.props.BoolProperty(name='Live Update Hack', get=get_live_update, set=set_live_update)

    bpy.utils.register_class(VIEWPORTEULER_PT_viewport_properties)


def unregister():
    bpy.utils.unregister_class(VIEWPORTEULER_PT_viewport_properties)

    delattr(bpy.types.WindowManager, 'viewporteuler_live_update')

    delattr(bpy.types.WindowManager, 'viewporteuler_viewport_rotation')

    set_live_update(None, False)


if __name__ == "__main__":
    register()

$\endgroup$
1
  • $\begingroup$ Really good looking addon code, thanks for this cool contribution. Instead of defining PI_x_2 yourself you could also use the existing math library math.tau. $\endgroup$
    – Leander
    Dec 14, 2023 at 17:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .