fix mouse_under in picker_group

Gizmo
ChristopheSeux 2024-02-20 13:48:24 +01:00
parent c31a73f80f
commit 917531f59a
5 changed files with 244 additions and 84 deletions

View File

@ -19,6 +19,8 @@ def draw_header(self, context):
self._draw(context) self._draw(context)
return return
scn = context.scene
layout = self.layout layout = self.layout
layout.template_header() layout.template_header()
#layout.separator_spacer() #layout.separator_spacer()
@ -42,6 +44,11 @@ def draw_header(self, context):
if not picker_group: if not picker_group:
return return
if scn.rig_picker.use_pick_bone:
layout.alert = True
layout.label(text='Auto Bone Assign')
layout.prop(scn.rig_picker, 'use_pick_bone', icon='PANEL_CLOSE', text='', emboss=False)
layout.separator_spacer() layout.separator_spacer()
layout.label(text=ob.name) layout.label(text=ob.name)

View File

@ -3,7 +3,7 @@ import bpy
import gpu import gpu
from gpu_extras.batch import batch_for_shader from gpu_extras.batch import batch_for_shader
import blf import blf
from mathutils import bvhtree, Vector from mathutils import bvhtree, Vector, Color
from mathutils.geometry import (intersect_point_quad_2d, intersect_point_tri_2d, from mathutils.geometry import (intersect_point_quad_2d, intersect_point_tri_2d,
intersect_tri_tri_2d) intersect_tri_tri_2d)
from ..constants import PICKERS from ..constants import PICKERS
@ -30,13 +30,15 @@ class Shape:
self.press = False self.press = False
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR') self.shader = gpu.shader.from_builtin('UNIFORM_COLOR')
self.shader.bind()
#self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR') #self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
#self.hover_shader.uniform_float("color", [1, 1, 1, 0.1]) #self.hover_shader.uniform_float("color", [1, 1, 1, 0.1])
self.hover_color = [1, 1, 1, 0.1]
self.color = color
self.hover_color = self.brighten_color(self.color)
self.tooltip = tooltip self.tooltip = tooltip
self.color = color
self.points = points self.points = points
self.polygons = polygons or [] self.polygons = polygons or []
self.edges = edges or [] self.edges = edges or []
@ -50,6 +52,14 @@ class Shape:
def color(self): def color(self):
return self._color return self._color
def brighten_color(self, color):
brighten_color = Color(color[:3])
brighten_color.v += 0.05
brighten_color.v *= 1.1
brighten_color.v = max(brighten_color.v, 0.15)
return [*brighten_color, color[3]]
@color.setter @color.setter
def color(self, color=None): def color(self, color=None):
if not color: if not color:
@ -69,11 +79,12 @@ class Shape:
def draw(self): def draw(self):
self.shader.bind() #self.shader.bind()
self.shader.uniform_float("color", self.color) self.shader.uniform_float("color", self.color)
if self.polygons: if self.polygons:
self.p_batch.draw(self.shader) self.p_batch.draw(self.shader)
if self.edges: if self.edges:
self.e_batch.draw(self.shader) self.e_batch.draw(self.shader)
@ -105,6 +116,7 @@ class BoneShape(Shape):
self.type = 'bone' self.type = 'bone'
self.bone = bone self.bone = bone
self.active_color = [1, 1, 1, 0.1] self.active_color = [1, 1, 1, 0.1]
self.select_color = self.brighten_color(self.hover_color)
self.bone_colors = self.get_bone_colors() self.bone_colors = self.get_bone_colors()
@ -174,49 +186,71 @@ class BoneShape(Shape):
return bone_colors return bone_colors
def draw_hided(self):
gpu.state.blend_set('ALPHA')
gpu.state.line_width_set(1.0)
line_color = (1, 1, 1, 0.1)
color = Color(self.color[:3])
if self.hover:
color.v += 0.05
color.v *= 1.1
line_color = (1, 1, 1, 0.3)
self.shader.uniform_float("color", (*color[:3], 0.5))
self.p_batch.draw(self.shader)
self.shader.uniform_float("color", line_color)
self.e_batch.draw(self.shader)
gpu.state.blend_set('NONE')
def draw_zero_scaled(self):
gpu.state.blend_set('ALPHA')
gpu.state.line_width_set(1.0)
line_color = (*self.bone_colors['normal'][:3], 0.2)
color = Color(self.color[:3])
if self.hover or self.select:
color.v += 0.05
color.v *= 1.1
line_color = (*self.bone_color[:3], 0.66)
self.shader.uniform_float("color", (*color[:3], 0.5))
self.p_batch.draw(self.shader)
self.shader.uniform_float("color", line_color)
self.e_batch.draw(self.shader)
gpu.state.blend_set('NONE')
def draw(self): def draw(self):
gpu.state.blend_set('ALPHA') gpu.state.blend_set('ALPHA')
gpu.state.line_width_set(1.0)
if self.hide: if self.hide:
self.shader.uniform_float("color", (*self.color[:3], 0.4)) return self.draw_hided()
self.p_batch.draw(self.shader)
#elif self.select:
# self.shader.uniform_float("color", self.bone_color)
# self.p_batch.draw(self.shader)
else:
super().draw()
# Overlay the fill slightly with the bone color elif self.bone.custom_shape_scale_xyz.length < 0.00001:
#self.shader.uniform_float("color", (*self.bone_color[:3], 0.1)) return self.draw_zero_scaled()
#self.p_batch.draw(self.shader)
# Draw Fill
color = self.color
if self.select: if self.select:
color = self.select_color
elif self.hover:
color = self.hover_color color = self.hover_color
if self.select or self.hover:
color = self.hover_color
if self.select and self.hover:
color = self.active_color
self.shader.uniform_float("color", color) self.shader.uniform_float("color", color)
self.p_batch.draw(self.shader) self.p_batch.draw(self.shader)
#Overlay the fill slightly with the bone color # Draw Outline
self.shader.uniform_float("color", (*self.bone_colors['normal'][:3], 0.1))
self.p_batch.draw(self.shader)
gpu.state.blend_set('NONE') gpu.state.blend_set('NONE')
#self.contour_shader.bind()
#print(self.bone_color)
if self.select or self.active: if self.select or self.active:
gpu.state.line_width_set(2.0) gpu.state.line_width_set(2.0)
#if not self.hide:
self.shader.uniform_float("color", self.bone_color) self.shader.uniform_float("color", self.bone_color)
#for b in self.contour_batches:
self.e_batch.draw(self.shader) self.e_batch.draw(self.shader)
gpu.state.line_width_set(1.0) gpu.state.line_width_set(1.0)
@ -359,8 +393,8 @@ class OperatorShape(Shape):
class Picker: class Picker:
def __init__(self, rig, shapes): def __init__(self, parent, rig, shapes):
self.parent = parent
self.region = bpy.context.region self.region = bpy.context.region
self.rig = rig self.rig = rig
@ -421,7 +455,7 @@ class Picker:
if shape.type=='bone' and shape.hover: if shape.type=='bone' and shape.hover:
shape.assign_bone_event() shape.assign_bone_event()
bpy.ops.rigpicker.save_picker() bpy.ops.rigpicker.save_picker(index=self.index)
def press_event(self, mode='SET'): def press_event(self, mode='SET'):
for shape in self.shapes: for shape in self.shapes:
@ -475,9 +509,9 @@ class Picker:
def border_select(self, border, mode): def border_select(self, border, mode):
border = [Vector(b) - Vector(self.translation) for b in border] border = [Vector(b) - Vector(self.translation) for b in border]
for shape in self.shapes: for shape in self.shapes:
shape.press = False
shape.hover = False
if shape.type != 'bone': if shape.type != 'bone':
continue continue
@ -519,16 +553,21 @@ class Picker:
relative_center = (self.rect[1] + self.rect[3]) * 0.5 relative_center = (self.rect[1] + self.rect[3]) * 0.5
return relative_center + self.translation return relative_center + self.translation
@property
def index(self):
return self.parent.pickers.index(self)
class PickerGroup: class PickerGroup:
def __init__(self, rig, pickers): def __init__(self, rig, pickers):
self.view_location = Vector((0, 0)) #self.view_location = Vector((0, 0))
self.region = bpy.context.region self.region = bpy.context.region
self.rig = rig self.rig = rig
self.pickers = [] self.pickers = []
self.mouse = Vector((0, 0)) self.mouse = Vector((0, 0))
self.location = Vector((0, 0))
self.hover_shape = None self.hover_shape = None
@ -542,7 +581,7 @@ class PickerGroup:
self.add_picker(picker) self.add_picker(picker)
def add_picker(self, picker): def add_picker(self, picker):
self.pickers.append(Picker(self.rig, picker)) self.pickers.append(Picker(self, self.rig, picker))
def draw(self): def draw(self):
y = 0 y = 0
@ -558,22 +597,23 @@ class PickerGroup:
def move_event(self, mouse): def move_event(self, mouse):
self.mouse = mouse self.mouse = mouse
location = self.region.view2d.region_to_view(*mouse) self.location = self.region.view2d.region_to_view(*mouse)
view_location = self.region.view2d.region_to_view(0, 0) # Try to detect view pan to remove tooltip
if view_location != self.view_location: # view_location = self.region.view2d.region_to_view(0, 0)
self.view_location = view_location # if view_location != self.view_location:
self.tooltip = '' # self.view_location = view_location
if self.timer: # self.tooltip = ''
self.timer.cancel() # if self.timer:
return # self.timer.cancel()
# return
hover_shape = None hover_shape = None
for picker in self.pickers: for picker in self.pickers:
if not picker.under_mouse(location): if not picker.under_mouse(self.location):
continue continue
picker.move_event(location) picker.move_event(self.location)
if picker.hover_shape: if picker.hover_shape:
hover_shape = picker.hover_shape hover_shape = picker.hover_shape
@ -593,16 +633,28 @@ class PickerGroup:
self.clear_tooltip() self.clear_tooltip()
for picker in self.pickers: for picker in self.pickers:
if picker.under_mouse(self.location):
picker.press_event(mode) picker.press_event(mode)
else:
for shape in picker.shapes:
shape.press = False
def release_event(self, mode='SET'): def release_event(self, mode='SET'):
if mode == 'SET': if mode == 'SET':
for bone in self.rig.data.bones: for bone in self.rig.data.bones:
bone.select = False bone.select = False
for picker in self.pickers: for picker in self.pickers:
if picker.under_mouse(self.location):
picker.release_event(mode) picker.release_event(mode)
else:
for shape in picker.shapes:
shape.press = False
def assign_bone_event(self):
for picker in self.pickers:
if picker.under_mouse(self.location):
picker.assign_bone_event()
def border_select(self, border, mode): def border_select(self, border, mode):
border = [self.region.view2d.region_to_view(*b) for b in border] border = [self.region.view2d.region_to_view(*b) for b in border]

View File

@ -98,6 +98,7 @@ def draw_callback_px():
if not picker_group or not picker_group.tooltip: if not picker_group or not picker_group.tooltip:
return return
region = bpy.context.region
text = picker_group.tooltip text = picker_group.tooltip
mouse = picker_group.tooltip_mouse mouse = picker_group.tooltip_mouse
ui_scale = bpy.context.preferences.system.ui_scale ui_scale = bpy.context.preferences.system.ui_scale
@ -106,15 +107,16 @@ def draw_callback_px():
font_id = 0 font_id = 0
blf.size(font_id, int(13 * ui_scale)) blf.size(font_id, int(13 * ui_scale))
margins = [12, 4]
text_size = blf.dimensions(font_id, text) text_size = blf.dimensions(font_id, text)
text_pos = (mouse[0] - text_size[0]*0.33, mouse[1] - (34*ui_scale)) #text_pos = (mouse[0] - text_size[0]*0.33, mouse[1] - (34*ui_scale))
text_pos = (region.width - text_size[0]-margins[0], margins[1])
bg_margins = [8, 4] bg_pos = (text_pos[0] - margins[0], text_pos[1] - margins[1]-1)
bg_pos = (text_pos[0] - bg_margins[0], text_pos[1] - bg_margins[1]-1) bg_size = (text_size[0] + 2*margins[0], text_size[1] + 2*margins[1])
bg_size = (text_size[0] + 2*bg_margins[0], text_size[1] + 2*bg_margins[1])
gpu.state.blend_set('ALPHA') gpu.state.blend_set('ALPHA')
draw_rect_2d(bg_pos, *bg_size, (0, 0, 0, 0.75)) draw_rect_2d(bg_pos, *bg_size, (0.1, 0.1, 0.1, 0.75))
gpu.state.blend_set('NONE') gpu.state.blend_set('NONE')

View File

@ -1,5 +1,5 @@
import bpy import bpy
from bpy.props import EnumProperty, IntProperty from bpy.props import EnumProperty, IntProperty, BoolProperty
from ..constants import PICKERS from ..constants import PICKERS
from ..core.bl_utils import get_view_3d_override from ..core.bl_utils import get_view_3d_override
from ..core.geometry_utils import bounding_rect from ..core.geometry_utils import bounding_rect
@ -9,7 +9,7 @@ from ..core.picker import get_picker_path, pack_picker, unpack_picker
#from core.picker import * #from core.picker import *
#from .utils import is_over_region #from .utils import is_over_region
import gpu import gpu
from mathutils import Vector from mathutils import Vector, Euler, Matrix
from gpu_extras.batch import batch_for_shader from gpu_extras.batch import batch_for_shader
from pathlib import Path from pathlib import Path
import json import json
@ -203,6 +203,7 @@ class RP_OT_picker_transform(bpy.types.Operator):
def poll(cls, context): def poll(cls, context):
return is_picker_space(context) return is_picker_space(context)
'''
def execute(self, context): def execute(self, context):
with context.temp_override(**get_view_3d_override()): with context.temp_override(**get_view_3d_override()):
if self.mode == 'TRANSLATE': if self.mode == 'TRANSLATE':
@ -213,36 +214,105 @@ class RP_OT_picker_transform(bpy.types.Operator):
bpy.ops.transform.resize("INVOKE_DEFAULT") bpy.ops.transform.resize("INVOKE_DEFAULT")
return {"FINISHED"} return {"FINISHED"}
'''
# def invoke(self, context, event): def invoke(self, context, event):
# self.mouse = event.mouse_region_x, event.mouse_region_y self.override = get_view_3d_override()
# self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones} if self.mode == 'TRANSLATE':
with context.temp_override(**self.override):
if event.alt:
bpy.ops.pose.loc_clear()
else:
bpy.ops.transform.translate("INVOKE_DEFAULT")
return {"FINISHED"}
# context.window_manager.modal_handler_add(self) elif self.mode == 'ROTATE' and event.alt:
# return {'RUNNING_MODAL'} with context.temp_override(**self.override):
bpy.ops.pose.rot_clear()
return {"FINISHED"}
# def modal(self, context, event): elif self.mode == 'SCALE' and event.alt:
with context.temp_override(**self.override):
bpy.ops.pose.scale_clear()
return {"FINISHED"}
# delta_x = (event.mouse_region_x - self.mouse[0]) / 1000 self.mouse = event.mouse_region_x, event.mouse_region_y
self.center = context.active_pose_bone.matrix.translation.copy()
self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def modal(self, context, event):
delta_x = (event.mouse_region_x - self.mouse[0]) / 100
#delta_y = (event.mouse_region_y - self.mouse[1]) / 1000 #delta_y = (event.mouse_region_y - self.mouse[1]) / 1000
#for bone, matrix in self.bone_data.items(): #for bone, matrix in self.bone_data.items():
# bone.matrix.translation = matrix.translation + Vector((delta_x, 0, delta_y)) # bone.matrix.#translation = matrix.translation + Vector((delta_x, 0, delta_y))
center = context.active_pose_bone.tail
scn = context.scene
cam = scn.camera
# #print(delta_x, delta_y)
# if event.type=="LEFTMOUSE" and event.value == 'RELEASE': #rotation_axis = cam.matrix_world.to_3x3().transposed().col[2]
# return {'FINISHED'} #rotation_matrix = Matrix.Rotation(delta_x, 4, Vector((0, 0, -1)) @ cam.matrix_world)
view_vec = Vector((0, 0, 1))
view_vec.rotate(cam.matrix_world)
rotation_matrix = Matrix.Rotation(delta_x, 4, view_vec)
# if event.type=="RIGHTMOUSE": #rot_mat = Matrix.Rotation(delta_x, 4, "Z")
#rot_mat = scn.camera.matrix_world.inverted() @ rot_mat
#rot_mat = Matrix.LocRotScale(center, Euler((0, 0, delta_x)), (1, 1, 1))
if event.type == "MOUSEMOVE":
for bone, matrix in self.bone_data.items():
#center =
mat = matrix.copy()
mat.translation -= self.center
mat = rotation_matrix @ mat
mat.translation += self.center
bone.matrix = mat
#rotation_matrix.translation = matrix.translation
#mat = matrix.copy().to_3x3()
#mat.rotate(rotation_matrix)
#mat = mat.to_4x4()
#mat.translation = matrix.translation
#bone.matrix = mat
#bone.matrix = matrix.inverted() @ (rotation_matrix @ bone_mat)
#bone.matrix.translation = 0, 0, 0
#bone.matrix = rotation_matrix @ matrix
#bone.matrix = matrix @ (matrix.inverted() @ rotation_matrix)
#bone.matrix.translation = matrix.translation
#print('Rotate', delta_x)
#with context.temp_override(**get_view_3d_override()):
#for bone, matrix in self.bone_data.items(): #for bone, matrix in self.bone_data.items():
# bone.matrix = matrix # bone.rotation_euler.x = delta_x
#bpy.ops.transform.rotate(value=delta_x)
# return {'CANCELLED'} #print(delta_x, delta_y)
# return {'RUNNING_MODAL'} if event.type=="LEFTMOUSE" and event.value == 'RELEASE':
return {'FINISHED'}
if event.type == "RIGHTMOUSE":
for bone, matrix in self.bone_data.items():
bone.matrix = matrix
return {'CANCELLED'}
return {'RUNNING_MODAL'}
class RP_OT_function_execute(bpy.types.Operator): class RP_OT_function_execute(bpy.types.Operator):
@ -534,18 +604,28 @@ def register_keymaps():
kmi.properties.mode = 'TRANSLATE' kmi.properties.mode = 'TRANSLATE'
keymaps.append((km, kmi)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("node.picker_transform", type="G", value="PRESS", alt=True)
kmi.properties.mode = 'TRANSLATE'
keymaps.append((km, kmi))
kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS") kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS")
kmi.properties.mode = 'ROTATE' kmi.properties.mode = 'ROTATE'
keymaps.append((km, kmi)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS", alt=True)
kmi.properties.mode = 'ROTATE'
keymaps.append((km, kmi))
kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS") kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS")
kmi.properties.mode = 'SCALE' kmi.properties.mode = 'SCALE'
keymaps.append((km, kmi)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("node.context_menu_picker", type="RIGHTMOUSE", value="PRESS") kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS", alt=True)
kmi.properties.mode = 'SCALE'
keymaps.append((km, kmi)) 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 = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS")
kmi.properties.mode = 'SET' kmi.properties.mode = 'SET'

View File

@ -1,9 +1,11 @@
import json import json
from os.path import abspath
from pathlib import Path from pathlib import Path
from mathutils import Matrix
import bpy import bpy
from bpy.types import Operator from bpy.types import Operator
from bpy.props import IntProperty
from mathutils import Matrix
from ..core.shape import get_picker_data from ..core.shape import get_picker_data
from ..core.addon_utils import is_shape from ..core.addon_utils import is_shape
@ -184,10 +186,27 @@ class RP_OT_save_picker(Operator):
bl_label = 'Store UI Data' bl_label = 'Store UI Data'
bl_idname = 'rigpicker.save_picker' bl_idname = 'rigpicker.save_picker'
index : IntProperty(default=-1)
def execute(self, context): def execute(self, context):
scn = context.scene scn = context.scene
ob = context.object ob = context.object
print('SAve Picker', self.index)
if self.index == -1:
collection = next((c for c in ob.users_collection if c.rig_picker.enabled), None) collection = next((c for c in ob.users_collection if c.rig_picker.enabled), None)
else:
source = context.active_object.data.rig_picker.sources[self.index].source
source = Path(bpy.path.abspath(source, library=ob.data.library)).resolve()
collection = next((c for c in set(scn.collection.children_recursive) if c.rig_picker.enabled
and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() == source), None)
print('source', source)
print('colleciton', collection)
if not collection:
self.report({"ERROR"}, 'No Picker found')
return {'CANCELLED'}
canvas = collection.rig_picker.canvas canvas = collection.rig_picker.canvas
rig = collection.rig_picker.rig rig = collection.rig_picker.rig