@@ -239,100 +239,151 @@ end
239
239
length (bvh. nodes) > Int32 (0 ) ? bvh. nodes[1 ]. bounds : Bounds3 ()
240
240
end
241
241
242
- @inline function intersect! (bvh:: BVHAccel{P} , ray:: AbstractRay ) where {P}
243
- hit = false
244
- interaction = SurfaceInteraction ()
242
+ """
243
+ _traverse_bvh(bvh::BVHAccel{P}, ray::AbstractRay, hit_callback::F) where {P, F<:Function}
244
+
245
+ Internal function that traverses the BVH to find ray-primitive intersections.
246
+ Uses a callback pattern to handle different intersection behaviors.
247
+
248
+ Arguments:
249
+ - `bvh`: The BVH acceleration structure
250
+ - `ray`: The ray to test for intersections
251
+ - `hit_callback`: Function called when primitive is tested. Signature:
252
+ hit_callback(primitive, ray) -> (continue_traversal::Bool, ray::AbstractRay, results::Any)
253
+
254
+ Returns:
255
+ - The final result from the hit_callback
256
+ """
257
+ @inline function traverse_bvh (hit_callback:: F , bvh:: BVHAccel{P} , ray:: AbstractRay ) where {P, F<: Function }
258
+ # Early return if BVH is empty
259
+ if length (bvh. nodes) == 0
260
+ return false , ray, nothing
261
+ end
262
+
263
+ # Prepare ray for traversal
245
264
ray = check_direction (ray)
246
265
inv_dir = 1f0 ./ ray. d
247
266
dir_is_neg = is_dir_negative (ray. d)
248
267
249
- to_visit_offset, current_node_i = Int32 (1 ), Int32 (1 )
268
+ # Initialize traversal stack
269
+ to_visit_offset = Int32 (1 )
270
+ current_node_idx = Int32 (1 )
250
271
nodes_to_visit = zeros (MVector{64 ,Int32})
251
272
primitives = bvh. primitives
252
- @_inbounds primitive = primitives[1 ]
253
273
nodes = bvh. nodes
274
+
275
+ # State variables to hold callback results
276
+ continue_search = true
277
+ prim1 = primitives[1 ]
278
+ result = hit_callback (prim1, ray, nothing )
279
+
280
+ # Traverse BVH
254
281
@_inbounds while true
255
- ln = nodes[current_node_i]
256
- if intersect_p (ln. bounds, ray, inv_dir, dir_is_neg)
257
- if ! ln. is_interior && ln. n_primitives > Int32 (0 )
258
- # Intersect ray with primitives in node.
259
- for i in Int32 (0 ): ln. n_primitives - Int32 (1 )
260
- offset = ln. offset % Int32
261
- tmp_primitive = primitives[offset+ i]
262
- tmp_hit, ray, tmp_interaction = intersect_p! (
263
- tmp_primitive, ray,
264
- )
265
- if tmp_hit
266
- hit = tmp_hit
267
- interaction = tmp_interaction
268
- primitive = tmp_primitive
282
+ current_node = nodes[current_node_idx]
283
+
284
+ # Test ray against current node's bounding box
285
+ if intersect_p (current_node. bounds, ray, inv_dir, dir_is_neg)
286
+ if ! current_node. is_interior && current_node. n_primitives > Int32 (0 )
287
+ # Leaf node - test all primitives
288
+ offset = current_node. offset % Int32
289
+
290
+ for i in Int32 (0 ): (current_node. n_primitives - Int32 (1 ))
291
+ primitive = primitives[offset + i]
292
+
293
+ # Call the callback for this primitive
294
+ continue_search, ray, result = hit_callback (primitive, ray, result)
295
+
296
+ # Early exit if callback requests it
297
+ if ! continue_search
298
+ return false , ray, result
269
299
end
270
300
end
271
- to_visit_offset == Int32 (1 ) && break
301
+
302
+ # Done with leaf, pop next node from stack
303
+ if to_visit_offset == Int32 (1 )
304
+ break
305
+ end
272
306
to_visit_offset -= Int32 (1 )
273
- current_node_i = nodes_to_visit[to_visit_offset]
307
+ current_node_idx = nodes_to_visit[to_visit_offset]
274
308
else
275
- if dir_is_neg[ln. split_axis] == Int32 (2 )
276
- nodes_to_visit[to_visit_offset] = current_node_i + Int32 (1 )
277
- current_node_i = ln. offset % Int32
309
+ # Interior node - push children to stack
310
+ if dir_is_neg[current_node. split_axis] == Int32 (2 )
311
+ nodes_to_visit[to_visit_offset] = current_node_idx + Int32 (1 )
312
+ current_node_idx = current_node. offset % Int32
278
313
else
279
- nodes_to_visit[to_visit_offset] = ln . offset % Int32
280
- current_node_i += Int32 (1 )
314
+ nodes_to_visit[to_visit_offset] = current_node . offset % Int32
315
+ current_node_idx += Int32 (1 )
281
316
end
282
317
to_visit_offset += Int32 (1 )
283
318
end
284
319
else
285
- to_visit_offset == 1 && break
320
+ # Miss - pop next node from stack
321
+ if to_visit_offset == Int32 (1 )
322
+ break
323
+ end
286
324
to_visit_offset -= Int32 (1 )
287
- current_node_i = nodes_to_visit[to_visit_offset]
325
+ current_node_idx = nodes_to_visit[to_visit_offset]
288
326
end
289
327
end
290
- return hit, primitive, interaction
328
+
329
+ # Return final state
330
+ return continue_search, ray, result
291
331
end
292
332
293
- @inline function intersect_p (bvh:: BVHAccel , ray:: AbstractRay )
333
+ # Initialization
334
+ closest_hit_callback (primitive, ray, :: Nothing ) = (false , primitive, Point3f (0.0 ))
294
335
295
- length (bvh. nodes) == Int32 (0 ) && return false
336
+ function closest_hit_callback (primitive, ray, prev_result:: Tuple{Bool, P, Point3f} ) where {P}
337
+ # Test intersection and update if closer
338
+ tmp_hit, ray, tmp_bary = intersect_p! (primitive, ray)
339
+ # Always continue search to find closest
340
+ return true , ray, ifelse (tmp_hit, (true , primitive, tmp_bary), prev_result)
341
+ end
296
342
297
- ray = check_direction (ray)
298
- inv_dir = 1f0 ./ ray. d
299
- dir_is_neg = is_dir_negative (ray. d)
343
+ """
344
+ intersect!(bvh::BVHAccel{P}, ray::AbstractRay) where {P}
300
345
301
- to_visit_offset, current_node_i = Int32 (1 ), Int32 (1 )
302
- nodes_to_visit = zeros (MVector{64 ,Int32})
303
- primitives = bvh. primitives
304
- @_inbounds while true
305
- ln = bvh. nodes[current_node_i]
306
- if intersect_p (ln. bounds, ray, inv_dir, dir_is_neg)
307
- if ! ln. is_interior && ln. n_primitives > Int32 (0 )
308
- for i in Int32 (0 ): ln. n_primitives- Int32 (1 )
309
- offset = ln. offset % Int32
310
- intersect_p (
311
- primitives[offset + i], ray,
312
- ) && return true
313
- end
314
- to_visit_offset == 1 && break
315
- to_visit_offset -= Int32 (1 )
316
- current_node_i = nodes_to_visit[to_visit_offset]
317
- else
318
- if dir_is_neg[ln. split_axis] == Int32 (2 )
319
- # @setindex 64 nodes_to_visit[to_visit_offset] = Int32(current_node_i + 1)
320
- nodes_to_visit[to_visit_offset] = current_node_i + Int32 (1 )
321
- current_node_i = ln. offset % Int32
322
- else
323
- # @setindex 64 nodes_to_visit[to_visit_offset] = Int32(ln.offset)
324
- nodes_to_visit[to_visit_offset] = ln. offset % Int32
325
- current_node_i += Int32 (1 )
326
- end
327
- to_visit_offset += Int32 (1 )
328
- end
329
- else
330
- to_visit_offset == Int32 (1 ) && break
331
- to_visit_offset -= Int32 (1 )
332
- current_node_i = Int32 (nodes_to_visit[to_visit_offset])
333
- end
346
+ Find the closest intersection between a ray and the primitives stored in a BVH.
347
+
348
+ Returns:
349
+ - `hit_found`: Boolean indicating if an intersection was found
350
+ - `hit_primitive`: The primitive that was hit (if any)
351
+ - `barycentric_coords`: Barycentric coordinates of the hit point
352
+ """
353
+ @inline function intersect! (bvh:: BVHAccel{P} , ray:: AbstractRay ) where {P}
354
+ # Traverse BVH with closest-hit callback
355
+ _, _, result = traverse_bvh (closest_hit_callback, bvh, ray)
356
+ return result:: Tuple{Bool, Triangle, Point3f}
357
+ end
358
+
359
+
360
+ any_hit_callback (primitive, current_ray, result:: Nothing ) = ()
361
+
362
+ # Define any-hit callback
363
+ function any_hit_callback (primitive, current_ray, :: Tuple{} )
364
+ # Test for intersection
365
+ if intersect_p (primitive, current_ray)
366
+ # Stop traversal on first hit
367
+ return false , current_ray, true
334
368
end
335
- false
369
+ # Continue search if no hit
370
+ return true , current_ray, false
371
+ end
372
+
373
+ """
374
+ intersect_p(bvh::BVHAccel, ray::AbstractRay)
375
+
376
+ Test if a ray intersects any primitive in the BVH (without finding the closest hit).
377
+
378
+ Returns:
379
+ - `hit_found`: Boolean indicating if any intersection was found
380
+ """
381
+ @inline function intersect_p (bvh:: BVHAccel , ray:: AbstractRay )
382
+ # Traverse BVH with any-hit callback
383
+ continue_search, _, result = traverse_bvh (any_hit_callback, bvh, ray)
384
+ # If traversal completed without finding a hit, return false
385
+ # Otherwise return the hit result (true)
386
+ return ! continue_search ? result : false
336
387
end
337
388
338
389
function calculate_ray_grid_bounds (bounds:: GeometryBasics.Rect , ray_direction:: Vec3f )
0 commit comments