diff --git a/__init__.py b/__init__.py index 52d40c3..bbe339c 100755 --- a/__init__.py +++ b/__init__.py @@ -1,7 +1,7 @@ bl_info = { "name": "gp interpolate", "author": "Christophe Seux, Samuel Bernou", - "version": (0, 5, 0), + "version": (0, 6, 0), "blender": (3, 6, 0), "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 a5d3a4d..90f289e 100644 --- a/interpolate_strokes/operators.py +++ b/interpolate_strokes/operators.py @@ -24,7 +24,7 @@ from mathutils.geometry import (barycentric_transform, ## TODO: add bake animation to empty for later GP layer parenting -## TODO: Occlusion management +## TODO: Convert operator to Modal to Stop animation class GP_OT_interpolate_stroke(bpy.types.Operator): bl_idname = "gp.interpolate_stroke" @@ -258,6 +258,7 @@ class GP_OT_interpolate_stroke(bpy.types.Operator): matrix_inv = np.array(gp.matrix_world.inverted(), dtype='float64')#.inverted() new_strokes = gp.data.layers.active.active_frame.strokes[-len(strokes_data):] + # for new_stroke, stroke_data in zip(new_strokes, strokes_data): for new_stroke, stroke_data in zip(reversed(new_strokes), reversed(strokes_data)): world_co_3d = [] for stroke, point_co, object_hit, hit_location, tri_a, tri_indices in stroke_data: @@ -291,47 +292,43 @@ class GP_OT_interpolate_stroke(bpy.types.Operator): new_stroke.points.update() - ## TODO: Occlusion management - ## Tag occlusion on points for removal (need to create all substrokes from existing strokes) - # if settings.method == 'GEOMETRY': - # ## WIP - # occlusion_list = [False]*len(world_co_3d) - # for i, nco in enumerate(world_co_3d): - # vec_direction = nco - origin - # ## Maybe distance need to be reduced by tiny segment... - # n_hit, _hit_location, _normal, _n_face_index, n_object_hit, _matrix = scn.ray_cast(dg, origin, vec_direction, distance=vec_direction.length) - # # if n_hit and n_object_hit != object_hit: # note: Arm could still hit from torso... - # if n_hit: - # # if there is a hit, it's occluded - # # Occluded ! - # occlusion_list[i] = True + ## Occlusion management + if settings.method == 'GEOMETRY' and 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, _hit_location, _normal, _n_face_index, n_object_hit, _matrix = scn.ray_cast(dg, origin, vec_direction, distance=dist) + # if there is a hit, it's occluded + if n_hit: + viz_list[i] = False - # if all(occlusion_list): - # # all occluded, Just remove stroke (! Safer to reverse both list in zip iteration !) - # gp.data.layers.active.active_frame.strokes.remove(new_stroke) + if all(viz_list): + # All visible, do nothing (just keep previous stroke) + continue - # if any(occlusion_list): - # # Create substroke according to indices in original stroke - # for sublist in index_list_from_bools(occlusion_list): - # ## Clear if only one isolated point ? - # # if len(sublist) == 1: - # # continue - # ns = gp.data.layers.active.active_frame.strokes.new() - # for elem in ('hardness', 'material_index', 'line_width'): - # setattr(ns, elem, getattr(new_strokes, elem)) + 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.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_strokes.points[point_index], elem)) + ns = gp.data.layers.active.active_frame.strokes.new() + for elem in ('hardness', 'material_index', 'line_width'): + setattr(ns, elem, getattr(new_stroke, elem)) - # ## Delete original stroke - # gp.data.layers.active.active_frame.strokes.remove(new_stroke) - + 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 + gp.data.layers.active.active_frame.strokes.remove(new_stroke) wm.progress_end() # Pgs - if debug: print(f"Paste'n'place time {time()-start - scan_time}s") else: diff --git a/interpolate_strokes/properties.py b/interpolate_strokes/properties.py index ace8f78..4cb87a0 100644 --- a/interpolate_strokes/properties.py +++ b/interpolate_strokes/properties.py @@ -29,6 +29,10 @@ class GP_PG_interpolate_settings(PropertyGroup): default=False, description='Apply the interpolation on the all keys forward or backward') + remove_occluded : BoolProperty(name='Remove Occluded Points', + default=False, + description='Remove point when occluded by geometry (works best in animation mode)') + search_range : FloatProperty( name="Search Range", description="Search range size when points are out of mesh\ diff --git a/ui.py b/ui.py index 1e06516..c7cd0b7 100755 --- a/ui.py +++ b/ui.py @@ -38,10 +38,10 @@ class GP_PT_interpolate(bpy.types.Panel): col.prop_search(settings, 'target_bone', settings.target_rig.pose, 'bones', text='Bone') col.prop(settings, 'use_bone_rotation', text='Use Bone Rotation') - elif settings.method == 'GEOMETRY': col.prop(settings, 'target_collection', text='Collection') col.prop(settings, 'search_range') + col.prop(settings, 'remove_occluded') elif settings.method == 'OBJECT': col.prop(settings, 'target_object', text='Object')