fixing bugs

master
ChristopheSeux 2023-01-18 16:02:42 +01:00
parent 9d339585f5
commit c673d03a6a
25 changed files with 228 additions and 149 deletions

View File

@ -1,8 +1,8 @@
bl_info = {
"name": "Bone Widget",
"author": "Christophe SEUX",
"version": (2, 0, 0),
"blender": (2, 92, 0),
"version": (2, 0, 1),
"blender": (3, 4, 1),
"description": "Create custom shapes for bone controller",
"warning": "",
"wiki_url": "",
@ -12,22 +12,37 @@ bl_info = {
import sys
if "bpy" in locals():
import importlib as imp
imp.reload(context)
sys.modules.update({"bone_widget.ctx": context.BW_context()})
imp.reload(shape_utils)
imp.reload(transform_utils)
imp.reload(icon_utils)
imp.reload(properties)
imp.reload(operators)
imp.reload(ui)
else:
from . import context
sys.modules.update({"bone_widget.ctx": context.BW_context()})
from . import operators
from . import ui
from . import properties
from . import shape_utils
from . import transform_utils
from . import icon_utils
import bpy
import sys
#sys.modules.update({"bone_widget.ctx": context.BW_ctx()})
from bone_widget import ctx
def register():
@ -38,6 +53,8 @@ def register():
#bpy.types.Scene.bone_widget = bpy.props.PointerProperty(type=BoneWidgetSettings)
#get_widgets(DefaultFolder, DefaultShapes)
#get_widgets(CustomFolder, CustomShapes)
from bone_widget import ctx
for f in ctx.folders:
f.load_widgets()

View File

@ -6,7 +6,7 @@ import os
from bone_widget.shape_utils import get_bone
class BW_ctx:
class BW_context:
def __init__(self):
self.module_dir = Path(__file__).parent
@ -48,11 +48,15 @@ class BW_ctx:
@property
def show_prefs_op(self):
ops = bpy.context.window_manager.operators
op_idname = self.get_op_id('show_preferences')
id_names = [
self.get_op_id('show_preferences'),
self.get_op_id('add_folder'),
self.get_op_id('remove_folder')
]
#transforms = self.prefs.transforms
if ops and ops[-1].bl_idname == op_idname:
return ops[-1]
if ops and ops[-1].bl_idname in id_names:
return True
@property
def category(self):
@ -70,6 +74,9 @@ class BW_ctx:
@property
def widget_col(self):
col_name = self.prefs.collection
col_name = col_name.format(ob=bpy.context.object)
col = bpy.data.collections.get(col_name)
if not col:
col = bpy.data.collections.new(col_name)
@ -278,6 +285,6 @@ class BW_ctx:
# ops_data = getattr(bpy.ops, ctx.id_name)
# getattr(ops_data, op)()
sys.modules.update({"bone_widget.ctx": BW_ctx()})

View File

@ -16,6 +16,8 @@ def bounds_2d_co(coords, width, height):
co_2d = [space_2d(region, rv3d, co) for co in coords]
print("co_2d", co_2d)
x_value = sorted(co_2d, key=lambda x:x[0])
y_value = sorted(co_2d, key=lambda x:x[1])
@ -54,6 +56,9 @@ def render_widget(shape, filepath, width=32, height=32) :
bm = bmesh.new()
bm.from_object(shape, dg)
width *= 2
height *= 2
coords = [shape.matrix_world @ v.co for v in bm.verts]
coords = bounds_2d_co(coords, width, height)
@ -66,45 +71,53 @@ def render_widget(shape, filepath, width=32, height=32) :
batch = batch_for_shader(shader, 'LINES', {"pos": coords}, indices=indices)
with offscreen.bind():
bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
fb = gpu.state.active_framebuffer_get()
fb.clear(color=(0.0, 0.0, 0.0, 0.0))
with gpu.matrix.push_pop():
# reset matrices -> use normalized device coordinates [-1, 1]
gpu.matrix.load_matrix(Matrix.Identity(4))
gpu.matrix.load_projection_matrix(Matrix.Identity(4))
bgl.glLineWidth(4)
bgl.glEnable( bgl.GL_LINE_SMOOTH )
bgl.glEnable(bgl.GL_BLEND)
shader.bind()
shader.uniform_float("color", (0, 0, 0, 0.1))
gpu.state.line_width_set(4*2)
#bgl.glEnable(bgl.GL_BLEND)
#bgl.glEnable( bgl.GL_LINE_SMOOTH )
shader.uniform_float("color", (0, 0, 0, 0.2))
batch.draw(shader)
bgl.glLineWidth(2)
gpu.state.line_width_set(2*2)
shader.uniform_float("color", (0.85, 0.85, 0.85, 1))
batch.draw(shader)
buffer = bgl.Buffer(bgl.GL_BYTE, width * height * 4)
bgl.glReadBuffer(bgl.GL_BACK)
bgl.glReadPixels(0, 0, width, height, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
buffer = fb.read_color(0, 0, width, height, 4, 0, 'FLOAT')
buffer.dimensions = width * height * 4
#buffer = bgl.Buffer(bgl.GL_BYTE, width * height * 4)
#bgl.glReadBuffer(bgl.GL_BACK)
#bgl.glReadPixels(0, 0, width, height, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
offscreen.free()
#icon_name = '.' + shape.name + '_icon.png'
icon_name = shape.name + '_icon.png'
icon_name = f'.{shape.name}_icon.png'
image = bpy.data.images.get(icon_name)
if image:
bpy.data.images.remove(image)
image = bpy.data.images.new(icon_name, width, height)
image = bpy.data.images.new(icon_name, width, height, alpha=True, float_buffer=True)
#image_data = np.asarray(buffer, dtype=np.uint8)
#image.pixels.foreach_set(image_data.flatten()/255)
#image_data = np.asarray(buffer, dtype=np.uint8)
image.pixels = [v / 255 for v in buffer]
image.pixels.foreach_set(buffer)
image.save_render(str(filepath))
image.scale(int(width/2), int(height/2))
image.filepath_raw = str(filepath)
image.save()
bpy.data.images.remove(image)

View File

@ -8,7 +8,7 @@ from bone_widget import ctx
from .transform_utils import transform_matrix, apply_mat_to_verts, get_bone_size_factor, \
get_bone_matrix
from .icon_utils import render_widget
from .shape_utils import get_clean_shape, symmetrize_bone_shape, link_to_col, get_bone
from .shape_utils import get_clean_shape, symmetrize_bone_shape, link_to_col, get_bone, custom_shape_matrix
import re
from pathlib import Path
@ -18,7 +18,7 @@ from mathutils import Matrix
import json
class BW_OT_copy_widgets(Operator) :
class BW_OT_copy_widgets(Operator):
bl_idname = 'bonewidget.copy_widgets'
bl_label = "Copy Widgets"
@ -39,7 +39,6 @@ class BW_OT_copy_widgets(Operator) :
else:
bones = ctx.bones
for b in bones:
if b.custom_shape:
s = b.custom_shape.copy()
@ -90,7 +89,7 @@ class BW_OT_copy_widgets(Operator) :
return {'FINISHED'}
class BW_OT_paste_widgets(Operator) :
class BW_OT_paste_widgets(Operator):
bl_idname = 'bonewidget.paste_widgets'
bl_label = "Paste Widgets"
@ -174,7 +173,7 @@ class BW_OT_paste_widgets(Operator) :
return {'FINISHED'}
class BW_OT_remove_unused_shape(Operator) :
class BW_OT_remove_unused_shape(Operator):
bl_idname = 'bonewidget.remove_unused_shape'
bl_label = "Remove Unused Shape"
@ -187,7 +186,7 @@ class BW_OT_remove_unused_shape(Operator) :
return {'FINISHED'}
class BW_OT_auto_color(Operator) :
class BW_OT_auto_color(Operator):
bl_idname = 'bonewidget.auto_color'
bl_label = "Auto Color"
bl_options = {'REGISTER', 'UNDO'}
@ -210,26 +209,26 @@ class BW_OT_auto_color(Operator) :
return {'FINISHED'}
class BW_OT_load_default_color(Operator) :
class BW_OT_load_default_color(Operator):
bl_idname = 'bonewidget.load_default_color'
bl_label = "Load Default Color"
bl_options = {'REGISTER', 'UNDO'}
select_color : FloatVectorProperty(name='Color', subtype='COLOR', default=[0.0, 1.0, 1.0])
active_color : FloatVectorProperty(name='Color', subtype='COLOR', default=[1.0, 1.0, 1.0])
select_color: FloatVectorProperty(name='Color', subtype='COLOR', default=[0.0, 1.0, 1.0])
active_color: FloatVectorProperty(name='Color', subtype='COLOR', default=[1.0, 1.0, 1.0])
def execute(self, context):
ob = context.object
colors = {
'root': [0, 0, 0],
'spine chest hips': [1, 1, 0],
'.R': [0, 0.035, 0.95],
'.L': [1, 0, 0],
'ik.R': [1, 0.1, 0.85],
'ik.L': [0.67, 0, 0.87],
'tweak.L': [0.75, 0.65, 0],
'tweak.R': [1, 0.6, 0],
'Black': [0, 0, 0],
'Yellow': [1, 1, 0],
'Red': [0, 0.035, 0.95],
'Blue': [1, 0, 0],
'Pink': [1, 0.1, 0.85],
'Purple': [0.67, 0, 0.87],
'Brown': [0.75, 0.65, 0],
'Orange': [1, 0.6, 0],
}
for k, v in colors.items():
@ -244,10 +243,10 @@ class BW_OT_load_default_color(Operator) :
bone_group.colors.select = self.select_color
bone_group.colors.active = self.active_color
return {'FINISHED'}
class BW_OT_copy_bone_groups(Operator) :
class BW_OT_copy_bone_groups(Operator):
bl_idname = 'bonewidget.copy_bone_groups'
bl_label = "Copy Bone Groups"
bl_options = {'REGISTER', 'UNDO'}
@ -265,23 +264,22 @@ class BW_OT_copy_bone_groups(Operator) :
for bg in ob.pose.bone_groups:
bone_groups[bg.name] = {
'colors': [bg.colors.normal[:], bg.colors.select[:], bg.colors.active[:]],
'bones' : [b.name for b in ob.pose.bones if b.bone_group == bg]
'bones': [b.name for b in ob.pose.bones if b.bone_group == bg]
}
blend_file = Path(gettempdir()) / 'bw_copy_bone_groups.json'
blend_file.write_text(json.dumps(bone_groups))
return {'FINISHED'}
class BW_OT_paste_bone_groups(Operator) :
class BW_OT_paste_bone_groups(Operator):
bl_idname = 'bonewidget.paste_bone_groups'
bl_label = "Paste Bone Groups"
bl_options = {'REGISTER', 'UNDO'}
clear : BoolProperty(default=False)
clear: BoolProperty(default=False)
@classmethod
def poll(self, context):
@ -316,7 +314,7 @@ class BW_OT_paste_bone_groups(Operator) :
return {'FINISHED'}
class BW_OT_add_folder(Operator) :
class BW_OT_add_folder(Operator):
bl_idname = 'bonewidget.add_folder'
bl_label = "Add Folder"
@ -327,12 +325,12 @@ class BW_OT_add_folder(Operator) :
return {'FINISHED'}
class BW_OT_remove_folder(Operator) :
class BW_OT_remove_folder(Operator):
bl_idname = 'bonewidget.remove_folder'
bl_label = "Remove Folder"
bl_options = {'REGISTER', 'UNDO'}
index : IntProperty()
index: IntProperty()
def execute(self, context):
@ -348,7 +346,7 @@ class BW_OT_remove_folder(Operator) :
return {'FINISHED'}
class BW_OT_refresh_folders(Operator) :
class BW_OT_refresh_folders(Operator):
bl_idname = 'bonewidget.refresh_folders'
bl_label = "Refresh"
@ -364,7 +362,7 @@ class BW_OT_rename_folder(Operator):
bl_label = "Rename Folder"
bl_options = {'REGISTER', 'UNDO'}
name : StringProperty(name='Name')
name: StringProperty(name='Name')
@classmethod
def poll(cls, context):
@ -384,6 +382,7 @@ class BW_OT_rename_folder(Operator):
class BW_OT_show_preferences(Operator):
"""Display the preferences to the tab panel"""
bl_idname = 'bonewidget.show_preferences'
bl_label = "Show Preferences"
bl_options = {'REGISTER'}
@ -394,11 +393,12 @@ class BW_OT_show_preferences(Operator):
class BW_OT_transform_widget(Operator):
"""Transform the Rotation Location or Scale of the selected shapes"""
bl_idname = 'bonewidget.transform_widget'
bl_label = "Transfom"
bl_options = {'REGISTER', 'UNDO'}
symmetrize : BoolProperty(name='Symmetrize', default=True)
symmetrize: BoolProperty(name='Symmetrize', default=True)
@classmethod
def poll(cls, context):
@ -410,11 +410,16 @@ class BW_OT_transform_widget(Operator):
class BW_OT_create_widget(Operator):
"""Create the widget shape and assign it to the bone"""
bl_idname = 'bonewidget.create_widget'
bl_label = "Create Widget"
bl_options = {'REGISTER', 'UNDO'}
symmetrize : BoolProperty(name='Symmetrize', default=True)
symmetrize: BoolProperty(name='Symmetrize', default=True)
@classmethod
def poll(cls, context):
return ctx.bone and ctx.active_widget
def invoke(self, context, event):
self.symmetrize = ctx.prefs.symmetrize
@ -429,7 +434,7 @@ class BW_OT_create_widget(Operator):
shape = data_src.objects[0]
for bone in ctx.selected_bones:
shape_copy = shape.copy()
#shape_copy = shape.copy()
if bpy.app.version_string < '3.0.0':
bone.custom_shape_scale = 1.0
@ -443,7 +448,7 @@ class BW_OT_create_widget(Operator):
#copy_shape = shape.copy()
#copy_shape.data = copy_shape.data.copy()
bone.custom_shape = get_clean_shape(bone, shape_copy, col=ctx.widget_col, prefix=ctx.prefs.prefix, separate=False)
bone.custom_shape = get_clean_shape(bone, shape, col=ctx.widget_col, prefix=ctx.prefs.prefix)
if self.symmetrize:
symmetrize_bone_shape(bone, prefix=ctx.prefs.prefix)
@ -460,7 +465,7 @@ class BW_OT_match_transform(Operator):
bl_label = "Match Transforms"
bl_options = {'REGISTER', 'UNDO'}
relative : BoolProperty(default=False)
relative: BoolProperty(default=False)
def execute(self, context):
for bone in ctx.selected_bones:
@ -518,7 +523,7 @@ class BW_OT_return_to_rig(Operator):
bl_label = "Return to rig"
bl_options = {'REGISTER', 'UNDO'}
symmetrize : BoolProperty(name='Symmetrize', default=True)
symmetrize: BoolProperty(name='Symmetrize', default=True)
def invoke(self, context, event):
self.symmetrize = ctx.prefs.symmetrize
@ -560,7 +565,7 @@ class BW_OT_symmetrize_widget(Operator):
bl_label = "Symmetrize"
bl_options = {'REGISTER', 'UNDO'}
match_transform : BoolProperty(True)
match_transform: BoolProperty(True)
def get_name_side(self, name, fallback=None):
if name.lower().endswith('.l'):
@ -569,24 +574,24 @@ class BW_OT_symmetrize_widget(Operator):
return 'RIGHT'
return fallback
def mirror_name(self, name) :
def mirror_name(self, name):
mirror = None
match = {
'R' : 'L',
'r' : 'l',
'L' : 'R',
'l' : 'r',
'R': 'L',
'r': 'l',
'L': 'R',
'l': 'r',
}
separator = ['.', '_']
if name.startswith(tuple(match.keys())):
if name[1] in separator :
if name[1] in separator:
mirror = match[name[0]] + name[1:]
if name.endswith(tuple(match.keys())):
if name[-2] in separator :
if name[-2] in separator:
mirror = name[:-1] + match[name[-1]]
return mirror
@ -648,9 +653,19 @@ class BW_OT_add_widget(Operator):
widget_path.parent.mkdir(exist_ok=True, parents=True)
bpy.data.libraries.write(str(widget_path), {shape})
render_widget(shape, icon_path)
widget = folder.add_widget(name)
# Copy the shape to apply the bone matrix to the mesh
shape_copy = shape.copy()
shape_copy.data = shape_copy.data.copy()
shape_copy.matrix_world = get_bone_matrix(bone)
mat = custom_shape_matrix(bone)
shape_copy.data.transform(mat)
render_widget(shape_copy, icon_path)
folder.add_widget(name)
bpy.data.objects.remove(shape_copy)
bpy.context.area.tag_redraw()
@ -681,7 +696,7 @@ class BW_OT_clean_widget(Operator):
bl_options = {'REGISTER', 'UNDO'}
all: BoolProperty(name='All', default=False)
symmetrize : BoolProperty(name='Symmetrize', default=True)
symmetrize: BoolProperty(name='Symmetrize', default=True)
@classmethod
def poll(cls, context):

View File

@ -19,7 +19,7 @@ from .shape_utils import symmetrize_bone_shape, get_flipped_bone
def undo_redo(self, context):
wm = context.window_manager
op_names = ('transform_widget','create_widget', 'match_transform', 'clean_widget')
op_ids = [ctx.get_op_id(o) for o in op_ids]
op_ids = [ctx.get_op_id(o) for o in op_names]
if (ops and ops[-1].bl_idname in op_ids):
bpy.ops.wm.undo()
@ -44,17 +44,17 @@ def transform_widgets(self, context):
class BW_PG_transforms(PropertyGroup):
size : FloatProperty(name='Size', default=1.0, min=0, update=transform_widgets)
xz_scale : FloatProperty(name='XZ Scale', default=1.0, min=0, update=transform_widgets)
slide : FloatProperty(name='Slide', default=0.0, update=transform_widgets)
size: FloatProperty(name='Size', default=1.0, min=0, update=transform_widgets)
xz_scale: FloatProperty(name='XZ Scale', default=1.0, min=0, update=transform_widgets)
slide: FloatProperty(name='Slide', default=0.0, update=transform_widgets)
loc : FloatVectorProperty(name='Location', default=(0.0, 0.0, 0.0),
loc: FloatVectorProperty(name='Location', default=(0.0, 0.0, 0.0),
subtype='TRANSLATION', update=transform_widgets)
rot : FloatVectorProperty(name = 'Rotation', default=(0.0, 0.0, 0.0),
rot: FloatVectorProperty(name = 'Rotation', default=(0.0, 0.0, 0.0),
subtype='EULER', step=100, precision=0, update=transform_widgets)
scale : FloatVectorProperty(name='Scale', default=(1.0, 1.0, 1.0),
scale: FloatVectorProperty(name='Scale', default=(1.0, 1.0, 1.0),
subtype='XYZ', update=transform_widgets)
@ -62,16 +62,19 @@ def rename_widget(self, context):
ctx.active_folder.rename_widget(self, self.name)
class BW_PG_widget(PropertyGroup):
name : StringProperty(update=rename_widget)
#path : StringProperty(subtype='FILE_PATH')
name: StringProperty(update=rename_widget)
source_bone: StringProperty()
#path: StringProperty(subtype='FILE_PATH')
def refresh(self, context):
bpy.ops.bonewidget.refresh_folders()
class BW_PG_folder(PropertyGroup):
icons = bpy.utils.previews.new()
path : StringProperty(subtype='FILE_PATH', default='Default')
expand : BoolProperty()
widgets : CollectionProperty(type=BW_PG_widget)
widget_index : IntProperty()
path: StringProperty(subtype='FILE_PATH', default='Default', update=refresh)
expand: BoolProperty()
widgets: CollectionProperty(type=BW_PG_widget)
widget_index: IntProperty()
@property
def abspath(self):
@ -82,28 +85,33 @@ class BW_PG_folder(PropertyGroup):
return Path(self.path).name
def load_icon(self, widget):
icon = self.get_widget_icon(widget)
icon_name = self.get_icon_name(widget)
icon = self.icons.get(icon_name)
if icon:
icon.reload()
else :
else:
icon_path = self.get_icon_path(widget)
self.icons.load(widget.name, str(icon_path), 'IMAGE', True)
self.icons.load(icon_name, str(icon_path), 'IMAGE', True)
def get_icon_name(self, widget):
return f'{Path(self.path).stem}_{widget.name}'
def get_widget_icon(self, widget):
return self.icons.get(widget.name)
return self.icons.get(self.get_icon_name(widget))
def add_widget(self, name):
name = self.get_widget_display_name(name)
w = self.widgets.get(name)
if w:
self.load_icon(w)
self.active_widget = w
return
w = self.widgets.add()
w.name = name
self.active_widget = w
self.load_icon(w)
return w
@ -194,25 +202,25 @@ class BW_PG_bone_color(PropertyGroup):
class BW_prefs(AddonPreferences):
bl_idname = __package__
default_folder : PointerProperty(type=BW_PG_folder)
folders : CollectionProperty(type=BW_PG_folder)
folder_index : IntProperty()
folder_enum : EnumProperty(name='Folders', items=lambda s,c : ctx.folder_items)
collection : StringProperty(name='Collection', default='Widget')
prefix : StringProperty(name='Prefix', default='WGT-')
category : StringProperty(name='Tab', default='Rigging', update=lambda s,c : update_tab())
default_folder: PointerProperty(type=BW_PG_folder)
folders: CollectionProperty(type=BW_PG_folder)
folder_index: IntProperty()
folder_enum: EnumProperty(name='Folders', items=lambda s,c: ctx.folder_items)
collection: StringProperty(name='Collection', default='Widgets', description='Name of the widget collection use {ob.name} to include the name of the rig')
prefix: StringProperty(name='Prefix', default='WGT-', description='Prefix for the shape object and data name')
category: StringProperty(name='Tab', default='Rigging', update=lambda s,c: update_tab())
symmetrize : BoolProperty(name='Symmetrize', default=True, update=undo_redo)
separate : BoolProperty(default=True, name="Separate", update=undo_redo)
match_transform : BoolProperty(default=True, name="Match Transform", update=undo_redo)
rename : BoolProperty(default=True, name="Rename", update=undo_redo)
symmetrize: BoolProperty(name='Symmetrize', default=True, update=undo_redo)
separate: BoolProperty(default=True, name="Separate", update=undo_redo)
match_transform: BoolProperty(default=True, name="Match Transform", update=undo_redo)
rename: BoolProperty(default=True, name="Rename", update=undo_redo)
show_transforms : BoolProperty(default=False)
transforms : PointerProperty(type=BW_PG_transforms)
show_transforms: BoolProperty(default=False)
transforms: PointerProperty(type=BW_PG_transforms)
grid_view : BoolProperty(name='Grid View', default=True)
#grid_view: BoolProperty(name='Grid View', default=True)
#use_custom_colors : BoolProperty(name='Custom Colors', default=False, update=set_default_colors)
#use_custom_colors: BoolProperty(name='Custom Colors', default=False, update=set_default_colors)
def draw(self, context):
draw_prefs(self.layout)
@ -231,6 +239,8 @@ classes = (
def register():
for cls in classes:
bpy.utils.register_class(cls)
#bpy.types.Armature.widget_collection = PointerProperty(type=bpy.types.Collection)
#update_folder_items()
@ -240,6 +250,7 @@ def unregister():
#for f in ctx.folders:
# bpy.utils.previews.remove(f.icons)
#del bpy.types.Armature.widget_collection
for cls in classes:
bpy.utils.unregister_class(cls)

View File

@ -1,6 +1,7 @@
import bpy
from .transform_utils import get_bone_matrix, transform_matrix
from mathutils import Matrix
#from bone_widget import ctx
@ -73,26 +74,42 @@ def link_to_col(shape, col):
col.objects.link(shape)
def get_clean_shape(bone, shape, separate=True, rename=True,
col=None, match=True, prefix='', apply_transforms=True):
def custom_shape_matrix(bone):
loc = bone.custom_shape_translation.copy()
rot = bone.custom_shape_rotation_euler.copy()
scale = bone.custom_shape_scale_xyz.copy()
old_shape = shape
old_bone = get_bone(old_shape)
bone.custom_shape = None
if bone.use_custom_shape_bone_size:
loc /= bone.bone.length
else:
scale /= bone.bone.length
return Matrix.LocRotScale(loc, rot, scale)
def get_clean_shape(bone, shape, separate=True, rename=True,
col=None, match=True, prefix='', apply_transforms=True):
#bone.custom_shape = None
#Remove bone shape if no other bone are using it
if bone.custom_shape:
old_bones = get_bones(bone.custom_shape)
old_bones.remove(bone)
print('old_bones', old_bones)
if old_bones:
rename_shape(bone.custom_shape, old_bones[0], prefix=prefix)
elif bone.custom_shape != shape:
bpy.data.objects.remove(bone.custom_shape)
if separate:
if old_bone:
rename_shape(old_shape, old_bone, prefix=prefix)
else:
bpy.data.objects.remove(old_shape)
shape = shape.copy()
shape.data = shape.data.copy()
bone.custom_shape = shape
if match:
shape.matrix_world = get_bone_matrix(bone)
if apply_transforms:
if bpy.app.version_string < '3.0.0':
@ -108,23 +125,19 @@ col=None, match=True, prefix='', apply_transforms=True):
bone.custom_shape_scale = 1
#mirror_bone.custom_shape_scale =
else:
loc = bone.custom_shape_translation
rot = bone.custom_shape_rotation_euler
scale = bone.custom_shape_scale_xyz
if not bone.use_custom_shape_bone_size:
scale /= bone.bone.length
mat = transform_matrix(loc=loc, rot=rot, scale=scale)
mat = custom_shape_matrix(bone)
shape.data.transform(mat)
bone.custom_shape_translation = 0, 0, 0
bone.custom_shape_rotation_euler = 0, 0, 0
bone.custom_shape_scale_xyz = 1, 1, 1
bone.custom_shape_translation = (0, 0, 0)
bone.custom_shape_rotation_euler = (0, 0, 0)
bone.custom_shape_scale_xyz = (1, 1, 1)
bone.use_custom_shape_bone_size = True
if match:
shape.matrix_world = get_bone_matrix(bone)
if rename:
rename_shape(shape, bone, prefix=prefix)
@ -134,10 +147,14 @@ col=None, match=True, prefix='', apply_transforms=True):
return shape
def get_bone(shape):
def get_bones(shape):
armatures = [o for o in bpy.context.scene.objects if o.type == 'ARMATURE']
return next((b for a in armatures for b in a.pose.bones if b.custom_shape == shape), None)
return [b for a in armatures for b in a.pose.bones if b.custom_shape == shape]
def get_bone(shape):
bones = get_bones(shape)
if bones:
return bones[0]
def symmetrize_bone_shape(bone, prefix=None):
active_side = get_side(bone.name, 'LEFT')

45
ui.py
View File

@ -59,24 +59,26 @@ def add_color_row(layout, data=None, name='', index=0):
'''
def draw_prefs(layout):
layout = layout.column(align=False)
add_row(layout, 'category', name='Tab', icon='COPY_ID')
add_row(layout, 'collection', name='Collection', icon='OUTLINER_COLLECTION')
add_row(layout, 'prefix', name='Prefix', icon='SYNTAX_OFF')
box = layout.box()
col = box.column(align=False)
layout.separator()
add_row(col, 'category', name='Tab', icon='COPY_ID')
add_row(col, 'collection', name='Collection', icon='OUTLINER_COLLECTION')
add_row(col, 'prefix', name='Prefix', icon='SYNTAX_OFF')
add_row(layout, 'path', data=ctx.prefs.default_folder, name='Folders',
col.separator()
add_row(col, 'path', data=ctx.prefs.default_folder, name='Folders',
icon='ADD', operator='bonewidget.add_folder')
for i, f in enumerate(ctx.prefs.folders):
add_row(layout, 'path', icon='REMOVE',
add_row(col, 'path', icon='REMOVE',
operator='bonewidget.remove_folder', data=f, properties={'index': i})
layout.separator()
col.separator()
split = layout.split(factor=0.33, align=True)
split = col.split(factor=0.33, align=True)
split.alignment= 'RIGHT'
split.label(text='Auto:')
@ -88,6 +90,10 @@ def draw_prefs(layout):
add_bool_row(col, 'match_transform', name='Match Transform', icon='TRANSFORM_ORIGINS')
add_bool_row(col, 'rename', name='Rename', icon='SYNTAX_OFF')
#bpy.ops.wm.save_userpref()
prefs_unsaved = bpy.context.preferences.is_dirty
layout.operator('wm.save_userpref', text="Save Preferences" + (" *" if prefs_unsaved else ""))
#col.prop(ctx.prefs, 'symmetrize', text='Symetrize', icon='MOD_MIRROR')
#col.prop(ctx.prefs, 'separate', text='Separate', icon='UNLINKED')
#col.prop(ctx.prefs, 'match_transform', text='Match Transform', icon='TRANSFORM_ORIGINS')
@ -115,17 +121,14 @@ def draw_prefs(layout):
class BW_UL_widget(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
icon = data.icons.get(item.name)
icon = data.get_widget_icon(item)
icon_id = icon.icon_id if icon else -1
#layout.scale_x = 1.5
row = layout.row(align=True)
row.use_property_split = False
if self.layout_type in {'DEFAULT', 'COMPACT'}:
layout.prop(item, 'name', text="", emboss=False, icon_value=icon_id)
elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon_id)
row.prop(item, 'name', text="", emboss=False, icon_value=icon_id)
class BW_MT_folder(Menu):
@ -142,7 +145,7 @@ class BW_MT_folder(Menu):
layout.operator('bonewidget.copy_widgets', icon='COPYDOWN')
layout.operator('bonewidget.paste_widgets', icon='PASTEDOWN')
layout.prop(ctx.prefs, 'grid_view')
#layout.prop(ctx.prefs, 'grid_view')
class BW_PT_transforms(Panel):
@ -231,7 +234,7 @@ class BW_PT_main(Panel):
if folder:
widget_col.template_list('BW_UL_widget', 'BW_widgets', folder, 'widgets', folder, 'widget_index',
rows=5, columns=self.get_nb_col(context), type='GRID' if ctx.prefs.grid_view else 'DEFAULT')
rows=5, columns=self.get_nb_col(context), type='DEFAULT')
#widget_col.template_list('UI_UL_list', 'BW_widgets', folder, 'widgets', folder, 'widget_index', rows=4)
#layout.prop(self, 'folder_enum')
@ -267,13 +270,9 @@ class BW_PT_main(Panel):
#opt_col = layout.column()
elif ctx.show_prefs_op:
layout.separator()
#layout.separator()
draw_prefs(layout)
#bpy.ops.wm.save_userpref()
prefs_unsaved = context.preferences.is_dirty
layout.operator('wm.save_userpref', text="Save Preferences" + (" *" if prefs_unsaved else ""))
classes = (
BW_UL_widget,

BIN
widgets/Custom/hips.blend Normal file

Binary file not shown.

BIN
widgets/Custom/hips.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
widgets/Custom/root.blend Normal file

Binary file not shown.

BIN
widgets/Custom/root.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
widgets/Custome/root.blend Normal file

Binary file not shown.

BIN
widgets/Custome/root.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

BIN
widgets/Default/bone_l.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 B

BIN
widgets/Default/chest.blend Normal file

Binary file not shown.

BIN
widgets/Default/chest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

BIN
widgets/Default/pelvis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
widgets/Default/torso.blend Normal file

Binary file not shown.

BIN
widgets/Default/torso.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB