mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-24 05:47:57 +00:00
snap: use GPU detection for solid and wireframe objects.
Now is also works with x-ray mode.
This commit is contained in:
committed by
Bruno Perdigão
parent
9673f01c21
commit
209d487f5f
@@ -750,6 +750,11 @@ class Raycast(bonsai.core.tool.Raycast):
|
|||||||
gpu.state.blend_set("NONE")
|
gpu.state.blend_set("NONE")
|
||||||
gpu.state.face_culling_set("NONE")
|
gpu.state.face_culling_set("NONE")
|
||||||
|
|
||||||
|
read_size = 2 * _SNAP_RADIUS_PX + 1
|
||||||
|
read_x = max(0, min(mx - _SNAP_RADIUS_PX, w - read_size))
|
||||||
|
read_y = max(0, min(my - _SNAP_RADIUS_PX, h - read_size))
|
||||||
|
|
||||||
|
buffers_list = []
|
||||||
with _offscreen.bind():
|
with _offscreen.bind():
|
||||||
fb = gpu.state.active_framebuffer_get()
|
fb = gpu.state.active_framebuffer_get()
|
||||||
fb.clear(color=(0.0, 0.0, 0.0, 0.0), depth=1.0)
|
fb.clear(color=(0.0, 0.0, 0.0, 0.0), depth=1.0)
|
||||||
@@ -762,35 +767,44 @@ class Raycast(bonsai.core.tool.Raycast):
|
|||||||
gpu.matrix.load_matrix(Matrix.Identity(4))
|
gpu.matrix.load_matrix(Matrix.Identity(4))
|
||||||
batch.draw(_encoding_shader)
|
batch.draw(_encoding_shader)
|
||||||
|
|
||||||
read_size = 2 * _SNAP_RADIUS_PX + 1
|
buf = fb.read_color(int(read_x), int(read_y),
|
||||||
read_x = max(0, min(mx - _SNAP_RADIUS_PX, w - read_size))
|
|
||||||
read_y = max(0, min(my - _SNAP_RADIUS_PX, h - read_size))
|
|
||||||
buf = fb.read_color(int(read_x), int(read_y),
|
|
||||||
read_size, read_size, 4, 0, "UBYTE")
|
read_size, read_size, 4, 0, "UBYTE")
|
||||||
|
if xray_mode: # gets all buffers
|
||||||
|
buffers_list.append(buf)
|
||||||
|
last_buf = buf # gets only the closest buffer
|
||||||
|
|
||||||
# Restore state
|
# Restore state
|
||||||
gpu.state.depth_mask_set(True)
|
gpu.state.depth_mask_set(True)
|
||||||
gpu.state.depth_test_set("LESS")
|
gpu.state.depth_test_set("LESS")
|
||||||
|
|
||||||
pixel_data = buf.to_list()
|
|
||||||
if not pixel_data or not pixel_data[0]:
|
|
||||||
return [], None
|
|
||||||
|
|
||||||
if tris:
|
if tris:
|
||||||
# Decode hits
|
# Decode hits
|
||||||
hits: set[tuple[int, int]] = set()
|
hits: set[int] = set()
|
||||||
|
|
||||||
if xray_mode:
|
if xray_mode:
|
||||||
for row in pixel_data:
|
vals_read: set[int] = set()
|
||||||
for px in row:
|
for buf in buffers_list:
|
||||||
|
pixel_data = buf.to_list()
|
||||||
|
if not pixel_data or not pixel_data[0]:
|
||||||
|
return [], None
|
||||||
|
centre_x = mx - int(read_x)
|
||||||
|
centre_y = my - int(read_y)
|
||||||
|
if 0 <= centre_y < len(pixel_data) and 0 <= centre_x < len(pixel_data[0]):
|
||||||
|
px = pixel_data[centre_y][centre_x]
|
||||||
val = _decode_wireframe_pixel(px[0], px[1], px[2], px[3])
|
val = _decode_wireframe_pixel(px[0], px[1], px[2], px[3])
|
||||||
|
if val in vals_read: # avoid getting all the tris from the same object
|
||||||
|
continue
|
||||||
|
vals_read.add(val)
|
||||||
if val > 0:
|
if val > 0:
|
||||||
val -= 1
|
val -= 1
|
||||||
obj_index = int(val) >> _TRI_OBJ_SHIFT
|
obj_index = int(val) >> _TRI_OBJ_SHIFT
|
||||||
face_index = int(val) & _TRI_FACE_MASK
|
|
||||||
if obj_index < len(_obj_list):
|
if obj_index < len(_obj_list):
|
||||||
hits.add((obj_index, face_index))
|
hits.add(obj_index)
|
||||||
else:
|
else:
|
||||||
|
pixel_data = last_buf.to_list()
|
||||||
|
if not pixel_data or not pixel_data[0]:
|
||||||
|
return [], None
|
||||||
centre_x = mx - int(read_x)
|
centre_x = mx - int(read_x)
|
||||||
centre_y = my - int(read_y)
|
centre_y = my - int(read_y)
|
||||||
if 0 <= centre_y < len(pixel_data) and 0 <= centre_x < len(pixel_data[0]):
|
if 0 <= centre_y < len(pixel_data) and 0 <= centre_x < len(pixel_data[0]):
|
||||||
@@ -799,42 +813,43 @@ class Raycast(bonsai.core.tool.Raycast):
|
|||||||
if val > 0:
|
if val > 0:
|
||||||
val -= 1
|
val -= 1
|
||||||
obj_index = int(val) >> _TRI_OBJ_SHIFT
|
obj_index = int(val) >> _TRI_OBJ_SHIFT
|
||||||
face_index = int(val) & _TRI_FACE_MASK
|
|
||||||
if obj_index < len(_obj_list):
|
if obj_index < len(_obj_list):
|
||||||
hits.add((obj_index, face_index))
|
hits.add(obj_index)
|
||||||
|
|
||||||
if not hits:
|
if not hits:
|
||||||
return []
|
return [], None
|
||||||
|
|
||||||
snaps: list[dict] = []
|
snaps: list[dict] = []
|
||||||
closest_obj = None
|
closest_obj = None
|
||||||
closest_dist = float("inf")
|
closest_dist = float("inf")
|
||||||
ray_origin, _, _ = cls.get_viewport_ray_data(context, event)
|
ray_origin, _, _ = cls.get_viewport_ray_data(context, event)
|
||||||
|
for obj_index in hits:
|
||||||
objs_to_raycast = []
|
|
||||||
for obj_index, face_index in hits:
|
|
||||||
obj = _obj_list[obj_index]
|
obj = _obj_list[obj_index]
|
||||||
objs_to_raycast.append(obj)
|
hit_obj, hit, face_index = cls.cast_rays_to_single_object(context, event, obj)
|
||||||
hit_obj, hit, face_index = cls.cast_rays_and_get_best_object(context, event, objs_to_raycast)
|
if hit:
|
||||||
|
snap: dict = {
|
||||||
|
"point": hit,
|
||||||
|
"type": "Face",
|
||||||
|
"group": "Object",
|
||||||
|
"object": hit_obj,
|
||||||
|
"face_index": face_index,
|
||||||
|
"distance": 9, # High value so it has low priority
|
||||||
|
}
|
||||||
|
dist = (hit - ray_origin).length
|
||||||
|
if dist < closest_dist:
|
||||||
|
closest_dist = dist
|
||||||
|
closest_obj = obj
|
||||||
|
|
||||||
snap: dict = {
|
snaps.append(snap)
|
||||||
"point": hit,
|
|
||||||
"type": "Face",
|
|
||||||
"group": "Object",
|
|
||||||
"object": obj,
|
|
||||||
"face_index": face_index,
|
|
||||||
"distance": 9, # High value so it has low priority
|
|
||||||
}
|
|
||||||
|
|
||||||
snaps.append(snap)
|
|
||||||
|
|
||||||
return snaps, closest_obj
|
return snaps, closest_obj
|
||||||
|
|
||||||
else:
|
else:
|
||||||
centre = (mx - int(read_x), my - int(read_y))
|
centre = (mx - int(read_x), my - int(read_y))
|
||||||
|
pixel_data = last_buf.to_list()
|
||||||
best = _find_closest_wireframe_pixel(pixel_data, *centre)
|
best = _find_closest_wireframe_pixel(pixel_data, *centre)
|
||||||
if best is None:
|
if best is None:
|
||||||
return []
|
return [], None
|
||||||
encoded, dx, dy = best
|
encoded, dx, dy = best
|
||||||
|
|
||||||
# Decode and build snap dicts
|
# Decode and build snap dicts
|
||||||
@@ -931,7 +946,7 @@ class Raycast(bonsai.core.tool.Raycast):
|
|||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
|
||||||
return snaps
|
return snaps, None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_gpu_solid_snaps(cls, context, event, objs_to_raycast):
|
def get_gpu_solid_snaps(cls, context, event, objs_to_raycast):
|
||||||
|
|||||||
@@ -390,30 +390,40 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
detected_snaps.append(point)
|
detected_snaps.append(point)
|
||||||
|
|
||||||
# Objects
|
# Objects
|
||||||
objs_to_raycast = tool.Raycast.filter_objects_to_raycast(context, event, objs_2d_bbox)
|
|
||||||
obj_face_gpu_raycast = tool.Raycast.get_gpu_solid_snaps(context, event, objs_to_raycast)
|
|
||||||
obj_wireframe_gpu_raycast = tool.Raycast.get_gpu_wireframe_snaps(context, event, objs_to_raycast)
|
|
||||||
print("FACES", obj_face_gpu_raycast)
|
|
||||||
print("WIREFRAME", obj_wireframe_gpu_raycast)
|
|
||||||
closest_snaps = tool.Raycast.ray_cast_and_get_closest_to_camera_snaps(context, event, objs_to_raycast)
|
|
||||||
detected_snaps.extend(closest_snaps)
|
|
||||||
|
|
||||||
xray_mode = (space.shading.type == "SOLID" and space.shading.show_xray) or (
|
xray_mode = (space.shading.type == "SOLID" and space.shading.show_xray) or (
|
||||||
space.shading.type == "WIREFRAME" and space.shading.show_xray_wireframe
|
space.shading.type == "WIREFRAME" and space.shading.show_xray_wireframe
|
||||||
)
|
)
|
||||||
|
objs_to_raycast = tool.Raycast.filter_objects_to_raycast(context, event, objs_2d_bbox)
|
||||||
|
snap_faces, closest_obj = tool.Raycast.get_gpu_solid_snaps(context, event, objs_to_raycast)
|
||||||
|
wireframe_snaps, _ = tool.Raycast.get_gpu_wireframe_snaps(context, event, objs_to_raycast)
|
||||||
|
|
||||||
for snap_obj in objs_to_raycast:
|
if not xray_mode:
|
||||||
for snap in closest_snaps:
|
for snap in snap_faces:
|
||||||
if snap_obj == snap["object"]:
|
if snap["object"] == closest_obj:
|
||||||
if not xray_mode:
|
detected_snaps.append(snap)
|
||||||
# If it is a solid object that is closest to camera it ignores all the rest
|
snap_points = tool.Raycast.ray_cast_by_proximity(context, event, snap["object"], snap["object"].data.polygons[snap["face_index"]])
|
||||||
if (
|
for point in snap_points:
|
||||||
"is_closest_to_camera" in snap
|
point["group"] = "Object"
|
||||||
and snap["is_closest_to_camera"]
|
detected_snaps.append(point)
|
||||||
and snap["group"] == "Object"
|
|
||||||
):
|
# Get wireframe snaps that are not occluded by face snap
|
||||||
closest_snap = [snap] # discards objects that aren't the closest
|
ray_origin = tool.Raycast.get_viewport_ray_data(context, event)[0]
|
||||||
detected_snaps = closest_snap
|
occl_dist = (snap["point"] - ray_origin).length + 1e-4
|
||||||
|
visible_wireframe_snaps = [
|
||||||
|
w for w in wireframe_snaps
|
||||||
|
if (w["point"] - ray_origin).length <= occl_dist
|
||||||
|
]
|
||||||
|
detected_snaps.extend(visible_wireframe_snaps)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# Doesn't include face snaps, only their edges and vertices
|
||||||
|
for snap in snap_faces:
|
||||||
|
snap_points = tool.Raycast.ray_cast_by_proximity(context, event, snap["object"], snap["object"].data.polygons[snap["face_index"]])
|
||||||
|
for point in snap_points:
|
||||||
|
point["group"] = "Object"
|
||||||
|
detected_snaps.append(point)
|
||||||
|
|
||||||
|
detected_snaps.extend(wireframe_snaps)
|
||||||
|
|
||||||
# snap to cut geometry (e.g. in plan view)
|
# snap to cut geometry (e.g. in plan view)
|
||||||
if CutDecorator.installed:
|
if CutDecorator.installed:
|
||||||
|
|||||||
Reference in New Issue
Block a user