245 lines
7.2 KiB
Python
245 lines
7.2 KiB
Python
|
|
||
|
import bpy
|
||
|
import shutil
|
||
|
from pathlib import Path
|
||
|
import os
|
||
|
|
||
|
import bpy.utils.previews
|
||
|
from bpy.types import PropertyGroup, AddonPreferences
|
||
|
from bpy.props import StringProperty, BoolProperty, CollectionProperty, \
|
||
|
FloatProperty, IntProperty, PointerProperty, EnumProperty, FloatVectorProperty
|
||
|
|
||
|
|
||
|
from bone_widget import ctx
|
||
|
from .ui import update_tab, draw_prefs
|
||
|
from .transform_utils import transform_mesh
|
||
|
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]
|
||
|
|
||
|
if (ops and ops[-1].bl_idname in op_ids):
|
||
|
bpy.ops.wm.undo()
|
||
|
bpy.ops.wm.redo()
|
||
|
|
||
|
def transform_widgets(self, context):
|
||
|
#bones = []
|
||
|
shapes = ctx.shapes
|
||
|
for shape, coords in shapes.items():
|
||
|
#bones.append(ctx.get_bone(shape))
|
||
|
#print(shape)
|
||
|
transform_mesh(shape.data, self.loc, self.rot, self.scale,
|
||
|
self.xz_scale, self.size, self.slide, coords=coords)
|
||
|
|
||
|
if ctx.prefs.symmetrize:
|
||
|
for bone in ctx.selected_bones:
|
||
|
flipped_bone = get_flipped_bone(bone)
|
||
|
if flipped_bone and flipped_bone.custom_shape in shapes:
|
||
|
continue
|
||
|
|
||
|
symmetrize_bone_shape(bone, prefix=ctx.prefs.prefix)
|
||
|
|
||
|
|
||
|
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)
|
||
|
|
||
|
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),
|
||
|
subtype='EULER', step=100, precision=0, update=transform_widgets)
|
||
|
|
||
|
scale : FloatVectorProperty(name='Scale', default=(1.0, 1.0, 1.0),
|
||
|
subtype='XYZ', update=transform_widgets)
|
||
|
|
||
|
|
||
|
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')
|
||
|
|
||
|
|
||
|
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()
|
||
|
|
||
|
@property
|
||
|
def abspath(self):
|
||
|
return ctx.abspath(self.path)
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
return Path(self.path).name
|
||
|
|
||
|
def load_icon(self, widget):
|
||
|
icon = self.get_widget_icon(widget)
|
||
|
if icon:
|
||
|
icon.reload()
|
||
|
else :
|
||
|
icon_path = self.get_icon_path(widget)
|
||
|
self.icons.load(widget.name, str(icon_path), 'IMAGE', True)
|
||
|
|
||
|
def get_widget_icon(self, widget):
|
||
|
return self.icons.get(widget.name)
|
||
|
|
||
|
def add_widget(self, name):
|
||
|
name = self.get_widget_display_name(name)
|
||
|
w = self.widgets.get(name)
|
||
|
if w:
|
||
|
self.load_icon(w)
|
||
|
return
|
||
|
|
||
|
w = self.widgets.add()
|
||
|
w.name = name
|
||
|
|
||
|
self.active_widget = w
|
||
|
|
||
|
self.load_icon(w)
|
||
|
|
||
|
return w
|
||
|
|
||
|
def remove_widget(self, widget):
|
||
|
index = self.widgets[:].index(widget)
|
||
|
|
||
|
widget_path = self.get_widget_path(widget)
|
||
|
icon_path = self.get_icon_path(widget)
|
||
|
|
||
|
if widget_path.exists():
|
||
|
os.remove(widget_path)
|
||
|
if icon_path.exists():
|
||
|
os.remove(icon_path)
|
||
|
|
||
|
self.widgets.remove(index)
|
||
|
self.widget_index = min(len(self.widgets)-1, index)
|
||
|
|
||
|
@property
|
||
|
def active_widget(self):
|
||
|
if self.widgets and self.widget_index < len(self.widgets):
|
||
|
return self.widgets[self.widget_index]
|
||
|
|
||
|
@active_widget.setter
|
||
|
def active_widget(self, widget):
|
||
|
self.widget_index = list(self.widgets).index(widget)
|
||
|
|
||
|
def get_widget_clean_name(self, widget):
|
||
|
name = widget if isinstance(widget, str) else widget.name
|
||
|
name = name.split('-', 1)[-1]
|
||
|
return bpy.path.clean_name(name).lower()
|
||
|
|
||
|
def get_widget_display_name(self, widget):
|
||
|
name = widget if isinstance(widget, str) else widget.name
|
||
|
return name.replace('_', ' ').title()
|
||
|
|
||
|
def get_widget_path(self, widget):
|
||
|
name = self.get_widget_clean_name(widget)
|
||
|
return (self.abspath/name).with_suffix('.blend')
|
||
|
|
||
|
def get_icon_path(self, widget):
|
||
|
name = self.get_widget_clean_name(widget)
|
||
|
return (self.abspath/name).with_suffix('.png')
|
||
|
|
||
|
def rename_widget(self, widget, name):
|
||
|
if not widget.get('_name'):
|
||
|
widget['_name'] = name
|
||
|
return
|
||
|
|
||
|
src_blend = self.get_widget_path(widget['_name'])
|
||
|
dst_blend = self.get_widget_path(name)
|
||
|
|
||
|
src_icon = self.get_icon_path(widget['_name'])
|
||
|
dst_icon = self.get_icon_path(name)
|
||
|
|
||
|
widget['_name'] = name
|
||
|
|
||
|
if not src_blend:
|
||
|
return
|
||
|
|
||
|
if not src_blend.exists():
|
||
|
self.remove_widget(widget)
|
||
|
|
||
|
if src_blend != dst_blend:
|
||
|
os.rename(str(src_blend), str(dst_blend))
|
||
|
|
||
|
if src_icon != dst_icon:
|
||
|
os.rename(str(src_icon), str(dst_icon))
|
||
|
|
||
|
self.load_icon(widget)
|
||
|
|
||
|
def load_widgets(self):
|
||
|
self.widgets.clear()
|
||
|
for widget_blend in self.abspath.glob('*.blend'):
|
||
|
self.add_widget(widget_blend.stem)
|
||
|
|
||
|
#class BW_PG_settings(PropertyGroup):
|
||
|
# pass
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
class BW_PG_bone_color(PropertyGroup):
|
||
|
value: FloatVectorProperty(name='Color', subtype='COLOR', default=[0.0, 0.0, 0.0])
|
||
|
name: StringProperty(name='Name')
|
||
|
|
||
|
|
||
|
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())
|
||
|
|
||
|
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)
|
||
|
|
||
|
grid_view : BoolProperty(name='Grid View', default=True)
|
||
|
|
||
|
#use_custom_colors : BoolProperty(name='Custom Colors', default=False, update=set_default_colors)
|
||
|
|
||
|
def draw(self, context):
|
||
|
draw_prefs(self.layout)
|
||
|
|
||
|
classes = (
|
||
|
BW_PG_bone_color,
|
||
|
BW_PG_widget,
|
||
|
BW_PG_transforms,
|
||
|
BW_PG_folder,
|
||
|
BW_prefs
|
||
|
)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def register():
|
||
|
for cls in classes:
|
||
|
bpy.utils.register_class(cls)
|
||
|
|
||
|
#update_folder_items()
|
||
|
|
||
|
|
||
|
def unregister():
|
||
|
#bpy.utils.previews.remove(ctx.prefs.default_folder.icons)
|
||
|
#for f in ctx.folders:
|
||
|
# bpy.utils.previews.remove(f.icons)
|
||
|
|
||
|
|
||
|
for cls in classes:
|
||
|
bpy.utils.unregister_class(cls)
|