Fix error when there is no hit
parent
6707d693d6
commit
4da4c2801d
|
@ -1,7 +1,7 @@
|
|||
bl_info = {
|
||||
"name": "gp interpolate",
|
||||
"author": "Christophe Seux, Samuel Bernou",
|
||||
"version": (0, 7, 1),
|
||||
"version": (0, 7, 2),
|
||||
"blender": (3, 6, 0),
|
||||
"location": "Sidebar > Gpencil Tab > Interpolate",
|
||||
"description": "Interpolate Grease pencil strokes over 3D",
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
from gp_interpolate.interpolate_strokes import (operators,
|
||||
properties,
|
||||
debug,
|
||||
#interpolate_simple
|
||||
)
|
||||
|
||||
modules = (
|
||||
properties,
|
||||
operators,
|
||||
debug
|
||||
#interpolate_simple,
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
import bpy
|
||||
import numpy as np
|
||||
from time import perf_counter, time, sleep
|
||||
from mathutils import Vector, Matrix
|
||||
|
||||
from ..utils import empty_at
|
||||
from gp_interpolate.utils import (matrix_transform,
|
||||
plane_on_bone,
|
||||
ray_cast_point,
|
||||
obj_ray_cast,
|
||||
intersect_with_tesselated_plane,
|
||||
triangle_normal,
|
||||
search_square,
|
||||
get_gp_draw_plane,
|
||||
create_plane,
|
||||
following_keys,
|
||||
index_list_from_bools,
|
||||
attr_set)
|
||||
|
||||
from mathutils.geometry import (barycentric_transform,
|
||||
intersect_point_tri,
|
||||
intersect_point_line,
|
||||
intersect_line_plane,
|
||||
tessellate_polygon)
|
||||
|
||||
|
||||
class GP_OT_debug_geometry_interpolation_targets(bpy.types.Operator):
|
||||
bl_idname = "gp.debug_geometry_interpolation_targets"
|
||||
bl_label = "Debug geometry interpolation target"
|
||||
bl_description = 'Identify position geometry used for interpolating select point at current frame'
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object and context.object.type == 'GPENCIL'\
|
||||
and context.mode in ('EDIT_GPENCIL', 'SCULPT_GPENCIL', 'PAINT_GPENCIL'):
|
||||
return True
|
||||
cls.poll_message_set("Need a Grease pencil object in Edit or Sculpt mode")
|
||||
return False
|
||||
|
||||
clear : bpy.props.BoolProperty(default=False, options={'SKIP_SAVE'})
|
||||
|
||||
def apply_and_store(self):
|
||||
# self.store = []
|
||||
# item = (prop, attr, [new_val])
|
||||
for item in self.store_list:
|
||||
prop, attr = item[:2]
|
||||
self.store.append( (prop, attr, getattr(prop, attr)) )
|
||||
if len(item) >= 3:
|
||||
setattr(prop, attr, item[2])
|
||||
|
||||
def restore(self):
|
||||
for prop, attr, old_val in self.store:
|
||||
setattr(prop, attr, old_val)
|
||||
|
||||
def exit_ops(self, context, status='INFO', text=None):
|
||||
if text:
|
||||
self.report({status}, text)
|
||||
|
||||
def execute(self, context):
|
||||
debug_col_name = '.gp_interpolation_debug'
|
||||
debug_col = context.scene.collection.children.get(debug_col_name)
|
||||
if not debug_col:
|
||||
debug_col = bpy.data.collections.get(debug_col_name)
|
||||
|
||||
if self.clear:
|
||||
if not debug_col:
|
||||
return {'CANCELLED'}
|
||||
|
||||
## Clear debug collection and remove
|
||||
for ob in reversed(debug_col.objects):
|
||||
bpy.data.objects.remove(ob)
|
||||
bpy.data.collections.remove(debug_col)
|
||||
return {'FINISHED'}
|
||||
|
||||
if not debug_col:
|
||||
debug_col = bpy.data.collections.new(debug_col_name)
|
||||
if debug_col.name not in context.scene.collection.children:
|
||||
context.scene.collection.children.link(debug_col)
|
||||
|
||||
|
||||
## Clear debug collection
|
||||
for ob in reversed(debug_col.objects):
|
||||
bpy.data.objects.remove(ob)
|
||||
|
||||
# return {'FINISHED'}
|
||||
|
||||
scn = bpy.context.scene
|
||||
|
||||
self.gp = context.object
|
||||
# matrix = np.array(self.gp.matrix_world, dtype='float64')
|
||||
# origin = np.array(scn.camera.matrix_world.to_translation(), 'float64')
|
||||
matrix = self.gp.matrix_world
|
||||
origin = scn.camera.matrix_world.to_translation()
|
||||
|
||||
settings = context.scene.gp_interpo_settings
|
||||
col = settings.target_collection
|
||||
if not col:
|
||||
col = scn.collection
|
||||
|
||||
# print('----')
|
||||
if not self.gp.data.layers.active:
|
||||
self.exit_ops(context, status='ERROR', text='No active layer')
|
||||
return {'CANCELLED'}
|
||||
if not self.gp.data.layers.active.active_frame:
|
||||
self.exit_ops(context, status='ERROR', text='No active frame')
|
||||
return {'CANCELLED'}
|
||||
|
||||
tgt_strokes = [s for s in self.gp.data.layers.active.active_frame.strokes if s.select]
|
||||
|
||||
## If nothing selected in sculpt/paint, Select all before triggering
|
||||
if not tgt_strokes and context.mode in ('SCULPT_GPENCIL', 'PAINT_GPENCIL'):
|
||||
for s in self.gp.data.layers.active.active_frame.strokes:
|
||||
s.select = True
|
||||
tgt_strokes = self.gp.data.layers.active.active_frame.strokes
|
||||
|
||||
if not tgt_strokes:
|
||||
self.exit_ops(context, status='ERROR', text='No stroke selected !')
|
||||
return {'CANCELLED'}
|
||||
|
||||
included_cols = [c.name for c in self.gp.users_collection]
|
||||
target_obj = None
|
||||
|
||||
if settings.method == 'GEOMETRY':
|
||||
if col != context.scene.collection:
|
||||
included_cols.append(col.name)
|
||||
## Maybe include a plane just behind geo ? probably bad idea
|
||||
|
||||
elif settings.method == 'OBJECT':
|
||||
if not settings.target_object:
|
||||
self.exit_ops(context, status='ERROR', text='No Object selected')
|
||||
return {'CANCELLED'}
|
||||
col = scn.collection # Reset collection filter
|
||||
target_obj = settings.target_object
|
||||
if target_obj.library:
|
||||
## Look if an override exists in scene to use instead of default object
|
||||
if (override := next((o for o in scn.objects if o.name == target_obj.name and o.override_library), None)):
|
||||
target_obj = override
|
||||
|
||||
## Get only active point
|
||||
points = [p for p in tgt_strokes[0].points if p.select]
|
||||
print('points: ', points)
|
||||
|
||||
dg = bpy.context.evaluated_depsgraph_get()
|
||||
|
||||
# nb_points = len(stroke.points)
|
||||
## Batch get absolute coordinate
|
||||
# local_co = np.empty(nb_points * 3, dtype='float64')
|
||||
# stroke.points.foreach_get('co', local_co)
|
||||
# local_co_3d = local_co.reshape((nb_points, 3))
|
||||
# world_co_3d = matrix_transform(local_co.reshape((nb_points, 3)), matrix)
|
||||
|
||||
stroke_data = []
|
||||
for i, point in enumerate(points):
|
||||
point_co_world = matrix @ point.co
|
||||
|
||||
if target_obj:
|
||||
object_hit, hit_location, tri, tri_indices = obj_ray_cast(target_obj, Vector(point_co_world), origin, dg)
|
||||
else:
|
||||
# Scene raycast
|
||||
object_hit, hit_location, tri, tri_indices = ray_cast_point(point_co_world, origin, dg)
|
||||
|
||||
## Increasing search range
|
||||
if not object_hit: # or object_hit not in col.all_objects[:]:
|
||||
found = False
|
||||
for iteration in range(1, 6):
|
||||
ct = 0
|
||||
for square_co in search_square(point_co_world, factor=settings.search_range * iteration):
|
||||
ct += 1
|
||||
if target_obj:
|
||||
object_hit, hit_location, tri, tri_indices = obj_ray_cast(target_obj, Vector(square_co), origin, dg)
|
||||
else:
|
||||
# scene raycast
|
||||
object_hit, hit_location, tri, tri_indices = ray_cast_point(square_co, origin, dg)
|
||||
|
||||
is_hit = 'HIT' if object_hit else ''
|
||||
empty_at(name=f'{is_hit}search_{iteration}({ct})', pos=square_co, collection=debug_col, size=0.001, show_name=True) # type='SPHERE'
|
||||
|
||||
if object_hit: # and object_hit in col.all_objects[:]:
|
||||
context.scene.cursor.location = hit_location
|
||||
## Get location coplanar with triangle
|
||||
hit_location = intersect_line_plane(origin, point_co_world, tri[0], triangle_normal(*tri))
|
||||
empty_at(name=f'{object_hit.name}_{i}-{iteration}', pos=hit_location, collection=debug_col, type='SPHERE', size=0.001, show_name=True)
|
||||
found = True
|
||||
# print(f'{si}:{i} iteration {iteration}') # Dbg
|
||||
break
|
||||
|
||||
if found:
|
||||
break
|
||||
|
||||
if not found:
|
||||
context.scene.cursor.location = point_co_world
|
||||
self.exit_ops(context, status='ERROR', text=f'point {i} could not find underlying geometry')
|
||||
return {'CANCELLED'}
|
||||
|
||||
else:
|
||||
## Hit at original position
|
||||
print(object_hit.name)
|
||||
empty_at(name=f'{object_hit.name}_{i}', pos=hit_location, collection=debug_col, type='SPHERE', size=0.001, show_name=True)
|
||||
|
||||
print('Done')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
GP_OT_debug_geometry_interpolation_targets,
|
||||
)
|
||||
|
||||
def register():
|
||||
for c in classes:
|
||||
bpy.utils.register_class(c)
|
||||
|
||||
|
||||
def unregister():
|
||||
for c in reversed(classes):
|
||||
bpy.utils.unregister_class(c)
|
|
@ -281,9 +281,10 @@ class GP_OT_interpolate_stroke(bpy.types.Operator):
|
|||
object_hit, hit_location, tri, tri_indices = obj_ray_cast(target_obj, Vector(square_co), origin, dg)
|
||||
else:
|
||||
# scene raycast
|
||||
object_hit, hit_location, tri, tri_indices = ray_cast_point(point_co_world, origin, dg)
|
||||
object_hit, hit_location, tri, tri_indices = ray_cast_point(square_co, origin, dg)
|
||||
|
||||
if object_hit: # and object_hit in col.all_objects[:]:
|
||||
if object_hit:
|
||||
## Get location coplanar with triangle
|
||||
hit_location = intersect_line_plane(origin, point_co_world, tri[0], triangle_normal(*tri))
|
||||
found = True
|
||||
# print(f'{si}:{i} iteration {iteration}') # Dbg
|
||||
|
|
6
ui.py
6
ui.py
|
@ -71,6 +71,12 @@ class GP_PT_interpolate(bpy.types.Panel):
|
|||
# col.operator('gp.parent_layer', text='Direct Parent').direct_parent = True
|
||||
# col.operator('gp.parent_layer', text='Empty Parent').direct_parent = False
|
||||
|
||||
if context.scene.gp_interpo_settings.method in ('GEOMETRY', 'OBJECT'):
|
||||
col.separator()
|
||||
row = col.row(align=True)
|
||||
row.operator('gp.debug_geometry_interpolation_targets', text='Debug points')
|
||||
row.operator('gp.debug_geometry_interpolation_targets', text='', icon='X').clear = True
|
||||
|
||||
classes = (
|
||||
GP_PT_interpolate,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue