Skip to content

Commit 15b28e4

Browse files
committed
Fix depth-aware sorting for offscreen entity rendering
- calculate distances from the camera to ensure proper selection order, improving visual accuracy and interaction responsiveness.
1 parent 9c7e1e1 commit 15b28e4

File tree

2 files changed

+58
-6
lines changed

2 files changed

+58
-6
lines changed

draw_handler.py

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import gpu
55
from bpy.types import Context, Operator
66
from bpy.utils import register_class, unregister_class
7+
from mathutils import Vector
78

89
from . import global_data
910
from .utilities.preferences import use_experimental
@@ -12,29 +13,78 @@
1213
logger = logging.getLogger(__name__)
1314

1415

16+
def get_entity_distance_from_camera(entity, context):
17+
"""Calculate the distance from the entity to the camera for depth sorting."""
18+
try:
19+
# Get camera/view location
20+
view_matrix = context.region_data.view_matrix
21+
camera_location = view_matrix.inverted().translation
22+
23+
# Get entity location (use placement method if available, otherwise try common location attributes)
24+
if hasattr(entity, 'placement'):
25+
entity_location = entity.placement()
26+
elif hasattr(entity, 'location'):
27+
entity_location = entity.location
28+
elif hasattr(entity, 'p1') and hasattr(entity.p1, 'location'):
29+
# For entities like workplanes that have a p1 point
30+
entity_location = entity.p1.location
31+
else:
32+
# Fallback: return a large distance so entity is drawn first (behind others)
33+
return float('inf')
34+
35+
# Calculate distance
36+
if hasattr(entity_location, '__len__') and len(entity_location) >= 3:
37+
entity_pos = Vector(entity_location[:3])
38+
else:
39+
entity_pos = Vector(entity_location)
40+
41+
return (camera_location - entity_pos).length
42+
43+
except Exception as e:
44+
logger.debug(f"Error calculating distance for entity {entity}: {e}")
45+
# Return large distance on error so entity is drawn first
46+
return float('inf')
47+
48+
1549
def draw_selection_buffer(context: Context):
16-
"""Draw elements offscreen"""
50+
"""Draw elements offscreen with depth-aware sorting"""
1751
region = context.region
1852

1953
# create offscreen
2054
width, height = region.width, region.height
2155
offscreen = global_data.offscreen = gpu.types.GPUOffScreen(width, height)
2256

2357
with offscreen.bind():
58+
# Enable depth testing for proper z-buffer behavior
59+
gpu.state.depth_test_set('LESS')
60+
gpu.state.depth_mask_set(True)
2461

2562
fb = gpu.state.active_framebuffer_get()
26-
fb.clear(color=(0.0, 0.0, 0.0, 0.0))
63+
fb.clear(color=(0.0, 0.0, 0.0, 0.0), depth=1.0)
2764

28-
entities = list(context.scene.sketcher.entities.all)
29-
for e in reversed(entities):
65+
# Get all selectable entities
66+
entities = []
67+
for e in context.scene.sketcher.entities.all:
3068
if e.slvs_index in global_data.ignore_list:
3169
continue
3270
if not hasattr(e, "draw_id"):
3371
continue
3472
if not e.is_selectable(context):
3573
continue
74+
entities.append(e)
75+
76+
# Sort entities by distance from camera (farthest first)
77+
# This ensures closer entities are drawn last and have selection priority
78+
entities.sort(key=lambda e: get_entity_distance_from_camera(e, context), reverse=True)
79+
80+
# Draw entities in distance-sorted order
81+
for e in entities:
3682
e.draw_id(context)
3783

84+
# Restore default depth state
85+
gpu.state.depth_test_set('NONE')
86+
gpu.state.depth_mask_set(False)
87+
3888

3989
def ensure_selection_texture(context: Context):
4090
if not global_data.redraw_selection_buffer:

model/workplane.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,17 @@ def draw_id(self, context):
9696
# This makes the entire plane selectable, not just the edges
9797
super().draw_id(context)
9898

99-
# Additionally draw the surface for selection
99+
# Additionally draw the surface for selection with slight depth offset
100+
# This ensures the surface is slightly behind the outline
100101
shader = self._id_shader
101102
shader.bind()
102103

103104
from ..utilities.index import index_to_rgb
104105
shader.uniform_float("color", (*index_to_rgb(self.slvs_index), 1.0))
105106

106107
coords = draw_rect_2d(0, 0, self.size, self.size)
107-
coords = [Vector(co)[:] for co in coords]
108+
# Add small negative Z offset to push surface slightly behind outline
109+
coords = [(co[0], co[1], co[2] - 0.001) for co in coords]
108110
indices = ((0, 1, 2), (0, 2, 3))
109111
batch = batch_for_shader(shader, "TRIS", {"pos": coords}, indices=indices)
110112
batch.draw(shader)

0 commit comments

Comments
 (0)