[feat] add more virtools group related operators
- support select/filter by virtools group. - support group/ungroup/clear in object context menu. - improve virtools group visit functions
This commit is contained in:
parent
240d5612df
commit
02c11ffe5a
@ -332,9 +332,9 @@ def import_bm(context, bmx_filepath, prefs_fncg, prefs_externalTexture, prefs_te
|
||||
|
||||
# write custom property
|
||||
if len(object_groupList) != 0:
|
||||
UTILS_virtools_prop.set_virtools_group_data(object_target, tuple(object_groupList))
|
||||
UTILS_virtools_prop.fill_virtools_group_data(object_target, tuple(object_groupList))
|
||||
else:
|
||||
UTILS_virtools_prop.set_virtools_group_data(object_target, None)
|
||||
UTILS_virtools_prop.fill_virtools_group_data(object_target, None)
|
||||
|
||||
# update view layer after all objects has been imported
|
||||
blender_viewLayer.update()
|
||||
|
@ -2,7 +2,7 @@ import bpy, mathutils
|
||||
from . import UTILS_functions
|
||||
|
||||
class BALLANCE_OT_super_align(bpy.types.Operator):
|
||||
"""Align object with 3ds Max way"""
|
||||
"""Align object with 3ds Max style"""
|
||||
bl_idname = "ballance.super_align"
|
||||
bl_label = "3ds Max Align"
|
||||
bl_options = {'UNDO'}
|
||||
@ -12,7 +12,7 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
|
||||
align_z: bpy.props.BoolProperty(name="Z position")
|
||||
|
||||
current_references: bpy.props.EnumProperty(
|
||||
name="Reference",
|
||||
name="Reference (Active Object)",
|
||||
items=(('MIN', "Min", ""),
|
||||
('CENTER', "Center (bound box)", ""),
|
||||
('POINT', "Center (axis)", ""),
|
||||
@ -21,7 +21,7 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
|
||||
)
|
||||
|
||||
target_references: bpy.props.EnumProperty(
|
||||
name="Target",
|
||||
name="Target (Other Objects)",
|
||||
items=(('MIN', "Min", ""),
|
||||
('CENTER', "Center (bound box)", ""),
|
||||
('POINT', "Center (axis)", ""),
|
||||
|
@ -436,7 +436,7 @@ def _set_for_group(obj, name_info):
|
||||
|
||||
|
||||
# apply to custom property
|
||||
UTILS_virtools_prop.set_virtools_group_data(obj, tuple(gps))
|
||||
UTILS_virtools_prop.fill_virtools_group_data(obj, tuple(gps))
|
||||
|
||||
# ==========================================
|
||||
# assemble funcs
|
||||
|
221
ballance_blender_plugin/OBJS_group_opers.py
Normal file
221
ballance_blender_plugin/OBJS_group_opers.py
Normal file
@ -0,0 +1,221 @@
|
||||
import bpy
|
||||
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop
|
||||
|
||||
class common_group_name_props(bpy.types.Operator):
|
||||
use_custom_name: bpy.props.BoolProperty(
|
||||
name="Use Custom Name",
|
||||
description="Whether use user defined group name.",
|
||||
default=False,
|
||||
)
|
||||
|
||||
group_name: bpy.props.EnumProperty(
|
||||
name="Group Name",
|
||||
description="Pick vanilla Ballance group name.",
|
||||
items=tuple((x, x, "") for x in UTILS_constants.propsVtGroups_availableGroups),
|
||||
)
|
||||
|
||||
custom_group_name: bpy.props.StringProperty(
|
||||
name="Custom Group Name",
|
||||
description="Input your custom group name.",
|
||||
default="",
|
||||
)
|
||||
|
||||
def get_group_name_string(self):
|
||||
return str(self.custom_group_name if self.use_custom_name else self.group_name)
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
class BALLANCE_OT_select_virtools_group(common_group_name_props):
|
||||
"""Select objects by Virtools Group."""
|
||||
bl_idname = "ballance.select_virtools_group"
|
||||
bl_label = "Select by Virtools Group"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
merge_selection: bpy.props.BoolProperty(
|
||||
name="Merge Selection",
|
||||
description="Merge selection, rather than re-select them.",
|
||||
default=False,
|
||||
)
|
||||
|
||||
ignore_hide: bpy.props.BoolProperty(
|
||||
name="Ignore Hide Property",
|
||||
description="Select objects without considering visibility.",
|
||||
default=False,
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
# iterate object
|
||||
for obj in bpy.context.scene.objects:
|
||||
# ignore hidden objects
|
||||
if (not self.ignore_hide) and obj.hide_get() == True:
|
||||
continue
|
||||
|
||||
# check group
|
||||
if UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string()):
|
||||
# select object
|
||||
obj.select_set(True)
|
||||
else:
|
||||
# if not in merge mode, deselect them
|
||||
if not self.merge_selection:
|
||||
obj.select_set(False)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self, 'ignore_hide')
|
||||
row.prop(self, 'merge_selection')
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, 'use_custom_name')
|
||||
if (self.use_custom_name):
|
||||
layout.prop(self, 'custom_group_name')
|
||||
else:
|
||||
layout.prop(self, 'group_name')
|
||||
|
||||
class BALLANCE_OT_filter_virtools_group(common_group_name_props):
|
||||
"""Filter objects by Virtools Group."""
|
||||
bl_idname = "ballance.filter_virtools_group"
|
||||
bl_label = "Filter by Virtools Group"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
reverse_selection: bpy.props.BoolProperty(
|
||||
name="Reverse",
|
||||
description="Reverse operation. Remove matched objects.",
|
||||
default=False,
|
||||
)
|
||||
|
||||
ignore_hide: bpy.props.BoolProperty(
|
||||
name="Ignore Hide Property",
|
||||
description="Select objects without considering visibility.",
|
||||
default=False,
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
# make a copy for all objects, to ensure it is not viotile
|
||||
# becuase we need deselect some objects in for statement
|
||||
selected = bpy.context.selected_objects[:]
|
||||
# iterate object
|
||||
for obj in selected:
|
||||
# ignore hidden objects
|
||||
if (not self.ignore_hide) and obj.hide_get() == True:
|
||||
continue
|
||||
|
||||
# check group and decide select
|
||||
is_selected = UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string())
|
||||
if self.reverse_selection:
|
||||
is_selected = not is_selected
|
||||
|
||||
# select object
|
||||
obj.select_set(is_selected)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self, 'ignore_hide')
|
||||
row.prop(self, 'reverse_selection')
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, 'use_custom_name')
|
||||
if (self.use_custom_name):
|
||||
layout.prop(self, 'custom_group_name')
|
||||
else:
|
||||
layout.prop(self, 'group_name')
|
||||
|
||||
|
||||
|
||||
class BALLANCE_OT_ctx_set_group(common_group_name_props):
|
||||
"""Grouping selected objects"""
|
||||
bl_idname = "ballance.ctx_set_group"
|
||||
bl_label = "Grouping Objects"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
return len(bpy.context.selected_objects) != 0
|
||||
|
||||
def execute(self, context):
|
||||
has_duplicated = False
|
||||
|
||||
# iterate object
|
||||
for obj in bpy.context.selected_objects:
|
||||
# try setting
|
||||
if not UTILS_virtools_prop.add_virtools_group_data(obj, self.get_group_name_string()):
|
||||
has_duplicated = True
|
||||
|
||||
# throw a warning if some objects have duplicated group
|
||||
if has_duplicated:
|
||||
UTILS_functions.show_message_box(("Some objects have duplicated group name.", "These objects have been omitted.", ), "Duplicated Group", 'ERROR')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, 'use_custom_name')
|
||||
if (self.use_custom_name):
|
||||
layout.prop(self, 'custom_group_name')
|
||||
else:
|
||||
layout.prop(self, 'group_name')
|
||||
|
||||
class BALLANCE_OT_ctx_unset_group(common_group_name_props):
|
||||
"""Ungrouping selected objects"""
|
||||
bl_idname = "ballance.ctx_unset_group"
|
||||
bl_label = "Ungrouping Objects"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
return len(bpy.context.selected_objects) != 0
|
||||
|
||||
def execute(self, context):
|
||||
lack_group = False
|
||||
|
||||
# iterate object
|
||||
for obj in bpy.context.selected_objects:
|
||||
# try unsetting
|
||||
if not UTILS_virtools_prop.remove_virtools_group_data(obj, self.get_group_name_string()):
|
||||
lack_group = True
|
||||
|
||||
# throw a warning if some objects have duplicated group
|
||||
if lack_group:
|
||||
UTILS_functions.show_message_box(("Some objects lack specified group name.", "These objects have been omitted.", ), "Lack Group", 'ERROR')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, 'use_custom_name')
|
||||
if (self.use_custom_name):
|
||||
layout.prop(self, 'custom_group_name')
|
||||
else:
|
||||
layout.prop(self, 'group_name')
|
||||
|
||||
class BALLANCE_OT_ctx_clear_group(bpy.types.Operator):
|
||||
"""Clear Virtools Groups for selected objects"""
|
||||
bl_idname = "ballance.ctx_clear_group"
|
||||
bl_label = "Clear Grouping"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
return len(bpy.context.selected_objects) != 0
|
||||
|
||||
def execute(self, context):
|
||||
# iterate object
|
||||
for obj in bpy.context.selected_objects:
|
||||
UTILS_virtools_prop.clear_virtools_group_data(obj)
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
|
@ -7,22 +7,36 @@ class BALLANCE_OT_add_virtools_group(bpy.types.Operator):
|
||||
bl_label = "Add Virtools Group"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
use_custom_name: bpy.props.BoolProperty(
|
||||
name="Use Custom Name",
|
||||
description="Whether use user defined group name.",
|
||||
default=False,
|
||||
)
|
||||
|
||||
group_name: bpy.props.EnumProperty(
|
||||
name="Group Name",
|
||||
description="Group name. For custom group name, please pick `CustomCKGroup` and change it later.",
|
||||
description="Pick vanilla Ballance group name.",
|
||||
items=tuple((x, x, "") for x in UTILS_constants.propsVtGroups_availableGroups),
|
||||
)
|
||||
|
||||
custom_group_name: bpy.props.StringProperty(
|
||||
name="Custom Group Name",
|
||||
description="Input your custom group name.",
|
||||
default="",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
return context.object is not None
|
||||
|
||||
def execute(self, context):
|
||||
# get name first
|
||||
gotten_group_name = str(self.custom_group_name if self.use_custom_name else self.group_name)
|
||||
|
||||
# try adding
|
||||
obj = context.object
|
||||
gp = UTILS_virtools_prop.get_virtools_group(obj)
|
||||
item = gp.add()
|
||||
item.name = ""
|
||||
item.group_name = str(self.group_name)
|
||||
if not UTILS_virtools_prop.add_virtools_group_data(obj, gotten_group_name):
|
||||
UTILS_functions.show_message_box(("Group name is duplicated!", ), "Duplicated Name", 'ERROR')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@ -31,7 +45,12 @@ class BALLANCE_OT_add_virtools_group(bpy.types.Operator):
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.prop(self, 'group_name')
|
||||
self.layout.prop(self, 'use_custom_name')
|
||||
if (self.use_custom_name):
|
||||
self.layout.prop(self, 'custom_group_name')
|
||||
else:
|
||||
self.layout.prop(self, 'group_name')
|
||||
|
||||
|
||||
class BALLANCE_OT_rm_virtools_group(bpy.types.Operator):
|
||||
"""Remove a Virtools Group for Active Object."""
|
||||
@ -44,24 +63,14 @@ class BALLANCE_OT_rm_virtools_group(bpy.types.Operator):
|
||||
if context.object is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
obj = context.object
|
||||
gp = UTILS_virtools_prop.get_virtools_group(obj)
|
||||
active_gp = UTILS_virtools_prop.get_active_virtools_group(obj)
|
||||
data = gp[active_gp]
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.object
|
||||
gp = UTILS_virtools_prop.get_virtools_group(obj)
|
||||
active_gp = UTILS_virtools_prop.get_active_virtools_group(obj)
|
||||
idx = int(active_gp)
|
||||
return int(active_gp) >= 0 and int(active_gp) < len(gp)
|
||||
|
||||
active_gp -= 1
|
||||
gp.remove(idx)
|
||||
def execute(self, context):
|
||||
obj = context.object
|
||||
UTILS_virtools_prop.remove_virtools_group_data_by_index(obj, int(UTILS_virtools_prop.get_active_virtools_group(obj)))
|
||||
return {'FINISHED'}
|
||||
|
||||
class BALLANCE_UL_virtools_group(bpy.types.UIList):
|
||||
|
@ -345,9 +345,7 @@ propsVtGroups_availableGroups = (
|
||||
|
||||
"Phys_Floors",
|
||||
"Phys_FloorRails",
|
||||
"Phys_FloorStopper",
|
||||
|
||||
"CustomCKGroup"
|
||||
"Phys_FloorStopper"
|
||||
)
|
||||
|
||||
|
||||
|
@ -98,19 +98,82 @@ def get_active_virtools_group(obj):
|
||||
def get_virtools_group(obj):
|
||||
return obj.virtools_group
|
||||
|
||||
def check_virtools_group_data(obj, probe):
|
||||
for item in get_virtools_group(obj):
|
||||
if probe == str(item.group_name):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def add_virtools_group_data(obj, new_data):
|
||||
# check exist
|
||||
if check_virtools_group_data(obj, new_data):
|
||||
# existed, give up
|
||||
return False
|
||||
|
||||
# "add" do not need operate active_virtools_group
|
||||
data = get_virtools_group(obj)
|
||||
it = data.add()
|
||||
it.name = ""
|
||||
it.group_name = new_data
|
||||
|
||||
return True
|
||||
|
||||
def remove_virtools_group_data(obj, rm_data):
|
||||
gp = get_virtools_group(obj)
|
||||
active_gp = get_active_virtools_group(obj)
|
||||
|
||||
for idx, item in enumerate(gp):
|
||||
if rm_data == str(item.group_name):
|
||||
# decrease active group if removed item is ahead of active group
|
||||
if idx <= active_gp:
|
||||
active_gp -= 1
|
||||
# remove
|
||||
gp.remove(idx)
|
||||
# indicate success
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def remove_virtools_group_data_by_index(obj, rm_idx):
|
||||
gp = get_virtools_group(obj)
|
||||
active_gp = get_active_virtools_group(obj)
|
||||
|
||||
# report error
|
||||
if rm_idx >= len(gp):
|
||||
return False
|
||||
|
||||
# remove
|
||||
if rm_idx <= active_gp:
|
||||
active_gp -= 1
|
||||
gp.remove(rm_idx)
|
||||
return True
|
||||
|
||||
def clear_virtools_group_data(obj):
|
||||
gp = get_virtools_group(obj)
|
||||
active_gp = get_active_virtools_group(obj)
|
||||
|
||||
gp.clear()
|
||||
active_gp = 0
|
||||
|
||||
def fill_virtools_group_data(obj, data_list):
|
||||
# clear first
|
||||
clear_virtools_group_data(obj)
|
||||
|
||||
# if no data to add, return
|
||||
if data_list is None:
|
||||
return
|
||||
|
||||
# add one by one after check duplication
|
||||
data = get_virtools_group(obj)
|
||||
for item in set(data_list):
|
||||
it = data.add()
|
||||
it.name = ""
|
||||
it.group_name = item
|
||||
|
||||
def get_virtools_group_data(obj):
|
||||
return tuple(str(item.group_name) for item in get_virtools_group(obj))
|
||||
|
||||
def set_virtools_group_data(obj, new_data):
|
||||
data = get_virtools_group(obj)
|
||||
data.clear()
|
||||
|
||||
if new_data is not None:
|
||||
for item in new_data:
|
||||
it = data.add()
|
||||
it.name = ""
|
||||
it.group_name = item
|
||||
|
||||
def register_props():
|
||||
bpy.types.Object.virtools_group = bpy.props.CollectionProperty(type=BALLANCE_PG_virtools_group)
|
||||
bpy.types.Object.active_virtools_group = bpy.props.IntProperty()
|
||||
|
@ -50,6 +50,8 @@ if "bpy" in locals():
|
||||
importlib.reload(OBJS_add_floors)
|
||||
if "OBJS_add_rails" in locals():
|
||||
importlib.reload(OBJS_add_rails)
|
||||
if "OBJS_group_opers" in locals():
|
||||
importlib.reload(OBJS_group_opers)
|
||||
|
||||
if "NAMES_rename_system" in locals():
|
||||
importlib.reload(NAMES_rename_system)
|
||||
@ -62,7 +64,7 @@ if "bpy" in locals():
|
||||
from . import UTILS_constants, UTILS_functions, UTILS_preferences, UTILS_virtools_prop
|
||||
from . import BMFILE_export, BMFILE_import
|
||||
from . import MODS_3dsmax_align, MODS_flatten_uv, MODS_rail_uv
|
||||
from . import OBJS_add_components, OBJS_add_floors, OBJS_add_rails
|
||||
from . import OBJS_add_components, OBJS_add_floors, OBJS_add_rails, OBJS_group_opers
|
||||
from . import NAMES_rename_system
|
||||
from . import PROPS_virtools_group, PROPS_virtools_material
|
||||
|
||||
@ -149,7 +151,13 @@ classes = (
|
||||
PROPS_virtools_group.BALLANCE_UL_virtools_group,
|
||||
PROPS_virtools_group.BALLANCE_PT_virtools_group,
|
||||
PROPS_virtools_material.BALLANCE_OT_apply_virtools_material,
|
||||
PROPS_virtools_material.BALLANCE_PT_virtools_material
|
||||
PROPS_virtools_material.BALLANCE_PT_virtools_material,
|
||||
|
||||
OBJS_group_opers.BALLANCE_OT_select_virtools_group,
|
||||
OBJS_group_opers.BALLANCE_OT_filter_virtools_group,
|
||||
OBJS_group_opers.BALLANCE_OT_ctx_set_group,
|
||||
OBJS_group_opers.BALLANCE_OT_ctx_unset_group,
|
||||
OBJS_group_opers.BALLANCE_OT_ctx_clear_group,
|
||||
|
||||
)
|
||||
|
||||
@ -172,6 +180,21 @@ def menu_func_ballance_add(self, context):
|
||||
def menu_func_ballance_rename(self, context):
|
||||
layout = self.layout
|
||||
layout.menu(BALLANCE_MT_OutlinerMenu.bl_idname)
|
||||
def menu_func_ballance_select(self, context):
|
||||
layout = self.layout
|
||||
layout.separator()
|
||||
layout.label(text="Ballance")
|
||||
layout.operator(OBJS_group_opers.BALLANCE_OT_select_virtools_group.bl_idname, icon='SELECT_SET')
|
||||
layout.operator(OBJS_group_opers.BALLANCE_OT_filter_virtools_group.bl_idname, icon='FILTER')
|
||||
def menu_func_ballance_grouping(self, context):
|
||||
layout = self.layout
|
||||
layout.separator()
|
||||
col = layout.column()
|
||||
col.operator_context = 'INVOKE_DEFAULT'
|
||||
col.label(text="Ballance")
|
||||
col.operator(OBJS_group_opers.BALLANCE_OT_ctx_set_group.bl_idname, icon='ADD', text="Group into...")
|
||||
col.operator(OBJS_group_opers.BALLANCE_OT_ctx_unset_group.bl_idname, icon='REMOVE', text="Ungroup from...")
|
||||
col.operator(OBJS_group_opers.BALLANCE_OT_ctx_clear_group.bl_idname, icon='TRASH', text="Clear Grouping")
|
||||
|
||||
|
||||
def register():
|
||||
@ -196,6 +219,9 @@ def register():
|
||||
bpy.types.VIEW3D_MT_add.append(menu_func_ballance_add)
|
||||
bpy.types.OUTLINER_HT_header.append(menu_func_ballance_rename)
|
||||
|
||||
bpy.types.VIEW3D_MT_select_object.append(menu_func_ballance_select)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.append(menu_func_ballance_grouping)
|
||||
|
||||
def unregister():
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_bm_import)
|
||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_bm_export)
|
||||
@ -204,6 +230,9 @@ def unregister():
|
||||
bpy.types.VIEW3D_MT_add.remove(menu_func_ballance_add)
|
||||
bpy.types.OUTLINER_HT_header.remove(menu_func_ballance_rename)
|
||||
|
||||
bpy.types.VIEW3D_MT_select_object.remove(menu_func_ballance_select)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.remove(menu_func_ballance_grouping)
|
||||
|
||||
UTILS_virtools_prop.unregister_props()
|
||||
del bpy.types.Scene.BallanceBlenderPluginProperty
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user