Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"name": "MustardUI",
"description": "Easy-to-use UI for human characters.",
"author": "Mustard",
"version": (2025, 7, 0),
"blender": (4, 2, 0),
"version": (2025, 8, 0),
"blender": (5, 0, 0),
"warning": "",
"doc_url": "https://github.com/Mustard2/MustardUI/wiki",
"category": "User Interface",
Expand Down
4 changes: 2 additions & 2 deletions blender_manifest.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
schema_version = "1.0.0"

id = "MustardUI"
version = "2025.7.0"
version = "2025.8.0"
name = "MustardUI"
tagline = "Easy-to-use UI for human characters"
maintainer = "Mustard"
Expand All @@ -11,7 +11,7 @@ website = "https://github.com/Mustard2/MustardUI/"

tags = ["User Interface"]

blender_version_min = "4.2.0"
blender_version_min = "5.0.0"

# License conforming to https://spdx.org/licenses/ (use "SPDX: prefix)
# https://docs.blender.org/manual/en/dev/advanced/extensions/licenses.html
Expand Down
1 change: 1 addition & 0 deletions custom_properties/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def outfit_switcher_poll_mesh(self, object):
outfit_piece: PointerProperty(name="Outfit Piece",
type=bpy.types.Object,
poll=outfit_switcher_poll_mesh)

outfit_enable_on_switch: BoolProperty(default=False,
name="Enable on Outfit Switch",
description="Set the value of this property to the max value when you "
Expand Down
16 changes: 16 additions & 0 deletions custom_properties/menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def draw(self, context):
for sec in rig_settings.body_custom_properties_sections:
op = layout.operator(MustardUI_Property_MenuAdd.bl_idname, text=sec.name, icon=sec.icon)
op.section = sec.name
op.outfit_is_nude = False
op.outfit = ""
op.outfit_piece = ""
op.hair_global = False
Expand All @@ -40,6 +41,7 @@ def draw(self, context):
text="Add as Global Outfit property",
icon="TRIA_RIGHT")
op.section = ""
op.outfit_is_nude = False
op.outfit = context.mustardui_propertyoutfitmenu_sel.name
op.outfit_piece = ""
op.hair_global = False
Expand All @@ -52,6 +54,7 @@ def draw(self, context):
text=obj.name[
len(context.mustardui_propertyoutfitmenu_sel.name + " - "):] if rig_settings.model_MustardUI_naming_convention else obj.name)
op.section = ""
op.outfit_is_nude = False
op.outfit = context.mustardui_propertyoutfitmenu_sel.name
op.outfit_piece = obj.name
op.hair_global = False
Expand All @@ -70,6 +73,17 @@ def draw(self, context):

layout = self.layout

if rig_settings.outfit_nude:
op = layout.operator(MustardUI_Property_MenuAdd.bl_idname,
text="Add as Nude property",
icon="TRIA_RIGHT")
op.section = ""
op.outfit_is_nude = True
op.outfit = ""
op.outfit_piece = ""
op.hair_global = False
op.hair = ""

outfit_indices = []
for i in range(0, len(rig_settings.outfits_collections)):
if rig_settings.outfits_collections[i].collection is not None:
Expand Down Expand Up @@ -108,6 +122,7 @@ def draw(self, context):
text="Add as Hair Global property",
icon="TRIA_RIGHT")
op.section = ""
op.outfit_is_nude = False
op.outfit = ""
op.outfit_piece = ""
op.hair_global = True
Expand All @@ -119,6 +134,7 @@ def draw(self, context):
text=obj.name[
len(rig_settings.hair_collection.name):] if rig_settings.model_MustardUI_naming_convention else obj.name)
op.section = ""
op.outfit_is_nude = False
op.outfit = ""
op.outfit_piece = ""
op.hair_global = False
Expand Down
6 changes: 4 additions & 2 deletions custom_properties/menus_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ def mustardui_property_menuadd(self, context):

if hasattr(context, 'button_prop') and res:

settings = bpy.context.scene.MustardUI_Settings
rig_settings = obj.MustardUI_RigSettings

layout = self.layout
Expand All @@ -15,18 +14,19 @@ def mustardui_property_menuadd(self, context):

op = layout.operator(MustardUI_Property_MenuAdd.bl_idname)
op.section = ""
op.outfit_is_nude = False
op.outfit = ""
op.outfit_piece = ""
op.hair = ""

sep = False
for collection in [x for x in rig_settings.outfits_collections if x.collection is not None]:
items = collection.collection.all_objects if rig_settings.outfit_config_subcollections else collection.collection.objects
for object in [x for x in items]:
if object == context.active_object:
op = layout.operator(MustardUI_Property_MenuAdd.bl_idname,
text="Add to " + context.active_object.name, icon="MOD_CLOTH")
op.section = ""
op.outfit_is_nude = False
op.outfit = collection.collection.name
op.outfit_piece = object.name
op.hair = ""
Expand All @@ -39,6 +39,7 @@ def mustardui_property_menuadd(self, context):
op = layout.operator(MustardUI_Property_MenuAdd.bl_idname,
text="Add to " + context.active_object.name, icon="PLUS")
op.section = ""
op.outfit_is_nude = False
op.outfit = rig_settings.extras_collection.name
op.outfit_piece = object.name
op.hair = ""
Expand All @@ -50,6 +51,7 @@ def mustardui_property_menuadd(self, context):
op = layout.operator(MustardUI_Property_MenuAdd.bl_idname,
text="Add to " + context.active_object.name, icon="STRANDS")
op.section = ""
op.outfit_is_nude = False
op.outfit = ""
op.outfit_piece = ""
op.hair = object.name
Expand Down
10 changes: 8 additions & 2 deletions custom_properties/ops_props.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class MustardUI_Property_MenuAdd(bpy.types.Operator):
bl_options = {'UNDO'}

section: StringProperty(default="")
outfit_is_nude: BoolProperty(default=False)
outfit: StringProperty(default="")
outfit_piece: StringProperty(default="")
hair: StringProperty(default="")
Expand All @@ -28,7 +29,12 @@ def poll(cls, context):
def execute(self, context):

res, obj = mustardui_active_object(context, config=1)
if self.outfit != "":

if self.outfit_is_nude and self.outfit != "":
self.report({'ERROR'}, 'MustardUI - An error occurred while adding the property (cannot be Nude and Outfit at the same time).')
return {'FINISHED'}

if self.outfit != "" or self.outfit_is_nude:
custom_props = obj.MustardUI_CustomPropertiesOutfit
elif self.hair != "" or self.hair_global:
custom_props = obj.MustardUI_CustomPropertiesHair
Expand Down Expand Up @@ -170,7 +176,7 @@ def execute(self, context):
cp.section = self.section

# Assign type
if self.outfit != "":
if self.outfit != "" or self.outfit_is_nude:
cp.cp_type = "OUTFIT"
elif self.hair != "" or self.hair_global:
cp.cp_type = "HAIR"
Expand Down
20 changes: 2 additions & 18 deletions menu/menu_body.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,9 @@
from . import MainPanel
from ..model_selection.active_object import *
from ..misc.prop_utils import *
from ..misc.ui_multiline import label_multiline
from ..warnings.ops_fix_old_UI import check_old_UI
from ..settings.rig import *
import textwrap


def _label_multiline(context, text, parent, icon):
chars = int(context.region.width / 7) # 7 pix on 1 character
wrapper = textwrap.TextWrapper(width=chars)
text_lines = wrapper.wrap(text=text)
if icon in ["", "NONE"]:
for i, text_line in enumerate(text_lines):
parent.label(text=text_line)
else:

for i, text_line in enumerate(text_lines):
if not i:
parent.label(text=text_line, icon=icon)
else:
parent.label(text=text_line, icon="BLANK1")


def draw_section(context, layout, obj, settings, rig_settings, custom_props, section, draw_sub = True):
Expand Down Expand Up @@ -49,7 +33,7 @@ def draw_section(context, layout, obj, settings, rig_settings, custom_props, sec
if not section.collapsed:
if section.description != "":
box2 = box.box()
_label_multiline(context=context, text=section.description, parent=box2,
label_multiline(context=context, text=section.description, parent=box2,
icon=section.description_icon)
for prop in custom_properties_section:
row = box.row()
Expand Down
9 changes: 8 additions & 1 deletion menu/menu_configure_others.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,16 @@ def draw(self, context):

box = layout.box()
box.label(text="Version", icon="INFO")
box.prop(rig_settings, "model_version", text="")
row = box.row(align=True)
row.prop(rig_settings, "model_version_vector", text="", expand=True)
box.prop(rig_settings, "model_version_type", text="")
box.prop(rig_settings, "model_version_date_enable")

box = layout.box()
box.label(text="Minimum Blender Version", icon="BLENDER")
row = box.row(align=True)
row.prop(rig_settings, "model_minimum_blender_version", text="")


def register():
bpy.utils.register_class(PANEL_PT_MustardUI_InitPanel_Others)
Expand Down
44 changes: 34 additions & 10 deletions menu/menu_outfits.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ def extract_items(collection, subcollections):

# Type: 0 - Standard, 1 - Locked Objects, 2 - Extras
def draw_outfit_piece(layout, obj, arm, rig_settings, physics_settings, settings, otype=0, level=0):

if otype < 0 or otype > 3:
return

Expand All @@ -36,11 +35,11 @@ def draw_outfit_piece(layout, obj, arm, rig_settings, physics_settings, settings

if rig_settings.model_MustardUI_naming_convention:
if otype == 0:
coll_name = rig_settings.outfits_list+' - '
coll_name = rig_settings.outfits_list + ' - '
elif otype == 1:
coll_name = rig_settings.model_name+' '
coll_name = rig_settings.model_name + ' '
else:
coll_name = rig_settings.extras_collection.name+' - '
coll_name = rig_settings.extras_collection.name + ' - '
row.operator("mustardui.object_visibility",
text=obj.name[len(coll_name):],
icon='OUTLINER_OB_' + obj.type, depress=not obj.hide_viewport).obj = obj.name
Expand All @@ -57,16 +56,18 @@ def draw_outfit_piece(layout, obj, arm, rig_settings, physics_settings, settings
if pi is not None:
col2 = row.column(align=True)
col2.enabled = physics_settings.enable_physics
col2.prop(pi, 'enable', text="", icon="PHYSICS" if pi.type != "COLLISION" else "MOD_PHYSICS")
col2.prop(obj.MustardUI_OutfitSettings, 'enable_pi_physics', text="",
icon="PHYSICS" if pi.type != "COLLISION" else "MOD_PHYSICS")
if pi.type != "COLLISION":
col2 = row.column(align=True)
col2.enabled = physics_settings.enable_physics
col2.prop(pi, 'collisions', text="", icon="MOD_PHYSICS")
col2.prop(obj.MustardUI_OutfitSettings, 'enable_pi_collisions', text="", icon="MOD_PHYSICS")
elif rig_settings.outfit_physics_support:
for m in obj.modifiers:
mtype = m.type
if mtype in ["CLOTH", "SOFT_BODY", "COLLISION"]:
row.prop(obj.MustardUI_OutfitSettings, 'physics', text="", icon="PHYSICS" if mtype != "COLLISION" else "MOD_PHYSICS")
row.prop(obj.MustardUI_OutfitSettings, 'physics', text="",
icon="PHYSICS" if mtype != "COLLISION" else "MOD_PHYSICS")
break

# Outfit custom properties
Expand All @@ -78,14 +79,18 @@ def draw_outfit_piece(layout, obj, arm, rig_settings, physics_settings, settings

if rig_settings.outfit_custom_properties_name_order:
custom_properties_obj = sorted([x for x in arm.MustardUI_CustomPropertiesOutfit if
(x.outfit == co_coll if otype != 1 else True) and x.outfit_piece == obj and not x.hidden],
(
x.outfit == co_coll if otype != 1 else True) and x.outfit_piece == obj and not x.hidden],
key=lambda x: x.name)
else:
custom_properties_obj = [x for x in arm.MustardUI_CustomPropertiesOutfit if
(x.outfit == co_coll if otype != 1 else True) and x.outfit_piece == obj and not x.hidden]
(
x.outfit == co_coll if otype != 1 else True) and x.outfit_piece == obj and not x.hidden]

if len(custom_properties_obj) > 0:
row.prop(obj.MustardUI_OutfitSettings, "additional_options_show" if otype != 1 else "additional_options_show_lock", toggle=True, icon="PREFERENCES")
row.prop(obj.MustardUI_OutfitSettings,
"additional_options_show" if otype != 1 else "additional_options_show_lock", toggle=True,
icon="PREFERENCES")
check_show = obj.MustardUI_OutfitSettings.additional_options_show if otype != 1 else obj.MustardUI_OutfitSettings.additional_options_show_lock
if check_show:
row2 = col.row(align=True)
Expand Down Expand Up @@ -184,6 +189,25 @@ def draw(self, context):
else:
box.label(text="This Collection seems empty", icon="ERROR")

elif rig_settings.outfit_nude and rig_settings.outfits_list == "Nude": # Outfit is nude below

if rig_settings.outfit_custom_properties_name_order:
custom_properties = sorted([x for x in arm.MustardUI_CustomPropertiesOutfit if
x.outfit is None and x.outfit_piece is None and not x.hidden and (
not x.advanced if not settings.advanced else True)],
key=lambda x: x.name)
else:
custom_properties = [x for x in arm.MustardUI_CustomPropertiesOutfit if
x.outfit is None and x.outfit_piece is None and not x.hidden and (
not x.advanced if not settings.advanced else True)]

if len(custom_properties) > 0:
row.prop(rig_settings, "outfit_global_custom_properties_collapse", text="", toggle=True,
icon="PREFERENCES")
if rig_settings.outfit_global_custom_properties_collapse:
mustardui_custom_properties_print(arm, settings, custom_properties, box,
rig_settings.outfit_custom_properties_icons)

# Locked objects
locked_objects = []
for coll in [x for x in rig_settings.outfits_collections if x.collection is not None]:
Expand Down
16 changes: 15 additions & 1 deletion menu/menu_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,21 @@ def draw(self, context):
col = box.column(align=True)
col.prop(settings, "advanced")

if rig_settings.model_version != '':
if tuple(rig_settings.model_version_vector) > (0, 0, 0):
box = layout.box()
box.label(text="Model Version: ", icon="INFO")
version = rig_settings.model_version_vector
version = str(version[0]) + "." + str(version[1]) + "." + str(version[2])
if rig_settings.model_version_type != "Standard":
version = version + " " + rig_settings.model_version_type
if rig_settings.model_version_date_enable and rig_settings.model_version_date != "":
version = version + ' - ' + rig_settings.model_version_date
prop = rig_settings.bl_rna.properties["model_version_type"]
icon = prop.enum_items[rig_settings.model_version_type].icon
box.label(text=version, icon=icon)

# Left for old compatibility (Deprecated in MustardUI 2025.8)
elif rig_settings.model_version_vector == (0, 0, 0) and rig_settings.model_version != '':
box = layout.box()
box.label(text="Model Version: ", icon="INFO")
version = rig_settings.model_version
Expand Down
18 changes: 17 additions & 1 deletion menu/menu_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
from . import MainPanel
from ..model_selection.active_object import *
from ..misc.prop_utils import *
from ..misc.ui_multiline import label_multiline
from ..warnings.ops_fix_old_UI import check_old_UI
from ..warnings.ops_fix_eevee_normals import check_eevee_normals


def check_blender_version(rig_settings):
current_blender_version = bpy.app.version
return tuple(current_blender_version) < tuple(rig_settings.model_minimum_blender_version)


class PANEL_PT_MustardUI_Warnings(MainPanel, bpy.types.Panel):
bl_idname = "PANEL_PT_MustardUI_Warnings"
bl_label = "Warnings"
Expand All @@ -26,7 +32,8 @@ def poll(cls, context):

rig_settings = obj.MustardUI_RigSettings

return poll and (check_eevee_normals(context.scene, settings) or rig_settings.diffeomorphic_support)
return poll and (check_eevee_normals(context.scene, settings) or rig_settings.diffeomorphic_support or
check_blender_version(rig_settings))

return poll

Expand Down Expand Up @@ -67,6 +74,15 @@ def draw(self, context):

box.operator("mustardui.warnings_fix_old_morphs", icon="TRIA_DOWN_BAR").ignore = False

if check_blender_version(rig_settings):
box = layout.box()
col = box.column(align=True)
mbv = rig_settings.model_minimum_blender_version
col.label(text="Minimum Blender version: " + str(mbv[0]) + "." + str(mbv[1]) + "." + str(mbv[2]),
icon="BLENDER")
col.label(text="The model might not work properly.", icon="BLANK1")
col.label(text="Update Blender to the latest version.", icon="BLANK1")


def register():
bpy.utils.register_class(PANEL_PT_MustardUI_Warnings)
Expand Down
Loading