822 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			822 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import bpy
 | |
| from bpy.props import EnumProperty, IntProperty, BoolProperty, StringProperty
 | |
| from bpy.types import Operator, Menu
 | |
| from ..constants import PICKERS, SHADERS
 | |
| from ..core.addon_utils import get_picker_collection
 | |
| from ..core.bl_utils import get_view_3d_override, split_path, eval_attr
 | |
| from ..core.geometry_utils import bounding_rect
 | |
| from ..core.picker import get_picker_path, pack_picker, unpack_picker, load_picker_data
 | |
| #from .func_bgl import draw_callback_px
 | |
| #from .func_bgl import select_bone
 | |
| #from core.picker import *
 | |
| #from .utils import is_over_region
 | |
| import gpu
 | |
| from mathutils import Vector, Euler, Matrix
 | |
| from gpu_extras.batch import batch_for_shader
 | |
| from pathlib import Path
 | |
| import json
 | |
| import os
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| def is_picker_space(space_data=None):
 | |
|     if not space_data:
 | |
|         space_data = bpy.context.space_data
 | |
|     if space_data and (space_data.type == 'NODE_EDITOR' and space_data.tree_type == 'RigPickerTree'):
 | |
|         return True
 | |
|     
 | |
|     return False
 | |
| 
 | |
| 
 | |
| class RP_OT_box_select(Operator):
 | |
|     """Box Select bones in the picker view"""
 | |
|     bl_idname = "node.rp_box_select"
 | |
|     bl_label = "Picker Box Select"
 | |
| 
 | |
|     mode: EnumProperty(items=[(i, i.title(), '') for i in ('SET', 'EXTEND', 'SUBSTRACT')])
 | |
| 
 | |
|     @classmethod
 | |
|     def poll(cls, context):
 | |
|         if not is_picker_space(context.space_data):
 | |
|             return
 | |
|             
 | |
|         ob = context.object
 | |
|         return ob and ob in PICKERS
 | |
| 
 | |
|     '''
 | |
|     def mode_from_event(self, event):
 | |
|         if event.alt:
 | |
|             return 'SUBSTRACT'
 | |
|         elif event.ctrl or event.shift:
 | |
|             return 'EXTEND'
 | |
|         else:
 | |
|             return 'SET'
 | |
|     '''
 | |
| 
 | |
|     def draw_callback(self):
 | |
|         #print('draw callback border')
 | |
|         if not self.draw_border:
 | |
|             return
 | |
| 
 | |
|         gpu.state.blend_set('ALPHA')
 | |
| 
 | |
|         #print('DRAW BORDER')
 | |
|         
 | |
|         self.color_shader.bind()
 | |
|         self.color_shader.uniform_float("color", self.bg_color)
 | |
|         self.bg_batch.draw(self.color_shader)
 | |
| 
 | |
|         self.dash_shader.bind()
 | |
|         matrix = gpu.matrix.get_projection_matrix()
 | |
| 
 | |
|         self.dash_shader.uniform_float("color", self.border_color)
 | |
|         self.dash_shader.uniform_float("viewMatrix", matrix)
 | |
|         self.dash_shader.uniform_float("dashSize", 5)
 | |
|         self.dash_shader.uniform_float("gapSize", 4)
 | |
|         
 | |
|         self.contour_batch.draw(self.dash_shader)
 | |
| 
 | |
|         gpu.state.blend_set('NONE')
 | |
| 
 | |
|     def invoke(self, context, event):
 | |
|         #print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
 | |
| 
 | |
|         if context.object.mode != 'POSE':
 | |
|             bpy.ops.object.posemode_toggle()
 | |
| 
 | |
|         self.timer = None
 | |
|         #self.mode = self.mode_from_event(event)
 | |
|         #self.invoke_event = event.copy()
 | |
|         self.region = context.region
 | |
|         self.draw_border = False
 | |
| 
 | |
|         self.picker = PICKERS[context.object]
 | |
| 
 | |
|         self.start_mouse = event.mouse_region_x, event.mouse_region_y
 | |
|         #self.shader = line_strip_shader
 | |
|         self.border_color = [1, 1, 1, 1]
 | |
|         self.bg_color = [1, 1, 1, 0.05]
 | |
| 
 | |
|         #args = (self, context)
 | |
|         self.color_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
 | |
|         self.dash_shader = SHADERS['dashed_line']
 | |
|         self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
 | |
|         
 | |
|         context.window_manager.modal_handler_add(self)
 | |
| 
 | |
|         self.picker.press_event(self.mode)
 | |
|         self.region.tag_redraw()
 | |
| 
 | |
|         return {'RUNNING_MODAL'}
 | |
|     
 | |
|     def modal(self, context, event):
 | |
| 
 | |
|         self.mouse = event.mouse_region_x, event.mouse_region_y
 | |
| 
 | |
|         self.border = bounding_rect((self.start_mouse, self.mouse))
 | |
| 
 | |
|         self.bg_batch = batch_for_shader(self.color_shader, 'TRI_FAN', {"pos": self.border})
 | |
|         self.contour_batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": self.border})
 | |
| 
 | |
|         self.draw_border = True
 | |
| 
 | |
|         self.region.tag_redraw()
 | |
| 
 | |
|         if event.value == 'RELEASE':
 | |
|             return self.release_event(context)
 | |
|                 
 | |
|         return {'RUNNING_MODAL'}
 | |
| 
 | |
|     def release_event(self, context):
 | |
| 
 | |
|         if get_picker_collection():
 | |
|             self.picker.assign_bone_event()
 | |
| 
 | |
|         elif (self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1]):
 | |
|             self.picker.border_select(self.border, self.mode)
 | |
|         else:
 | |
|             self.picker.move_event(self.mouse)
 | |
|             self.picker.release_event(self.mode)
 | |
|         
 | |
|         bpy.ops.ed.undo_push(message="Box Select")
 | |
|         
 | |
|         return self.exit(context)
 | |
| 
 | |
|     def exit(self, context):
 | |
|         bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
 | |
|         context.region.tag_redraw()
 | |
|         return {'FINISHED'}
 | |
| 
 | |
| 
 | |
| class RP_OT_picker_transform(Operator):
 | |
|     """Transform Bones in the picker view"""
 | |
|     bl_idname = "node.picker_transform"
 | |
|     bl_label = "Transform Bone in Picker View"
 | |
|     
 | |
|     mode : EnumProperty(items=[(m, m.title(), '') for m in ('ROTATE', 'SCALE')])
 | |
| 
 | |
|     @classmethod
 | |
|     def poll(cls, context):
 | |
|         return context.selected_pose_bones and is_picker_space(context.space_data)
 | |
| 
 | |
|     '''
 | |
|     def draw_callback(self):
 | |
|         gpu.state.blend_set('ALPHA')
 | |
|         gpu.state.line_width_set(2)
 | |
| 
 | |
|         self.dash_shader.bind()
 | |
|         matrix = gpu.matrix.get_projection_matrix()
 | |
| 
 | |
|         self.dash_shader.uniform_float("color", [0, 0, 0, 1])
 | |
|         self.dash_shader.uniform_float("viewMatrix", matrix)
 | |
|         self.dash_shader.uniform_float("dashSize", 5)
 | |
|         self.dash_shader.uniform_float("gapSize", 4)
 | |
| 
 | |
|         self.batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": [self.view_center, self.mouse]})
 | |
|         self.batch.draw(self.dash_shader)
 | |
| 
 | |
|         gpu.state.line_width_set(1)
 | |
|         gpu.state.blend_set('NONE')
 | |
|         '''
 | |
| 
 | |
|     def invoke(self, context, event):
 | |
|         self.override = get_view_3d_override()
 | |
| 
 | |
|         self.view_center = Vector((int(context.region.width / 2), int(context.region.height / 2)))
 | |
|         self.mouse_start = Vector((event.mouse_region_x, event.mouse_region_y))
 | |
|         self.mouse = Vector((0, 0))
 | |
| 
 | |
|         transform_type = context.scene.tool_settings.transform_pivot_point
 | |
|         self.center = context.active_pose_bone.matrix.to_translation()
 | |
|         if transform_type == 'MEDIAN_POINT':
 | |
|             origins = [b.matrix.to_translation() for b in context.selected_pose_bones]
 | |
|             self.center = sum(origins, Vector()) / len(context.selected_pose_bones)
 | |
|         # self.bone_data = {}
 | |
|         # for bone in context.selected_pose_bones:
 | |
|         #     self.bone_data[bone] = bone.matrix.copy()
 | |
|         self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}
 | |
| 
 | |
|         space = self.override['area'].spaces.active
 | |
| 
 | |
|         view_matrix = space.region_3d.view_matrix
 | |
|         if space.region_3d.view_perspective == 'CAMERA':
 | |
|             view_matrix = context.scene.camera.matrix_world
 | |
| 
 | |
|         context.window.cursor_modal_set('MOVE_X')
 | |
|         self.view_vector = Vector((0, 0, 1))
 | |
|         if self.mode == 'ROTATE':
 | |
|             self.view_vector.rotate(view_matrix)
 | |
|             
 | |
| 
 | |
|         elif self.mode == 'SCALE':
 | |
|             self.view_vector = Vector((0, 0, 0))
 | |
|         
 | |
|         self.transform_type = "VIEW"
 | |
|         self.transform_types = ["VIEW", 'GLOBAL', 'LOCAL']
 | |
|         if context.scene.transform_orientation_slots[0].type == 'LOCAL':
 | |
|             self.transform_types = ["VIEW", 'LOCAL', 'GLOBAL']
 | |
| 
 | |
|         self.transform_orientation = context.scene.transform_orientation_slots[0].type
 | |
| 
 | |
|         #self.dash_shader = SHADERS['dashed_line']
 | |
|         
 | |
| 
 | |
|         #self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
 | |
|         context.window_manager.modal_handler_add(self)
 | |
| 
 | |
|         return {'RUNNING_MODAL'}
 | |
| 
 | |
|     def change_transform_type(self):
 | |
|         indices = {0: 1, 1:2, 2:1}
 | |
|         index = self.transform_types.index(self.transform_type)
 | |
|         new_index = indices[index]
 | |
|         self.transform_type = self.transform_types[new_index]
 | |
| 
 | |
|     def release_event(self, context):
 | |
|         scn = bpy.context.scene
 | |
| 
 | |
|         # Insert keyframe        
 | |
|         #bpy.ops.ed.undo_push(message="Transform")
 | |
|         if scn.tool_settings.use_keyframe_insert_auto:
 | |
|             try:
 | |
|                 bpy.ops.animtoolbox.insert_keyframe()
 | |
|             except Exception:
 | |
|                 self.report({"WARNING"}, 'You need animtoolbox addon for inserting keyframe')
 | |
| 
 | |
|         self.exit(context)
 | |
| 
 | |
|     def exit(self, context):
 | |
|         #bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
 | |
|         context.window.cursor_modal_restore()
 | |
|         #context.region.tag_redraw()
 | |
|         
 | |
|     def modal(self, context, event):
 | |
|         scn = context.scene
 | |
|         self.mouse = Vector((event.mouse_region_x, event.mouse_region_y))
 | |
| 
 | |
|         if self.mode == 'ROTATE':
 | |
|             delta = (event.mouse_region_x - self.mouse_start[0]) / 100
 | |
|         elif self.mode == 'SCALE':
 | |
|             delta = 1 + (event.mouse_region_x - self.mouse_start[0]) / 100 
 | |
|             #delta = (self.mouse - self.view_center).length / (self.mouse_start - self.view_center).length
 | |
| 
 | |
| 
 | |
|         transform_type = self.transform_type
 | |
|         #self.batch = batch_for_shader(self.dash_shader, 'LINE_STRIP', {"pos": [self.mouse_start, self.mouse]})
 | |
|         #context.area.tag_redraw()
 | |
|         
 | |
|         #print(event.type, event.value)
 | |
|         if self.mode == 'ROTATE' and event.type == "R" and event.value == 'PRESS':
 | |
|             with context.temp_override(**self.override):
 | |
|                 self.exit(context)
 | |
|                 bpy.ops.transform.trackball('INVOKE_DEFAULT')
 | |
|             return {'FINISHED'}
 | |
| 
 | |
|         if event.type == "LEFTMOUSE" and event.value == 'RELEASE':
 | |
|             self.release_event(context)
 | |
|             return {'FINISHED'}
 | |
| 
 | |
|         if event.type in {"RIGHTMOUSE", "ESC"}:
 | |
|             for bone, matrix in self.bone_data.items():
 | |
|                 bone.matrix = matrix
 | |
|             self.exit(context)
 | |
|             return {'CANCELLED'}
 | |
| 
 | |
|         elif event.type == "X" and event.value == 'PRESS':
 | |
|             self.change_transform_type()
 | |
|             self.view_vector = Vector((1, 0, 0))
 | |
|             #transform_type = self.transform_orientation
 | |
| 
 | |
|         elif event.type == "Y" and event.value == 'PRESS':
 | |
|             self.change_transform_type()
 | |
|             self.view_vector = Vector((0, 1, 0))
 | |
|             #transform_type = self.transform_orientation
 | |
| 
 | |
|         elif event.type == "Z" and event.value == 'PRESS':
 | |
|             self.change_transform_type()
 | |
|             self.view_vector = Vector((0, 0, 1))
 | |
|             #transform_type = self.transform_orientation
 | |
| 
 | |
|         elif event.type == "MOUSEMOVE":
 | |
|             
 | |
|             if self.mode == 'ROTATE':
 | |
|                 transform_matrix = Matrix.Rotation(delta, 4, self.view_vector)
 | |
|             elif self.mode == 'SCALE':
 | |
|                 if self.view_vector.length:
 | |
|                     transform_matrix = Matrix.Scale(delta, 4, self.view_vector)
 | |
|                 else:
 | |
|                     transform_matrix = Matrix.Scale(delta, 4)
 | |
| 
 | |
|             for bone, matrix in self.bone_data.items():
 | |
|                 center = self.center
 | |
|                 if scn.tool_settings.transform_pivot_point == "INDIVIDUAL_ORIGINS":
 | |
|                     center = matrix.to_translation()
 | |
| 
 | |
|                 if self.mode == 'ROTATE':
 | |
|                     if transform_type == 'LOCAL':
 | |
|                         view_vector = self.view_vector.copy()
 | |
|                         view_vector.rotate(matrix)
 | |
|                         transform_matrix = Matrix.Rotation(delta, 4, view_vector)
 | |
| 
 | |
|                 elif self.mode == 'SCALE':                    
 | |
|                     if transform_type == 'LOCAL':
 | |
|                         view_vector = self.view_vector.copy()
 | |
|                         view_vector.rotate(matrix)
 | |
|                         transform_matrix = Matrix.Scale(delta, 4, view_vector)
 | |
| 
 | |
|                 mat = matrix.copy()
 | |
|                 mat.translation -= center
 | |
| 
 | |
|                 mat = transform_matrix @ mat
 | |
| 
 | |
|                 mat.translation += center
 | |
| 
 | |
|                 bone.matrix = mat
 | |
| 
 | |
|         return {'RUNNING_MODAL'}
 | |
| 
 | |
| 
 | |
| class RP_OT_toogle_property(Operator):
 | |
|     """Invert a bone custom property"""
 | |
| 
 | |
|     bl_idname = "rigpicker.toogle_property"
 | |
|     bl_label = 'Toogle Property'
 | |
| 
 | |
|     data_path : StringProperty()
 | |
| 
 | |
|     @classmethod
 | |
|     def poll(cls, context):
 | |
|         return is_picker_space(context.space_data)
 | |
| 
 | |
|     def execute(self,context):
 | |
|         ob = context.object
 | |
|         
 | |
|         try:
 | |
|             value = ob.path_resolve(self.data_path)
 | |
|         except Exception:
 | |
|             return {'CANCELLED'}
 | |
| 
 | |
|         data = ob
 | |
|         prop_name = self.data_path
 | |
|         #if '.' in self.data_path:
 | |
|         #    data, prop = prop_name.rsplit('.', 1)
 | |
|         
 | |
|         value = type(value)(not value)
 | |
|         exec(f'{repr(ob)}.{self.data_path} = {value}')
 | |
| 
 | |
|         #setattr(data, prop_name, value)
 | |
|         bpy.ops.object.mode_set(mode='OBJECT')
 | |
|         bpy.ops.object.mode_set(mode='POSE')
 | |
| 
 | |
|         if context.scene.tool_settings.use_keyframe_insert_auto:
 | |
|             bone_name, _ = split_path(self.data_path)
 | |
|             ob.keyframe_insert(data_path=self.data_path, group=bone_name)
 | |
| 
 | |
|         context.area.tag_redraw()
 | |
| 
 | |
|         return {'FINISHED'}
 | |
| 
 | |
| 
 | |
| class RP_OT_reload_picker(Operator):
 | |
|     """Reload the picker shapes"""
 | |
| 
 | |
|     bl_idname = "rigpicker.reload_picker"
 | |
|     bl_label = 'Reload Picker'
 | |
| 
 | |
|     #@classmethod
 | |
|     #def poll(cls, context):
 | |
|     #    if not is_picker_space(context):
 | |
|     #        return
 | |
| 
 | |
|     def execute(self, context):
 | |
|         if context.object.type == 'ARMATURE':
 | |
|             rig = context.object
 | |
|         else:
 | |
|             collection = get_picker_collection(context.object)
 | |
|             rig = collection.rig_picker.rig
 | |
| 
 | |
|         #print('Reload', rig)
 | |
|         load_picker_data(rig)
 | |
| 
 | |
|         for area in context.screen.areas:
 | |
|             if is_picker_space(area.spaces.active):
 | |
|                 area.tag_redraw()
 | |
|  
 | |
| 
 | |
|         return {"FINISHED"}
 | |
| 
 | |
| 
 | |
| class RP_OT_toogle_bone_layer(Operator):
 | |
|     """Toogle bone layer visibility when double clicking on a bone"""
 | |
|     bl_idname = "rigpicker.toogle_bone_layer"
 | |
|     bl_label = 'Toogle Bone Layer'
 | |
| 
 | |
|     @classmethod
 | |
|     def poll(cls, context):
 | |
|         if not is_picker_space(context.space_data):
 | |
|             return
 | |
| 
 | |
|         ob = context.object
 | |
|         picker = PICKERS.get(ob)
 | |
| 
 | |
|         if picker.hover_shape and picker.hover_shape.type == 'bone':
 | |
|             return True
 | |
| 
 | |
|     def execute(self, context):
 | |
|         ob = context.object
 | |
|         picker = PICKERS.get(ob)
 | |
| 
 | |
|         bone = picker.hover_shape.bone
 | |
|         hide = picker.hover_shape.hide
 | |
| 
 | |
|         if bone:
 | |
|             for layer in bone.bone.collections:
 | |
|                 layer.is_visible = hide
 | |
| 
 | |
|         context.region.tag_redraw()
 | |
| 
 | |
|         return {"FINISHED"}
 | |
| 
 | |
| 
 | |
| class RP_OT_context_menu_picker(Operator):
 | |
|     """Display Menu with the custom properties of the hovered bone if any or the active bone"""
 | |
|     bl_idname = "node.context_menu_picker"
 | |
|     bl_label = 'Context Menu Picker'
 | |
| 
 | |
|     @classmethod
 | |
|     def poll(cls, context):
 | |
|         return is_picker_space(context.space_data)
 | |
| 
 | |
|     def execute(self, context):
 | |
|         bpy.ops.wm.call_menu(name='RP_MT_context_menu')
 | |
| 
 | |
|         return {"FINISHED"}
 | |
| 
 | |
| 
 | |
| class RP_OT_pack_picker(Operator):
 | |
|     """Pack Unpack the picker on the rig"""
 | |
|     bl_idname = "rigpicker.pack_picker"
 | |
|     bl_label = 'Pack Picker'
 | |
| 
 | |
|     @classmethod
 | |
|     def poll(cls, context):
 | |
|         ob = context.object
 | |
|         return (ob and ob.type == 'ARMATURE' and ob.data.rig_picker.sources)
 | |
| 
 | |
|     def execute(self, context):
 | |
|         print('Pack Pickers...')
 | |
|         
 | |
|         rig = context.object
 | |
| 
 | |
|         if 'pickers' in rig.data.rig_picker.keys():
 | |
|             unpack_picker(rig)
 | |
|             self.report({"INFO"}, f'The picker is unpacked')
 | |
|             return {"FINISHED"}
 | |
| 
 | |
|         pack_picker(rig)
 | |
| 
 | |
|         if not rig.data.rig_picker['pickers']:
 | |
|             self.report({"ERROR"}, f'No picker have been packed')
 | |
|             return {"CANCELLED"}    
 | |
| 
 | |
|         elif len(rig.data.rig_picker['pickers']) < len(rig.data.rig_picker.sources):
 | |
|             self.report({"WARNING"}, f'No all pickers have been packed')
 | |
|             return {"FINISHED"}    
 | |
| 
 | |
|         self.report({"INFO"}, f'The picker is packed')
 | |
| 
 | |
|         return {"FINISHED"}
 | |
| 
 | |
| 
 | |
| class RP_OT_call_operator(Operator):
 | |
|     bl_idname = "rigpicker.call_operator"
 | |
|     bl_label = 'Call operator'
 | |
| 
 | |
|     operator : StringProperty()
 | |
|     arguments : StringProperty()
 | |
|     invoke: BoolProperty()
 | |
|     view_3d : BoolProperty()
 | |
| 
 | |
|     @classmethod
 | |
|     def poll(cls, context):
 | |
|         return is_picker_space(context.space_data)
 | |
| 
 | |
|     @classmethod
 | |
|     def description(cls, context, properties):
 | |
|         try:
 | |
|             op = eval_attr(bpy.ops, properties.operator).get_rna_type()
 | |
|             return op.description
 | |
|         except AttributeError:
 | |
|             return 'Call operator'
 | |
| 
 | |
|     def execute(self, context):        
 | |
|         #context.area.tag_redraw()
 | |
| 
 | |
|         override = {}
 | |
|         if self.view_3d:
 | |
|             override = get_view_3d_override()
 | |
|         
 | |
|         arguments = json.loads(self.arguments or "{}")
 | |
| 
 | |
|         with context.temp_override(**override):
 | |
|             try:
 | |
|                 ops = eval_attr(bpy.ops, self.operator)
 | |
|                 if self.invoke:
 | |
|                     ops('INVOKE_DEFAULT', **arguments)
 | |
|                 else:
 | |
|                     ops(**arguments)
 | |
| 
 | |
|             except Exception as e:
 | |
|                 print(e)
 | |
|                 self.report({"ERROR"}, f'The operator {self.operator} cannot be called')
 | |
|                 return {"CANCELLED"}
 | |
|         
 | |
|         context.area.tag_redraw()
 | |
| 
 | |
|         return {"FINISHED"}
 | |
| 
 | |
| 
 | |
| class RP_MT_context_menu(Menu):
 | |
|     bl_label = "Context Menu"
 | |
| 
 | |
|     # Set the menu operators and draw functions
 | |
|     def draw(self, context):
 | |
|         layout = self.layout
 | |
|         col = layout.column()
 | |
|         col.operator_context = 'INVOKE_DEFAULT'
 | |
|         #col.use_property_split = True
 | |
| 
 | |
|         ob = context.object
 | |
|         picker = PICKERS.get(ob)
 | |
| 
 | |
|         if picker.hover_shape and picker.hover_shape.type == 'bone':
 | |
|             bone = picker.hover_shape.bone
 | |
|         else:
 | |
|             bone = context.active_pose_bone
 | |
| 
 | |
|         # Draw Space Switch Operator
 | |
|         if getattr(ob.data, 'space_switch'):
 | |
|             space_switch = ob.data.space_switch
 | |
| 
 | |
|             data_paths = [f'pose.bones["{bone.name}"]["{k}"]' for k in bone.keys()]
 | |
|             space_bone = next((s for s in space_switch.bones if s.data_path in data_paths), None)
 | |
|             if space_bone:
 | |
| 
 | |
|                 index = list(space_switch.bones).index(space_bone)
 | |
|                 value = ob.path_resolve(space_bone.data_path)
 | |
|                 space = next((s.name for s in space_bone.spaces if s.value == value), None)
 | |
| 
 | |
|                 op = col.operator("spaceswitch.change_space", text=f'({space})', icon='PINNED')
 | |
|                 op.index=index
 | |
|             col.separator()
 | |
| 
 | |
|         if bone:    
 | |
|             for key in bone.keys():
 | |
|                 col.prop(bone,f'["{key}"]', slider=True)
 | |
| 
 | |
|         #layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
 | |
|         #layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
 | |
| 
 | |
| 
 | |
| class RP_OT_add_picker_collection(Operator):
 | |
|     bl_idname = "rigpicker.add_picker_collection"
 | |
|     bl_label = "Add a Picker Collection"
 | |
|     bl_description = "Add a Picker Collection"
 | |
|     bl_options = {'UNDO', 'REGISTER'}
 | |
| 
 | |
|     @classmethod
 | |
|     def poll(cls, context):
 | |
|         ob = context.object
 | |
|         return (ob and ob.type == 'ARMATURE')
 | |
| 
 | |
|     def execute(self, context):
 | |
|         scn = context.scene
 | |
|         ob = context.object
 | |
| 
 | |
|         name = ob.name
 | |
|         if name.endswith('_rig'):
 | |
|             name = name[:-4]
 | |
| 
 | |
|         canvas_name = f'canevas_{name}'
 | |
|         canvas_points = [(-0.5, 0.5, 0), (0.5, 0.5, 0), (0.5, -0.5, 0), (-0.5, -0.5, 0)]
 | |
|         canvas_faces = [(0, 1, 2, 3)]
 | |
|         
 | |
|         canvas_mesh = bpy.data.meshes.new(canvas_name)
 | |
|         canvas_mesh.from_pydata(canvas_points, [], canvas_faces)
 | |
|         canvas_mesh.update(calc_edges=True)
 | |
| 
 | |
|         canvas_ob = bpy.data.objects.new(canvas_name, canvas_mesh)
 | |
|         canvas_ob.rig_picker.shape_type = 'DISPLAY'
 | |
| 
 | |
|         col = bpy.data.collections.new(f'Picker {name}')
 | |
|         col.rig_picker.enabled = True
 | |
|         col.rig_picker.rig = ob
 | |
|         col.rig_picker.canvas = canvas_ob
 | |
| 
 | |
|         col.objects.link(canvas_ob)
 | |
| 
 | |
|         scn.collection.children.link(col)
 | |
| 
 | |
|         self.report({"INFO"}, f"New Picker Collection {col.name} Created")
 | |
| 
 | |
|         return {'FINISHED'}
 | |
| 
 | |
| 
 | |
| class RP_OT_add_picker_source(Operator):
 | |
|     bl_idname = "rigpicker.add_picker_source"
 | |
|     bl_label = "Add a Picker source"
 | |
|     bl_description = "Add a Picker source"
 | |
|     bl_options = {'UNDO', 'REGISTER'}
 | |
| 
 | |
|     def execute(self, context):
 | |
|         arm = context.object.data
 | |
|         arm.rig_picker.sources.add()
 | |
| 
 | |
|         return {'FINISHED'}
 | |
| 
 | |
| 
 | |
| class RP_OT_remove_picker_source(Operator):
 | |
|     bl_idname = "rigpicker.remove_picker_source"
 | |
|     bl_label = "Delete a Picker source"
 | |
|     bl_description = "Delete a Picker source"
 | |
|     bl_options = {'UNDO', 'REGISTER'}
 | |
| 
 | |
|     index : IntProperty(default=-1)
 | |
| 
 | |
|     def execute(self, context):
 | |
|         arm = context.object.data
 | |
|         arm.rig_picker.sources.remove(self.index)
 | |
| 
 | |
|         return {'FINISHED'}
 | |
| 
 | |
| 
 | |
| class RP_OT_fit_picker(Operator):
 | |
|     bl_idname = "rigpicker.fit_picker"
 | |
|     bl_label = "Fit Picker"
 | |
|     bl_description = "Fit Picker in 2d view"
 | |
|     bl_options = {'UNDO', 'REGISTER'}
 | |
| 
 | |
|     index : IntProperty()
 | |
| 
 | |
|     @classmethod
 | |
|     def poll(cls, context):
 | |
|         return is_picker_space(context.space_data) and PICKERS.get(context.object)
 | |
| 
 | |
|     def execute(self, context):
 | |
|         ob = context.object
 | |
|         picker_group = PICKERS[ob]
 | |
| 
 | |
|         if self.index >= len(picker_group.pickers):
 | |
|             return {"CANCELLED"}
 | |
| 
 | |
|         picker = picker_group.pickers[self.index]
 | |
|         view2d = context.region.view2d
 | |
| 
 | |
|         if self.index == -1:
 | |
|             #print('Picker Group')
 | |
|             picker = picker_group
 | |
| 
 | |
|         view_rect = [view2d.view_to_region(*co, clip=False) for co in picker.rect]
 | |
|         bpy.ops.view2d.zoom_border(
 | |
|                 xmin=round(view_rect[3][0]), xmax=round(view_rect[1][0]), 
 | |
|                 ymin=round(view_rect[3][1]), ymax=round(view_rect[1][1]), 
 | |
|                 wait_for_input=False, zoom_out=False
 | |
|         )
 | |
| 
 | |
|         region_center = Vector((context.region.width * 0.5, context.region.height * 0.5))
 | |
|         view_center = view2d.region_to_view(*region_center)
 | |
| 
 | |
|         view_offset = Vector(view_center) + (picker.center - Vector(view_center))
 | |
|         region_offset = view2d.view_to_region(*view_offset, clip=False)
 | |
| 
 | |
|         delta = Vector(region_offset) - region_center
 | |
| 
 | |
|         bpy.ops.view2d.pan(deltax=round(delta[0]), deltay=round(delta[1]))
 | |
| 
 | |
|         return {'FINISHED'}
 | |
| 
 | |
| 
 | |
| keymaps = []
 | |
| def register_keymaps():
 | |
|     wm = bpy.context.window_manager
 | |
| 
 | |
|     km = wm.keyconfigs.addon.keymaps.new(name="Node Editor", space_type="NODE_EDITOR")
 | |
| 
 | |
|     for i in range(10):
 | |
|         kmi = km.keymap_items.new("rigpicker.fit_picker", type=f"NUMPAD_{i}", value="PRESS")
 | |
|         kmi.properties.index = i - 1
 | |
|         keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.call_operator", type="NUMPAD_PERIOD", value="PRESS")
 | |
|     kmi.properties.operator = "view3d.view_selected"
 | |
|     kmi.properties.view_3d = True
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS")
 | |
|     kmi.properties.operator = "pose.select_all"
 | |
|     kmi.properties.arguments = '{"action": "SELECT"}'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS", alt=True)
 | |
|     kmi.properties.operator = "pose.select_all"
 | |
|     kmi.properties.arguments = '{"action": "DESELECT"}'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK")
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS")
 | |
|     kmi.properties.operator = "animtoolbox.reset_bone"
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.call_operator", type="K", value="PRESS")
 | |
|     kmi.properties.operator = "animtoolbox.insert_keyframe"
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("anim.keyframe_delete_v3d", type="K", value="PRESS", alt=True)
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS")
 | |
|     kmi.properties.operator = 'transform.translate'
 | |
|     kmi.properties.view_3d = True
 | |
|     kmi.properties.invoke = True
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS", alt=True)
 | |
|     kmi.properties.operator = 'pose.loc_clear'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS")
 | |
|     kmi.properties.mode = 'ROTATE'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.call_operator", type="R", value="PRESS", alt=True)
 | |
|     kmi.properties.operator = 'pose.rot_clear'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS")
 | |
|     kmi.properties.mode = 'SCALE'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("rigpicker.call_operator", type="S", value="PRESS", alt=True)
 | |
|     kmi.properties.operator = 'pose.scale_clear'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("node.context_menu_picker", type="RIGHTMOUSE", value="PRESS")
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS")
 | |
|     kmi.properties.mode = 'SET'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", shift=True)
 | |
|     kmi.properties.mode = 'EXTEND'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     #kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", ctrl=True)
 | |
|     kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True)
 | |
|     kmi.properties.mode = 'SUBSTRACT'
 | |
|     keymaps.append((km, kmi))
 | |
| 
 | |
|     #km = wm.keyconfigs.addon.keymaps.new(name="View2D")
 | |
|     #kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS")
 | |
|     #kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')"
 | |
|     #keymaps.append((km, kmi))
 | |
| 
 | |
| 
 | |
| def unregister_keymaps():
 | |
|     for km, kmi in keymaps:
 | |
|         km.keymap_items.remove(kmi)
 | |
|     keymaps.clear()
 | |
| 
 | |
| classes = (
 | |
|     RP_OT_box_select,
 | |
|     RP_OT_toogle_property,
 | |
|     RP_OT_reload_picker,
 | |
|     RP_OT_toogle_bone_layer,
 | |
|     RP_OT_call_operator,
 | |
|     RP_MT_context_menu,
 | |
|     RP_OT_picker_transform,
 | |
|     RP_OT_context_menu_picker,
 | |
|     RP_OT_pack_picker,
 | |
|     RP_OT_add_picker_source,
 | |
|     RP_OT_remove_picker_source,
 | |
|     RP_OT_fit_picker,
 | |
|     RP_OT_add_picker_collection
 | |
|     #RP_OT_ui_draw
 | |
| )
 | |
| 
 | |
| 
 | |
| def register():
 | |
|     for cls in classes:
 | |
|         bpy.utils.register_class(cls)
 | |
|     
 | |
|     register_keymaps()
 | |
| 
 | |
| 
 | |
| def unregister():
 | |
|     unregister_keymaps()
 | |
|     for cls in reversed(classes):
 | |
|         bpy.utils.unregister_class(cls) |