From 071f4fd13b19c945571f615f53d5ad42cb229af0 Mon Sep 17 00:00:00 2001 From: pullusb Date: Wed, 24 Jul 2024 11:59:35 +0200 Subject: [PATCH] simplify occlusion removal code uing native operators Better UI --- __init__.py | 4 +- interpolate_strokes/operators.py | 64 ++++++++++++++------------------ ui.py | 14 +++++-- 3 files changed, 40 insertions(+), 42 deletions(-) diff --git a/__init__.py b/__init__.py index 6de670e..c0c5ab1 100755 --- a/__init__.py +++ b/__init__.py @@ -1,7 +1,7 @@ bl_info = { - "name": "gp interpolate", + "name": "GP Interpolate", "author": "Christophe Seux, Samuel Bernou", - "version": (0, 8, 1), + "version": (0, 8, 2), "blender": (4, 0, 2), "location": "Sidebar > Gpencil Tab > Interpolate", "description": "Interpolate Grease pencil strokes over 3D", diff --git a/interpolate_strokes/operators.py b/interpolate_strokes/operators.py index ce4416f..2389449 100644 --- a/interpolate_strokes/operators.py +++ b/interpolate_strokes/operators.py @@ -158,7 +158,6 @@ class GP_OT_interpolate_stroke(GP_OT_interpolate_stroke_base): if obj: object_hit, hit_location, tri, tri_indices = obj_ray_cast(obj, square_co, origin, dg) else: - # scene raycast object_hit, hit_location, tri, tri_indices = ray_cast_point(square_co, origin, dg) if object_hit: @@ -228,7 +227,6 @@ class GP_OT_interpolate_stroke(GP_OT_interpolate_stroke_base): elif self.settings.method == 'GEOMETRY': if col != context.scene.collection: included_cols.append(col.name) - ## Maybe include a plane just behind geo ? probably bad idea elif self.settings.method == 'OBJECT': if not self.settings.target_object: @@ -263,7 +261,7 @@ class GP_OT_interpolate_stroke(GP_OT_interpolate_stroke_base): vl_col = bpy.context.view_layer.layer_collection.children.get(intercol.name) intercol.hide_viewport = vl_col.exclude = vl_col.hide_viewport = False - # Override collection + ## Override collection col = intercol dg = bpy.context.evaluated_depsgraph_get() @@ -275,9 +273,10 @@ class GP_OT_interpolate_stroke(GP_OT_interpolate_stroke_base): point_co_world = self.gp.matrix_world @ point.co if target_obj: + ## Object raycast object_hit, hit_location, tri, tri_indices = obj_ray_cast(target_obj, point_co_world, origin, dg) else: - # scene raycast + ## Scene raycast object_hit, hit_location, tri, tri_indices = ray_cast_point(point_co_world, origin, dg) ## Iterative increasing search range when no surface hit @@ -342,10 +341,13 @@ class GP_OT_interpolate_stroke(GP_OT_interpolate_stroke_base): mesh=True) dg = bpy.context.evaluated_depsgraph_get() - # Get only just pasted strokes + + ## Get pasted stroke new_strokes = [s for s in self.gp.data.layers.active.active_frame.strokes if s.select] - # new_strokes = self.gp.data.layers.active.active_frame.strokes[-len(self.strokes_data):] - + ## Keep reference to all accessible other strokes (in all accessible layer) + other_strokes = [s for l in self.gp.data.layers if l.active_frame and not l.lock for s in l.active_frame.strokes if not s.select] + + occluded_points = [] # For new_stroke, stroke_data in zip(new_strokes, self.strokes_data): for new_stroke, stroke_data in zip(list(new_strokes), list(self.strokes_data)): world_co_3d = [] @@ -364,39 +366,27 @@ class GP_OT_interpolate_stroke(GP_OT_interpolate_stroke_base): ## Occlusion management if self.settings.method == 'GEOMETRY' and self.settings.remove_occluded: - viz_list = [True]*len(world_co_3d) - for i, nco in enumerate(world_co_3d): - vec_direction = nco - origin - ## Reduced distance slightly to avoid occlusion on same source... - dist = vec_direction.length - 0.001 - n_hit, _, _, _, _, _ = scn.ray_cast(dg, origin, vec_direction, distance=dist) - # If there is a hit, it's occluded + for i, point in enumerate(new_stroke.points): + point_co = world_co_3d[i] + vec_direction = point_co - origin + ## Raycast with slightly reduced distance (avoid occlusion on same source) + n_hit, _, _, _, _, _ = scn.ray_cast(dg, origin, vec_direction, distance=vec_direction.length - 0.001) if n_hit: - viz_list[i] = False + occluded_points.append(point) - if all(viz_list): - # All visible, do nothing (just keep previous stroke) - continue - - if any(viz_list): - # Create sub-strokes according to indices in original stroke - for sublist in index_list_from_bools(viz_list): - ## Clear if only one isolated point ? - if len(sublist) == 1: - continue - - ns = self.gp.data.layers.active.active_frame.strokes.new() - for elem in ('hardness', 'material_index', 'line_width'): - setattr(ns, elem, getattr(new_stroke, elem)) - - ns.points.add(len(sublist)) - for i, point_index in enumerate(sublist): - for elem in ('uv_factor', 'uv_fill', 'uv_rotation', 'pressure', 'co', 'strength', 'vertex_color'): - setattr(ns.points[i], elem, getattr(new_stroke.points[point_index], elem)) - - ## Delete original stroke - self.gp.data.layers.active.active_frame.strokes.remove(new_stroke) + if occluded_points: + ## Select only occluded point + bpy.ops.gpencil.select_all(action='DESELECT') + for point in occluded_points: + point.select = True + ## remove points + bpy.ops.gpencil.delete(type='POINTS') + ## restore selection (keep new strokes selected) + bpy.ops.gpencil.select_all(action='SELECT') + for stroke in other_strokes: + stroke.select = False + self.loop_count += 1 if self.loop_count >= len(self.frames_to_jump): self.exit_modal(context) diff --git a/ui.py b/ui.py index 3d87746..0cf86dc 100755 --- a/ui.py +++ b/ui.py @@ -15,6 +15,7 @@ class GP_PT_interpolate(bpy.types.Panel): layout = self.layout layout.use_property_split = True + layout.use_property_decorate = False col = layout.column(align=False) ## Interpolation buttons @@ -32,10 +33,17 @@ class GP_PT_interpolate(bpy.types.Panel): next_text = f'{scn.frame_current} > {scn.frame_preview_end if scn.use_preview_range else scn.frame_end}' row = col.row(align=True) - row.scale_x = 3 + # row.scale_y = 1.2 + direction_button_row = row.row(align=True) + direction_button_row.scale_x = 3 ops_id = "gp.interpolate_stroke_tri" if settings.method == 'TRI' else "gp.interpolate_stroke" - row.operator(ops_id, text=prev_text, icon=prev_icon).next = False - row.operator(ops_id, text=next_text, icon=next_icon).next = True + direction_button_row.operator(ops_id, text=prev_text, icon=prev_icon).next = False + direction_button_row.operator(ops_id, text=next_text, icon=next_icon).next = True + + ## Button for interactive mode + # interactive_mode_row = row.row() + # interactive_mode_row.operator(ops_id, text='', icon='ACTION_TWEAK') # SNAP_INCREMENT, ACTION_TWEAK, CON_ACTION, CENTER_ONLY + col.prop(settings, 'use_animation', text='Animation') col.prop(settings, 'method', text='Method')