diff --git a/nijigp/__init__.py b/nijigp/__init__.py index 6d908fd..380067e 100644 --- a/nijigp/__init__.py +++ b/nijigp/__init__.py @@ -17,7 +17,7 @@ "author" : "https://github.com/chsh2/nijiGPen", "description" : "Tools modifying Grease Pencil strokes in a 2D plane", "blender" : (3, 3, 0), - "version" : (0, 2, 1), + "version" : (0, 2, 2), "location" : "View3D > Sidebar > NijiGP, in Draw and Edit mode of Grease Pencil objects", "warning" : "This addon is still in an early stage of development", "category" : "Object" diff --git a/nijigp/operator_mesh.py b/nijigp/operator_mesh.py index 8cc92b5..b853163 100644 --- a/nijigp/operator_mesh.py +++ b/nijigp/operator_mesh.py @@ -7,6 +7,38 @@ MAX_DEPTH = 4096 +class MeshManagement(bpy.types.Operator): + """Manage mesh objects generated from the active GPencil object""" + bl_idname = "gpencil.nijigp_mesh_management" + bl_label = "Generated Mesh Management" + bl_category = 'View' + bl_options = {'REGISTER', 'UNDO'} + + action: bpy.props.EnumProperty( + name='Action', + items=[('HIDE', 'Hide', ''), + ('SHOW', 'Show', ''), + ('CLEAR', 'Clear', '')], + default='SHOW', + description='Actions to perform on children meshes' + ) + + def draw(self, context): + layout = self.layout + row = layout.row() + row.prop(self, "action", text = "Action") + + def execute(self, context): + current_gp_obj = context.object + for obj in current_gp_obj.children: + if 'nijigp_mesh' in obj: + obj.hide_set(self.action == 'HIDE') + obj.hide_render = (self.action == 'HIDE') + if self.action == 'CLEAR': + bpy.data.objects.remove(obj, do_unlink=True) + + return {'FINISHED'} + class MeshGenerationByNormal(bpy.types.Operator): """Generate a planar mesh with an interpolated normal map calculated from the selected strokes""" bl_idname = "gpencil.nijigp_mesh_generation_normal" @@ -15,11 +47,19 @@ class MeshGenerationByNormal(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO'} vertical_gap: bpy.props.FloatProperty( - name='Resolution', + name='Vertical Gap', default=0.05, min=0, unit='LENGTH', description='Mininum vertical space between generated meshes' ) + ignore_mode: bpy.props.EnumProperty( + name='Ignore', + items=[('NONE', 'None', ''), + ('LINE', 'All Lines', ''), + ('OPEN', 'All Open Lines', '')], + default='NONE', + description='Skip strokes without fill' + ) mesh_style: bpy.props.EnumProperty( name='Mesh Style', items=[('TRI', 'Delaunay Triangulation', ''), @@ -49,6 +89,7 @@ def draw(self, context): layout.label(text = "Multi-Object Alignment:") box1 = layout.box() box1.prop(self, "vertical_gap", text = "Vertical Gap") + box1.prop(self, "ignore_mode") layout.label(text = "Geometry Options:") box2 = layout.box() box2.label(text = "Mesh Style:") @@ -66,6 +107,17 @@ def execute(self, context): self.report({"ERROR"}, "Please install dependencies in the Preferences panel.") return {'FINISHED'} + current_gp_obj = context.object + # Ignore specific strokes by deselecting them + for i,layer in enumerate(current_gp_obj.data.layers): + if not layer.lock and hasattr(layer.active_frame, "strokes"): + for j,stroke in enumerate(layer.active_frame.strokes): + if stroke.select: + if self.ignore_mode == 'LINE' and is_stroke_line(stroke, current_gp_obj): + stroke.select = False + if self.ignore_mode == 'OPEN' and is_stroke_line(stroke, current_gp_obj) and not stroke.use_cyclic: + stroke.select = False + # Preprocess using Offset operator # The triangle library may crash in several cases, which should be avoided with every effort # https://www.cs.cmu.edu/~quake/triangle.trouble.html @@ -78,7 +130,6 @@ def execute(self, context): bpy.ops.gpencil.nijigp_offset_selected() # Convert selected strokes to 2D polygon point lists - current_gp_obj = context.object stroke_info = [] stroke_list = [] mesh_names = [] @@ -411,11 +462,19 @@ class MeshGenerationByOffsetting(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO'} vertical_gap: bpy.props.FloatProperty( - name='Resolution', + name='Vertical Gap', default=0, min=0, unit='LENGTH', description='Mininum vertical space between generated meshes' ) + ignore_mode: bpy.props.EnumProperty( + name='Ignore', + items=[('NONE', 'None', ''), + ('LINE', 'All Lines', ''), + ('OPEN', 'All Open Lines', '')], + default='NONE', + description='Skip strokes without fill' + ) offset_amount: bpy.props.FloatProperty( name='Offset', default=0.1, soft_min=0, unit='LENGTH', @@ -495,6 +554,7 @@ def draw(self, context): layout.label(text = "Multi-Object Alignment:") box1 = layout.box() box1.prop(self, "vertical_gap", text = "Vertical Gap") + box1.prop(self, "ignore_mode") layout.label(text = "Geometry Options:") box2 = layout.box() box2.prop(self, "offset_amount", text = "Offset Amount") @@ -544,6 +604,10 @@ def execute(self, context): if not layer.lock and hasattr(layer.active_frame, "strokes"): for j,stroke in enumerate(layer.active_frame.strokes): if stroke.select: + if self.ignore_mode == 'LINE' and is_stroke_line(stroke, current_gp_obj): + continue + if self.ignore_mode == 'OPEN' and is_stroke_line(stroke, current_gp_obj) and not stroke.use_cyclic: + continue stroke_info.append([stroke, i, j]) stroke_list.append(stroke) mesh_names.append('Offset_' + layer.info + '_' + str(j)) diff --git a/nijigp/ui_panels.py b/nijigp/ui_panels.py index b9e4374..849615f 100644 --- a/nijigp/ui_panels.py +++ b/nijigp/ui_panels.py @@ -139,15 +139,37 @@ class NIJIGP_PT_edit_panel_mesh(bpy.types.Panel): def draw(self, context): layout = self.layout - scene = context.scene - obj = context.object + layout.label(text="Preprocessing:") row = layout.row() row.operator("gpencil.stroke_sample", text="Resample") row.operator("gpencil.stroke_smooth", text="Smooth") + layout.label(text="Generation Methods:") row = layout.row() row.operator("gpencil.nijigp_mesh_generation_offset", text="Frustum by Offset", icon="CONE") row = layout.row() row.operator("gpencil.nijigp_mesh_generation_normal", text="Planar with Normals", icon="NORMALS_FACE") + layout.label(text="Mesh Management:") + row = layout.row() + row.operator("gpencil.nijigp_mesh_management", text="Hide").action = 'HIDE' + row.operator("gpencil.nijigp_mesh_management", text="Show").action = 'SHOW' + row.operator("gpencil.nijigp_mesh_management", text="Clear").action = 'CLEAR' + +class NIJIGP_PT_draw_panel_mesh(bpy.types.Panel): + bl_idname = 'NIJIGP_PT_draw_panel_mesh' + bl_label = "Strokes to Meshes" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "NijiGP" + bl_context = "greasepencil_paint" + + def draw(self, context): + layout = self.layout + + layout.label(text="Mesh Management:") + row = layout.row() + row.operator("gpencil.nijigp_mesh_management", text="Hide").action = 'HIDE' + row.operator("gpencil.nijigp_mesh_management", text="Show").action = 'SHOW' + row.operator("gpencil.nijigp_mesh_management", text="Clear").action = 'CLEAR' \ No newline at end of file diff --git a/nijigp/utils.py b/nijigp/utils.py index 9974219..5d0e53b 100644 --- a/nijigp/utils.py +++ b/nijigp/utils.py @@ -225,6 +225,14 @@ def overlapping_strokes(s1, s2): return False +def is_stroke_line(stroke, gp_obj): + """ + Check if a stroke does not have fill material + """ + mat_idx = stroke.material_index + material = gp_obj.material_slots[mat_idx].material + return not material.grease_pencil.show_fill + def is_stroke_locked(stroke, gp_obj): """ Check if a stroke has the material that is being locked or invisible