diff options
author | Pullusb <bernou.samuel@gmail.com> | 2021-07-12 22:28:00 +0300 |
---|---|---|
committer | Pullusb <bernou.samuel@gmail.com> | 2021-07-12 22:28:00 +0300 |
commit | 4ecb089854c286cd20fc508ce4fe6dab9aaa5387 (patch) | |
tree | 613fe5815948ba4bf25da023a428be7e9fb86e9f /greasepencil_tools | |
parent | 5be927b2f67d7ca3f453fe598f5c37a506be91ef (diff) |
GPencil Tools: Box deform multi object edit
Box deform not limited to active object in object mode. Now affect all selected grease pencils objects.
Also can keep lattice object on confirm when using Shift + valid buttons.
Diffstat (limited to 'greasepencil_tools')
-rw-r--r-- | greasepencil_tools/__init__.py | 2 | ||||
-rw-r--r-- | greasepencil_tools/box_deform.py | 131 | ||||
-rw-r--r-- | greasepencil_tools/prefs.py | 1 |
3 files changed, 100 insertions, 34 deletions
diff --git a/greasepencil_tools/__init__.py b/greasepencil_tools/__init__.py index 646f4862..0a8042c9 100644 --- a/greasepencil_tools/__init__.py +++ b/greasepencil_tools/__init__.py @@ -21,7 +21,7 @@ bl_info = { "name": "Grease Pencil Tools", "description": "Extra tools for Grease Pencil", "author": "Samuel Bernou, Antonio Vazquez, Daniel Martinez Lara, Matias Mendiola", -"version": (1, 5, 0), +"version": (1, 5, 2), "blender": (2, 91, 0), "location": "Sidebar > Grease Pencil > Grease Pencil Tools", "warning": "", diff --git a/greasepencil_tools/box_deform.py b/greasepencil_tools/box_deform.py index 0ac08987..a9a47ce2 100644 --- a/greasepencil_tools/box_deform.py +++ b/greasepencil_tools/box_deform.py @@ -31,6 +31,31 @@ def region_to_location(viewcoords, depthcoords): from bpy_extras import view3d_utils return view3d_utils.region_2d_to_location_3d(bpy.context.region, bpy.context.space_data.region_3d, viewcoords, depthcoords) +def store_cage(self, vg_name): + import time + unique_id = time.strftime(r'%y%m%d%H%M%S') # ex: 20210711111117 + # name = f'gp_lattice_{unique_id}' + name = f'{self.gp_obj.name}_lat{unique_id}' + vg = self.gp_obj.vertex_groups.get(vg_name) + if vg: + vg.name = name + for o in self.other_gp: + vg = o.vertex_groups.get(vg_name) + if vg: + vg.name = name + + self.cage.name = name + self.cage.data.name = name + mod = self.gp_obj.grease_pencil_modifiers.get('tmp_lattice') + if mod: + mod.name = name #f'Lattice_{unique_id}' + mod.vertex_group = name + for o in self.other_gp: + mod = o.grease_pencil_modifiers.get('tmp_lattice') + if mod: + mod.name = name + mod.vertex_group = name + def assign_vg(obj, vg_name, delete=False): ## create vertex group vg = obj.vertex_groups.get(vg_name) @@ -51,6 +76,10 @@ def view_cage(obj): gp = obj.data gpl = gp.layers + from_obj = bpy.context.mode == 'OBJECT' + all_gps = [o for o in bpy.context.selected_objects if o.type == 'GPENCIL'] + other_gp = [o for o in all_gps if o is not obj] + coords = [] initial_mode = bpy.context.mode @@ -73,13 +102,14 @@ def view_cage(obj): # get real location coords.append(obj.matrix_world @ p.co) - elif bpy.context.mode == 'OBJECT':#object mode -> all points - for l in gpl:# if l.hide:continue# only visible ? (might break things) - if not len(l.frames): - continue#skip frameless layer - for s in l.active_frame.strokes: - for p in s.points: - coords.append(obj.matrix_world @ p.co) + elif bpy.context.mode == 'OBJECT': # object mode -> all points of all selected gp objects + for gpo in all_gps: + for l in gpo.data.layers:# if l.hide:continue# only visible ? (might break things) + if not len(l.frames): + continue # skip frameless layer + for s in l.active_frame.strokes: + for p in s.points: + coords.append(gpo.matrix_world @ p.co) elif bpy.context.mode == 'PAINT_GPENCIL': # get last stroke points coordinated @@ -189,7 +219,7 @@ def view_cage(obj): ## Master (root) collection bpy.context.scene.collection.objects.link(cage) - # spawn cage and align it to view (Again ! align something to a vector !!! argg) + # spawn cage and align it to view r3d = bpy.context.space_data.region_3d viewmat = r3d.view_matrix @@ -205,17 +235,28 @@ def view_cage(obj): lattice.points_v = 2 lattice.points_w = 1 - lattice.interpolation_type_u = lattice_interp#'KEY_LINEAR'-'KEY_BSPLINE' - lattice.interpolation_type_v = lattice_interp#'KEY_LINEAR'-'KEY_BSPLINE' - lattice.interpolation_type_w = lattice_interp#'KEY_LINEAR'-'KEY_BSPLINE' + lattice.interpolation_type_u = lattice_interp #'KEY_LINEAR'-'KEY_BSPLINE' + lattice.interpolation_type_v = lattice_interp + lattice.interpolation_type_w = lattice_interp mod = obj.grease_pencil_modifiers.new('tmp_lattice', 'GP_LATTICE') + if from_obj: + mods = [] + for o in other_gp: + mods.append( o.grease_pencil_modifiers.new('tmp_lattice', 'GP_LATTICE') ) # move to top if modifiers exists for _ in range(len(obj.grease_pencil_modifiers)): bpy.ops.object.gpencil_modifier_move_up(modifier='tmp_lattice') + if from_obj: + for o in other_gp: + for _ in range(len(o.grease_pencil_modifiers)): + bpy.ops.object.gpencil_modifier_move_up({'object':o}, modifier='tmp_lattice') mod.object = cage + if from_obj: + for m in mods: + m.object = cage if initial_mode == 'PAINT_GPENCIL': mod.layer = gpl.active.info @@ -225,29 +266,32 @@ def view_cage(obj): if bpy.context.mode == 'EDIT_GPENCIL': mod.vertex_group = vg.name - #Go in object mode if not already + # Go in object mode if not already if bpy.context.mode != 'OBJECT': bpy.ops.object.mode_set(mode='OBJECT') # Store name of deformed object in case of 'revive modal' cage.vertex_groups.new(name=obj.name) + if from_obj: + for o in other_gp: + cage.vertex_groups.new(name=o.name) ## select and make cage active # cage.select_set(True) bpy.context.view_layer.objects.active = cage - obj.select_set(False)#deselect GP object - bpy.ops.object.mode_set(mode='EDIT')# go in lattice edit mode - bpy.ops.lattice.select_all(action='SELECT')# select all points + obj.select_set(False) # deselect GP object + bpy.ops.object.mode_set(mode='EDIT') # go in lattice edit mode + bpy.ops.lattice.select_all(action='SELECT') # select all points if prefs.use_clic_drag: ## Eventually change tool mode to tweak for direct point editing (reset after before leaving) - bpy.ops.wm.tool_set_by_id(name="builtin.select")# Tweaktoolcode + bpy.ops.wm.tool_set_by_id(name="builtin.select") # Tweaktoolcode return cage def back_to_obj(obj, gp_mode, org_lattice_toolset, context): - if context.mode == 'EDIT_LATTICE' and org_lattice_toolset:# Tweaktoolcode - restore the active tool used by lattice edit.. - bpy.ops.wm.tool_set_by_id(name = org_lattice_toolset)# Tweaktoolcode + if context.mode == 'EDIT_LATTICE' and org_lattice_toolset: # Tweaktoolcode - restore the active tool used by lattice edit.. + bpy.ops.wm.tool_set_by_id(name = org_lattice_toolset) # Tweaktoolcode # gp object active and selected bpy.ops.object.mode_set(mode='OBJECT') @@ -260,7 +304,7 @@ def delete_cage(cage): bpy.data.objects.remove(cage) bpy.data.lattices.remove(lattice) -def apply_cage(gp_obj, cage): +def apply_cage(gp_obj): mod = gp_obj.grease_pencil_modifiers.get('tmp_lattice') multi_user = None if mod: @@ -270,7 +314,8 @@ def apply_cage(gp_obj, cage): other_user = [o for o in bpy.data.objects if o is not gp_obj and o.data is old] gp_obj.data = gp_obj.data.copy() - bpy.ops.object.gpencil_modifier_apply(apply_as='DATA', modifier=mod.name) + # bpy.ops.object.gpencil_modifier_apply(apply_as='DATA', modifier=mod.name) + bpy.ops.object.gpencil_modifier_apply({'object': gp_obj}, apply_as='DATA', modifier=mod.name) if multi_user: for o in other_user: # relink @@ -281,17 +326,22 @@ def apply_cage(gp_obj, cage): else: print('tmp_lattice modifier not found to apply...') - delete_cage(cage) - -def cancel_cage(gp_obj, cage): +def cancel_cage(self): #remove modifier - mod = gp_obj.grease_pencil_modifiers.get('tmp_lattice') + mod = self.gp_obj.grease_pencil_modifiers.get('tmp_lattice') if mod: - gp_obj.grease_pencil_modifiers.remove(mod) + self.gp_obj.grease_pencil_modifiers.remove(mod) else: - print('tmp_lattice modifier not found to remove...') + print(f'tmp_lattice modifier not found to remove on {self.gp_obj.name}') + + for ob in self.other_gp: + mod = ob.grease_pencil_modifiers.get('tmp_lattice') + if mod: + ob.grease_pencil_modifiers.remove(mod) + else: + print(f'tmp_lattice modifier not found to remove on {ob.name}') - delete_cage(cage) + delete_cage(self.cage) class GP_OT_latticeGpDeform(bpy.types.Operator): @@ -413,8 +463,16 @@ valid:Spacebar/Enter, cancel:Del/Backspace/Tab/Ctrl+T" context.window_manager.boxdeform_running = False self.restore_prefs(context) back_to_obj(self.gp_obj, self.gp_mode, self.org_lattice_toolset, context) - apply_cage(self.gp_obj, self.cage)#must be in object mode - assign_vg(self.gp_obj, 'lattice_cage_deform_group', delete=True) + if event.shift: + # Let the cage as is with a unique ID + store_cage(self, 'lattice_cage_deform_group') + else: + apply_cage(self.gp_obj) # must be in object mode + assign_vg(self.gp_obj, 'lattice_cage_deform_group', delete=True) + for o in self.other_gp: + apply_cage(o) + assign_vg(o, 'lattice_cage_deform_group', delete=True) + delete_cage(self.cage) # back to original mode if self.gp_mode != 'OBJECT': @@ -448,7 +506,7 @@ valid:Spacebar/Enter, cancel:Del/Backspace/Tab/Ctrl+T" context.window_manager.boxdeform_running = False self.restore_prefs(context) back_to_obj(self.gp_obj, self.gp_mode, self.org_lattice_toolset, context) - cancel_cage(self.gp_obj, self.cage) + cancel_cage(self) assign_vg(self.gp_obj, 'lattice_cage_deform_group', delete=True) context.area.header_text_set(None) if self.gp_mode != 'OBJECT': @@ -523,16 +581,23 @@ valid:Spacebar/Enter, cancel:Del/Backspace/Tab/Ctrl+T" ## silent return return {'CANCELLED'} - #paint need VG workaround. object need good shortcut if context.mode not in ('EDIT_GPENCIL', 'OBJECT', 'PAINT_GPENCIL'): - # self.report({'WARNING'}, "Works only in following GPencil modes: edit")# ERROR + # self.report({'WARNING'}, "Works only in following GPencil modes: object / edit/ paint")# ERROR ## silent return return {'CANCELLED'} + # bpy.ops.ed.undo_push(message="Box deform step")#don't work as expected (+ might be obsolete) # https://developer.blender.org/D6147 <- undo forget self.gp_obj = context.object + + self.from_object = context.mode == 'OBJECT' + self.all_gps = self.other_gp = [] + if self.from_object: + self.all_gps = [o for o in bpy.context.selected_objects if o.type == 'GPENCIL'] + self.other_gp = [o for o in self.all_gps if o is not self.gp_obj] + # Clean potential failed previous job (delete tmp lattice) mod = self.gp_obj.grease_pencil_modifiers.get('tmp_lattice') if mod: @@ -549,7 +614,7 @@ valid:Spacebar/Enter, cancel:Del/Backspace/Tab/Ctrl+T" self.report({'ERROR'}, "Grease pencil object already has a lattice modifier (multi-lattices are enabled in blender 2.93+)") return {'CANCELLED'} - self.gp_mode = context.mode#store mode for restore + self.gp_mode = context.mode # store mode for restore # All good, create lattice and start modal diff --git a/greasepencil_tools/prefs.py b/greasepencil_tools/prefs.py index 57cf0ee5..929197d5 100644 --- a/greasepencil_tools/prefs.py +++ b/greasepencil_tools/prefs.py @@ -230,6 +230,7 @@ class GPT_MT_box_deform_doc(bpy.types.Menu): col.separator() col.label(text="Shortcuts:", icon='HAND') col.label(text="Spacebar / Enter : Confirm") + col.label(text="Shift + Spacebar / Enter : Confirm and let the lattice in place") col.label(text="Delete / Backspace / Tab(twice) / Ctrl+T : Cancel") col.label(text="M : Toggle between Linear and Spline mode at any moment") col.label(text="1-9 top row number : Subdivide the box") |