|
4 | 4 | import gpu
|
5 | 5 | from bpy.types import Context, Operator
|
6 | 6 | from bpy.utils import register_class, unregister_class
|
| 7 | +from mathutils import Vector |
7 | 8 |
|
8 | 9 | from . import global_data
|
9 | 10 | from .utilities.preferences import use_experimental
|
|
12 | 13 | logger = logging.getLogger(__name__)
|
13 | 14 |
|
14 | 15 |
|
| 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 | + |
15 | 49 | def draw_selection_buffer(context: Context):
|
16 |
| - """Draw elements offscreen""" |
| 50 | + """Draw elements offscreen with depth-aware sorting""" |
17 | 51 | region = context.region
|
18 | 52 |
|
19 | 53 | # create offscreen
|
20 | 54 | width, height = region.width, region.height
|
21 | 55 | offscreen = global_data.offscreen = gpu.types.GPUOffScreen(width, height)
|
22 | 56 |
|
23 | 57 | 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) |
24 | 61 |
|
25 | 62 | 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) |
27 | 64 |
|
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: |
30 | 68 | if e.slvs_index in global_data.ignore_list:
|
31 | 69 | continue
|
32 | 70 | if not hasattr(e, "draw_id"):
|
33 | 71 | continue
|
34 | 72 | if not e.is_selectable(context):
|
35 | 73 | 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: |
36 | 82 | e.draw_id(context)
|
37 | 83 |
|
| 84 | + # Restore default depth state |
| 85 | + gpu.state.depth_test_set('NONE') |
| 86 | + gpu.state.depth_mask_set(False) |
| 87 | + |
38 | 88 |
|
39 | 89 | def ensure_selection_texture(context: Context):
|
40 | 90 | if not global_data.redraw_selection_buffer:
|
|
0 commit comments