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

@ -18,6 +18,8 @@ def draw_header(self, context):
if not context.space_data.tree_type == 'RigPickerTree':
self._draw(context)
return
scn = context.scene
layout = self.layout
layout.template_header()
@ -42,6 +44,11 @@ def draw_header(self, context):
if not picker_group:
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.label(text=ob.name)

View File

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

View File

@ -1,5 +1,5 @@
import bpy
from bpy.props import EnumProperty, IntProperty
from bpy.props import EnumProperty, IntProperty, BoolProperty
from ..constants import PICKERS
from ..core.bl_utils import get_view_3d_override
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 .utils import is_over_region
import gpu
from mathutils import Vector
from mathutils import Vector, Euler, Matrix
from gpu_extras.batch import batch_for_shader
from pathlib import Path
import json
@ -196,13 +196,14 @@ class RP_OT_picker_transform(bpy.types.Operator):
"""Tooltip"""
bl_idname = "node.picker_transform"
bl_label = "Move Bone in Picker View"
mode : EnumProperty(items=[(m, m.title(), '') for m in ('TRANSLATE', 'ROTATE', 'SCALE')])
@classmethod
def poll(cls, context):
return is_picker_space(context)
'''
def execute(self, context):
with context.temp_override(**get_view_3d_override()):
if self.mode == 'TRANSLATE':
@ -213,36 +214,105 @@ class RP_OT_picker_transform(bpy.types.Operator):
bpy.ops.transform.resize("INVOKE_DEFAULT")
return {"FINISHED"}
'''
# def invoke(self, context, event):
# self.mouse = event.mouse_region_x, event.mouse_region_y
def invoke(self, context, event):
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)
# return {'RUNNING_MODAL'}
elif self.mode == 'ROTATE' and event.alt:
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
# delta_y = (event.mouse_region_y - self.mouse[1]) / 1000
self.mouse = event.mouse_region_x, event.mouse_region_y
# for bone, matrix in self.bone_data.items():
# bone.matrix.translation = matrix.translation + Vector((delta_x, 0, delta_y))
self.center = context.active_pose_bone.matrix.translation.copy()
self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}
# #print(delta_x, delta_y)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
# if event.type=="LEFTMOUSE" and event.value == 'RELEASE':
# return {'FINISHED'}
def modal(self, context, event):
# if event.type=="RIGHTMOUSE":
# for bone, matrix in self.bone_data.items():
# bone.matrix = matrix
delta_x = (event.mouse_region_x - self.mouse[0]) / 100
#delta_y = (event.mouse_region_y - self.mouse[1]) / 1000
# return {'CANCELLED'}
#for bone, matrix in self.bone_data.items():
# bone.matrix.#translation = matrix.translation + Vector((delta_x, 0, delta_y))
center = context.active_pose_bone.tail
# return {'RUNNING_MODAL'}
scn = context.scene
cam = scn.camera
#rotation_axis = cam.matrix_world.to_3x3().transposed().col[2]
#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)
#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():
# bone.rotation_euler.x = delta_x
#bpy.ops.transform.rotate(value=delta_x)
#print(delta_x, delta_y)
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):
@ -534,18 +604,28 @@ def register_keymaps():
kmi.properties.mode = 'TRANSLATE'
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.properties.mode = 'ROTATE'
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.properties.mode = 'SCALE'
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))
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'

View File

@ -1,9 +1,11 @@
import json
from os.path import abspath
from pathlib import Path
from mathutils import Matrix
import bpy
from bpy.types import Operator
from bpy.props import IntProperty
from mathutils import Matrix
from ..core.shape import get_picker_data
from ..core.addon_utils import is_shape
@ -184,10 +186,27 @@ class RP_OT_save_picker(Operator):
bl_label = 'Store UI Data'
bl_idname = 'rigpicker.save_picker'
index : IntProperty(default=-1)
def execute(self, context):
scn = context.scene
ob = context.object
collection = next((c for c in ob.users_collection if c.rig_picker.enabled), None)
print('SAve Picker', self.index)
if self.index == -1:
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
rig = collection.rig_picker.rig