Skip to main content
deleted 4627 characters in body
Source Link

Edit. (repeating from my blenderartists post)

Update! I’ve derived a rudimentary way of acheiving the kind of controls i need, thank—in small part—to chat gpt and stack exchange. It turned out that using a modal operator was all it took to override the standard blender controls. Though I couldn’t find a way to easily manipulate the viewport rotation and focal length. instead I create a temporary camera and set it as the default scene camera and automatically switch the first viewport in the current area to camera view. Though you can use it in any area by switching to camera view in that area. I need to make it so that the mmb is only overridden when the mouse is in a View3d region, and the rotation speed of the camera should be slower when the viewport is zoomed in and faster when its zoomed out, but this is a pretty decent proof of concept for now. Right now the script must be run manually and pressing ESC deletes the temporary camera and restores the default controls.

import bpy
from mathutils import *


class CustomViewportControls(bpy.types.Operator):
    """overrides blenders standard viewport controls"""
    bl_idname = "wm.custom_viewport_controls"
    bl_label = "Custom Viewport Controls"
    
    mmb_pressed = False
    cam = None
    cam_obj = None
    cam_mat = Matrix.Rotation(3.1415926/2,3,'X')
    ini_mpos = Vector((0.0,0.0))
    rot_state= Vector((0.0,0.0))


    def init_custom_navigation(self):
        if not "Pivot_cam" in bpy.data.cameras:
                self.cam=bpy.data.cameras.new("Pivot_cam")
                self.cam.lens=10
        else:
            self.cam=bpy.data.cameras['Pivot_cam']
        if not "Pivot_cam" in bpy.data.objects:  
            self.cam_obj=bpy.data.objects.new("Pivot_cam",self.cam)
            self.cam_obj.rotation_euler=self.cam_mat.to_euler()
            bpy.context.scene.collection.objects.link(self.cam_obj)
            bpy.context.scene.camera=self.cam_obj
        else:
            self.cam_obj=bpy.data.objects['Pivot_cam']
            
            
        for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                # Get the region of the 3D Viewport
                area.spaces[0].region_3d.view_perspective='CAMERA'
                break
        
    def destroy_custom_navigation(self):
        if "Pivot_cam" in bpy.data.objects:
            bpy.data.objects.remove(bpy.data.objects['Pivot_cam'])
            
        for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                # Get the region of the 3D Viewport
                area.spaces[0].region_3d.view_perspective='PERSP'
                break
        
    
    def modal(self, context, event):
        rot_state=self.rot_state
        cam_mat=self.cam_mat
        mpos=Vector((event.mouse_x, event.mouse_y))
        mdist=mpos-self.ini_mpos
        
        if event.type in {'ESC'}:
            self.cancel(context)
            return {'CANCELLED'}
        
        if event.type == 'MOUSEMOVE' and self.mmb_pressed:
            ## code for panning the viewport
            #print("mouse moved", mdist)
            rot_state+=mdist
            angle_x=Matrix.Rotation(rot_state[0]/500,3,'Y')
            angle_y=Matrix.Rotation(-rot_state[1]/500,3,'X')
            self.cam_obj.rotation_euler=(cam_mat @ angle_x @ angle_y).to_euler()
            self.ini_mpos = mpos
            return {'RUNNING_MODAL'}
        
        if event.type in {'MIDDLEMOUSE'}and not event.ctrl:
            #print("mmb", event.value)
            if event.value == "PRESS":
                self.mmb_pressed = True
                self.ini_mpos= mpos
            else:
                self.mmb_pressed = False
            return {'RUNNING_MODAL'}

        if event.type in {'ESC'}:
            return {'FINISHED'}
        
        return {'PASS_THROUGH'}
    
    def invoke(self, context, event):
        print("INVOKE")
        self.init_custom_navigation()
        self.execute(context)
        return {'RUNNING_MODAL'}

    def execute(self, context):
        wm = context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        self.destroy_custom_navigation()
        return {'FINISHED'}

def register():
    bpy.utils.register_class(CustomViewportControls)

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

if __name__ == "__main__":
    register()
    bpy.ops.wm.custom_viewport_controls('INVOKE_DEFAULT')

Edit. (repeating from my blenderartists post)

Update! I’ve derived a rudimentary way of acheiving the kind of controls i need, thank—in small part—to chat gpt and stack exchange. It turned out that using a modal operator was all it took to override the standard blender controls. Though I couldn’t find a way to easily manipulate the viewport rotation and focal length. instead I create a temporary camera and set it as the default scene camera and automatically switch the first viewport in the current area to camera view. Though you can use it in any area by switching to camera view in that area. I need to make it so that the mmb is only overridden when the mouse is in a View3d region, and the rotation speed of the camera should be slower when the viewport is zoomed in and faster when its zoomed out, but this is a pretty decent proof of concept for now. Right now the script must be run manually and pressing ESC deletes the temporary camera and restores the default controls.

import bpy
from mathutils import *


class CustomViewportControls(bpy.types.Operator):
    """overrides blenders standard viewport controls"""
    bl_idname = "wm.custom_viewport_controls"
    bl_label = "Custom Viewport Controls"
    
    mmb_pressed = False
    cam = None
    cam_obj = None
    cam_mat = Matrix.Rotation(3.1415926/2,3,'X')
    ini_mpos = Vector((0.0,0.0))
    rot_state= Vector((0.0,0.0))


    def init_custom_navigation(self):
        if not "Pivot_cam" in bpy.data.cameras:
                self.cam=bpy.data.cameras.new("Pivot_cam")
                self.cam.lens=10
        else:
            self.cam=bpy.data.cameras['Pivot_cam']
        if not "Pivot_cam" in bpy.data.objects:  
            self.cam_obj=bpy.data.objects.new("Pivot_cam",self.cam)
            self.cam_obj.rotation_euler=self.cam_mat.to_euler()
            bpy.context.scene.collection.objects.link(self.cam_obj)
            bpy.context.scene.camera=self.cam_obj
        else:
            self.cam_obj=bpy.data.objects['Pivot_cam']
            
            
        for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                # Get the region of the 3D Viewport
                area.spaces[0].region_3d.view_perspective='CAMERA'
                break
        
    def destroy_custom_navigation(self):
        if "Pivot_cam" in bpy.data.objects:
            bpy.data.objects.remove(bpy.data.objects['Pivot_cam'])
            
        for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                # Get the region of the 3D Viewport
                area.spaces[0].region_3d.view_perspective='PERSP'
                break
        
    
    def modal(self, context, event):
        rot_state=self.rot_state
        cam_mat=self.cam_mat
        mpos=Vector((event.mouse_x, event.mouse_y))
        mdist=mpos-self.ini_mpos
        
        if event.type in {'ESC'}:
            self.cancel(context)
            return {'CANCELLED'}
        
        if event.type == 'MOUSEMOVE' and self.mmb_pressed:
            ## code for panning the viewport
            #print("mouse moved", mdist)
            rot_state+=mdist
            angle_x=Matrix.Rotation(rot_state[0]/500,3,'Y')
            angle_y=Matrix.Rotation(-rot_state[1]/500,3,'X')
            self.cam_obj.rotation_euler=(cam_mat @ angle_x @ angle_y).to_euler()
            self.ini_mpos = mpos
            return {'RUNNING_MODAL'}
        
        if event.type in {'MIDDLEMOUSE'}and not event.ctrl:
            #print("mmb", event.value)
            if event.value == "PRESS":
                self.mmb_pressed = True
                self.ini_mpos= mpos
            else:
                self.mmb_pressed = False
            return {'RUNNING_MODAL'}

        if event.type in {'ESC'}:
            return {'FINISHED'}
        
        return {'PASS_THROUGH'}
    
    def invoke(self, context, event):
        print("INVOKE")
        self.init_custom_navigation()
        self.execute(context)
        return {'RUNNING_MODAL'}

    def execute(self, context):
        wm = context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        self.destroy_custom_navigation()
        return {'FINISHED'}

def register():
    bpy.utils.register_class(CustomViewportControls)

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

if __name__ == "__main__":
    register()
    bpy.ops.wm.custom_viewport_controls('INVOKE_DEFAULT')
added 2399 characters in body
Source Link

Edit_no2Edit. (repeating from my blenderartists post)

ChatUpdate! I’ve derived a rudimentary way of acheiving the kind of controls i need, thank—in small part—to chat gpt did not spitand stack exchange. It turned out anything immediately usable, butthat using a modal operator was all it led metook to override the standard blender controls. Though I couldn’t find a way to easily manipulate the viewport rotation and focal length. instead I create a temporary camera and set it as the default scene camera and automatically switch the first viewport in the correct directioncurrent area to camera view. It appearsThough you can use it in any area by switching to camera view in that area. I need to use a modal operatormake it so that listens forthe mmb clicksis only overridden when the mouse is in a View3d region, and passes through everything else unless the mmbrotation speed of the camera should be slower when the viewport is being heldzoomed in and faster when its zoomed out, but this is a pretty decent proof of concept for now.

so far Right now the code looks like this:script must be run manually and pressing ESC deletes the temporary camera and restores the default controls.

import bpy
from mathutils import *


class CustomViewportControls(bpy.types.Operator):
    """overrides blenders standard viewport controls"""
    bl_idname = "wm.custom_viewport_controls"
    bl_label = "Custom Viewport Controls"
    
    mmb_pressed = False
    cam = None
    defcam_obj modal= None
    cam_mat = Matrix.Rotation(self3.1415926/2,3,'X')
 context   ini_mpos = Vector((0.0,0.0))
    rot_state= Vector((0.0,0.0))


 event   def init_custom_navigation(self):
        if eventnot "Pivot_cam" in bpy.typedata.cameras:
 == 'MOUSEMOVE' and             self.mmb_pressedcam=bpy.data.cameras.new("Pivot_cam")
                self.cam.lens=10
        else:
            ##self.cam=bpy.data.cameras['Pivot_cam']
 code for panning the viewport   if not "Pivot_cam" in bpy.data.objects:  
            printself.cam_obj=bpy.data.objects.new("mouse"Pivot_cam",self.cam)
 moved"           self.cam_obj.rotation_euler=self.cam_mat.to_euler()
            bpy.context.scene.collection.objects.link(self.cam_obj)
            bpy.context.scene.camera=self.cam_obj
        #else:
 need to add a conditional to determin if the mouse is beingself.cam_obj=bpy.data.objects['Pivot_cam']
 used in a View3D or not      
            
        for area in bpy.context.screen.areas:
            if eventarea.type in== {'WHEELUPMOUSE','WHEELDOWNMOUSE','MIDDLEMOUSE'}'VIEW_3D':
                # Get the region of the 3D Viewport
            if event   area.typespaces[0].region_3d.view_perspective='CAMERA'
 in {'WHEELUPMOUSE'}              break
        
    def destroy_custom_navigation(self):
        if "Pivot_cam" in bpy.data.objects:
     print("Zoom in"      bpy.data.objects.remove(bpy.data.objects['Pivot_cam'])
             
        for area in bpy.context.screen.areas:
            if eventarea.type in== {'WHEELDOWNMOUSE'}'VIEW_3D':
                print("Zoom# out")Get the region of the 3D Viewport
            if event   area.typespaces[0].region_3d.view_perspective='PERSP'
 in {'MIDDLEMOUSE'}              break
        
    
    def modal(self, context, event):
        rot_state=self.rot_state
        printcam_mat=self.cam_mat
        mpos=Vector("mmb"(event.mouse_x, event.valuemouse_y))
        mdist=mpos-self.ini_mpos
        
        if event.valuetype ==in "PRESS"{'ESC'}:
            self.cancel(context)
            return {'CANCELLED'}
        
        if event.type == 'MOUSEMOVE' and self.mmb_pressed:
 = True
          ## code for panning the viewport
 else:           #print("mouse moved", mdist)
            rot_state+=mdist
        self.mmb_pressed = False  angle_x=Matrix.Rotation(rot_state[0]/500,3,'Y')
            angle_y=Matrix.Rotation(-rot_state[1]/500,3,'X')
            self.cam_obj.rotation_euler=(cam_mat @ angle_x @ angle_y).to_euler()
        else:    self.ini_mpos = mpos
            return {'PASS_THROUGH''RUNNING_MODAL'}
        
#        if event.type in {'ESC''MIDDLEMOUSE'}and not event.ctrl:
#            self.cancel#print(context"mmb", event.value)
#            if event.value == "PRESS":
                self.mmb_pressed = True
                self.ini_mpos= mpos
            else:
                self.mmb_pressed = False
            return {'CANCELLED''RUNNING_MODAL'} 

        if event.type in {'ESC'}:
            return {'RUNNING_MODAL''FINISHED'}
        
        return {'PASS_THROUGH'}
    
    def invoke(self, context, event):
        print("INVOKE")
        self.init_custom_navigation()
        self.execute(context)
        return {'RUNNING_MODAL'}

    def execute(self, context):
        wm = context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        # should probably set up a way to turn the custom controlls on and offself.destroy_custom_navigation()
        passreturn {'FINISHED'}

def register():
    bpy.utils.register_class(CustomViewportControls)

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

if __name__ == "__main__":
    register()
    bpy.ops.wm.custom_viewport_controls('INVOKE_DEFAULT')

Edit_no2.

Chat gpt did not spit out anything immediately usable, but it led me in the correct direction. It appears that I need to use a modal operator that listens for mmb clicks and passes through everything else unless the mmb is being held.

so far the code looks like this:

import bpy

class CustomViewportControls(bpy.types.Operator):
    """overrides blenders standard viewport controls"""
    bl_idname = "wm.custom_viewport_controls"
    bl_label = "Custom Viewport Controls"
    
    mmb_pressed = False
    
    def modal(self, context, event):
        if event.type == 'MOUSEMOVE' and self.mmb_pressed:
            ## code for panning the viewport
            print("mouse moved")
        
        # need to add a conditional to determin if the mouse is being used in a View3D or not 
        if event.type in {'WHEELUPMOUSE','WHEELDOWNMOUSE','MIDDLEMOUSE'}:
            
            if event.type in {'WHEELUPMOUSE'}:
                print("Zoom in")
            if event.type in {'WHEELDOWNMOUSE'}:
                print("Zoom out")  
            if event.type in {'MIDDLEMOUSE'}:
                print("mmb", event.value)
                if event.value == "PRESS":
                    self.mmb_pressed = True
                else:
                    self.mmb_pressed = False
                    
        else:
            return {'PASS_THROUGH'}

#        if event.type in {'ESC'}:
#            self.cancel(context)
#            return {'CANCELLED'}
        
        return {'RUNNING_MODAL'}
    
    def invoke(self, context, event):
        print("INVOKE")
        self.execute(context)
        return {'RUNNING_MODAL'}

    def execute(self, context):
        wm = context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        # should probably set up a way to turn the custom controlls on and off
        pass

def register():
    bpy.utils.register_class(CustomViewportControls)

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

if __name__ == "__main__":
    register()
    bpy.ops.wm.custom_viewport_controls('INVOKE_DEFAULT')

Edit. (repeating from my blenderartists post)

Update! I’ve derived a rudimentary way of acheiving the kind of controls i need, thank—in small part—to chat gpt and stack exchange. It turned out that using a modal operator was all it took to override the standard blender controls. Though I couldn’t find a way to easily manipulate the viewport rotation and focal length. instead I create a temporary camera and set it as the default scene camera and automatically switch the first viewport in the current area to camera view. Though you can use it in any area by switching to camera view in that area. I need to make it so that the mmb is only overridden when the mouse is in a View3d region, and the rotation speed of the camera should be slower when the viewport is zoomed in and faster when its zoomed out, but this is a pretty decent proof of concept for now. Right now the script must be run manually and pressing ESC deletes the temporary camera and restores the default controls.

import bpy
from mathutils import *


class CustomViewportControls(bpy.types.Operator):
    """overrides blenders standard viewport controls"""
    bl_idname = "wm.custom_viewport_controls"
    bl_label = "Custom Viewport Controls"
    
    mmb_pressed = False
    cam = None
    cam_obj = None
    cam_mat = Matrix.Rotation(3.1415926/2,3,'X')
    ini_mpos = Vector((0.0,0.0))
    rot_state= Vector((0.0,0.0))


    def init_custom_navigation(self):
        if not "Pivot_cam" in bpy.data.cameras:
                self.cam=bpy.data.cameras.new("Pivot_cam")
                self.cam.lens=10
        else:
            self.cam=bpy.data.cameras['Pivot_cam']
        if not "Pivot_cam" in bpy.data.objects:  
            self.cam_obj=bpy.data.objects.new("Pivot_cam",self.cam)
            self.cam_obj.rotation_euler=self.cam_mat.to_euler()
            bpy.context.scene.collection.objects.link(self.cam_obj)
            bpy.context.scene.camera=self.cam_obj
        else:
            self.cam_obj=bpy.data.objects['Pivot_cam']
            
            
        for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                # Get the region of the 3D Viewport
                area.spaces[0].region_3d.view_perspective='CAMERA'
                break
        
    def destroy_custom_navigation(self):
        if "Pivot_cam" in bpy.data.objects:
            bpy.data.objects.remove(bpy.data.objects['Pivot_cam'])
             
        for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                # Get the region of the 3D Viewport
                area.spaces[0].region_3d.view_perspective='PERSP'
                break
        
    
    def modal(self, context, event):
        rot_state=self.rot_state
        cam_mat=self.cam_mat
        mpos=Vector((event.mouse_x, event.mouse_y))
        mdist=mpos-self.ini_mpos
        
        if event.type in {'ESC'}:
            self.cancel(context)
            return {'CANCELLED'}
        
        if event.type == 'MOUSEMOVE' and self.mmb_pressed:
            ## code for panning the viewport
            #print("mouse moved", mdist)
            rot_state+=mdist
            angle_x=Matrix.Rotation(rot_state[0]/500,3,'Y')
            angle_y=Matrix.Rotation(-rot_state[1]/500,3,'X')
            self.cam_obj.rotation_euler=(cam_mat @ angle_x @ angle_y).to_euler()
            self.ini_mpos = mpos
            return {'RUNNING_MODAL'}
        
        if event.type in {'MIDDLEMOUSE'}and not event.ctrl:
            #print("mmb", event.value)
            if event.value == "PRESS":
                self.mmb_pressed = True
                self.ini_mpos= mpos
            else:
                self.mmb_pressed = False
            return {'RUNNING_MODAL'} 

        if event.type in {'ESC'}:
            return {'FINISHED'}
        
        return {'PASS_THROUGH'}
    
    def invoke(self, context, event):
        print("INVOKE")
        self.init_custom_navigation()
        self.execute(context)
        return {'RUNNING_MODAL'}

    def execute(self, context):
        wm = context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        self.destroy_custom_navigation()
        return {'FINISHED'}

def register():
    bpy.utils.register_class(CustomViewportControls)

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

if __name__ == "__main__":
    register()
    bpy.ops.wm.custom_viewport_controls('INVOKE_DEFAULT')
added 2236 characters in body
Source Link

enter image description hereit can be done its just annoying

Edit_no2.

Chat gpt did not spit out anything immediately usable, but it led me in the correct direction. It appears that I need to use a modal operator that listens for mmb clicks and passes through everything else unless the mmb is being held.

so far the code looks like this:

import bpy

class CustomViewportControls(bpy.types.Operator):
    """overrides blenders standard viewport controls"""
    bl_idname = "wm.custom_viewport_controls"
    bl_label = "Custom Viewport Controls"
    
    mmb_pressed = False
    
    def modal(self, context, event):
        if event.type == 'MOUSEMOVE' and self.mmb_pressed:
            ## code for panning the viewport
            print("mouse moved")
        
        # need to add a conditional to determin if the mouse is being used in a View3D or not 
        if event.type in {'WHEELUPMOUSE','WHEELDOWNMOUSE','MIDDLEMOUSE'}:
            
            if event.type in {'WHEELUPMOUSE'}:
                print("Zoom in")
            if event.type in {'WHEELDOWNMOUSE'}:
                print("Zoom out")  
            if event.type in {'MIDDLEMOUSE'}:
                print("mmb", event.value)
                if event.value == "PRESS":
                    self.mmb_pressed = True
                else:
                    self.mmb_pressed = False
                    
        else:
            return {'PASS_THROUGH'}

#        if event.type in {'ESC'}:
#            self.cancel(context)
#            return {'CANCELLED'}
        
        return {'RUNNING_MODAL'}
    
    def invoke(self, context, event):
        print("INVOKE")
        self.execute(context)
        return {'RUNNING_MODAL'}

    def execute(self, context):
        wm = context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        # should probably set up a way to turn the custom controlls on and off
        pass

def register():
    bpy.utils.register_class(CustomViewportControls)

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

if __name__ == "__main__":
    register()
    bpy.ops.wm.custom_viewport_controls('INVOKE_DEFAULT')

enter image description here

it can be done its just annoying

Edit_no2.

Chat gpt did not spit out anything immediately usable, but it led me in the correct direction. It appears that I need to use a modal operator that listens for mmb clicks and passes through everything else unless the mmb is being held.

so far the code looks like this:

import bpy

class CustomViewportControls(bpy.types.Operator):
    """overrides blenders standard viewport controls"""
    bl_idname = "wm.custom_viewport_controls"
    bl_label = "Custom Viewport Controls"
    
    mmb_pressed = False
    
    def modal(self, context, event):
        if event.type == 'MOUSEMOVE' and self.mmb_pressed:
            ## code for panning the viewport
            print("mouse moved")
        
        # need to add a conditional to determin if the mouse is being used in a View3D or not 
        if event.type in {'WHEELUPMOUSE','WHEELDOWNMOUSE','MIDDLEMOUSE'}:
            
            if event.type in {'WHEELUPMOUSE'}:
                print("Zoom in")
            if event.type in {'WHEELDOWNMOUSE'}:
                print("Zoom out")  
            if event.type in {'MIDDLEMOUSE'}:
                print("mmb", event.value)
                if event.value == "PRESS":
                    self.mmb_pressed = True
                else:
                    self.mmb_pressed = False
                    
        else:
            return {'PASS_THROUGH'}

#        if event.type in {'ESC'}:
#            self.cancel(context)
#            return {'CANCELLED'}
        
        return {'RUNNING_MODAL'}
    
    def invoke(self, context, event):
        print("INVOKE")
        self.execute(context)
        return {'RUNNING_MODAL'}

    def execute(self, context):
        wm = context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        # should probably set up a way to turn the custom controlls on and off
        pass

def register():
    bpy.utils.register_class(CustomViewportControls)

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

if __name__ == "__main__":
    register()
    bpy.ops.wm.custom_viewport_controls('INVOKE_DEFAULT')
added 155 characters in body
Source Link
Loading
Source Link
Loading