61 int total_bins = theta_div * phi_div;
63 depth_values.resize(total_bins, std::numeric_limits<float>::max());
68 bool isCovered(
int theta,
int phi)
const {
73 void setCovered(
int theta,
int phi,
float depth) {
194 std::vector<RayQuery> queries;
210 base_memory += targets.size() *
sizeof(
uint);
229 void addRays(
const std::vector<RayQuery> &queries) {
230 for (
const auto &query: queries) {
246 std::vector<HitResult> all_results;
249 for (
const auto &packet:
packets) {
250 all_results.insert(all_results.end(), packet.results.begin(), packet.results.end());
269 size_t total_memory = 0;
270 for (
const auto &packet:
packets) {
271 total_memory += packet.getMemoryUsage();
313 std::vector<uint>
findCollisions(
const std::vector<uint> &UUIDs,
bool allow_spatial_culling =
true);
322 std::vector<uint>
findCollisions(
const std::vector<uint> &primitive_UUIDs,
const std::vector<uint> &object_IDs,
bool allow_spatial_culling =
true);
333 std::vector<uint>
findCollisions(
const std::vector<uint> &query_UUIDs,
const std::vector<uint> &query_object_IDs,
const std::vector<uint> &target_UUIDs,
const std::vector<uint> &target_object_IDs,
bool allow_spatial_culling =
true);
360 std::vector<HitResult>
castRays(
const std::vector<RayQuery> &ray_queries, RayTracingStats *stats =
nullptr);
389 std::vector<HitResult>
castRaysOptimized(
const std::vector<RayQuery> &ray_queries, RayTracingStats *stats =
nullptr);
391#ifdef HELIOS_CUDA_AVAILABLE
399 std::vector<HitResult> castRaysGPU(
const std::vector<RayQuery> &ray_queries, RayTracingStats &stats);
408 bool processRayStream(RayStream &ray_stream, RayTracingStats *stats =
nullptr);
415 size_t soa_memory_bytes = 0;
416 size_t quantized_memory_bytes = 0;
417 float quantized_reduction_percent = 0.0f;
443 std::vector<std::vector<HitResult>>
calculateVoxelPathLengths(
const helios::vec3 &scan_origin,
const std::vector<helios::vec3> &ray_directions,
const std::vector<helios::vec3> &voxel_centers,
const std::vector<helios::vec3> &voxel_sizes);
455 std::vector<HitResult> &hit_results);
486 std::vector<std::vector<std::vector<std::vector<uint>>>>
getGridCells();
549 bool approxSame(
float a,
float b,
float absTol,
float relTol)
const;
639 int optimizeLayout(
const std::vector<uint> &UUIDs,
float learning_rate = 0.01f,
int max_iterations = 1000);
650 std::vector<std::pair<uint, uint>>
findCollisionsWithinDistance(
const std::vector<uint> &query_UUIDs,
const std::vector<uint> &target_UUIDs,
float max_distance);
735 void buildBVH(
const std::vector<uint> &UUIDs = {});
742 void updateBVH(
const std::vector<uint> &UUIDs,
bool force_rebuild =
false);
817 void registerTree(
uint tree_object_id,
const std::vector<uint> &tree_primitives);
889 void getBVHStatistics(
size_t &node_count,
size_t &leaf_count,
size_t &max_depth)
const;
895 static int selfTest(
int argc,
char **argv);
902 bool gpu_acceleration_enabled;
912 struct CachedPrimitive {
914 std::vector<helios::vec3> vertices;
918 CachedPrimitive(
helios::PrimitiveType t,
const std::vector<helios::vec3> &v) : type(t), vertices(v) {
923 std::unordered_map<uint, CachedPrimitive> primitive_cache;
928 void buildPrimitiveCache();
933 HitResult intersectPrimitiveThreadSafe(
const helios::vec3 &origin,
const helios::vec3 &direction,
uint primitive_id,
float max_distance);
952 uint primitive_start;
953 uint primitive_count;
956 BVHNode() : aabb_min(0, 0, 0), aabb_max(0, 0, 0), left_child(0xFFFFFFFF), right_child(0xFFFFFFFF), primitive_start(0), primitive_count(0), is_leaf(false) {
966 std::vector<helios::vec3> aabb_mins;
967 std::vector<helios::vec3> aabb_maxs;
968 std::vector<uint32_t> left_children;
969 std::vector<uint32_t> right_children;
972 std::vector<uint32_t> primitive_starts;
973 std::vector<uint32_t> primitive_counts;
974 std::vector<uint8_t> is_leaf_flags;
977 size_t node_count = 0;
979 BVHNodesSoA() =
default;
984 void reserve(
size_t capacity) {
985 aabb_mins.reserve(capacity);
986 aabb_maxs.reserve(capacity);
987 left_children.reserve(capacity);
988 right_children.reserve(capacity);
989 primitive_starts.reserve(capacity);
990 primitive_counts.reserve(capacity);
991 is_leaf_flags.reserve(capacity);
1000 left_children.clear();
1001 right_children.clear();
1002 primitive_starts.clear();
1003 primitive_counts.clear();
1004 is_leaf_flags.clear();
1011 [[nodiscard]]
size_t getMemoryUsage()
const {
1012 return (aabb_mins.size() + aabb_maxs.size()) *
sizeof(
helios::vec3) + (left_children.size() + right_children.size() + primitive_starts.size() + primitive_counts.size()) *
sizeof(uint32_t) + is_leaf_flags.size() *
sizeof(uint8_t);
1018 std::vector<BVHNode> bvh_nodes;
1021 size_t next_available_node_index;
1024 BVHNodesSoA bvh_nodes_soa;
1030 std::vector<uint> primitive_indices;
1033 std::unordered_map<uint, std::pair<helios::vec3, helios::vec3>> primitive_aabbs_cache;
1036 std::unordered_set<uint> dirty_primitive_cache;
1039 std::vector<std::vector<std::vector<std::vector<uint>>>> grid_cells;
1049 std::vector<std::vector<std::vector<int>>> voxel_ray_counts;
1052 std::vector<std::vector<std::vector<int>>> voxel_transmitted;
1055 std::vector<std::vector<std::vector<float>>> voxel_path_lengths;
1058 std::vector<std::vector<std::vector<int>>> voxel_hit_before;
1061 std::vector<std::vector<std::vector<int>>> voxel_hit_after;
1064 std::vector<std::vector<std::vector<int>>> voxel_hit_inside;
1067 std::vector<std::vector<std::vector<std::vector<float>>>> voxel_individual_path_lengths;
1072 std::vector<int> voxel_ray_counts_flat;
1073 std::vector<int> voxel_transmitted_flat;
1074 std::vector<float> voxel_path_lengths_flat;
1075 std::vector<int> voxel_hit_before_flat;
1076 std::vector<int> voxel_hit_after_flat;
1077 std::vector<int> voxel_hit_inside_flat;
1080 std::vector<float> voxel_individual_path_lengths_flat;
1081 std::vector<size_t> voxel_individual_path_offsets;
1082 std::vector<size_t> voxel_individual_path_counts;
1085 bool use_flat_arrays;
1088 bool voxel_data_initialized;
1098 float max_collision_distance;
1101 std::unordered_map<uint, helios::vec3> primitive_centroids_cache;
1109 uint *d_primitive_indices;
1112 bool gpu_memory_allocated;
1115 std::set<uint> last_processed_uuids;
1118 std::set<uint> last_processed_deleted_uuids;
1121 std::set<uint> static_geometry_cache;
1124 std::set<uint> last_bvh_geometry;
1133 bool automatic_bvh_rebuilds;
1136 mutable bool batch_mode_skip_bvh_check;
1141 bool hierarchical_bvh_enabled;
1144 std::vector<BVHNode> static_bvh_nodes;
1147 std::vector<uint> static_bvh_primitives;
1150 bool static_bvh_valid;
1153 std::set<uint> last_static_bvh_geometry;
1156 void updateHierarchicalBVH(
const std::set<uint> &requested_geometry,
bool force_rebuild);
1164 bool tree_based_bvh_enabled;
1167 float tree_isolation_distance;
1170 std::unordered_map<uint, uint> object_to_tree_map;
1173 std::vector<uint> static_obstacle_primitives;
1176 struct ObstacleSpatialGrid {
1178 std::unordered_map<int64_t, std::vector<uint>> grid_cells;
1180 [[nodiscard]] int64_t getGridKey(
float x,
float y)
const {
1181 auto grid_x =
static_cast<int32_t
>(std::floor(x / cell_size));
1182 auto grid_y =
static_cast<int32_t
>(std::floor(y / cell_size));
1183 return (
static_cast<int64_t
>(grid_x) << 32) |
static_cast<uint32_t
>(grid_y);
1186 [[nodiscard]] std::vector<uint> getRelevantObstacles(
const helios::vec3 &position,
float radius)
const;
1189 mutable ObstacleSpatialGrid obstacle_spatial_grid;
1190 bool obstacle_spatial_grid_initialized;
1209 float angular_distance;
1211 std::vector<int> sample_indices;
1217 struct SpatialHashGrid {
1222 std::vector<std::vector<Cell>> grid;
1223 int theta_resolution;
1228 explicit SpatialHashGrid(
int theta_res = 32,
int phi_res = 16) : theta_resolution(theta_res), phi_resolution(phi_res) {
1229 grid.resize(theta_resolution);
1230 for (
auto &row: grid) {
1231 row.resize(phi_resolution);
1233 theta_step =
M_PI / theta_resolution;
1234 phi_step = 2.0f *
M_PI / phi_resolution;
1238 for (
auto &row: grid) {
1239 for (
auto &cell: row) {
1240 cell.sample_indices.clear();
1245 [[nodiscard]] std::pair<int, int> getGridIndex(
const helios::vec3 &direction)
const {
1247 float theta = acosf(std::max(-1.0f, std::min(1.0f, direction.
z)));
1248 float phi = atan2f(direction.
y, direction.
x);
1252 int theta_idx = std::min((
int) (theta / theta_step), theta_resolution - 1);
1253 int phi_idx = std::min((
int) (phi / phi_step), phi_resolution - 1);
1255 return {theta_idx, phi_idx};
1258 void addSample(
size_t sample_idx,
const helios::vec3 &direction) {
1259 auto [theta_idx, phi_idx] = getGridIndex(direction);
1260 grid[theta_idx][phi_idx].sample_indices.push_back(sample_idx);
1263 [[nodiscard]] std::vector<size_t> getNearbyIndices(
const helios::vec3 &direction,
int radius = 1)
const {
1264 auto [center_theta, center_phi] = getGridIndex(direction);
1265 std::vector<size_t> nearby_indices;
1267 for (
int dt = -radius; dt <= radius; dt++) {
1268 for (
int dp = -radius; dp <= radius; dp++) {
1269 int theta_idx = center_theta + dt;
1270 int phi_idx = (center_phi + dp + phi_resolution) % phi_resolution;
1272 if (theta_idx >= 0 && theta_idx < theta_resolution) {
1273 const auto &cell = grid[theta_idx][phi_idx];
1274 nearby_indices.insert(nearby_indices.end(), cell.sample_indices.begin(), cell.sample_indices.end());
1279 return nearby_indices;
1287 uint tree_object_id;
1290 std::vector<BVHNode> nodes;
1291 std::vector<uint> primitive_indices;
1292 BVHNodesSoA soa_structure;
1295 TreeBVH() : tree_object_id(0), tree_center(0, 0, 0), tree_radius(0), soa_dirty(true) {
1300 primitive_indices.clear();
1301 soa_structure = BVHNodesSoA();
1307 std::unordered_map<uint, TreeBVH> tree_bvh_map;
1317 void ensureBVHCurrent();
1338 void buildBVHRecursive(
uint node_index,
size_t primitive_start,
size_t primitive_count,
int depth);
1348#ifdef HELIOS_CUDA_AVAILABLE
1401 void traverseBVHSIMD(
const helios::vec3 *ray_origins,
const helios::vec3 *ray_directions,
int count, HitResult *results);
1406 void traverseBVHSIMDImpl(
const helios::vec3 *ray_origins,
const helios::vec3 *ray_directions,
int count, HitResult *results);
1457 bool findNearestRayIntersection(
const helios::vec3 &origin,
const helios::vec3 &direction,
const std::set<uint> &candidate_UUIDs,
float &nearest_distance,
float max_distance = -1.0f);
1467 std::vector<helios::vec3> sampleDirectionsInCone(
const helios::vec3 &apex,
const helios::vec3 ¢ral_axis,
float half_angle,
int num_samples);
1480 std::vector<Gap> detectGapsInCone(
const helios::vec3 &apex,
const helios::vec3 ¢ral_axis,
float half_angle,
float height,
int num_samples);
1491 std::vector<uint> getCandidatePrimitivesInCone(
const helios::vec3 &apex,
const helios::vec3 ¢ral_axis,
float half_angle,
float height);
1502 std::vector<uint> getCandidatesUsingSpatialGrid(
const Cone &cone,
const helios::vec3 &apex,
const helios::vec3 ¢ral_axis,
float half_angle,
float height);
1510 float calculateGapAngularSize(
const std::vector<RaySample> &gap_samples,
const helios::vec3 ¢ral_axis);
1517 void scoreGapsByFishEyeMetric(std::vector<Gap> &gaps,
const helios::vec3 ¢ral_axis);
1542 void calculateVoxelRayPathLengths_CPU(
const std::vector<helios::vec3> &ray_origins,
const std::vector<helios::vec3> &ray_directions);
1549 bool calculateVoxelRayPathLengths_GPU(
const std::vector<helios::vec3> &ray_origins,
const std::vector<helios::vec3> &ray_directions);
1556 bool validateVoxelIndices(
const helios::int3 &ijk)
const;
1572 std::vector<std::pair<helios::int3, float>> traverseVoxelGrid(
const helios::vec3 &ray_origin,
const helios::vec3 &ray_direction)
const;
1581 inline size_t flatIndex(
int i,
int j,
int k)
const {
1582 return static_cast<size_t>(i) *
static_cast<size_t>(voxel_grid_divisions.
y) *
static_cast<size_t>(voxel_grid_divisions.
z) +
static_cast<size_t>(j) *
static_cast<size_t>(voxel_grid_divisions.
z) +
static_cast<size_t>(k);
1590 inline size_t flatIndex(
const helios::int3 &ijk)
const {
1591 return flatIndex(ijk.
x, ijk.
y, ijk.
z);
1594#ifdef HELIOS_CUDA_AVAILABLE
1598 void allocateGPUMemory();
1603 void freeGPUMemory();
1608 void transferBVHToGPU();
1614 void markBVHDirty();
1622 void incrementalUpdateBVH(
const std::set<uint> &added_geometry,
const std::set<uint> &removed_geometry,
const std::set<uint> &final_geometry);
1628 void updatePrimitiveAABBCache(
uint uuid);
1634 void optimizedRebuildBVH(
const std::set<uint> &final_geometry);
1641 bool validateUUIDs(
const std::vector<uint> &UUIDs)
const;
1659 void castRaysCPU(
const std::vector<RayQuery> &ray_queries, std::vector<HitResult> &results, RayTracingStats &stats);
1661#ifdef HELIOS_CUDA_AVAILABLE
1668 void castRaysGPU(
const std::vector<RayQuery> &ray_queries, std::vector<HitResult> &results, RayTracingStats &stats);
1677 void ensureOptimizedBVH();
1682 std::vector<HitResult> castRaysSoA(
const std::vector<RayQuery> &ray_queries, RayTracingStats &stats);
1687 HitResult castRaySoATraversal(
const RayQuery &query, RayTracingStats &stats);
1693 HitResult castRayBVHTraversal(
const RayQuery &query);
1698 inline bool aabbIntersectSoA(
const helios::vec3 &ray_origin,
const helios::vec3 &ray_direction,
float max_distance,
size_t node_index)
const;
1709 HitResult intersectPrimitive(
const RayQuery &query,
uint primitive_id);
1730 int calculateOptimalBinCount(
float cone_half_angle,
int geometry_count);
1738 std::vector<uint> filterPrimitivesParallel(
const Cone &cone,
const std::vector<uint> &primitive_uuids);
1746 void projectGeometryToBins(
const Cone &cone,
const std::vector<uint> &filtered_uuids, AngularBins &bins);
1754 std::vector<Gap> findGapsInCoverageMap(
const AngularBins &bins,
const Cone &cone);
1765 bool sphericalCoordsToBinIndices(
float theta,
float phi,
const AngularBins &bins,
int &theta_bin,
int &phi_bin);
1775 float cartesianToSphericalCone(
const helios::vec3 &vector,
const helios::vec3 &cone_axis,
float &theta,
float &phi);
1783 void projectGeometryToBinsSerial(
const Cone &cone,
const std::vector<uint> &filtered_uuids, AngularBins &bins);
1794 Gap floodFillGap(
const AngularBins &bins,
int start_theta,
int start_phi, std::vector<std::vector<bool>> &visited,
const Cone &cone);
1804 float calculateBinSolidAngle(
int theta_bin,
int phi_bin,
const AngularBins &bins,
float cone_half_angle);
1814 helios::vec3 binIndicesToCartesian(
int theta_bin,
int phi_bin,
const AngularBins &bins,
const Cone &cone);
1824 void addUnoccupiedNeighbors(
int theta,
int phi,
const AngularBins &bins, std::vector<std::vector<bool>> &visited, std::queue<std::pair<int, int>> &queue);