@@ -239,100 +239,151 @@ end
239239 length (bvh. nodes) > Int32 (0 ) ? bvh. nodes[1 ]. bounds : Bounds3 ()
240240end
241241
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
245264 ray = check_direction (ray)
246265 inv_dir = 1f0 ./ ray. d
247266 dir_is_neg = is_dir_negative (ray. d)
248267
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 )
250271 nodes_to_visit = zeros (MVector{64 ,Int32})
251272 primitives = bvh. primitives
252- @_inbounds primitive = primitives[1 ]
253273 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
254281 @_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
269299 end
270300 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
272306 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]
274308 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
278313 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 )
281316 end
282317 to_visit_offset += Int32 (1 )
283318 end
284319 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
286324 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]
288326 end
289327 end
290- return hit, primitive, interaction
328+
329+ # Return final state
330+ return continue_search, ray, result
291331end
292332
293- @inline function intersect_p (bvh:: BVHAccel , ray:: AbstractRay )
333+ # Initialization
334+ closest_hit_callback (primitive, ray, :: Nothing ) = (false , primitive, Point3f (0.0 ))
294335
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
296342
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}
300345
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
334368 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
336387end
337388
338389function calculate_ray_grid_bounds (bounds:: GeometryBasics.Rect , ray_direction:: Vec3f )
0 commit comments