#ifndef ARE_INCLUDE_CORE_BVH_H #define ARE_INCLUDE_CORE_BVH_H #include "basic/types.h" #include "resource/buffer.h" #include "scene/mesh.h" #include #include namespace are { // Axis-aligned bounding box struct AABB { Vec3 min_; Vec3 max_; // Construct AABB from min and max points AABB(const Vec3 &min = Vec3(0.0f), const Vec3 &max = Vec3(0.0f)) : min_(min), max_(max) { } // Expand AABB to include point void expand(const Vec3 &point); // Expand AABB to include another AABB void expand(const AABB &other); // Get center of AABB Vec3 center() const { return (min_ + max_) * 0.5f; } // Get surface area of AABB float surface_area() const; // Check if AABB is valid bool is_valid() const; }; // Triangle primitive for BVH struct Triangle { Vec3 v0_, v1_, v2_; Vec3 n0_, n1_, n2_; Vec2 uv0_, uv1_, uv2_; Vec3 t0_, t1_, t2_; // Tangents for each vertex uint material_id_; // Get bounding box of triangle AABB get_bounds() const; // Get centroid of triangle Vec3 get_centroid() const; }; // BVH node for GPU // Internal node: left_first_ = left child index, count_ = 0 (right child = left_first_ + 1) // Leaf node: left_first_ = triangle offset in sorted array, count_ = triangle count struct BVHNode { Vec3 aabb_min_; uint left_first_; // Left child index (internal) or first primitive index (leaf) Vec3 aabb_max_; uint count_; // 0 for internal node, >0 for leaf (triangle count) }; // GPU-friendly BVH node layout (std430 aligned) struct BVHNodeGpu { Vec4 aabb_min_left_first_; ///< xyz = aabb min, w = left_first (uint) Vec4 aabb_max_count_; ///< xyz = aabb max, w = count (uint, 0 for internal) }; // Compact triangle for intersection testing only (48 bytes = 3 x vec4) // Precomputes edge vectors to avoid redundant calculation in Moller-Trumbore struct TriangleCompactGpu { Vec4 v0_material_; ///< xyz = v0 position, w = material_id (uint) Vec4 e1_; ///< xyz = v1 - v0 (precomputed edge 1) Vec4 e2_; ///< xyz = v2 - v0 (precomputed edge 2) }; // Full triangle attributes fetched only after confirmed hit (112 bytes = 7 x vec4) struct TriangleAttrGpu { Vec4 n0_; ///< xyz = normal at v0 Vec4 n1_; ///< xyz = normal at v1 Vec4 n2_; ///< xyz = normal at v2 Vec4 uv0_uv1_; ///< xy = uv0, zw = uv1 Vec4 uv2_; ///< xy = uv2 Vec4 t0_; ///< xyz = tangent at v0 Vec4 t1_; ///< xyz = tangent at v1 }; /* * @brief Bounding Volume Hierarchy using top-down SAH construction * * Algorithm: * 1. Extract triangles from meshes and transform to world space * 2. Sort triangles by Morton code for spatial coherence * 3. Build BVH top-down using SAH (Surface Area Heuristic) with 16-bin evaluation * 4. Node layout ensures children are at consecutive indices for GPU efficiency * * Node layout (GPU-friendly): * - Internal nodes: left_first_ = left child index, right = left_first_ + 1 * - Leaf nodes: left_first_ = triangle offset, count_ = triangle count * * Time complexity: O(n log n) average with SAH binning * Space complexity: O(n) */ class BVH { public: // Constructor BVH(); // Destructor ~BVH(); /* * @brief Build BVH from meshes * @param meshes Mesh list * @return True if build succeeded */ bool build(const std::vector> &meshes); /* * @brief Upload BVH to GPU * @param node_buffer Buffer for BVH nodes * @param triangle_buffer Buffer for compact triangles (intersection only) * @param attr_buffer Buffer for triangle attributes (fetched on hit) * @return True if upload succeeded */ bool upload_to_gpu(Buffer &node_buffer, Buffer &triangle_buffer, Buffer &attr_buffer); /* * @brief Get total node count * @return Node count */ uint get_node_count() const { return static_cast(nodes_.size()); } /* * @brief Get total triangle count * @return Triangle count */ uint get_triangle_count() const { return static_cast(triangles_.size()); } // Clear BVH data void clear(); private: std::vector nodes_; std::vector triangles_; std::vector triangle_indices_; // Indirection array for partitioning /* * @brief Extract triangles from meshes and transform to world space */ void extract_triangles_(const std::vector> &meshes); /* * @brief Sort triangles by Morton code for spatial coherence */ void sort_triangles_by_morton_(); /* * @brief Recursively build BVH using SAH * @param node_idx Current node index to fill * @param first_prim First primitive index in triangle_indices_ * @param prim_count Number of primitives */ void build_recursive_(uint node_idx, uint first_prim, uint prim_count); /* * @brief Find best split using SAH with binning * @param first_prim First primitive index * @param prim_count Primitive count * @param axis Best split axis (output) * @param split_pos Best split position (output) * @return SAH cost of best split */ float find_best_split_(uint first_prim, uint prim_count, int &axis, float &split_pos); /* * @brief Calculate node bounds */ AABB calculate_bounds_(uint first_prim, uint prim_count); /* * @brief Calculate centroid bounds */ AABB calculate_centroid_bounds_(uint first_prim, uint prim_count); }; } // namespace are #endif // ARE_INCLUDE_CORE_BVH_H