fix issue. add feat.

- add ignored bmesh update function in flatten uv
- finish select by group, add, rm, clear group functions.
This commit is contained in:
yyc12345 2023-12-30 22:17:42 +08:00
parent c5ce6e8a2c
commit 20aba6c273
5 changed files with 244 additions and 14 deletions

3
.gitattributes vendored
View File

@ -2,5 +2,6 @@
*.png binary *.png binary
# our generated mesh should be save as binary # our generated mesh should be save as binary
*.bin binary *.bin binary
# the compressed json data should be binary # the raw json data should be binary
# although i edit it manually
bbp_ng/raw_jsons/*.json binary bbp_ng/raw_jsons/*.json binary

View File

@ -0,0 +1,198 @@
import bpy
import enum
from . import PROP_virtools_group
from . import UTIL_functions
#region Select by Group
class SelectMode(enum.IntEnum):
Set = enum.auto()
Extend = enum.auto()
Subtract = enum.auto()
Difference = enum.auto()
Intersect = enum.auto()
_g_SelectModeDesc: dict[SelectMode, tuple[str, str, str]] = {
SelectMode.Set: ('Set', 'Sets a new selection.', 'SELECT_SET'),
SelectMode.Extend: ('Extend', 'Adds newly selected items to the existing selection.', 'SELECT_EXTEND'),
SelectMode.Subtract: ('Subtract', 'Removes newly selected items from the existing selection.', 'SELECT_SUBTRACT'),
SelectMode.Difference: ('Invert', 'Inverts the selection.', 'SELECT_DIFFERENCE'),
SelectMode.Intersect: ('Intersect', 'Selects items that intersect with the existing selection.', 'SELECT_INTERSECT')
}
_g_EnumHelper_SelectMode: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
SelectMode,
lambda x: str(x.value),
lambda x: SelectMode(int(x)),
lambda x: _g_SelectModeDesc[x][0],
lambda x: _g_SelectModeDesc[x][1],
lambda x: _g_SelectModeDesc[x][2]
)
class BBP_OT_select_object_by_virtools_group(bpy.types.Operator, PROP_virtools_group.SharedGroupNameInputProperties):
"""Select Objects by Virtools Group"""
bl_idname = "bbp.select_object_by_virtools_group"
bl_label = "Select by Virtools Group"
bl_options = {'UNDO'}
selection_mode: bpy.props.EnumProperty(
name = "Mode",
description = "Selection mode",
items = _g_EnumHelper_SelectMode.generate_items(),
default = _g_EnumHelper_SelectMode.to_selection(SelectMode.Intersect)
)
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def execute(self, context):
_select_object_by_virtools_group(
self.general_get_group_name(),
_g_EnumHelper_SelectMode.get_selection(self.selection_mode)
)
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.label(text='Selection Mode')
layout.prop(self, 'selection_mode', expand = True)
layout.separator()
layout.label(text='Group Parameters')
self.draw_group_name_input(layout)
def _select_object_by_virtools_group(group_name: str, mode: SelectMode) -> None:
match(mode):
case SelectMode.Set:
# iterate all objects and directly set
for obj in bpy.context.scene.objects:
# check group and decide whether select this obj
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
obj.select_set(gp.contain_group(group_name))
case SelectMode.Extend:
# also iterate all objects
for obj in bpy.context.scene.objects:
# but only increase selection, for selected object, skip check
if obj.select_get(): continue
# if not selected, check whether add it.
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
if gp.contain_group(group_name):
obj.select_set(True)
case SelectMode.Subtract:
# subtract only involving selected item. so we get selected objest first
# and copy it (because we need modify it)
# and iterate it to reduce useless operations
selected = bpy.context.selected_objects[:]
for obj in selected:
# remove matched only
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
if gp.contain_group(group_name):
obj.select_set(False)
case SelectMode.Difference:
# construct a selected obj set for convenient operations
selected_set = set(bpy.context.selected_objects)
# iterate all objects
for obj in bpy.context.scene.objects:
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
# use xor to select
# in_selected XOR in_group
obj.select_set((obj in selected_set) ^ gp.contain_group(group_name))
case SelectMode.Intersect:
# like subtract, only iterate selected obj
selected = bpy.context.selected_objects[:]
for obj in selected:
# but remove not matched
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
if not gp.contain_group(group_name):
obj.select_set(False)
case _:
raise UTIL_functions.BBPException('invalid selection mode')
#endregion
#region Objects Group Opers
class BBP_OT_add_objects_virtools_group(bpy.types.Operator, PROP_virtools_group.SharedGroupNameInputProperties):
"""Grouping Selected Objects"""
bl_idname = "bbp.add_objects_virtools_group"
bl_label = "Grouping Objects"
bl_options = {'UNDO'}
@classmethod
def poll(self, context):
return len(bpy.context.selected_objects) != 0
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def execute(self, context):
group_name: str = self.general_get_group_name()
for obj in bpy.context.selected_objects:
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
gp.add_group(group_name)
return {'FINISHED'}
def draw(self, context):
self.draw_group_name_input(self.layout)
class BBP_OT_rm_objects_virtools_group(bpy.types.Operator, PROP_virtools_group.SharedGroupNameInputProperties):
"""Ungrouping Selected Objects"""
bl_idname = "bbp.rm_objects_virtools_group"
bl_label = "Ungrouping Objects"
bl_options = {'UNDO'}
@classmethod
def poll(self, context):
return len(bpy.context.selected_objects) != 0
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def execute(self, context):
group_name: str = self.general_get_group_name()
for obj in bpy.context.selected_objects:
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
gp.remove_group(group_name)
return {'FINISHED'}
def draw(self, context):
self.draw_group_name_input(self.layout)
class BBP_OT_clear_objects_virtools_group(bpy.types.Operator):
"""Clear Virtools Groups on Selected Objects"""
bl_idname = "bbp.clear_objects_virtools_group"
bl_label = "Clear All Groups"
bl_options = {'UNDO'}
@classmethod
def poll(self, context):
return len(bpy.context.selected_objects) != 0
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_confirm(self, event)
def execute(self, context):
# iterate object
for obj in bpy.context.selected_objects:
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as gp:
gp.clear_groups()
return {'FINISHED'}
#endregion
def register():
bpy.utils.register_class(BBP_OT_select_object_by_virtools_group)
bpy.utils.register_class(BBP_OT_add_objects_virtools_group)
bpy.utils.register_class(BBP_OT_rm_objects_virtools_group)
bpy.utils.register_class(BBP_OT_clear_objects_virtools_group)
def unregister():
bpy.utils.unregister_class(BBP_OT_clear_objects_virtools_group)
bpy.utils.unregister_class(BBP_OT_rm_objects_virtools_group)
bpy.utils.unregister_class(BBP_OT_add_objects_virtools_group)
bpy.utils.unregister_class(BBP_OT_select_object_by_virtools_group)

View File

@ -301,6 +301,8 @@ def _real_flatten_uv(mesh: bpy.types.Mesh, reference_edge: int, scale_data: _Fla
# just get abs for the u component # just get abs for the u component
_set_face_vertex_uv(face, uv_layer, idx, (abs(ppuv.x), ppuv.y)) _set_face_vertex_uv(face, uv_layer, idx, (abs(ppuv.x), ppuv.y))
# show the updates in the viewport
bmesh.update_edit_mesh(mesh)
# return process result # return process result
return no_processed_count return no_processed_count

View File

@ -290,7 +290,7 @@ class BBP_UL_virtools_groups(bpy.types.UIList):
def draw_item(self, context, layout: bpy.types.UILayout, data, item: BBP_PG_virtools_group, icon, active_data, active_propname): def draw_item(self, context, layout: bpy.types.UILayout, data, item: BBP_PG_virtools_group, icon, active_data, active_propname):
layout.label(text = item.group_name, translate = False, icon_value = _get_group_icon_by_name(item.group_name)) layout.label(text = item.group_name, translate = False, icon_value = _get_group_icon_by_name(item.group_name))
class BBP_OT_add_virtools_groups(bpy.types.Operator, SharedGroupNameInputProperties): class BBP_OT_add_virtools_group(bpy.types.Operator, SharedGroupNameInputProperties):
"""Add a Virtools Group for Active Object.""" """Add a Virtools Group for Active Object."""
bl_idname = "bbp.add_virtools_groups" bl_idname = "bbp.add_virtools_groups"
bl_label = "Add to Virtools Groups" bl_label = "Add to Virtools Groups"
@ -313,7 +313,7 @@ class BBP_OT_add_virtools_groups(bpy.types.Operator, SharedGroupNameInputPropert
def draw(self, context): def draw(self, context):
self.draw_group_name_input(self.layout) self.draw_group_name_input(self.layout)
class BBP_OT_rm_virtools_groups(bpy.types.Operator): class BBP_OT_rm_virtools_group(bpy.types.Operator):
"""Remove a Virtools Group for Active Object.""" """Remove a Virtools Group for Active Object."""
bl_idname = "bbp.rm_virtools_groups" bl_idname = "bbp.rm_virtools_groups"
bl_label = "Remove from Virtools Groups" bl_label = "Remove from Virtools Groups"
@ -389,8 +389,8 @@ class BBP_PT_virtools_groups(bpy.types.Panel):
) )
col = row.column(align=True) col = row.column(align=True)
col.operator(BBP_OT_add_virtools_groups.bl_idname, icon='ADD', text="") col.operator(BBP_OT_add_virtools_group.bl_idname, icon='ADD', text="")
col.operator(BBP_OT_rm_virtools_groups.bl_idname, icon='REMOVE', text="") col.operator(BBP_OT_rm_virtools_group.bl_idname, icon='REMOVE', text="")
col.separator() col.separator()
col.operator(BBP_OT_clear_virtools_groups.bl_idname, icon='TRASH', text="") col.operator(BBP_OT_clear_virtools_groups.bl_idname, icon='TRASH', text="")
@ -400,8 +400,8 @@ def register():
# register all classes # register all classes
bpy.utils.register_class(BBP_PG_virtools_group) bpy.utils.register_class(BBP_PG_virtools_group)
bpy.utils.register_class(BBP_UL_virtools_groups) bpy.utils.register_class(BBP_UL_virtools_groups)
bpy.utils.register_class(BBP_OT_add_virtools_groups) bpy.utils.register_class(BBP_OT_add_virtools_group)
bpy.utils.register_class(BBP_OT_rm_virtools_groups) bpy.utils.register_class(BBP_OT_rm_virtools_group)
bpy.utils.register_class(BBP_OT_clear_virtools_groups) bpy.utils.register_class(BBP_OT_clear_virtools_groups)
bpy.utils.register_class(BBP_PT_virtools_groups) bpy.utils.register_class(BBP_PT_virtools_groups)
@ -416,7 +416,7 @@ def unregister():
bpy.utils.unregister_class(BBP_PT_virtools_groups) bpy.utils.unregister_class(BBP_PT_virtools_groups)
bpy.utils.unregister_class(BBP_OT_clear_virtools_groups) bpy.utils.unregister_class(BBP_OT_clear_virtools_groups)
bpy.utils.unregister_class(BBP_OT_rm_virtools_groups) bpy.utils.unregister_class(BBP_OT_rm_virtools_group)
bpy.utils.unregister_class(BBP_OT_add_virtools_groups) bpy.utils.unregister_class(BBP_OT_add_virtools_group)
bpy.utils.unregister_class(BBP_UL_virtools_groups) bpy.utils.unregister_class(BBP_UL_virtools_groups)
bpy.utils.unregister_class(BBP_PG_virtools_group) bpy.utils.unregister_class(BBP_PG_virtools_group)

View File

@ -33,7 +33,7 @@ from . import PROP_preferences, PROP_ptrprop_resolver, PROP_virtools_material, P
from . import OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools from . import OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools
from . import OP_UV_flatten_uv, OP_UV_rail_uv from . import OP_UV_flatten_uv, OP_UV_rail_uv
from . import OP_ADDS_component, OP_ADDS_bme from . import OP_ADDS_component, OP_ADDS_bme
from . import OP_OBJECT_legacy_align from . import OP_OBJECT_legacy_align, OP_OBJECT_virtools_group
#region Menu #region Menu
@ -46,9 +46,15 @@ class BBP_MT_View3DMenu(bpy.types.Menu):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.label(text = 'UV', icon = 'UV')
layout.operator(OP_UV_flatten_uv.BBP_OT_flatten_uv.bl_idname) layout.operator(OP_UV_flatten_uv.BBP_OT_flatten_uv.bl_idname)
layout.operator(OP_UV_rail_uv.BBP_OT_rail_uv.bl_idname) layout.operator(OP_UV_rail_uv.BBP_OT_rail_uv.bl_idname)
layout.separator()
layout.label(text = 'Align', icon = 'SNAP_ON')
layout.operator(OP_OBJECT_legacy_align.BBP_OT_legacy_align.bl_idname) layout.operator(OP_OBJECT_legacy_align.BBP_OT_legacy_align.bl_idname)
layout.separator()
layout.label(text = 'Select', icon = 'SELECT_SET')
layout.operator(OP_OBJECT_virtools_group.BBP_OT_select_object_by_virtools_group.bl_idname)
class BBP_MT_AddBmeMenu(bpy.types.Menu): class BBP_MT_AddBmeMenu(bpy.types.Menu):
"""Add Ballance Floor""" """Add Ballance Floor"""
@ -95,27 +101,44 @@ class BBP_MT_AddComponentsMenu(bpy.types.Menu):
MenuDrawer_t = typing.Callable[[typing.Any, typing.Any], None] MenuDrawer_t = typing.Callable[[typing.Any, typing.Any], None]
def menu_drawer_import(self, context): def menu_drawer_import(self, context) -> None:
layout: bpy.types.UILayout = self.layout layout: bpy.types.UILayout = self.layout
layout.operator(OP_IMPORT_bmfile.BBP_OT_import_bmfile.bl_idname, text = "Ballance Map (.bmx)") layout.operator(OP_IMPORT_bmfile.BBP_OT_import_bmfile.bl_idname, text = "Ballance Map (.bmx)")
layout.operator(OP_IMPORT_virtools.BBP_OT_import_virtools.bl_idname, text = "Virtools File (.nmo/.cmo/.vmo) (experimental)") layout.operator(OP_IMPORT_virtools.BBP_OT_import_virtools.bl_idname, text = "Virtools File (.nmo/.cmo/.vmo) (experimental)")
def menu_drawer_export(self, context): def menu_drawer_export(self, context) -> None:
layout: bpy.types.UILayout = self.layout layout: bpy.types.UILayout = self.layout
layout.operator(OP_EXPORT_bmfile.BBP_OT_export_bmfile.bl_idname, text = "Ballance Map (.bmx)") layout.operator(OP_EXPORT_bmfile.BBP_OT_export_bmfile.bl_idname, text = "Ballance Map (.bmx)")
layout.operator(OP_EXPORT_virtools.BBP_OT_export_virtools.bl_idname, text = "Virtools File (.nmo/.cmo/.vmo) (experimental)") layout.operator(OP_EXPORT_virtools.BBP_OT_export_virtools.bl_idname, text = "Virtools File (.nmo/.cmo/.vmo) (experimental)")
def menu_drawer_view3d(self, context): def menu_drawer_view3d(self, context) -> None:
layout: bpy.types.UILayout = self.layout layout: bpy.types.UILayout = self.layout
layout.menu(BBP_MT_View3DMenu.bl_idname) layout.menu(BBP_MT_View3DMenu.bl_idname)
def menu_drawer_add(self, context): def menu_drawer_add(self, context) -> None:
layout: bpy.types.UILayout = self.layout layout: bpy.types.UILayout = self.layout
layout.separator() layout.separator()
layout.label(text="Ballance") layout.label(text="Ballance")
layout.menu(BBP_MT_AddBmeMenu.bl_idname, icon='MESH_CUBE') layout.menu(BBP_MT_AddBmeMenu.bl_idname, icon='MESH_CUBE')
layout.menu(BBP_MT_AddRailMenu.bl_idname, icon='MESH_CIRCLE') layout.menu(BBP_MT_AddRailMenu.bl_idname, icon='MESH_CIRCLE')
layout.menu(BBP_MT_AddComponentsMenu.bl_idname, icon='MESH_ICOSPHERE') layout.menu(BBP_MT_AddComponentsMenu.bl_idname, icon='MESH_ICOSPHERE')
def menu_drawer_grouping(self, context) -> None:
layout: bpy.types.UILayout = self.layout
layout.separator()
# NOTE: because outline context may change operator context
# so it will cause no popup window when click operator in outline.
# thus we create a sub layout and set its operator context as 'INVOKE_DEFAULT'
# thus, all operators can pop up normally.
col = layout.column()
col.operator_context = 'INVOKE_DEFAULT'
col.label(text="Virtools Group")
col.operator(OP_OBJECT_virtools_group.BBP_OT_add_objects_virtools_group.bl_idname, icon = 'ADD', text = "Group into...")
col.operator(OP_OBJECT_virtools_group.BBP_OT_rm_objects_virtools_group.bl_idname, icon = 'REMOVE', text = "Ungroup from...")
col.operator(OP_OBJECT_virtools_group.BBP_OT_clear_objects_virtools_group.bl_idname, icon = 'TRASH', text = "Clear All Groups")
#endregion #endregion
#region Register and Unregister. #region Register and Unregister.
@ -141,6 +164,10 @@ g_BldMenus: tuple[MenuEntry, ...] = (
MenuEntry(bpy.types.TOPBAR_MT_file_import, True, menu_drawer_import), MenuEntry(bpy.types.TOPBAR_MT_file_import, True, menu_drawer_import),
MenuEntry(bpy.types.TOPBAR_MT_file_export, True, menu_drawer_export), MenuEntry(bpy.types.TOPBAR_MT_file_export, True, menu_drawer_export),
MenuEntry(bpy.types.VIEW3D_MT_add, True, menu_drawer_add), MenuEntry(bpy.types.VIEW3D_MT_add, True, menu_drawer_add),
# register double (for 2 menus)
MenuEntry(bpy.types.VIEW3D_MT_object_context_menu, True, menu_drawer_grouping),
MenuEntry(bpy.types.OUTLINER_MT_object, True, menu_drawer_grouping),
) )
def register() -> None: def register() -> None:
@ -166,6 +193,7 @@ def register() -> None:
OP_ADDS_bme.register() OP_ADDS_bme.register()
OP_OBJECT_legacy_align.register() OP_OBJECT_legacy_align.register()
OP_OBJECT_virtools_group.register()
# register other classes # register other classes
for cls in g_BldClasses: for cls in g_BldClasses:
@ -188,6 +216,7 @@ def unregister() -> None:
bpy.utils.unregister_class(cls) bpy.utils.unregister_class(cls)
# unregister modules # unregister modules
OP_OBJECT_virtools_group.unregister()
OP_OBJECT_legacy_align.unregister() OP_OBJECT_legacy_align.unregister()
OP_ADDS_bme.unregister() OP_ADDS_bme.unregister()