From f27222f108e7da508baa7aad4424dd43b4ad3025 Mon Sep 17 00:00:00 2001 From: Maxi Date: Fri, 14 Feb 2025 18:29:36 +0100 Subject: [PATCH 01/27] Added tool to rename objs in collection by collection name. Added right click menu for collections in outliner --- __init__.py | 9 +++++---- op/collection_ops.py | 13 +++++++++++++ ui/menus.py | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 op/collection_ops.py diff --git a/__init__.py b/__init__.py index 24b0906..998d654 100644 --- a/__init__.py +++ b/__init__.py @@ -1,8 +1,7 @@ import bpy -from . ui.menus import load_menus_itools, unload_menus_itools, VIEW3D_MT_object_mode_itools, VIEW3D_MT_edit_mesh_itools, VIEW3D_MT_edit_lattice_itools, VIEW3D_MT_edit_uvs_itools +from . ui.menus import load_menus_itools, unload_menus_itools, VIEW3D_MT_object_mode_itools, VIEW3D_MT_edit_mesh_itools, VIEW3D_MT_edit_lattice_itools, VIEW3D_MT_edit_uvs_itools, VIEW3D_MT_edit_outliner_itools from . ui.pies import VIEW3D_MT_PIE_SSC_Duplicate,VIEW3D_MT_PIE_SM_uv ,VIEW3D_MT_PIE_SM_looptools, VIEW3D_MT_PIE_SM_lattice, VIEW3D_MT_PIE_SSC_New_Obj,VIEW3D_MT_PIE_TransformOptions, VIEW3D_MT_PIE_SM_object, VIEW3D_MT_PIE_SM_mesh, VIEW3D_MT_PIE_SM_curve from . ui.pannels import VIEW3D_PT_Itools -#from . utils.debug import MaxivzToolsDebug_PT_Panel, DebugOp from . op.super_smart_create import SuperSmartCreate from . op.radial_symmetry import QuickRadialSymmetry from . op.quick_align import QuickAlign @@ -18,6 +17,7 @@ from . op.quick_pipe import QuickPipe from . op.rebase_cylinder import RebaseCylinder from . op.uv_functions import QuickRotateUv90Pos, QuickRotateUv90Neg, SeamsFromSharps, UvsFromSharps +from . op.collection_ops import RenameObjsByCollection from . utils.user_prefs import AddonPreferences, OBJECT_OT_addon_prefs_example, MenuPlaceholder, unregister_keymaps, get_enable_legacy_tools bl_info = { @@ -37,7 +37,7 @@ classes = (VIEW3D_PT_Itools, VIEW3D_MT_PIE_SSC_Duplicate, VIEW3D_MT_PIE_SSC_New_Obj, RebaseCylinder, VIEW3D_MT_object_mode_itools, VIEW3D_MT_edit_mesh_itools, VIEW3D_MT_edit_lattice_itools, VIEW3D_MT_PIE_SM_object, VIEW3D_MT_PIE_SM_mesh, TransformOptionsPie, - VIEW3D_MT_edit_uvs_itools, VIEW3D_MT_PIE_TransformOptions, SuperSmartCreate, TransformModeCycle, QuickAlign, + VIEW3D_MT_edit_uvs_itools, VIEW3D_MT_PIE_TransformOptions,VIEW3D_MT_edit_outliner_itools, SuperSmartCreate, TransformModeCycle, QuickAlign, QuickRadialSymmetry,QuickPivot, QuickEditPivot, SelectionModeCycle, QuickSelectionEdge, QuickSelectionVert, QuickSelectionFace, VIEW3D_MT_PIE_SM_lattice, FlexiBezierToolsCreate, ContextSensitiveSlide, TargetWeldToggle, QuickModifierToggle, @@ -49,7 +49,8 @@ QuickRotateUv90Pos, QuickRotateUv90Neg, UvsFromSharps,QuickPipe, MenuPlaceholder, SmartModify, LatticeResolution2x2x2, SnapPresetsOp, PropEditOp, TransformPivotPointOp, - LatticeResolution3x3x3, LatticeResolution4x4x4, QuickHpLpNamer, ChildrenVisibility) + LatticeResolution3x3x3, LatticeResolution4x4x4, QuickHpLpNamer, ChildrenVisibility, + RenameObjsByCollection) legacy_classes = (SmartExtrudeModal, SmartTranslate) diff --git a/op/collection_ops.py b/op/collection_ops.py new file mode 100644 index 0000000..39ea368 --- /dev/null +++ b/op/collection_ops.py @@ -0,0 +1,13 @@ +import bpy + +class RenameObjsByCollection(bpy.types.Operator): + bl_idname = "collection.rename_objs_by_collection" + bl_label = "Rename Objects By Collection" + bl_description = "Renames all objects in collection to reflect the collection name" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + active_col = bpy.context.collection + for obj_num, obj in enumerate(active_col.objects): + obj.name = f"{active_col.name}_{obj_num}" + return {'FINISHED'} \ No newline at end of file diff --git a/ui/menus.py b/ui/menus.py index d4472e7..150cd8c 100644 --- a/ui/menus.py +++ b/ui/menus.py @@ -77,6 +77,13 @@ def draw(self, context): layout.operator("mesh.smart_modify", text="Smart Modify") +class VIEW3D_MT_edit_outliner_itools(bpy.types.Menu): + bl_label = "Interactive Tools" + + def draw(self, context): + layout = self.layout + layout.operator('collection.rename_objs_by_collection', text="Rename Objs by Collection Name") + def menu_object_mode_itools(self, context): self.layout.menu("VIEW3D_MT_object_mode_itools") self.layout.separator() @@ -96,12 +103,19 @@ def menu_edit_uvs_itools(self, context): self.layout.menu("VIEW3D_MT_edit_uvs_itools") self.layout.separator() +def menu_edit_outliner_itools(self, context): + bl_label = "Interactive Tools" + + self.layout.menu("VIEW3D_MT_edit_outliner_itools") + self.layout.separator() + def load_menus_itools(): bpy.types.VIEW3D_MT_object_context_menu.prepend(menu_object_mode_itools) bpy.types.VIEW3D_MT_edit_mesh_context_menu.prepend(menu_edit_mesh_itools) bpy.types.VIEW3D_MT_edit_lattice_context_menu.prepend(menu_edit_lattice_itools) bpy.types.IMAGE_MT_uvs_context_menu.prepend(menu_edit_uvs_itools) + bpy.types.OUTLINER_MT_collection.prepend(menu_edit_outliner_itools) def unload_menus_itools(): @@ -109,3 +123,5 @@ def unload_menus_itools(): bpy.types.VIEW3D_MT_edit_mesh_context_menu.remove(menu_edit_mesh_itools) bpy.types.VIEW3D_MT_edit_lattice_context_menu.remove(menu_edit_lattice_itools) bpy.types.IMAGE_MT_uvs_context_menu.remove(menu_edit_uvs_itools) + bpy.types.OUTLINER_MT_collection.remove(menu_edit_outliner_itools) + From 36afecfaa908e56df56354309ada926e969ed0ea Mon Sep 17 00:00:00 2001 From: Maxi Date: Mon, 17 Feb 2025 15:50:18 +0100 Subject: [PATCH 02/27] Added edit collection offset toggle operator --- __init__.py | 4 ++-- op/collection_ops.py | 36 +++++++++++++++++++++++++++++++++++- ui/menus.py | 4 ++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/__init__.py b/__init__.py index 998d654..9504aac 100644 --- a/__init__.py +++ b/__init__.py @@ -17,7 +17,7 @@ from . op.quick_pipe import QuickPipe from . op.rebase_cylinder import RebaseCylinder from . op.uv_functions import QuickRotateUv90Pos, QuickRotateUv90Neg, SeamsFromSharps, UvsFromSharps -from . op.collection_ops import RenameObjsByCollection +from . op.collection_ops import RenameObjsByCollection, EditCollectionOffset from . utils.user_prefs import AddonPreferences, OBJECT_OT_addon_prefs_example, MenuPlaceholder, unregister_keymaps, get_enable_legacy_tools bl_info = { @@ -50,7 +50,7 @@ MenuPlaceholder, SmartModify, LatticeResolution2x2x2, SnapPresetsOp, PropEditOp, TransformPivotPointOp, LatticeResolution3x3x3, LatticeResolution4x4x4, QuickHpLpNamer, ChildrenVisibility, - RenameObjsByCollection) + RenameObjsByCollection, EditCollectionOffset) legacy_classes = (SmartExtrudeModal, SmartTranslate) diff --git a/op/collection_ops.py b/op/collection_ops.py index 39ea368..11ec244 100644 --- a/op/collection_ops.py +++ b/op/collection_ops.py @@ -10,4 +10,38 @@ def execute(self, context): active_col = bpy.context.collection for obj_num, obj in enumerate(active_col.objects): obj.name = f"{active_col.name}_{obj_num}" - return {'FINISHED'} \ No newline at end of file + return {'FINISHED'} + + +class EditCollectionOffset(bpy.types.Operator): + bl_idname = "collection.edit_collection_offset_toggle" + bl_label = "Edit Collection Offset" + bl_description = "Spawns a locator to edit the offset of the collection" + bl_options = {'REGISTER', 'UNDO'} + + def edit_collection_offset_toggle(self, collection, context): + locator_name = collection.name + "_origin" + locator = bpy.data.objects.get(locator_name) + + if not locator: + if collection.objects: + locator = bpy.data.objects.new(locator_name, None) + locator.empty_display_type = 'ARROWS' + + collection.objects.link(locator) + locator.select_set(True) + locator.location = collection.instance_offset + context.view_layer.objects.active = locator + + else: + # Set the collection offset to the locator location and delete it + collection.instance_offset = locator.location + bpy.data.objects.remove(locator) + + + def execute(self, context): + active_col = bpy.context.collection + self.edit_collection_offset_toggle(active_col, context) + return {'FINISHED'} + + diff --git a/ui/menus.py b/ui/menus.py index 150cd8c..1a7ed3e 100644 --- a/ui/menus.py +++ b/ui/menus.py @@ -83,6 +83,10 @@ class VIEW3D_MT_edit_outliner_itools(bpy.types.Menu): def draw(self, context): layout = self.layout layout.operator('collection.rename_objs_by_collection', text="Rename Objs by Collection Name") + layout.operator('collection.edit_collection_offset_toggle', text="Edit Collection Offset Toggle") + + + def menu_object_mode_itools(self, context): self.layout.menu("VIEW3D_MT_object_mode_itools") From bef0ba30372088f4c4728d3aa718c6f11dc5a230 Mon Sep 17 00:00:00 2001 From: Maxi Date: Mon, 17 Feb 2025 17:17:45 +0100 Subject: [PATCH 03/27] First version of color objs by collection --- __init__.py | 4 +-- op/collection_ops.py | 66 ++++++++++++++++++++++++++++++++++++++++++-- op/handlers.py | 19 +++++++++++++ ui/menus.py | 2 ++ utils/itools.py | 14 ++++++++++ 5 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 op/handlers.py diff --git a/__init__.py b/__init__.py index 9504aac..146b2c4 100644 --- a/__init__.py +++ b/__init__.py @@ -17,7 +17,7 @@ from . op.quick_pipe import QuickPipe from . op.rebase_cylinder import RebaseCylinder from . op.uv_functions import QuickRotateUv90Pos, QuickRotateUv90Neg, SeamsFromSharps, UvsFromSharps -from . op.collection_ops import RenameObjsByCollection, EditCollectionOffset +from . op.collection_ops import RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection from . utils.user_prefs import AddonPreferences, OBJECT_OT_addon_prefs_example, MenuPlaceholder, unregister_keymaps, get_enable_legacy_tools bl_info = { @@ -50,7 +50,7 @@ MenuPlaceholder, SmartModify, LatticeResolution2x2x2, SnapPresetsOp, PropEditOp, TransformPivotPointOp, LatticeResolution3x3x3, LatticeResolution4x4x4, QuickHpLpNamer, ChildrenVisibility, - RenameObjsByCollection, EditCollectionOffset) + RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection) legacy_classes = (SmartExtrudeModal, SmartTranslate) diff --git a/op/collection_ops.py b/op/collection_ops.py index 11ec244..dd80f1e 100644 --- a/op/collection_ops.py +++ b/op/collection_ops.py @@ -1,4 +1,67 @@ import bpy +import random + +from .. utils.itools import get_collection_top_level_parent + +COLLECTION_COLOR = "Collection Color" + +#TODO: Make this into an option for the tool +# Option to use the parent collection's color if it exists +USE_PARENT_COLLECTION_COLOR = False + +def get_collection_color(collection): + """Returns the color of a collection, if it has a tag it uses that one, if it doesnt it assigns one""" + if collection is None: + return (1, 1, 1, 1) + + if USE_PARENT_COLLECTION_COLOR: + parent_collection = get_collection_top_level_parent(collection) + if parent_collection: + return get_collection_color(parent_collection) + + + if collection.color_tag != 'NONE': + color_map = { + 'COLOR_01': (1.0, 0.0, 0.0, 1.0), + 'COLOR_02': (1.0, 0.7, 0.4, 1.0), + 'COLOR_03': (1.0, 0.95, 0.5, 1.0), + 'COLOR_04': (0.48, 0.8, 0.48, 1.0), + 'COLOR_05': (0.36, 0.71, 0.91, 1.0), + 'COLOR_06': (0.55, 0.35, 0.85, 1.0), + 'COLOR_07': (0.77, 0.45, 0.72, 1.0), + 'COLOR_08': (0.47, 0.33, 0.25, 1.0), + } + + return color_map.get(collection.color_tag, 1) + + + if not collection.get(COLLECTION_COLOR): + collection[COLLECTION_COLOR] = (random.random(), random.random(), random.random(), 1) + + return collection[COLLECTION_COLOR] + +def assign_object_collection_colors(): + """Assigns viewport display colors to objects based on their collections.""" + for obj in bpy.data.objects: + if obj.type not in {'MESH', 'CURVE'}: + continue + + collection = next((col for col in bpy.data.collections if obj.name in col.objects), None) + + if collection: + color = get_collection_color(collection) + obj.color = color + +class ColorObjsByCollection(bpy.types.Operator): + bl_idname = "collection.color_objs_by_collection" + bl_label = "Color Objects By Collection" + bl_description = "Renames all objects in collection to reflect the collection name" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + assign_object_collection_colors() + return {'FINISHED'} + class RenameObjsByCollection(bpy.types.Operator): bl_idname = "collection.rename_objs_by_collection" @@ -43,5 +106,4 @@ def execute(self, context): active_col = bpy.context.collection self.edit_collection_offset_toggle(active_col, context) return {'FINISHED'} - - + \ No newline at end of file diff --git a/op/handlers.py b/op/handlers.py new file mode 100644 index 0000000..b83a529 --- /dev/null +++ b/op/handlers.py @@ -0,0 +1,19 @@ +import bpy +import bmesh +from mathutils import Vector + + + +def fit_to_view_scale_handlers(scene): + """Handler that triggers fit to view on new object creation""" + + pass + + +def load_handlers(): + bpy.app.handlers.depsgraph_update_post.append(fit_to_view_scale_handlers) + pass + +def unload_handlers(): + bpy.app.handlers.depsgraph_update_post.remove(fit_to_view_scale_handlers) + diff --git a/ui/menus.py b/ui/menus.py index 1a7ed3e..14fa2ef 100644 --- a/ui/menus.py +++ b/ui/menus.py @@ -83,7 +83,9 @@ class VIEW3D_MT_edit_outliner_itools(bpy.types.Menu): def draw(self, context): layout = self.layout layout.operator('collection.rename_objs_by_collection', text="Rename Objs by Collection Name") + layout.operator('collection.color_objs_by_collection', text="Color Objs by Collection Name") layout.operator('collection.edit_collection_offset_toggle', text="Edit Collection Offset Toggle") + diff --git a/utils/itools.py b/utils/itools.py index 3003f48..6870f48 100644 --- a/utils/itools.py +++ b/utils/itools.py @@ -334,3 +334,17 @@ def get_children(obj_name): if ob.parent.name == obj_name: children.append(ob) return children + +def get_collection_top_level_parent(collection): + """Finds the top-level parent of a collection by checking all collections in the scene.""" + parent_level = [] + for parent_collection in bpy.data.collections: + print(f"Parent {parent_collection}") + children_recursive = parent_collection.children_recursive + if collection in children_recursive: + parent_level.append((parent_collection, len(children_recursive))) + + if parent_level: + return max(parent_level, key=lambda p: p[1])[0] + + return None From 66466821383bdb723b5a763616c07f618b550bf3 Mon Sep 17 00:00:00 2001 From: Maxi Date: Tue, 18 Feb 2025 07:35:58 +0100 Subject: [PATCH 04/27] Added collection tools to itools panel --- ui/pannels.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ui/pannels.py b/ui/pannels.py index 6326816..e309471 100644 --- a/ui/pannels.py +++ b/ui/pannels.py @@ -61,6 +61,14 @@ def draw(self, context): row = layout.row(align=True) row.operator('mesh.quick_hplp_namer', text="Quick Hp Lp Namer") + layout.label(text="Collections") + row = layout.row() + row.operator('collection.edit_collection_offset_toggle', text="Edit Collection Offset") + row = layout.row() + row.operator('collection.rename_objs_by_collection', text="Rename Objects By Collection") + row = layout.row() + row.operator('collection.color_objs_by_collection', text="Color Objects By Collection") + layout.label(text="Pie Menus") row = layout.row() row.operator('mesh.smart_modify', text="Smart Modify Pie") From 65348787a1099a9ddfded5d6efde4e6e7ff43677 Mon Sep 17 00:00:00 2001 From: Maxi Date: Tue, 18 Feb 2025 21:56:17 +0100 Subject: [PATCH 05/27] First iteration of quick convex --- __init__.py | 3 +- op/quick_convex.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++ ui/pannels.py | 4 +++ utils/itools.py | 11 +++++++ utils/materials.py | 8 +++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 op/quick_convex.py create mode 100644 utils/materials.py diff --git a/__init__.py b/__init__.py index 146b2c4..3301247 100644 --- a/__init__.py +++ b/__init__.py @@ -16,6 +16,7 @@ from . op.quick_lattice import QuickLattice, LatticeResolution2x2x2, LatticeResolution3x3x3, LatticeResolution4x4x4 from . op.quick_pipe import QuickPipe from . op.rebase_cylinder import RebaseCylinder +from . op.quick_convex import QuickConvexHull from . op.uv_functions import QuickRotateUv90Pos, QuickRotateUv90Neg, SeamsFromSharps, UvsFromSharps from . op.collection_ops import RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection from . utils.user_prefs import AddonPreferences, OBJECT_OT_addon_prefs_example, MenuPlaceholder, unregister_keymaps, get_enable_legacy_tools @@ -50,7 +51,7 @@ MenuPlaceholder, SmartModify, LatticeResolution2x2x2, SnapPresetsOp, PropEditOp, TransformPivotPointOp, LatticeResolution3x3x3, LatticeResolution4x4x4, QuickHpLpNamer, ChildrenVisibility, - RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection) + RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection, QuickConvexHull) legacy_classes = (SmartExtrudeModal, SmartTranslate) diff --git a/op/quick_convex.py b/op/quick_convex.py new file mode 100644 index 0000000..c6729a1 --- /dev/null +++ b/op/quick_convex.py @@ -0,0 +1,78 @@ +import bpy +import bmesh +from mathutils import Vector +from .. utils.materials import get_material +from ..utils.itools import duplicate_object + +HULL_PREFIX = "UCX" #TODO. Make this a setting +HULL_MAT_COLOR = (0, 1, 0, 1) #TODO. Make this a setting + + +class QuickConvexHull(bpy.types.Operator): + bl_idname = "mesh.quick_convex_hull" + bl_label = "Quick Convex Hull" + bl_description = "Makes a quick convex hull from selection, parents it to selected object and adds the desired prefix" + bl_options = {'REGISTER', 'UNDO'} + + def assign_collision_mat(self, obj): + # Get collision material and assign green color, create it if its missing + get_material("Collision") + mat = bpy.data.materials.get("Collision") + mat.diffuse_color = HULL_MAT_COLOR + obj.data.materials.append(mat) + + @classmethod + def poll(cls, context): + return context.object is not None + + def execute(self, context): + edit_mode = False + og_selection = context.selected_objects + og_active = context.view_layer.objects.active + + for obj in og_selection: + if og_active.mode == 'EDIT': + edit_mode = True + bpy.ops.object.mode_set(mode="OBJECT") + + + depsgraph = bpy.context.evaluated_depsgraph_get() + bm = bmesh.new() + bm.from_object(obj, depsgraph) + + selected_verts = [] + + if edit_mode: + selected_verts = [v for v in bm.verts if v.select] + + else: + selected_verts = [v for v in bm.verts] + + if not selected_verts: + print("ERROR: No elements selected, please make a selection") + return {'FINISHED'} + + + op_data = bmesh.ops.convex_hull(bm, input=selected_verts, use_existing_faces=True) + delete_verts = [v for v in bm.verts if v not in selected_verts] + delete_verts += op_data["geom_interior"] + op_data["geom_unused"] + + if delete_verts: + bmesh.ops.delete(bm, geom=delete_verts) + + # Finish up, write the bmesh into a new mesh + new_bmesh = bpy.data.meshes.new(f"{HULL_PREFIX}_{og_selection[0].name}") + bm.to_mesh(new_bmesh) + bm.free() + + convex_hull = bpy.data.objects.new(f"{HULL_PREFIX}_{og_selection[0].name}", new_bmesh) + bpy.context.collection.objects.link(convex_hull) + # Parent the convex hull to the original object and copy transforms + convex_hull.parent = obj + + self.assign_collision_mat(convex_hull) + + if edit_mode: + bpy.ops.object.mode_set(mode="EDIT") + + return {'FINISHED'} \ No newline at end of file diff --git a/ui/pannels.py b/ui/pannels.py index e309471..22d195b 100644 --- a/ui/pannels.py +++ b/ui/pannels.py @@ -59,8 +59,12 @@ def draw(self, context): row.operator('mesh.context_sensitive_slide', text="CS Slide") row.operator('mesh.context_sensitive_bevel', text="CS Bevel") row = layout.row(align=True) + row.operator('mesh.quick_convex_hull', text="Quick Convex Hull") + row = layout.row(align=True) row.operator('mesh.quick_hplp_namer', text="Quick Hp Lp Namer") + + layout.label(text="Collections") row = layout.row() row.operator('collection.edit_collection_offset_toggle', text="Edit Collection Offset") diff --git a/utils/itools.py b/utils/itools.py index 6870f48..15202fa 100644 --- a/utils/itools.py +++ b/utils/itools.py @@ -348,3 +348,14 @@ def get_collection_top_level_parent(collection): return max(parent_level, key=lambda p: p[1])[0] return None + +def duplicate_object(target_obj,linked_data = False): + # Duplicate the object and mesh + new_obj = target_obj.copy() + if not linked_data: + new_obj.data = target_obj.data.copy() + bpy.context.collection.objects.link(new_obj) + + new_obj.matrix_world = target_obj.matrix_world.copy() + + return new_obj \ No newline at end of file diff --git a/utils/materials.py b/utils/materials.py new file mode 100644 index 0000000..97a18c6 --- /dev/null +++ b/utils/materials.py @@ -0,0 +1,8 @@ +import bpy + +def get_material(material_name): + """Get material by material name, if it doesnt exist create it""" + mat = bpy.data.materials.get(material_name) + if not mat: + mat = bpy.data.materials.new(name=material_name) + return mat \ No newline at end of file From 3c173ad31c612b2ed30288dfa8328968f7ad2520 Mon Sep 17 00:00:00 2001 From: Maxi Date: Tue, 18 Feb 2025 22:14:30 +0100 Subject: [PATCH 06/27] Added quick convex prefix setting --- op/quick_convex.py | 11 +++++------ utils/user_prefs.py | 13 +++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/op/quick_convex.py b/op/quick_convex.py index c6729a1..4a01750 100644 --- a/op/quick_convex.py +++ b/op/quick_convex.py @@ -1,11 +1,9 @@ import bpy import bmesh -from mathutils import Vector from .. utils.materials import get_material -from ..utils.itools import duplicate_object +from ..utils.user_prefs import get_quickconvex_prefix -HULL_PREFIX = "UCX" #TODO. Make this a setting -HULL_MAT_COLOR = (0, 1, 0, 1) #TODO. Make this a setting +HULL_MAT_COLOR = (0, 1, 0, 1) #TODO. Make this a setting in the future class QuickConvexHull(bpy.types.Operator): @@ -27,6 +25,7 @@ def poll(cls, context): def execute(self, context): edit_mode = False + hull_prefix = get_quickconvex_prefix() og_selection = context.selected_objects og_active = context.view_layer.objects.active @@ -61,11 +60,11 @@ def execute(self, context): bmesh.ops.delete(bm, geom=delete_verts) # Finish up, write the bmesh into a new mesh - new_bmesh = bpy.data.meshes.new(f"{HULL_PREFIX}_{og_selection[0].name}") + new_bmesh = bpy.data.meshes.new(f"{hull_prefix}_{og_selection[0].name}") bm.to_mesh(new_bmesh) bm.free() - convex_hull = bpy.data.objects.new(f"{HULL_PREFIX}_{og_selection[0].name}", new_bmesh) + convex_hull = bpy.data.objects.new(f"{hull_prefix}_{og_selection[0].name}", new_bmesh) bpy.context.collection.objects.link(convex_hull) # Parent the convex hull to the original object and copy transforms convex_hull.parent = obj diff --git a/utils/user_prefs.py b/utils/user_prefs.py index a6927d5..7eef8b1 100644 --- a/utils/user_prefs.py +++ b/utils/user_prefs.py @@ -194,6 +194,11 @@ def get_quickhplp_hp_suffix(): prefs = get_addon_preferences() return prefs.quickhplp_hp_suffix +def get_quickconvex_prefix(): + prefs = get_addon_preferences() + return prefs.quickconvex_prefix + + def get_enable_wireshaded_cs(): prefs = get_addon_preferences() @@ -306,6 +311,10 @@ class AddonPreferences(AddonPreferences): quickhplp_hp_suffix: StringProperty(name="High Poly suffix", description="Suffix to use for High Poly Meshes", default="_high") + + quickconvex_prefix: StringProperty(name="Quick Convex Hull Prefix", + description="Prefix to use for new Quick Convex Hull mesh naming", + default="UCX") enable_wireshaded_cs: BoolProperty(name="Wireframe / Shaded Context Sensitive Mode", description="Enables context sensitive mode for the Wireframe / Shaded Tool", @@ -425,6 +434,10 @@ def draw_general(self, context): row = box.row(align=True) row.prop(self, "transform_mode_cycle_cyclic", toggle=False) + #Quick Convex Hull + row = box.row(align=True) + row.prop(self, "quickconvex_prefix", toggle=False) + row = box.row(align=True) row.prop(self, "enable_legacy_tools", toggle=False) From 1f9775113c0544373501a0c94b857e4cb21c2560 Mon Sep 17 00:00:00 2001 From: Maxi Date: Wed, 19 Feb 2025 20:19:42 +0100 Subject: [PATCH 07/27] Added new toggles to panel Added handlers for collision color --- __init__.py | 15 ++++++-- op/collection_ops.py | 21 +++++++---- op/{quick_convex.py => collision_ops.py} | 7 ++-- op/handlers.py | 19 +++++----- ui/pannels.py | 28 ++++++++++++--- utils/constants.py | 10 ++++++ utils/custom_data.py | 45 ++++++++++++++++++++++++ 7 files changed, 117 insertions(+), 28 deletions(-) rename op/{quick_convex.py => collision_ops.py} (94%) create mode 100644 utils/constants.py create mode 100644 utils/custom_data.py diff --git a/__init__.py b/__init__.py index 3301247..d5010cf 100644 --- a/__init__.py +++ b/__init__.py @@ -16,11 +16,12 @@ from . op.quick_lattice import QuickLattice, LatticeResolution2x2x2, LatticeResolution3x3x3, LatticeResolution4x4x4 from . op.quick_pipe import QuickPipe from . op.rebase_cylinder import RebaseCylinder -from . op.quick_convex import QuickConvexHull +from .op.collision_ops import QuickConvexHull from . op.uv_functions import QuickRotateUv90Pos, QuickRotateUv90Neg, SeamsFromSharps, UvsFromSharps from . op.collection_ops import RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection from . utils.user_prefs import AddonPreferences, OBJECT_OT_addon_prefs_example, MenuPlaceholder, unregister_keymaps, get_enable_legacy_tools - +from .utils.custom_data import ToggleItoolsProperty +from .op.handlers import load_handlers, unload_handlers bl_info = { "name": "Interactive Tools", "author": "Maxi Vazquez, Ajfurey", @@ -51,7 +52,8 @@ MenuPlaceholder, SmartModify, LatticeResolution2x2x2, SnapPresetsOp, PropEditOp, TransformPivotPointOp, LatticeResolution3x3x3, LatticeResolution4x4x4, QuickHpLpNamer, ChildrenVisibility, - RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection, QuickConvexHull) + RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection, QuickConvexHull, + ToggleItoolsProperty) legacy_classes = (SmartExtrudeModal, SmartTranslate) @@ -69,6 +71,9 @@ def register(): # register_keymaps() + #Register Handlers + load_handlers() + def unregister(): from bpy.utils import unregister_class @@ -81,6 +86,10 @@ def unregister(): for cls in reversed(classes): unregister_class(cls) + #Unregister Handlers + unload_handlers() + + if __name__ == "__main__": register() diff --git a/op/collection_ops.py b/op/collection_ops.py index dd80f1e..704b702 100644 --- a/op/collection_ops.py +++ b/op/collection_ops.py @@ -2,19 +2,15 @@ import random from .. utils.itools import get_collection_top_level_parent - -COLLECTION_COLOR = "Collection Color" - -#TODO: Make this into an option for the tool -# Option to use the parent collection's color if it exists -USE_PARENT_COLLECTION_COLOR = False +from .. utils.custom_data import itools_data_get +from ..utils.constants import COLLECTION_COLOR, COLLECTION_COLORS_USE_PARENT_COLOR def get_collection_color(collection): """Returns the color of a collection, if it has a tag it uses that one, if it doesnt it assigns one""" if collection is None: return (1, 1, 1, 1) - if USE_PARENT_COLLECTION_COLOR: + if itools_data_get(COLLECTION_COLORS_USE_PARENT_COLOR): parent_collection = get_collection_top_level_parent(collection) if parent_collection: return get_collection_color(parent_collection) @@ -61,6 +57,17 @@ class ColorObjsByCollection(bpy.types.Operator): def execute(self, context): assign_object_collection_colors() return {'FINISHED'} + +class ColorObjsByCollection(bpy.types.Operator): + bl_idname = "collection.color_objs_by_collection" + bl_label = "Color Objects By Collection" + bl_description = """Sets the color of the objects to the color of its collection. + If the collection has a color tag it will use it, if it doesnt it will generate a random one""" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + assign_object_collection_colors() + return {'FINISHED'} class RenameObjsByCollection(bpy.types.Operator): diff --git a/op/quick_convex.py b/op/collision_ops.py similarity index 94% rename from op/quick_convex.py rename to op/collision_ops.py index 4a01750..f29054c 100644 --- a/op/quick_convex.py +++ b/op/collision_ops.py @@ -1,9 +1,8 @@ import bpy import bmesh -from .. utils.materials import get_material +from ..utils.materials import get_material from ..utils.user_prefs import get_quickconvex_prefix - -HULL_MAT_COLOR = (0, 1, 0, 1) #TODO. Make this a setting in the future +from ..utils.constants import CONVEXHULL_MAT_COLOR class QuickConvexHull(bpy.types.Operator): @@ -16,7 +15,7 @@ def assign_collision_mat(self, obj): # Get collision material and assign green color, create it if its missing get_material("Collision") mat = bpy.data.materials.get("Collision") - mat.diffuse_color = HULL_MAT_COLOR + mat.diffuse_color = CONVEXHULL_MAT_COLOR obj.data.materials.append(mat) @classmethod diff --git a/op/handlers.py b/op/handlers.py index b83a529..2a94722 100644 --- a/op/handlers.py +++ b/op/handlers.py @@ -1,19 +1,18 @@ import bpy import bmesh -from mathutils import Vector +from ..utils.constants import COLLISION_COLORS_UPDATE +from ..utils.custom_data import itools_data_get +from .collection_ops import assign_object_collection_colors - - -def fit_to_view_scale_handlers(scene): - """Handler that triggers fit to view on new object creation""" - - pass +def update_collection_colors(scene): + if itools_data_get(COLLISION_COLORS_UPDATE): + assign_object_collection_colors() + print("HANDLER RECOLOR RUNNING") def load_handlers(): - bpy.app.handlers.depsgraph_update_post.append(fit_to_view_scale_handlers) - pass + bpy.app.handlers.depsgraph_update_post.append(update_collection_colors) def unload_handlers(): - bpy.app.handlers.depsgraph_update_post.remove(fit_to_view_scale_handlers) + bpy.app.handlers.depsgraph_update_post.remove(update_collection_colors) diff --git a/ui/pannels.py b/ui/pannels.py index 22d195b..ef49559 100644 --- a/ui/pannels.py +++ b/ui/pannels.py @@ -1,7 +1,19 @@ import bpy -from bpy.utils import register_class, unregister_class from ..utils.user_prefs import get_enable_legacy_tools +from ..utils.constants import COLLISION_COLORS_UPDATE, COLLECTION_COLORS_USE_PARENT_COLOR, DESCRIPTION_DIC +from ..utils.custom_data import itools_data_get +def icon_toggle(row, operator_name, icon_name, constant): + depress = False + if itools_data_get(constant): + depress = True + op = row.operator(operator_name, text="", icon = icon_name, depress = depress) + op.prop_name = constant + + if constant in DESCRIPTION_DIC: + op.description = f"{DESCRIPTION_DIC[constant]} \nCLICK: ON/OFF" + + return op class VIEW3D_PT_Itools(bpy.types.Panel): bl_idname = "VIEW3D_PT_Itools" @@ -59,8 +71,6 @@ def draw(self, context): row.operator('mesh.context_sensitive_slide', text="CS Slide") row.operator('mesh.context_sensitive_bevel', text="CS Bevel") row = layout.row(align=True) - row.operator('mesh.quick_convex_hull', text="Quick Convex Hull") - row = layout.row(align=True) row.operator('mesh.quick_hplp_namer', text="Quick Hp Lp Namer") @@ -70,8 +80,18 @@ def draw(self, context): row.operator('collection.edit_collection_offset_toggle', text="Edit Collection Offset") row = layout.row() row.operator('collection.rename_objs_by_collection', text="Rename Objects By Collection") - row = layout.row() + + #Color Object By Collection row + row = layout.row(align = True) row.operator('collection.color_objs_by_collection', text="Color Objects By Collection") + icon_toggle(row, "itools.toggle_property", "FILE_REFRESH", COLLISION_COLORS_UPDATE) + icon_toggle(row, "itools.toggle_property", "ORIENTATION_PARENT", COLLECTION_COLORS_USE_PARENT_COLOR) + + + + layout.label(text="Collisions") + row = layout.row(align=True) + row.operator('mesh.quick_convex_hull', text="Quick Convex Hull") layout.label(text="Pie Menus") row = layout.row() diff --git a/utils/constants.py b/utils/constants.py new file mode 100644 index 0000000..fb122f7 --- /dev/null +++ b/utils/constants.py @@ -0,0 +1,10 @@ +COLLISION_COLORS_UPDATE = "collision_colors_update" +COLLECTION_COLORS_USE_PARENT_COLOR = "collection_colors_use_parent_color" +COLLISION_COLLECTION_UPDATE = "collision_collection_update" +COLLECTION_COLOR = "Collection Color" #TODO: Make this into an option for the tool +CONVEXHULL_MAT_COLOR = (0, 1, 0, 1) + +DESCRIPTION_DIC ={ + COLLISION_COLORS_UPDATE: "Automatically update collision colors", + COLLECTION_COLORS_USE_PARENT_COLOR: "Use top level parent to set the color of the collections" +} \ No newline at end of file diff --git a/utils/custom_data.py b/utils/custom_data.py new file mode 100644 index 0000000..8631aba --- /dev/null +++ b/utils/custom_data.py @@ -0,0 +1,45 @@ +import bpy + +def get_itools_data_obj(): + obj_name = "Itools_data_obj" + # Check if the object exists, make it if it doesnt + if obj_name not in bpy.data.objects: + obj = bpy.data.objects.new(obj_name, None) + obj.use_fake_user = True + + return bpy.data.objects[obj_name] + +def itools_data_get(data_key): + """Looks for itools data on data obj, if it doesnt find it returns nothing""" + itools_data_obj = get_itools_data_obj() + + return itools_data_obj.get(data_key, None) + +def itools_data_set(data_key, value): + """Sets data in itools data obj""" + itools_data_obj = get_itools_data_obj() + itools_data_obj[data_key] = value + itools_data_obj.update_tag() + +class ToggleItoolsProperty(bpy.types.Operator): + """Toggles target property""" + bl_idname = "itools.toggle_property" + bl_label = "" + + prop_name: bpy.props.StringProperty() + description: bpy.props.StringProperty(default="Toggles Property") + + @classmethod + def description(cls, context, properties): + return properties.description + + def execute(self, context): + prop_value = itools_data_get(self.prop_name) + print(prop_value) + if prop_value: + itools_data_set(self.prop_name, not prop_value) + + else: + itools_data_set(self.prop_name, True) + + return {'FINISHED'} \ No newline at end of file From a5462cf102e1d8958a81cf95985802d0711337c7 Mon Sep 17 00:00:00 2001 From: Maxi Date: Wed, 19 Feb 2025 21:24:35 +0100 Subject: [PATCH 08/27] Add new pie menu for making objects Added new empty bezier tool --- __init__.py | 4 ++- op/new_objects.py | 20 ++++++++++++++ op/super_smart_create.py | 2 +- ui/pie_menus/make_new.py | 60 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 op/new_objects.py create mode 100644 ui/pie_menus/make_new.py diff --git a/__init__.py b/__init__.py index d5010cf..b8e9214 100644 --- a/__init__.py +++ b/__init__.py @@ -2,6 +2,7 @@ from . ui.menus import load_menus_itools, unload_menus_itools, VIEW3D_MT_object_mode_itools, VIEW3D_MT_edit_mesh_itools, VIEW3D_MT_edit_lattice_itools, VIEW3D_MT_edit_uvs_itools, VIEW3D_MT_edit_outliner_itools from . ui.pies import VIEW3D_MT_PIE_SSC_Duplicate,VIEW3D_MT_PIE_SM_uv ,VIEW3D_MT_PIE_SM_looptools, VIEW3D_MT_PIE_SM_lattice, VIEW3D_MT_PIE_SSC_New_Obj,VIEW3D_MT_PIE_TransformOptions, VIEW3D_MT_PIE_SM_object, VIEW3D_MT_PIE_SM_mesh, VIEW3D_MT_PIE_SM_curve from . ui.pannels import VIEW3D_PT_Itools +from . ui.pie_menus.make_new import VIEW3D_MT_PIE_Make_New from . op.super_smart_create import SuperSmartCreate from . op.radial_symmetry import QuickRadialSymmetry from . op.quick_align import QuickAlign @@ -21,6 +22,7 @@ from . op.collection_ops import RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection from . utils.user_prefs import AddonPreferences, OBJECT_OT_addon_prefs_example, MenuPlaceholder, unregister_keymaps, get_enable_legacy_tools from .utils.custom_data import ToggleItoolsProperty +from .op.new_objects import AddBezierEmpty from .op.handlers import load_handlers, unload_handlers bl_info = { "name": "Interactive Tools", @@ -53,7 +55,7 @@ SnapPresetsOp, PropEditOp, TransformPivotPointOp, LatticeResolution3x3x3, LatticeResolution4x4x4, QuickHpLpNamer, ChildrenVisibility, RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection, QuickConvexHull, - ToggleItoolsProperty) + ToggleItoolsProperty, VIEW3D_MT_PIE_Make_New, AddBezierEmpty) legacy_classes = (SmartExtrudeModal, SmartTranslate) diff --git a/op/new_objects.py b/op/new_objects.py new file mode 100644 index 0000000..8b816a7 --- /dev/null +++ b/op/new_objects.py @@ -0,0 +1,20 @@ +import bpy + +class AddBezierEmpty(bpy.types.Operator): + bl_idname = "curve.add_bezier_empty" + bl_label = "Add Bezier Empty" + bl_description = "Adds Empty Bezier Curve and sets its as active" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + curve_data = bpy.data.curves.new('BezierCurve', 'CURVE') + curve_data.dimensions = '3D' + curve_data.splines.new('BEZIER') + + curve_object = bpy.data.objects.new('BezierCurve', curve_data) + + # Link the object to the scene and set active + bpy.context.collection.objects.link(curve_object) + bpy.context.view_layer.objects.active = curve_object + + return{'FINISHED'} diff --git a/op/super_smart_create.py b/op/super_smart_create.py index 50a952b..9f25127 100644 --- a/op/super_smart_create.py +++ b/op/super_smart_create.py @@ -68,7 +68,7 @@ def super_smart_create(self): bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_SSC_Duplicate") else: - bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_SSC_New_Obj") + bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_Make_New") # if Vertex is selected elif mode == 'VERT': diff --git a/ui/pie_menus/make_new.py b/ui/pie_menus/make_new.py new file mode 100644 index 0000000..3549809 --- /dev/null +++ b/ui/pie_menus/make_new.py @@ -0,0 +1,60 @@ +import bpy +from bpy.types import Menu + +class VIEW3D_MT_PIE_Make_New(Menu): + bl_label = "New" + + def draw(self, context): + layout = self.layout + pie = layout.menu_pie() + + # 1 - LEFT + submenu = pie.column() + container = submenu.box() + column = container.column() + row = column.row(align=True) + + row.operator("object.empty_add", text="Empty", icon="OUTLINER_OB_EMPTY").type = 'ARROWS' + row = column.row(align=True) + row.operator("object.gpencil_add", text="Gpencil", icon="OUTLINER_OB_GREASEPENCIL").type = 'EMPTY' + + row = column.row(align=True) + row.operator("object.camera_add", text="Camera", icon="OUTLINER_OB_CAMERA") + + row = column.row(align=True) + row.operator("object.light_add", text="Light", icon="OUTLINER_OB_LIGHT").type = 'POINT' + + + + # 2 - RIGHT + submenu = pie.column() + container = submenu.box() + column = container.column() + + row = column.row(align=True) + row.operator("mesh.primitive_cube_add", text="Cube", icon="MESH_CUBE") + + row = column.row(align=True) + row.operator("mesh.primitive_cylinder_add", text="Cylinder", icon="MESH_CYLINDER") + + row = column.row(align=True) + row.operator("mesh.primitive_uv_sphere_add", text="Sphere", icon="MESH_UVSPHERE") + + row = column.row(align=True) + row.operator("curve.add_bezier_empty", text="Curve", icon="IPO_EASE_IN") + + # 3 - BOTTOM + + submenu = pie.column() + column = submenu.column() + + has_collections = bool(bpy.data.collections) + column.enabled = has_collections + + + column.operator_context = 'INVOKE_REGION_WIN' + column.operator( + "object.collection_instance_add", + text="Collection Instance..." if has_collections else "No Collections to Instance", + icon='OUTLINER_OB_GROUP_INSTANCE', + ) From ce441ef32673c95536efe97261eca56be56b5d1d Mon Sep 17 00:00:00 2001 From: Maxi Date: Wed, 19 Feb 2025 21:57:39 +0100 Subject: [PATCH 09/27] Renamed bezier empty to simple. added option to draw primitives --- __init__.py | 4 ++-- op/new_objects.py | 33 ++++++++++++++++++++++++++------- ui/pie_menus/make_new.py | 12 +++++++++++- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/__init__.py b/__init__.py index b8e9214..1799bed 100644 --- a/__init__.py +++ b/__init__.py @@ -22,7 +22,7 @@ from . op.collection_ops import RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection from . utils.user_prefs import AddonPreferences, OBJECT_OT_addon_prefs_example, MenuPlaceholder, unregister_keymaps, get_enable_legacy_tools from .utils.custom_data import ToggleItoolsProperty -from .op.new_objects import AddBezierEmpty +from .op.new_objects import AddBezierSimple from .op.handlers import load_handlers, unload_handlers bl_info = { "name": "Interactive Tools", @@ -55,7 +55,7 @@ SnapPresetsOp, PropEditOp, TransformPivotPointOp, LatticeResolution3x3x3, LatticeResolution4x4x4, QuickHpLpNamer, ChildrenVisibility, RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection, QuickConvexHull, - ToggleItoolsProperty, VIEW3D_MT_PIE_Make_New, AddBezierEmpty) + ToggleItoolsProperty, VIEW3D_MT_PIE_Make_New, AddBezierSimple) legacy_classes = (SmartExtrudeModal, SmartTranslate) diff --git a/op/new_objects.py b/op/new_objects.py index 8b816a7..08e3c09 100644 --- a/op/new_objects.py +++ b/op/new_objects.py @@ -1,20 +1,39 @@ import bpy -class AddBezierEmpty(bpy.types.Operator): - bl_idname = "curve.add_bezier_empty" - bl_label = "Add Bezier Empty" - bl_description = "Adds Empty Bezier Curve and sets its as active" +class AddBezierSimple(bpy.types.Operator): + bl_idname = "curve.add_bezier_simple" + bl_label = "Add Bezier Simple" + bl_description = "Adds Simple Bezier Curve and sets its as active" bl_options = {'REGISTER', 'UNDO'} + mode: bpy.props.StringProperty(default="Simple") + @classmethod + def poll(cls, context): + return context.mode == "OBJECT" + def execute(self, context): curve_data = bpy.data.curves.new('BezierCurve', 'CURVE') curve_data.dimensions = '3D' - curve_data.splines.new('BEZIER') + + if self.mode == "Simple": + spline = curve_data.splines.new('BEZIER') + spline.bezier_points.add(1) + spline.bezier_points[0].co = (0, 0, 0) + spline.bezier_points[1].co = (0, 0, 1) curve_object = bpy.data.objects.new('BezierCurve', curve_data) + curve_object.location = context.scene.cursor.location # Link the object to the scene and set active - bpy.context.collection.objects.link(curve_object) - bpy.context.view_layer.objects.active = curve_object + context.collection.objects.link(curve_object) + context.view_layer.objects.active = curve_object + curve_object.select_set(True) + + + if self.mode == "Draw": + if context.mode == "OBJECT": + bpy.ops.object.mode_set(mode = 'EDIT') + bpy.ops.wm.tool_set_by_id(name="builtin.draw") + context.scene.tool_settings.curve_paint_settings.depth_mode = "SURFACE" return{'FINISHED'} diff --git a/ui/pie_menus/make_new.py b/ui/pie_menus/make_new.py index 3549809..dd148cf 100644 --- a/ui/pie_menus/make_new.py +++ b/ui/pie_menus/make_new.py @@ -33,15 +33,24 @@ def draw(self, context): row = column.row(align=True) row.operator("mesh.primitive_cube_add", text="Cube", icon="MESH_CUBE") + row.operator("wm.tool_set_by_id", text="", icon="GREASEPENCIL").name = "builtin.primitive_cube_add" + row = column.row(align=True) row.operator("mesh.primitive_cylinder_add", text="Cylinder", icon="MESH_CYLINDER") + row.operator("wm.tool_set_by_id", text="", icon="GREASEPENCIL").name = "builtin.primitive_cylinder_add" + row = column.row(align=True) row.operator("mesh.primitive_uv_sphere_add", text="Sphere", icon="MESH_UVSPHERE") + row.operator("wm.tool_set_by_id", text="", icon="GREASEPENCIL").name = "builtin.primitive_uv_sphere_add" + row = column.row(align=True) - row.operator("curve.add_bezier_empty", text="Curve", icon="IPO_EASE_IN") + row.operator("curve.add_bezier_simple", text="Curve", icon="IPO_EASE_IN").mode = "Simple" + row.operator("curve.add_bezier_simple", text="", icon="GREASEPENCIL").mode = "Draw" + + # 3 - BOTTOM @@ -58,3 +67,4 @@ def draw(self, context): text="Collection Instance..." if has_collections else "No Collections to Instance", icon='OUTLINER_OB_GROUP_INSTANCE', ) + From 1dc352cc97d6c60de45d132917071dd7df5bbd37 Mon Sep 17 00:00:00 2001 From: Maxi Date: Wed, 19 Feb 2025 22:02:13 +0100 Subject: [PATCH 10/27] Style consistency pass --- ui/pie_menus/make_new.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/pie_menus/make_new.py b/ui/pie_menus/make_new.py index dd148cf..88e36b9 100644 --- a/ui/pie_menus/make_new.py +++ b/ui/pie_menus/make_new.py @@ -48,6 +48,7 @@ def draw(self, context): row = column.row(align=True) row.operator("curve.add_bezier_simple", text="Curve", icon="IPO_EASE_IN").mode = "Simple" + row.emboss = "PULLDOWN_MENU" row.operator("curve.add_bezier_simple", text="", icon="GREASEPENCIL").mode = "Draw" From 4ddb9bc1a4828e74f404b29ffbd7d3afed5431a2 Mon Sep 17 00:00:00 2001 From: Maxi Date: Thu, 20 Feb 2025 17:01:35 +0100 Subject: [PATCH 11/27] -Added collision collection function and handler -Fixed collision assigment for quick convex --- __init__.py | 10 ++++----- op/collision_ops.py | 55 ++++++++++++++++++++++++++++++++++++++++++--- op/handlers.py | 13 ++++++++--- ui/pannels.py | 8 ++++++- utils/constants.py | 6 ++++- 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/__init__.py b/__init__.py index 1799bed..c8b4f1c 100644 --- a/__init__.py +++ b/__init__.py @@ -17,13 +17,13 @@ from . op.quick_lattice import QuickLattice, LatticeResolution2x2x2, LatticeResolution3x3x3, LatticeResolution4x4x4 from . op.quick_pipe import QuickPipe from . op.rebase_cylinder import RebaseCylinder -from .op.collision_ops import QuickConvexHull +from . op.collision_ops import QuickConvexHull, CollisionCollectionUpdate from . op.uv_functions import QuickRotateUv90Pos, QuickRotateUv90Neg, SeamsFromSharps, UvsFromSharps from . op.collection_ops import RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection from . utils.user_prefs import AddonPreferences, OBJECT_OT_addon_prefs_example, MenuPlaceholder, unregister_keymaps, get_enable_legacy_tools -from .utils.custom_data import ToggleItoolsProperty -from .op.new_objects import AddBezierSimple -from .op.handlers import load_handlers, unload_handlers +from . utils.custom_data import ToggleItoolsProperty +from . op.new_objects import AddBezierSimple +from . op.handlers import load_handlers, unload_handlers bl_info = { "name": "Interactive Tools", "author": "Maxi Vazquez, Ajfurey", @@ -54,7 +54,7 @@ MenuPlaceholder, SmartModify, LatticeResolution2x2x2, SnapPresetsOp, PropEditOp, TransformPivotPointOp, LatticeResolution3x3x3, LatticeResolution4x4x4, QuickHpLpNamer, ChildrenVisibility, - RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection, QuickConvexHull, + RenameObjsByCollection, EditCollectionOffset, ColorObjsByCollection, QuickConvexHull, CollisionCollectionUpdate, ToggleItoolsProperty, VIEW3D_MT_PIE_Make_New, AddBezierSimple) legacy_classes = (SmartExtrudeModal, SmartTranslate) diff --git a/op/collision_ops.py b/op/collision_ops.py index f29054c..1f982ba 100644 --- a/op/collision_ops.py +++ b/op/collision_ops.py @@ -2,8 +2,55 @@ import bmesh from ..utils.materials import get_material from ..utils.user_prefs import get_quickconvex_prefix -from ..utils.constants import CONVEXHULL_MAT_COLOR +from ..utils.constants import CONVEXHULL_MAT_COLOR, COLLISION_PREFIXES, COLLISION, DESCRIPTION_DIC, COLLISION_COLLECTION_UPDATE + +def get_collision_collection(): + """Returns collision collection, if it doesnt exist it creates it and returns it""" + if COLLISION in bpy.data.collections: + return bpy.data.collections[COLLISION] + + col = bpy.data.collections.new(COLLISION) + col.color_tag = "COLOR_04" + bpy.context.scene.collection.children.link(col) + return col + +def update_global_collision_collection(): + """Adds collision objs into global collision collection""" + collision_col = get_collision_collection() + + for obj in bpy.context.scene.objects: + if not obj.name.startswith(tuple(COLLISION_PREFIXES)): + continue + + + if obj.name in collision_col.objects: + continue + + collision_col.objects.link(obj) + + #Remove objs from collision colection that no longer posses a proper prefix + for obj in list(collision_col.objects): + if obj.name.startswith(tuple(COLLISION_PREFIXES)): + continue + + collision_col.objects.unlink(obj) + collection_count = sum(1 for col in bpy.data.collections if obj.name in col.objects) + + #If its in no other collection link it to the parent scene so obj is not lost + if collection_count > 0: + bpy.context.scene.objects.link(obj) + + + +class CollisionCollectionUpdate(bpy.types.Operator): + bl_idname = "itools.collision_collection_update" + bl_label = "Collision Collection Update" + bl_description = DESCRIPTION_DIC[COLLISION_COLLECTION_UPDATE] + bl_options = {'REGISTER', 'UNDO'} + def execute(self, context): + update_global_collision_collection() + return {'FINISHED'} class QuickConvexHull(bpy.types.Operator): bl_idname = "mesh.quick_convex_hull" @@ -64,10 +111,12 @@ def execute(self, context): bm.free() convex_hull = bpy.data.objects.new(f"{hull_prefix}_{og_selection[0].name}", new_bmesh) - bpy.context.collection.objects.link(convex_hull) - # Parent the convex hull to the original object and copy transforms convex_hull.parent = obj + for col in og_active.users_collection: + if convex_hull.name not in col.objects: + col.objects.link(convex_hull) + self.assign_collision_mat(convex_hull) if edit_mode: diff --git a/op/handlers.py b/op/handlers.py index 2a94722..f51514d 100644 --- a/op/handlers.py +++ b/op/handlers.py @@ -1,18 +1,25 @@ import bpy import bmesh -from ..utils.constants import COLLISION_COLORS_UPDATE +from ..utils.constants import COLLISION_COLORS_UPDATE, COLLISION_COLLECTION_UPDATE from ..utils.custom_data import itools_data_get from .collection_ops import assign_object_collection_colors +from .collision_ops import update_global_collision_collection def update_collection_colors(scene): if itools_data_get(COLLISION_COLORS_UPDATE): assign_object_collection_colors() - print("HANDLER RECOLOR RUNNING") - +def update_collision_collection(scene): + if itools_data_get(COLLISION_COLLECTION_UPDATE): + update_global_collision_collection() + def load_handlers(): bpy.app.handlers.depsgraph_update_post.append(update_collection_colors) + bpy.app.handlers.depsgraph_update_post.append(update_collision_collection) + def unload_handlers(): bpy.app.handlers.depsgraph_update_post.remove(update_collection_colors) + bpy.app.handlers.depsgraph_update_post.remove(update_collision_collection) + diff --git a/ui/pannels.py b/ui/pannels.py index ef49559..331a375 100644 --- a/ui/pannels.py +++ b/ui/pannels.py @@ -1,6 +1,6 @@ import bpy from ..utils.user_prefs import get_enable_legacy_tools -from ..utils.constants import COLLISION_COLORS_UPDATE, COLLECTION_COLORS_USE_PARENT_COLOR, DESCRIPTION_DIC +from ..utils.constants import COLLISION_COLORS_UPDATE, COLLECTION_COLORS_USE_PARENT_COLOR, DESCRIPTION_DIC, COLLISION_COLLECTION_UPDATE from ..utils.custom_data import itools_data_get def icon_toggle(row, operator_name, icon_name, constant): @@ -92,6 +92,12 @@ def draw(self, context): layout.label(text="Collisions") row = layout.row(align=True) row.operator('mesh.quick_convex_hull', text="Quick Convex Hull") + row = layout.row(align=True) + row.operator('itools.collision_collection_update', text="Collision Collection Update") + icon_toggle(row, "itools.toggle_property", "FILE_REFRESH", COLLISION_COLLECTION_UPDATE) + """BRUSH_DATA""" + + layout.label(text="Pie Menus") row = layout.row() diff --git a/utils/constants.py b/utils/constants.py index fb122f7..1793c6f 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -1,10 +1,14 @@ +COLLISION = "Collision" COLLISION_COLORS_UPDATE = "collision_colors_update" COLLECTION_COLORS_USE_PARENT_COLOR = "collection_colors_use_parent_color" COLLISION_COLLECTION_UPDATE = "collision_collection_update" +COLLISION_PREFIXES = ["UCX", "UBX","USP"] COLLECTION_COLOR = "Collection Color" #TODO: Make this into an option for the tool CONVEXHULL_MAT_COLOR = (0, 1, 0, 1) + DESCRIPTION_DIC ={ COLLISION_COLORS_UPDATE: "Automatically update collision colors", - COLLECTION_COLORS_USE_PARENT_COLOR: "Use top level parent to set the color of the collections" + COLLECTION_COLORS_USE_PARENT_COLOR: "Use top level parent to set the color of the collections", + COLLISION_COLLECTION_UPDATE: "Automatically add collision objects to a global collision collection" } \ No newline at end of file From 9d8c2f39db1432696bb62bad41ae0f714c4f2a14 Mon Sep 17 00:00:00 2001 From: Maxi Date: Sat, 8 Mar 2025 15:30:59 +0100 Subject: [PATCH 12/27] Fixed bug with transform options align to face normal in Blender 4.2 --- op/misc.py | 5 ++--- ui/pies.py | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/op/misc.py b/op/misc.py index 98352c8..ea3a3e5 100644 --- a/op/misc.py +++ b/op/misc.py @@ -375,16 +375,15 @@ def set_preset(self, context): bpy.context.scene.tool_settings.use_snap_align_rotation = False elif self.mode == 4: - bpy.context.scene.tool_settings.snap_elements = {'FACE'} + bpy.context.scene.tool_settings.snap_elements_individual = {'FACE_PROJECT'} bpy.context.scene.tool_settings.snap_target = 'CENTER' bpy.context.scene.tool_settings.use_snap_align_rotation = True - bpy.context.scene.tool_settings.use_snap_project = True + elif self.mode == 5: bpy.context.scene.tool_settings.snap_elements = {'EDGE_MIDPOINT'} bpy.context.scene.tool_settings.snap_target = 'MEDIAN' bpy.context.scene.tool_settings.use_snap_align_rotation = False - bpy.context.scene.tool_settings.use_snap_project = False def execute(self, context): self.set_preset(context) diff --git a/ui/pies.py b/ui/pies.py index 0ce168f..e1de364 100644 --- a/ui/pies.py +++ b/ui/pies.py @@ -135,6 +135,8 @@ def draw(self, context): row.operator("object.move_to_collection", text="Move To Collection", icon = "DECORATE_DRIVER") row = column.row(align=False) row.operator("object.link_to_collection", text="Link To Collection", icon = "DECORATE_LINKED") + row = column.row(align=False) + row.operator("collection.edit_collection_offset_toggle", text="Edit Collection Offset", icon = "EMPTY_DATA") # 6 - RIGHT submenu = pie.column() @@ -463,9 +465,8 @@ def draw(self, context): pie.operator("mesh.snap_presets_op", text="Vert Center", icon="SNAP_VERTEX").mode = 2 # 2 - BOTTOM - if(bpy.context.scene.tool_settings.snap_elements == {'FACE'} and - bpy.context.scene.tool_settings.use_snap_align_rotation == True and - bpy.context.scene.tool_settings.use_snap_project == True): + if(bpy.context.scene.tool_settings.snap_elements == {'FACE_PROJECT'} and + bpy.context.scene.tool_settings.use_snap_align_rotation == True): pie.operator("mesh.snap_presets_op", text="Face Normal", icon="SNAP_FACE",depress=True).mode = 4 else: pie.operator("mesh.snap_presets_op", text="Face Normal", icon="SNAP_FACE").mode = 4 From 37bbf89ecd804c32bcca2e7c2d2b4f279d31e0e6 Mon Sep 17 00:00:00 2001 From: Maxi Date: Sun, 9 Mar 2025 07:58:13 +0100 Subject: [PATCH 13/27] Added new obj orientation settings to pie menu, added plane prim --- ui/pie_menus/make_new.py | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/ui/pie_menus/make_new.py b/ui/pie_menus/make_new.py index 88e36b9..340418a 100644 --- a/ui/pie_menus/make_new.py +++ b/ui/pie_menus/make_new.py @@ -1,6 +1,18 @@ import bpy from bpy.types import Menu +NEW_OBJ_ALIGNMENT = "itools_new_obj_alignment" + +bpy.types.Scene.itools_new_obj_alignment = bpy.props.EnumProperty( + items=[ + ('WORLD', "World", "First option"), + ('CURSOR', "Cursor", "Second option"), + ('VIEW', "View", "Third option") + ], + name="Dropdown Setting", + description="Choose an option" +) + class VIEW3D_MT_PIE_Make_New(Menu): bl_label = "New" @@ -8,6 +20,9 @@ def draw(self, context): layout = self.layout pie = layout.menu_pie() + #Get orientation Setting + + # 1 - LEFT submenu = pie.column() container = submenu.box() @@ -24,25 +39,29 @@ def draw(self, context): row = column.row(align=True) row.operator("object.light_add", text="Light", icon="OUTLINER_OB_LIGHT").type = 'POINT' - - # 2 - RIGHT submenu = pie.column() container = submenu.box() column = container.column() + obj_align = context.scene.itools_new_obj_alignment + + row = column.row(align=True) + row.operator("mesh.primitive_plane_add", text="Plane", icon="MESH_PLANE").align = obj_align + + row = column.row(align=True) - row.operator("mesh.primitive_cube_add", text="Cube", icon="MESH_CUBE") + row.operator("mesh.primitive_cube_add", text="Cube", icon="MESH_CUBE").align = obj_align row.operator("wm.tool_set_by_id", text="", icon="GREASEPENCIL").name = "builtin.primitive_cube_add" row = column.row(align=True) - row.operator("mesh.primitive_cylinder_add", text="Cylinder", icon="MESH_CYLINDER") + row.operator("mesh.primitive_cylinder_add", text="Cylinder", icon="MESH_CYLINDER").align = obj_align row.operator("wm.tool_set_by_id", text="", icon="GREASEPENCIL").name = "builtin.primitive_cylinder_add" row = column.row(align=True) - row.operator("mesh.primitive_uv_sphere_add", text="Sphere", icon="MESH_UVSPHERE") + row.operator("mesh.primitive_uv_sphere_add", text="Sphere", icon="MESH_UVSPHERE").align = obj_align row.operator("wm.tool_set_by_id", text="", icon="GREASEPENCIL").name = "builtin.primitive_uv_sphere_add" @@ -51,6 +70,10 @@ def draw(self, context): row.emboss = "PULLDOWN_MENU" row.operator("curve.add_bezier_simple", text="", icon="GREASEPENCIL").mode = "Draw" + row = column.row(align=True) + + row.prop(context.scene, NEW_OBJ_ALIGNMENT, text="Align") + # 3 - BOTTOM @@ -69,3 +92,9 @@ def draw(self, context): icon='OUTLINER_OB_GROUP_INSTANCE', ) + # 4 - TOP + + + + + From 83766001c5b878ab9cc5c21681e8aa2ca1495dbc Mon Sep 17 00:00:00 2001 From: Maxi Date: Sat, 26 Jul 2025 12:56:30 +0200 Subject: [PATCH 14/27] FIxed grease_pencil_add so it works with blender 4.5 --- ui/pie_menus/make_new.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pie_menus/make_new.py b/ui/pie_menus/make_new.py index 340418a..8da6ded 100644 --- a/ui/pie_menus/make_new.py +++ b/ui/pie_menus/make_new.py @@ -31,7 +31,7 @@ def draw(self, context): row.operator("object.empty_add", text="Empty", icon="OUTLINER_OB_EMPTY").type = 'ARROWS' row = column.row(align=True) - row.operator("object.gpencil_add", text="Gpencil", icon="OUTLINER_OB_GREASEPENCIL").type = 'EMPTY' + row.operator("object.grease_pencil_add", text="Gpencil", icon="OUTLINER_OB_GREASEPENCIL").type = 'EMPTY' row = column.row(align=True) row.operator("object.camera_add", text="Camera", icon="OUTLINER_OB_CAMERA") From 21515f39f4ec17baf1e7718a997fda2f6e813cd3 Mon Sep 17 00:00:00 2001 From: Maxi Date: Sat, 26 Jul 2025 13:28:08 +0200 Subject: [PATCH 15/27] Fixed rebase cylinder for blender 4.5, removed unnecesary code --- op/rebase_cylinder.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/op/rebase_cylinder.py b/op/rebase_cylinder.py index fa59972..c481038 100644 --- a/op/rebase_cylinder.py +++ b/op/rebase_cylinder.py @@ -213,12 +213,6 @@ def restore_settings(self, context, selection): mod.merge_threshold = self.original_merge_distance - def __init__(self): - print("Start") - - def __del__(self): - print("End") - @classmethod def poll(cls, context): if bpy.context.object != None: From e1153eb04c942aad9e52a46525c7ac4977df8f01 Mon Sep 17 00:00:00 2001 From: Maxi Date: Sat, 26 Jul 2025 13:30:30 +0200 Subject: [PATCH 16/27] Removed unnecesary code, fix for blender 4.5 --- op/radial_symmetry.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/op/radial_symmetry.py b/op/radial_symmetry.py index c29069c..8a3184d 100644 --- a/op/radial_symmetry.py +++ b/op/radial_symmetry.py @@ -222,12 +222,6 @@ def restore_settings(self, context, selection): elif self.original_sym_axis == 2: bpy.data.objects[self.offset_obj].rotation_euler = (0, 0, math.radians(360 / self.original_sym_count)) - def __init__(self): - print("Start") - - def __del__(self): - print("End") - @classmethod def poll(cls, context): return context.mode == 'OBJECT' and len(context.selected_objects) > 0 From 60a1fee69b187d2df802949ea6c1a6d4e52b167f Mon Sep 17 00:00:00 2001 From: Maxi Date: Sat, 26 Jul 2025 13:36:02 +0200 Subject: [PATCH 17/27] Fixed for blender 4.5 --- op/quick_pipe.py | 13 ------------- utils/debug.py | 6 ------ 2 files changed, 19 deletions(-) diff --git a/op/quick_pipe.py b/op/quick_pipe.py index ad3674d..565249c 100644 --- a/op/quick_pipe.py +++ b/op/quick_pipe.py @@ -143,19 +143,6 @@ def restore_settings(self, context, selection): bpy.context.object.data.bevel_resolution = self.original_resolution bpy.context.object.data.bevel_depth = self.original_depth - def __init__(self): - print("Start") - - def __del__(self): - print("End") - - """ - @classmethod - def poll(cls, context): - return ((context.mode == 'OBJECT' and bpy.context.object.modifiers.find("Cylindrical Sides") > -1) or - bpy.context.mode == 'EDIT_MESH') - """ - def execute(self, context): #self.sync_ui_settings() self.calculate_depth(context, bpy.data.objects[self.selection]) diff --git a/utils/debug.py b/utils/debug.py index 58204c2..81e9a66 100644 --- a/utils/debug.py +++ b/utils/debug.py @@ -40,12 +40,6 @@ class DebugOpModal(bpy.types.Operator): mode = 0 - def __init__(self): - print("Start") - - def __del__(self): - print("End") - def execute(self, context): context.object.location.x = self.value / 100.0 return {'FINISHED'} From 1bcd4b6532516025ebe624c8b4a7c63ab0ecb1e2 Mon Sep 17 00:00:00 2001 From: Maxi Date: Sat, 26 Jul 2025 13:38:58 +0200 Subject: [PATCH 18/27] Updated to work witn blender 4.5, 4.4. Cleanup --- op/handlers.py | 2 -- op/smart_extrude.py | 3 ++- op/smart_transform.py | 7 ++----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/op/handlers.py b/op/handlers.py index f51514d..5e441c3 100644 --- a/op/handlers.py +++ b/op/handlers.py @@ -1,5 +1,4 @@ import bpy -import bmesh from ..utils.constants import COLLISION_COLORS_UPDATE, COLLISION_COLLECTION_UPDATE from ..utils.custom_data import itools_data_get from .collection_ops import assign_object_collection_colors @@ -22,4 +21,3 @@ def unload_handlers(): bpy.app.handlers.depsgraph_update_post.remove(update_collection_colors) bpy.app.handlers.depsgraph_update_post.remove(update_collision_collection) - diff --git a/op/smart_extrude.py b/op/smart_extrude.py index 1b0be39..46e3951 100644 --- a/op/smart_extrude.py +++ b/op/smart_extrude.py @@ -72,7 +72,8 @@ def context_sensitive_extend(self, context): bpy.ops.curve.extrude_move(CURVE_OT_extrude={"mode": 'TRANSLATION'}, TRANSFORM_OT_translate={"value": (0, 0, 0)}) - def __init__(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.initial_mouse_pos = Vector((0, 0, 0)) self.translation_accumulator = Vector((0, 0, 0)) self.initial_pos = Vector((0, 0, 0)) diff --git a/op/smart_transform.py b/op/smart_transform.py index a4640e2..f138791 100644 --- a/op/smart_transform.py +++ b/op/smart_transform.py @@ -147,14 +147,11 @@ def calculate_translation(self, context, event): bpy.ops.transform.translate(value=translation, orient_type='GLOBAL') return True - def __init__(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.initial_mouse_pos = Vector((0, 0, 0)) self.translation_accumulator = Vector((0, 0, 0)) self.initial_pos = Vector((0, 0, 0)) - print("Start") - - def __del__(self): - print("End") def execute(self, context): return {'FINISHED'} From 523ff43b9d0929acf027a2fb672a864f7937dc44 Mon Sep 17 00:00:00 2001 From: Maxi Date: Sat, 26 Jul 2025 13:47:59 +0200 Subject: [PATCH 19/27] Updated collection ops --- op/collection_ops.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/op/collection_ops.py b/op/collection_ops.py index 704b702..650e88b 100644 --- a/op/collection_ops.py +++ b/op/collection_ops.py @@ -1,7 +1,7 @@ import bpy import random -from .. utils.itools import get_collection_top_level_parent +from .. utils.itools import get_collection_top_level_parent, get_selected from .. utils.custom_data import itools_data_get from ..utils.constants import COLLECTION_COLOR, COLLECTION_COLORS_USE_PARENT_COLOR @@ -92,6 +92,11 @@ class EditCollectionOffset(bpy.types.Operator): def edit_collection_offset_toggle(self, collection, context): locator_name = collection.name + "_origin" locator = bpy.data.objects.get(locator_name) + + selection = get_selected() + + if selection: + bpy.ops.object.select_all(action='DESELECT') if not locator: if collection.objects: From 4a86137133ab03482320c251ca332be6ec89ce19 Mon Sep 17 00:00:00 2001 From: Maxi Date: Sat, 26 Jul 2025 15:57:25 +0200 Subject: [PATCH 20/27] Cleanup --- op/mesh_modes.py | 1 - op/pivot.py | 1 - op/quick_lattice.py | 1 - op/smart_extrude.py | 4 ---- op/uv_functions.py | 2 -- utils/itools.py | 3 --- 6 files changed, 12 deletions(-) diff --git a/op/mesh_modes.py b/op/mesh_modes.py index 18dc7e5..5ac14bd 100644 --- a/op/mesh_modes.py +++ b/op/mesh_modes.py @@ -86,7 +86,6 @@ class SelectionModeCycle(bpy.types.Operator): def execute(self, context): mode = itools.get_mode() - print(mode) if mode == 'OBJECT': bpy.ops.object.editmode_toggle() diff --git a/op/pivot.py b/op/pivot.py index 19c01c2..867da99 100644 --- a/op/pivot.py +++ b/op/pivot.py @@ -38,7 +38,6 @@ def create_pivot(self, context, obj): pivot = bpy.context.active_object pivot.name = obj.name + ".PivotHelper" pivot.location = obj.location - print("Pivot") def get_pivot(self, context, obj): pivot = obj.name + ".PivotHelper" diff --git a/op/quick_lattice.py b/op/quick_lattice.py index 571c37f..7b6da7a 100644 --- a/op/quick_lattice.py +++ b/op/quick_lattice.py @@ -90,7 +90,6 @@ def setup_lattice(self, context, selection): # Make sure no axis is 0 as this caused the bug where you couldnt move the lattice. for axis in range(3): - print(axis) if dimensions[axis] == 0: dimensions[axis] = 0.001 diff --git a/op/smart_extrude.py b/op/smart_extrude.py index 46e3951..1361306 100644 --- a/op/smart_extrude.py +++ b/op/smart_extrude.py @@ -77,10 +77,6 @@ def __init__(self, *args, **kwargs): self.initial_mouse_pos = Vector((0, 0, 0)) self.translation_accumulator = Vector((0, 0, 0)) self.initial_pos = Vector((0, 0, 0)) - print("Start") - - def __del__(self): - print("End") def execute(self, context): return {'FINISHED'} diff --git a/op/uv_functions.py b/op/uv_functions.py index 1a924b1..eec1294 100644 --- a/op/uv_functions.py +++ b/op/uv_functions.py @@ -39,8 +39,6 @@ class QuickRotateUv90Pos(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): - original_pos = selected_uv_verts_pos() - print(original_pos) bpy.ops.transform.rotate(value=math.radians(90), orient_axis='Z') new_pos = selected_uv_verts_pos() return{'FINISHED'} diff --git a/utils/itools.py b/utils/itools.py index 15202fa..06bdb8f 100644 --- a/utils/itools.py +++ b/utils/itools.py @@ -155,7 +155,6 @@ def active_get(item=True): # Sets active object based on name def active_set(obj, item=True): if item: - print(obj) bpy.context.view_layer.objects.active = obj else: bpy.context.view_layer.objects.active = bpy.data.objects[obj] @@ -304,9 +303,7 @@ def convert_selection(selection, to): def update_indexes(mode=''): bm = get_bmesh() if not mode: - print("Try to get mode") mode = get_mode() - print(mode) if 'VERT' or 'ALL' in mode: bm.verts.index_update() From 29a7c6e51e6fb26b41b9169db4c2c530de5d6f91 Mon Sep 17 00:00:00 2001 From: Maxi Date: Mon, 8 Dec 2025 19:21:44 +0100 Subject: [PATCH 21/27] Remove operator that didnt exist anymore from menus, so it doesnt generate errors --- ui/menus.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/menus.py b/ui/menus.py index 14fa2ef..189500b 100644 --- a/ui/menus.py +++ b/ui/menus.py @@ -15,7 +15,6 @@ def draw(self, context): layout.separator() layout.operator("mesh.quick_pivot", text="Quick Origin") layout.operator("mesh.simple_edit_pivot", text="Edit Origin") - layout.operator("mesh.transform_orientation_pie_pie", text="Quick Transform Orientation") layout.operator('mesh.quick_align', text="Quick Align") layout.operator('mesh.quick_lattice', text="Quick Lattice") layout.operator('mesh.rebase_cylinder', text="Edit Rebased Cylinder") @@ -44,7 +43,6 @@ def draw(self, context): layout.separator() layout.operator("mesh.quick_pivot", text="Quick Origin") - layout.operator("mesh.transform_orientation_pie_pie", text="Quick Transform Orientation") layout.operator('mesh.quick_pipe', text="Quick Pipe") layout.operator('mesh.quick_lattice', text="Quick Lattice") layout.operator('mesh.rebase_cylinder', text="Rebase Cylinder") From 11a8c55d5c26163e397ca7d145af368a001c12a4 Mon Sep 17 00:00:00 2001 From: Maxi Date: Mon, 8 Dec 2025 19:37:24 +0100 Subject: [PATCH 22/27] Cleanup --- op/collection_ops.py | 13 ++----------- utils/itools.py | 1 - 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/op/collection_ops.py b/op/collection_ops.py index 650e88b..89f69cd 100644 --- a/op/collection_ops.py +++ b/op/collection_ops.py @@ -47,22 +47,13 @@ def assign_object_collection_colors(): if collection: color = get_collection_color(collection) obj.color = color - -class ColorObjsByCollection(bpy.types.Operator): - bl_idname = "collection.color_objs_by_collection" - bl_label = "Color Objects By Collection" - bl_description = "Renames all objects in collection to reflect the collection name" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - assign_object_collection_colors() - return {'FINISHED'} class ColorObjsByCollection(bpy.types.Operator): bl_idname = "collection.color_objs_by_collection" bl_label = "Color Objects By Collection" bl_description = """Sets the color of the objects to the color of its collection. - If the collection has a color tag it will use it, if it doesnt it will generate a random one""" + If the collection has a color tag it will use it, if it doesnt it will generate a random one + The color is visible in Solid shading mode, with color mode set to attribute""" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): diff --git a/utils/itools.py b/utils/itools.py index 06bdb8f..6f11688 100644 --- a/utils/itools.py +++ b/utils/itools.py @@ -336,7 +336,6 @@ def get_collection_top_level_parent(collection): """Finds the top-level parent of a collection by checking all collections in the scene.""" parent_level = [] for parent_collection in bpy.data.collections: - print(f"Parent {parent_collection}") children_recursive = parent_collection.children_recursive if collection in children_recursive: parent_level.append((parent_collection, len(children_recursive))) From 316eca18069d7cd94eadd865498394c1fbd2f66d Mon Sep 17 00:00:00 2001 From: Maxi Date: Mon, 8 Dec 2025 20:08:16 +0100 Subject: [PATCH 23/27] Added option to change collision prefixes in the user pannel --- op/collision_ops.py | 9 +++++---- utils/constants.py | 1 - utils/user_prefs.py | 9 ++++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/op/collision_ops.py b/op/collision_ops.py index 1f982ba..2f32ae6 100644 --- a/op/collision_ops.py +++ b/op/collision_ops.py @@ -1,8 +1,8 @@ import bpy import bmesh from ..utils.materials import get_material -from ..utils.user_prefs import get_quickconvex_prefix -from ..utils.constants import CONVEXHULL_MAT_COLOR, COLLISION_PREFIXES, COLLISION, DESCRIPTION_DIC, COLLISION_COLLECTION_UPDATE +from ..utils.user_prefs import get_quickconvex_prefix, get_collision_prefixes +from ..utils.constants import CONVEXHULL_MAT_COLOR, COLLISION, DESCRIPTION_DIC, COLLISION_COLLECTION_UPDATE def get_collision_collection(): """Returns collision collection, if it doesnt exist it creates it and returns it""" @@ -17,9 +17,10 @@ def get_collision_collection(): def update_global_collision_collection(): """Adds collision objs into global collision collection""" collision_col = get_collision_collection() + collision_prefixes = tuple(get_collision_prefixes().split(",")) for obj in bpy.context.scene.objects: - if not obj.name.startswith(tuple(COLLISION_PREFIXES)): + if not obj.name.startswith(collision_prefixes): continue @@ -30,7 +31,7 @@ def update_global_collision_collection(): #Remove objs from collision colection that no longer posses a proper prefix for obj in list(collision_col.objects): - if obj.name.startswith(tuple(COLLISION_PREFIXES)): + if obj.name.startswith(collision_prefixes): continue collision_col.objects.unlink(obj) diff --git a/utils/constants.py b/utils/constants.py index 1793c6f..629a6d9 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -2,7 +2,6 @@ COLLISION_COLORS_UPDATE = "collision_colors_update" COLLECTION_COLORS_USE_PARENT_COLOR = "collection_colors_use_parent_color" COLLISION_COLLECTION_UPDATE = "collision_collection_update" -COLLISION_PREFIXES = ["UCX", "UBX","USP"] COLLECTION_COLOR = "Collection Color" #TODO: Make this into an option for the tool CONVEXHULL_MAT_COLOR = (0, 1, 0, 1) diff --git a/utils/user_prefs.py b/utils/user_prefs.py index 7eef8b1..a881499 100644 --- a/utils/user_prefs.py +++ b/utils/user_prefs.py @@ -198,7 +198,9 @@ def get_quickconvex_prefix(): prefs = get_addon_preferences() return prefs.quickconvex_prefix - +def get_collision_prefixes(): + prefs = get_addon_preferences() + return prefs.collision_prefixes def get_enable_wireshaded_cs(): prefs = get_addon_preferences() @@ -315,6 +317,10 @@ class AddonPreferences(AddonPreferences): quickconvex_prefix: StringProperty(name="Quick Convex Hull Prefix", description="Prefix to use for new Quick Convex Hull mesh naming", default="UCX") + + collision_prefixes: StringProperty(name="Collision Prefixes", + description="Prefixes to use when adding objects to the collision collection. Separate the prefixes with a comma ',' and leave no space between them", + default="UCX,UBX,USP") enable_wireshaded_cs: BoolProperty(name="Wireframe / Shaded Context Sensitive Mode", description="Enables context sensitive mode for the Wireframe / Shaded Tool", @@ -437,6 +443,7 @@ def draw_general(self, context): #Quick Convex Hull row = box.row(align=True) row.prop(self, "quickconvex_prefix", toggle=False) + row.prop(self, "collision_prefixes", toggle=False) row = box.row(align=True) row.prop(self, "enable_legacy_tools", toggle=False) From a2fbadd6adcb89107c5af4e0c108b1bef27fac32 Mon Sep 17 00:00:00 2001 From: Maxi Date: Mon, 8 Dec 2025 20:10:06 +0100 Subject: [PATCH 24/27] Reorganized preferences menu --- utils/user_prefs.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/utils/user_prefs.py b/utils/user_prefs.py index a881499..af022e4 100644 --- a/utils/user_prefs.py +++ b/utils/user_prefs.py @@ -425,6 +425,16 @@ def draw_general(self, context): row = box.row(align=True) row.prop(self, "quickhplp_hp_suffix", toggle=False) + #Quick Convex Hull + box = layout.box() + row = box.row(align=True) + row.label(text="Collision:") + row = box.row(align=True) + row.prop(self, "quickconvex_prefix", toggle=False) + + row = box.row(align=True) + row.prop(self, "collision_prefixes", toggle=False) + #Other box = layout.box() row = box.row(align=True) @@ -440,10 +450,6 @@ def draw_general(self, context): row = box.row(align=True) row.prop(self, "transform_mode_cycle_cyclic", toggle=False) - #Quick Convex Hull - row = box.row(align=True) - row.prop(self, "quickconvex_prefix", toggle=False) - row.prop(self, "collision_prefixes", toggle=False) row = box.row(align=True) row.prop(self, "enable_legacy_tools", toggle=False) From b4b86a15df78b1e62b6cc26269e39bb86d06a34d Mon Sep 17 00:00:00 2001 From: Maxi Date: Mon, 8 Dec 2025 20:41:16 +0100 Subject: [PATCH 25/27] Fixed quick selection to work with curves and grease pencil --- op/mesh_modes.py | 18 +++++++++++++++--- utils/itools.py | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/op/mesh_modes.py b/op/mesh_modes.py index 5ac14bd..820403b 100644 --- a/op/mesh_modes.py +++ b/op/mesh_modes.py @@ -20,7 +20,7 @@ def quick_selection(target_mode, safe_mode=False): if current_object != None: #Convert types from Mesh to Gpencil space - if current_object.type == 'GPENCIL': + if current_object.type == 'GREASEPENCIL': if target_mode == 'VERT': target_mode = 'POINT' elif target_mode == 'EDGE': @@ -28,6 +28,10 @@ def quick_selection(target_mode, safe_mode=False): elif target_mode == 'FACE': target_mode = 'SEGMENT' + #Convert types from Mesh to Curve space + if current_object.type == 'CURVE': + target_mode = "EDIT_CURVE" + other_modes = itools.list_difference(['VERT', 'EDGE', 'FACE', 'POINT', 'STROKE', 'SEGMENT', 'OBJECT'], [target_mode]) sticky = get_enable_sticky_selection() @@ -62,8 +66,8 @@ def quick_selection(target_mode, safe_mode=False): store_sel_data(current_mode) itools.set_mode('OBJECT') - if current_object.type == 'GPENCIL': - bpy.ops.object.mode_set(mode="EDIT_GPENCIL") + if current_object.type == 'GREASEPENCIL': + bpy.ops.object.mode_set(mode="EDIT") if current_mode in other_modes: if target_mode == 'POINT': @@ -75,6 +79,14 @@ def quick_selection(target_mode, safe_mode=False): elif current_mode == target_mode: bpy.ops.object.mode_set(mode="OBJECT") + + if current_object.type == 'CURVE': + if current_mode == "OBJECT": + bpy.ops.object.mode_set(mode="EDIT") + + else: + bpy.ops.object.mode_set(mode="OBJECT") + diff --git a/utils/itools.py b/utils/itools.py index 6f11688..0bb00fe 100644 --- a/utils/itools.py +++ b/utils/itools.py @@ -41,7 +41,7 @@ def get_mode(): elif selection_mode[2]: return 'FACE' - if mode == 'EDIT_GPENCIL': + if mode == 'EDIT_GREASE_PENCIL': return bpy.context.scene.tool_settings.gpencil_selectmode_edit return mode From 922501abc96e9cb2ae7a413751020d53e70a61bb Mon Sep 17 00:00:00 2001 From: Maxi Date: Mon, 8 Dec 2025 20:59:33 +0100 Subject: [PATCH 26/27] Added option to enable duplicate mode in super smart create in object mode --- op/super_smart_create.py | 4 ++-- utils/user_prefs.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/op/super_smart_create.py b/op/super_smart_create.py index 9f25127..a44e786 100644 --- a/op/super_smart_create.py +++ b/op/super_smart_create.py @@ -1,7 +1,7 @@ import bpy from ..utils import itools as itools from ..utils import mesh as mesh -from ..utils.user_prefs import get_f2_active, get_ssc_switch_modes +from ..utils.user_prefs import get_f2_active, get_ssc_switch_modes, get_ssc_duplicate_pie_enable class SuperSmartCreate(bpy.types.Operator): bl_idname = "mesh.super_smart_create" @@ -64,7 +64,7 @@ def super_smart_create(self): mode = itools.get_mode() if mode == 'OBJECT': - if len(itools.get_selected()) > 0: + if len(itools.get_selected()) > 0 and get_ssc_duplicate_pie_enable(): bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_SSC_Duplicate") else: diff --git a/utils/user_prefs.py b/utils/user_prefs.py index af022e4..4793936 100644 --- a/utils/user_prefs.py +++ b/utils/user_prefs.py @@ -149,6 +149,10 @@ def get_ssc_switch_modes(): prefs = get_addon_preferences() return prefs.ssc_switch_modes +def get_ssc_duplicate_pie_enable(): + prefs = get_addon_preferences() + return prefs.scc_duplicate_pie_enable + def get_ssc_qblocker_integration(): prefs = get_addon_preferences() @@ -285,6 +289,10 @@ class AddonPreferences(AddonPreferences): ssc_bezierutilities_integration: BoolProperty(name="Super Smart Create Bezier Utilities Integration", description="Use Flexi Bezier Tool for spline creation, needs Beier Utilities to be used", default=False) + + scc_duplicate_pie_enable: BoolProperty(name="Super Smart Duplicate Pie Enable", + description="Enables the duplicate pie when in object mode when at least an object is selected", + default=False) enable_sticky_selection: BoolProperty(name="Selection Sticky Mode", description="Enables Sticky Selection when using Quick Select Modes and Selection Cycle", @@ -374,7 +382,9 @@ def draw_general(self, context): row = box.row(align=True) row.prop(self, "ssc_switch_modes", toggle=False) - + row = box.row(align=True) + row.prop(self, "scc_duplicate_pie_enable", toggle=False) + if qblocker_active: row = box.row(align=True) row.prop(self, "ssc_qblocker_integration", toggle=True) @@ -434,7 +444,7 @@ def draw_general(self, context): row = box.row(align=True) row.prop(self, "collision_prefixes", toggle=False) - + #Other box = layout.box() row = box.row(align=True) From d3824af4070f6ac4018bd29750ad916563c02f9e Mon Sep 17 00:00:00 2001 From: Maxi Date: Mon, 8 Dec 2025 20:59:59 +0100 Subject: [PATCH 27/27] Version bump, increased minumum version --- __init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/__init__.py b/__init__.py index c8b4f1c..44bceb0 100644 --- a/__init__.py +++ b/__init__.py @@ -28,9 +28,9 @@ "name": "Interactive Tools", "author": "Maxi Vazquez, Ajfurey", "description": "Collection of context sensitive tools", - "blender": (4, 2, 0), + "blender": (4, 5, 0), "location": "View3D", - "version": (1, 4, 1), + "version": (1, 5, 0), "tracker_url": "https://github.com/maxivz/interactivetoolsblender/issues", "wiki_url": "https://maxivz.github.io/interactivetoolsblenderdocs.github.io/", "warning": "",