gp_toolbox/OP_depth_move.py

120 lines
4.4 KiB
Python

import bpy
from mathutils import Vector
class ODM_OT_depth_move(bpy.types.Operator):
bl_idname = "object.depth_proportional_move"
bl_label = "Depth move"
bl_description = "Move object in the depth from camera POV while retaining same size in framing"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.object and context.object.type != 'CAMERA' # and context.scene.camera
def invoke(self, context, event):
self.init_mouse_x = event.mouse_x
self.cam = bpy.context.scene.camera
if not self.cam:
self.report({'ERROR'}, 'No active camera')
return {"CANCELLED"}
self.cam_pos = self.cam.matrix_world.translation
self.mode = 'distance'
self.objects = [o for o in context.selected_objects if o.type != 'CAMERA']
self.init_mats = [o.matrix_world.copy() for o in self.objects]
if self.cam.data.type == 'ORTHO':
context.area.header_text_set(f'Move factor: 0.00')
# distance is view vector based
self.view_vector = Vector((0,0,-1))
self.view_vector.rotate(self.cam.matrix_world)
else:
self.init_vecs = [o.matrix_world.translation - self.cam_pos for o in self.objects]
self.init_dists = [v.length for v in self.init_vecs]
context.area.header_text_set(f'Move factor: 0.00 | Mode: {self.mode} (M to switch)')
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def modal(self, context, event):
if self.mode == 'distance':
factor = 0.1
if event.shift:
factor = 0.01
else:
# Smaller factor for proportional dist
factor = 0.01
if event.shift:
factor = 0.001
if event.type in {'MOUSEMOVE'}:
diff = (event.mouse_x - self.init_mouse_x) * factor
if self.cam.data.type == 'ORTHO':
# just push in view vector direction
context.area.header_text_set(f'Move factor: {diff:.2f}')
for i, obj in enumerate(self.objects):
new_vec = self.init_mats[i].translation + (self.view_vector * diff)
obj.matrix_world.translation = new_vec
else:
# Push from camera point and scale accordingly
context.area.header_text_set(f'Move factor: {diff:.2f} | Mode: {self.mode} (M to switch)')
for i, obj in enumerate(self.objects):
if self.mode == 'distance':
## move with the same length for everyone
new_vec = self.init_vecs[i] + (self.init_vecs[i].normalized() * diff)
else:
## move with proportional factor from individual distance vector to camera
new_vec = self.init_vecs[i] + (self.init_vecs[i] * diff)
obj.matrix_world.translation = self.cam_pos + new_vec
dist_percentage = new_vec.length / self.init_dists[i]
obj.scale = self.init_mats[i].to_scale() * dist_percentage
if event.type in {'M'} and event.value == 'PRESS':
# Switch mode
self.mode = 'distance' if self.mode == 'proportional' else 'proportional'
if event.type in {'LEFTMOUSE'} and event.value == 'PRESS':
context.area.header_text_set(None)
return {"FINISHED"}
if event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
for i, obj in enumerate(self.objects):
obj.matrix_world = self.init_mats[i]
context.area.header_text_set(None)
return {"CANCELLED"}
return {"RUNNING_MODAL"}
""" # Own standalone panel
class ODM_PT_sudden_depth_panel(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Gpencil"
bl_label = "Depth move"
def draw(self, context):
layout = self.layout
row = layout.row()
row.operator('object.depth_proportional_move', text='Depth move', icon='TRANSFORM_ORIGINS')
"""
### --- REGISTER ---
classes=(
ODM_OT_depth_move,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)