408 lines
19 KiB
Python
408 lines
19 KiB
Python
#!/usr/bin/env python -------------------------------- -*- coding: utf-8 -*-#
|
|
# 2023 3DMish <Mish7913@gmail.com> #
|
|
|
|
# ----- ##### BEGIN GPL LICENSE BLOCK ##### ----- #
|
|
# #
|
|
# This program is free software; you can redistribute it and/or #
|
|
# modify it under the terms of the GNU General Public License #
|
|
# as published by the Free Software Foundation; either version 2 #
|
|
# of the License, or (at your option) any later version. #
|
|
# #
|
|
# This program is distributed in the hope that it will be useful, #
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
# GNU General Public License for more details. #
|
|
# #
|
|
# ----- ##### END GPL LICENSE BLOCK ##### ----- #
|
|
|
|
import bpy, os;
|
|
|
|
def ver_less(v1, v2, v3): return True if ((v1, v2, v3) > bpy.app.version) else False;
|
|
def ver_more(v1, v2, v3): return True if ((v1, v2, v3) <= bpy.app.version) else False;
|
|
|
|
if ver_more(2,78,0): import bpy.utils.previews;
|
|
if ver_more(2,60,0): lc_prew = bpy.utils.previews.new();
|
|
|
|
data_path = os.path.dirname(os.path.abspath(__file__));
|
|
icons_path = os.path.dirname(os.path.abspath(__file__)) + "/m7a_icons";
|
|
addon_id = os.path.basename(os.path.abspath(os.path.join(__file__, os.pardir, os.pardir)));
|
|
if (ver_more(2,80,0)): asset_libraries = bpy.context.preferences.filepaths.asset_libraries;
|
|
|
|
user_path = bpy.utils.resource_path('USER');
|
|
config_path = os.path.join(user_path, "config");
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def m7a_load_icon(image):
|
|
if (image in lc_prew): thumb = lc_prew[image];
|
|
else: thumb = lc_prew.load(image, image, 'IMAGE');
|
|
return thumb.icon_id;
|
|
|
|
def lc_icon (code):
|
|
path = data_path + "/m7a_icons/";
|
|
images = {
|
|
"KNIFE": m7a_load_icon(path + "m7a_knife" + ".png"),
|
|
"LOOP_CUT": m7a_load_icon(path + "m7a_loop_cut" + ".png"),
|
|
"INSET": m7a_load_icon(path + "m7a_inset" + ".png"),
|
|
"EXTRUDE": m7a_load_icon(path + "m7a_extrude" + ".png"),
|
|
"EXTRUDE_NORMALS": m7a_load_icon(path + "m7a_extrude_normals" + ".png"),
|
|
"EDGE_SLIDE": m7a_load_icon(path + "m7a_edge_slide" + ".png"),
|
|
"VERTEX_SLIDE": m7a_load_icon(path + "m7a_vertex_slide" + ".png"),
|
|
"X0": m7a_load_icon(path + "m7a_x0" + ".png"),
|
|
"FILL": m7a_load_icon(path + "m7a_fill" + ".png"),
|
|
"FILL_GRID": m7a_load_icon(path + "m7a_fill_grid" + ".png"),
|
|
"VIEW_3D": m7a_load_icon(path + "m7a_view3d" + ".png"),
|
|
"TO_QUADS": m7a_load_icon(path + "m7a_to_quads" + ".png"),
|
|
"BIRD": m7a_load_icon(path + "m7a_bird" + ".png"),
|
|
"EXTRUDE_INTERSECT": m7a_load_icon(path + "m7a_extrude_intersect" + ".png"),
|
|
"FROSTING": m7a_load_icon(path + "m7a_frosting" + ".png"),
|
|
"FLOWER": m7a_load_icon(path + "m7a_flower" + ".png"),
|
|
"SPRINKLES": m7a_load_icon(path + "m7a_sprinkles" + ".png"),
|
|
"CHERRY": m7a_load_icon(path + "m7a_cherry" + ".png"),
|
|
"LIGHTNING": m7a_load_icon(path + "m7a_lightning" + ".png"),
|
|
"BEADS_WIREFRAME": m7a_load_icon(path + "m7a_beads_wireframe" + ".png"),
|
|
"ELECTRIC": m7a_load_icon(path + "m7a_electric" + ".png"),
|
|
"CIRCLE_ARRAY": m7a_load_icon(path + "m7a_circle_array" + ".png"),
|
|
"BRIDGE": m7a_load_icon(path + "m7a_bridge" + ".png"),
|
|
"DUPLICATE": m7a_load_icon(path + "m7a_duplicate" + ".png"),
|
|
"DUPLICATE_LINKED": m7a_load_icon(path + "m7a_duplicate_linked" + ".png"),
|
|
"MALE": m7a_load_icon(path + "m7a_male" + ".png"),
|
|
"FEMALE": m7a_load_icon(path + "m7a_female" + ".png"),
|
|
"DEL_KEYS": m7a_load_icon(path + "m7a_delete_keys" + ".png"),
|
|
"DUP_MOVE": m7a_load_icon(path + "m7a_duplicate_move" + ".png"),
|
|
"COLOR_FILL": m7a_load_icon(path + "m7a_to_fill" + ".png"),
|
|
"KEYBOARD": m7a_load_icon(path + "m7a_keyboard" + ".png"),
|
|
}
|
|
if code in images: return images[code];
|
|
else: return 0;
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def lc_split_row (lc_layout, width, align=True):
|
|
return lc_layout.row(align=align) if (lc_width() > width) else lc_layout;
|
|
|
|
def lc_width (): return bpy.context.region.width;
|
|
def lc_min (width): return lc_width() > width;
|
|
|
|
def lc_set_width (lc_element, width, width_x = 0):
|
|
if ver_more(2,80,0): lc_element.ui_units_x = width;
|
|
else: lc_element.scale_x = width/10 if (width_x == 0) else width_x;
|
|
|
|
def lc_cont_x (lc_layout, width=1.0, is_row=True, active=True, is_align=True, alert=False):
|
|
if is_row: lc_cont_btn = lc_layout.row(align=is_align);
|
|
else: lc_cont_btn = lc_layout.column(align=is_align);
|
|
lc_cont_btn.scale_x = width if ver_more(2,80,0) else width+0.05 if (width != 1.0) else width;
|
|
lc_cont_btn.active = active;
|
|
lc_cont_btn.alert = alert;
|
|
return lc_cont_btn;
|
|
|
|
def lc_scale (lc_layout, width=1.0, is_row=True, active=True, is_align=True, y=None):
|
|
if is_row: lc_cont_btn = lc_layout.row(align=is_align);
|
|
else: lc_cont_btn = lc_layout.column(align=is_align);
|
|
lc_cont_btn.scale_x = width;
|
|
lc_cont_btn.scale_y = y if (y != None) else width;
|
|
lc_cont_btn.active = active;
|
|
return lc_cont_btn;
|
|
|
|
def lc_cont_y (lc_layout, height=1.0, is_row=True, active=True):
|
|
if is_row: lc_cont_btn = lc_layout.row(align=True);
|
|
else: lc_cont_btn = lc_layout.column(align=True);
|
|
lc_cont_btn.scale_y = height;
|
|
lc_cont_btn.active = active;
|
|
return lc_cont_btn;
|
|
|
|
def lc_fixed (lc_layout, value, is_row=True):
|
|
if is_row: lc_cont_btn = lc_layout.row(align=True);
|
|
else: lc_cont_btn = lc_layout.column(align=True);
|
|
lc_cont_btn.ui_units_x = value;
|
|
lc_cont_btn.scale_y = value;
|
|
return lc_cont_btn;
|
|
|
|
def lc_fixed_x (lc_layout, width, is_row=True):
|
|
if is_row: lc_cont_btn = lc_layout.row(align=True);
|
|
else: lc_cont_btn = lc_layout.column(align=True);
|
|
lc_cont_btn.ui_units_x = width;
|
|
return lc_cont_btn;
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def lc_return (small, large, min = None, max = None):
|
|
if (min != None) and (max != None):
|
|
return small if (lc_width() > min) and (lc_width() < max) else large;
|
|
elif (min != None) and (max == None):
|
|
return small if (lc_width() > min) else large;
|
|
elif (min == None) and (max != None):
|
|
return small if (lc_width() < max) else large;
|
|
else: return small;
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def q_register_class (classes):
|
|
for cls in classes:
|
|
if (cls):
|
|
try: bpy.utils.register_class(cls);
|
|
except: pass
|
|
|
|
def q_unregister_class (classes):
|
|
for cls in classes:
|
|
if (cls):
|
|
try: bpy.utils.unregister_class(cls);
|
|
except: pass
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def r_remove_attr (cont, name):
|
|
if hasattr(cont, name): delattr(cont, name);
|
|
|
|
def r_register_class (cont, name = ""):
|
|
|
|
def func_reg (f_name):
|
|
if not (hasattr(bpy.types, f_name)) and (f_name in cont):
|
|
if (cont[f_name] != None):
|
|
bpy.utils.register_class(cont[f_name]);
|
|
|
|
if (name == ""):
|
|
for key in cont.keys():
|
|
func_reg (key);
|
|
else: func_reg (name);
|
|
|
|
def r_unregister_class (cont, name = ""):
|
|
def func_unreg (f_name):
|
|
if hasattr(bpy.types, f_name):
|
|
if not (cont == None): cont[f_name] = getattr(bpy.types, f_name);
|
|
bpy.utils.unregister_class(getattr(bpy.types, f_name));
|
|
|
|
if (name == ""):
|
|
for key in cont.keys():
|
|
func_unreg (key);
|
|
else: func_unreg (name);
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def bpy_preferences (path = "", name = ""):
|
|
if (path == "addons"):
|
|
if ver_more(2,80,0):
|
|
if (name in bpy.context.preferences.addons.keys()):
|
|
return bpy.context.preferences.addons[name].preferences;
|
|
else: return None;
|
|
else:
|
|
if (name in bpy.context.user_preferences.addons.keys()):
|
|
return bpy.context.user_preferences.addons[name].preferences;
|
|
else: return None;
|
|
|
|
else:
|
|
if ver_more(2,80,0): return bpy.context.preferences;
|
|
else: return bpy.context.user_preferences;
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def get_prop (prop_name):
|
|
if hasattr(bpy_preferences("addons", addon_id), "m7a_props"):
|
|
if hasattr(bpy_preferences("addons", addon_id).m7a_props, prop_name):
|
|
return getattr(bpy_preferences("addons", addon_id).m7a_props, prop_name);
|
|
return False;
|
|
|
|
def get_class (cont, name):
|
|
if hasattr(bpy.types, name):
|
|
return getattr(bpy.types, name);
|
|
else: return None;
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def lc_switcher (lc_layout, context, label, icon, convert=True, asset=True, delete=True):
|
|
a_obj = context.active_object;
|
|
|
|
lc_row = lc_layout.row(align=True);
|
|
lc_row.label(text=label, icon=icon);
|
|
|
|
if (convert):
|
|
if ver_more(3,0,0): lc_row.menu("VIEW3D_MT_object_convert", text="", icon="MAT_SPHERE_SKY");
|
|
else: lc_row.operator_menu_enum("object.convert", "target", text="", icon="MAT_SPHERE_SKY")
|
|
|
|
if ver_more(3,0,0):
|
|
if (asset) and not (context.mode.split("_")[0] in {"EDIT", "PAINT", "SCULPT", "VERTEX"}):
|
|
lc_row.separator();
|
|
if not (a_obj.asset_data): lc_row.operator("asset.mark", text="", icon="ASSET_MANAGER", emboss=False);
|
|
else: lc_row.operator("asset.clear", text="", icon="ASSET_MANAGER", depress=True).set_fake_user = False;
|
|
|
|
lc_row_append = lc_row.row(align = True);
|
|
|
|
if (delete):
|
|
lc_row_del = lc_row.row(align = True);
|
|
lc_row_del.separator();
|
|
lc_row_del.operator("object.delete", text="", icon="TRASH" if ver_more(3,0,0) else "ZOOMOUT", emboss=False);
|
|
lc_row_del.active = not (context.selected_objects == []);
|
|
|
|
return lc_row_append;
|
|
|
|
def lc_add_btn (lc_layout, id, text="", icon=None, mode=None, toggle=None, hint=None):
|
|
lc_layout.separator();
|
|
if (icon == None): lc_btn = lc_layout.operator(id, text=text, emboss=False);
|
|
else: lc_btn = lc_layout.operator(id, text=text, icon=icon, emboss=False);
|
|
|
|
if (hint != None): lc_btn.hint = "Set UI Mode: " + hint;
|
|
if (mode != None): lc_btn.mode = mode;
|
|
if (mode != None): lc_btn.toggle = toggle;
|
|
|
|
return lc_btn;
|
|
|
|
def lc_tool_data():
|
|
return bpy.context.workspace.tools.from_space_view3d_mode(bpy.context.mode, create=False);
|
|
|
|
def lc_panel (lc_layout, context, lc_prop, lc_label, lc_icon=None, add_btn=None, empty=False, c_label=""):
|
|
lc_box = lc_layout.box().column(align = True); is_open = False;
|
|
|
|
lc_icon_1 = "DOWNARROW_HLT" if ver_more(3,0,0) else "TRIA_DOWN";
|
|
lc_icon_2 = "RIGHTARROW" if ver_more(3,0,0) else "TRIA_RIGHT";
|
|
|
|
lc_label_row = lc_box.row(align = True);
|
|
lc_label_opt = lc_label_row.row(align = True);
|
|
|
|
if (empty): lc_label_row.label(text=lc_label, icon="DOT");
|
|
|
|
lc_label_row.separator();
|
|
|
|
if not (empty):
|
|
if (getattr(context.scene, lc_prop)):
|
|
if (add_btn == None) and (lc_icon != None):
|
|
lc_label_row.label(text="", icon=lc_icon);
|
|
lc_box.separator(); is_open = True;
|
|
else:
|
|
if (lc_icon != None):
|
|
lc_label_row.label(text="", icon=lc_icon);
|
|
|
|
lc_label_opt.prop(
|
|
context.scene, lc_prop, emboss=False,
|
|
icon=lc_icon_1 if (getattr(context.scene, lc_prop)) else lc_icon_2,
|
|
text=lc_label if (is_open == False) else c_label if (c_label != "") else lc_label,
|
|
);
|
|
|
|
return lc_box, lc_label_row, is_open;
|
|
|
|
def lc_center_label (lc_layout, text):
|
|
lc_row = lc_layout.row(align = True);
|
|
lc_row.alignment = "CENTER";
|
|
lc_row.label(text=text);
|
|
return lc_row;
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def lc_template_switcher (lc_main, context):
|
|
if (context.object):
|
|
lc_icon_name = context.object.type.upper();
|
|
lc_icon_name = lc_icon_name.replace("LIGHT_PROBE", "LIGHTPROBE");
|
|
lc_icon_name = lc_icon_name.replace("GPENCIL", "GREASEPENCIL");
|
|
lc_text_name = context.object.type.replace("_", " ").title();
|
|
lc_swtch_row = lc_switcher(
|
|
lc_main, context, lc_text_name,
|
|
icon="OUTLINER_DATA_"+lc_icon_name,
|
|
convert=context.object.type in {'MESH','CURVE','SURFACE','GPENCIL','FONT'} and context.mode in {"OBJECT"},
|
|
delete=context.mode in {"OBJECT","EDIT_CURVE","EDIT_ARMATURE","POSE",
|
|
'EDIT_TEXT','EDIT_METABALL','EDIT_LATTICE'},
|
|
asset=ver_more(2,79,5)
|
|
);
|
|
|
|
sw = 240 if ver_more(2,79,5) else 180;
|
|
|
|
if (context.object.type in {'MESH'}):
|
|
if (context.mode in {"OBJECT"}):
|
|
if lc_min(sw): lc_add_btn(lc_swtch_row, "object.mode_set", "", "VPAINT_HLT", 'VERTEX_PAINT', False);
|
|
if lc_min(sw+30):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "TPAINT_HLT", 'TEXTURE_PAINT', False);
|
|
if lc_min(sw+50):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "WPAINT_HLT", 'WEIGHT_PAINT', False);
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "EDITMODE_HLT", 'EDIT', False);
|
|
if lc_min(sw+70):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "SCULPTMODE_HLT", 'SCULPT', False);
|
|
else: # -------------------------------------------------------------------------------------
|
|
if (context.mode not in {"PAINT_VERTEX"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "VPAINT_HLT", 'VERTEX_PAINT', False);
|
|
if (context.mode not in {"SCULPT"}):
|
|
if ver_more(2,79,5) or lc_min(sw+30):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "SCULPTMODE_HLT", 'SCULPT', False);
|
|
if (context.mode not in {"EDIT_MESH"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "EDITMODE_HLT", 'EDIT', False);
|
|
if (context.mode not in {"PAINT_WEIGHT"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "WPAINT_HLT", 'WEIGHT_PAINT', False);
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "OBJECT_DATAMODE", 'OBJECT', False);
|
|
if (context.mode not in {"PAINT_TEXTURE"}) and lc_min(sw):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "TPAINT_HLT", 'TEXTURE_PAINT', False);
|
|
|
|
elif (context.object.type in {'CURVE', 'FONT', 'META', 'LATTICE'}):
|
|
if (context.mode in {"OBJECT"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "EDITMODE_HLT", 'EDIT', False);
|
|
else: # -------------------------------------------------------------------------------------
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "OBJECT_DATAMODE", 'OBJECT', False);
|
|
|
|
elif (context.object.type == "ARMATURE"):
|
|
if (context.mode in {"OBJECT"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "EDITMODE_HLT", 'EDIT', False);
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "ARMATURE_DATA", 'POSE', False);
|
|
else: # -------------------------------------------------------------------------------------
|
|
if (context.mode not in {"EDIT_ARMATURE"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "EDITMODE_HLT", 'EDIT', False);
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "OBJECT_DATAMODE", 'OBJECT', False);
|
|
if (context.mode not in {"POSE"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "ARMATURE_DATA", 'POSE', False);
|
|
|
|
elif (context.object.type == "GPENCIL"):
|
|
if (context.mode in {"OBJECT"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "GREASEPENCIL", 'PAINT_GPENCIL', False);
|
|
if lc_min(sw+30):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "EDITMODE_HLT", 'EDIT_GPENCIL', False);
|
|
if lc_min(sw+50):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "SCULPTMODE_HLT", 'SCULPT_GPENCIL', False);
|
|
else: # -------------------------------------------------------------------------------------
|
|
if (context.mode not in {"SCULPT_GPENCIL"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "SCULPTMODE_HLT", 'SCULPT_GPENCIL', False);
|
|
if (context.mode not in {"PAINT_GPENCIL"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "GREASEPENCIL", 'PAINT_GPENCIL', False);
|
|
if (context.mode not in {"EDIT_GPENCIL"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "EDITMODE_HLT", 'EDIT_GPENCIL', False);
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "OBJECT_DATAMODE", 'OBJECT', False);
|
|
if (context.mode not in {"VERTEX_GPENCIL"}):
|
|
lc_add_btn(lc_swtch_row, "object.mode_set", "", "VPAINT_HLT", 'VERTEX_GPENCIL', False);
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def new_asset_path (name, path):
|
|
bpy.ops.preferences.asset_library_add();
|
|
asset = asset_libraries[''];
|
|
asset.name = name; asset.path = path;
|
|
return asset;
|
|
|
|
def remove_asset_path (name):
|
|
for i in range(0, len(asset_libraries)):
|
|
if (asset_libraries[i].name == name):
|
|
bpy.ops.preferences.asset_library_remove(i);
|
|
break;
|
|
|
|
def is_exist_asset_pack (name): return name in asset_libraries;
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def bezier (value, h1, h2):
|
|
v2 = 1 - value;
|
|
return 3.0 * (v2 ** 2.0 * value) * h1 + 3.0 * (v2 * value ** 2.0) * h2 + value ** 3.0;
|
|
|
|
def float_curve (input): return bezier(input, 0.010575, 0.2393);
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def lc_space_separator (cont, left_size, right_size, center_size = 0, math = 1):
|
|
if ver_more(2,79,5):
|
|
return cont.separator_spacer();
|
|
else:
|
|
lc_size = lc_width();
|
|
if ((left_size+right_size+center_size) < lc_size):
|
|
scale = ((lc_size-(left_size+right_size+center_size))/33)/math;
|
|
lc_separator = lc_cont_x(cont, width=scale).label(text=" ");
|
|
return lc_separator;
|
|
|
|
# --------------------------------------------------------
|
|
|
|
def get_data_display_type (obj):
|
|
if ver_less(2,79,5): return obj.data.draw_type;
|
|
else: return obj.data.display_type;
|
|
|