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();
569 int optimizeLayout(
const std::vector<uint> &UUIDs,
float learning_rate = 0.01f,
int max_iterations = 1000);
580 std::vector<std::pair<uint, uint>>
findCollisionsWithinDistance(
const std::vector<uint> &query_UUIDs,
const std::vector<uint> &target_UUIDs,
float max_distance);
665 void buildBVH(
const std::vector<uint> &UUIDs = {});
672 void updateBVH(
const std::vector<uint> &UUIDs,
bool force_rebuild =
false);
747 void registerTree(
uint tree_object_id,
const std::vector<uint> &tree_primitives);
819 void getBVHStatistics(
size_t &node_count,
size_t &leaf_count,
size_t &max_depth)
const;
825 static int selfTest(
int argc,
char **argv);
832 bool gpu_acceleration_enabled;
842 struct CachedPrimitive {
844 std::vector<helios::vec3> vertices;
848 CachedPrimitive(
helios::PrimitiveType t,
const std::vector<helios::vec3> &v) : type(t), vertices(v) {
853 std::unordered_map<uint, CachedPrimitive> primitive_cache;
858 void buildPrimitiveCache();
863 HitResult intersectPrimitiveThreadSafe(
const helios::vec3 &origin,
const helios::vec3 &direction,
uint primitive_id,
float max_distance);
882 uint primitive_start;
883 uint primitive_count;
886 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) {
896 std::vector<helios::vec3> aabb_mins;
897 std::vector<helios::vec3> aabb_maxs;
898 std::vector<uint32_t> left_children;
899 std::vector<uint32_t> right_children;
902 std::vector<uint32_t> primitive_starts;
903 std::vector<uint32_t> primitive_counts;
904 std::vector<uint8_t> is_leaf_flags;
907 size_t node_count = 0;
909 BVHNodesSoA() =
default;
914 void reserve(
size_t capacity) {
915 aabb_mins.reserve(capacity);
916 aabb_maxs.reserve(capacity);
917 left_children.reserve(capacity);
918 right_children.reserve(capacity);
919 primitive_starts.reserve(capacity);
920 primitive_counts.reserve(capacity);
921 is_leaf_flags.reserve(capacity);
930 left_children.clear();
931 right_children.clear();
932 primitive_starts.clear();
933 primitive_counts.clear();
934 is_leaf_flags.clear();
941 [[nodiscard]]
size_t getMemoryUsage()
const {
942 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);
948 std::vector<BVHNode> bvh_nodes;
951 size_t next_available_node_index;
954 BVHNodesSoA bvh_nodes_soa;
960 std::vector<uint> primitive_indices;
963 std::unordered_map<uint, std::pair<helios::vec3, helios::vec3>> primitive_aabbs_cache;
966 std::unordered_set<uint> dirty_primitive_cache;
969 std::vector<std::vector<std::vector<std::vector<uint>>>> grid_cells;
979 std::vector<std::vector<std::vector<int>>> voxel_ray_counts;
982 std::vector<std::vector<std::vector<int>>> voxel_transmitted;
985 std::vector<std::vector<std::vector<float>>> voxel_path_lengths;
988 std::vector<std::vector<std::vector<int>>> voxel_hit_before;
991 std::vector<std::vector<std::vector<int>>> voxel_hit_after;
994 std::vector<std::vector<std::vector<int>>> voxel_hit_inside;
997 std::vector<std::vector<std::vector<std::vector<float>>>> voxel_individual_path_lengths;
1002 std::vector<int> voxel_ray_counts_flat;
1003 std::vector<int> voxel_transmitted_flat;
1004 std::vector<float> voxel_path_lengths_flat;
1005 std::vector<int> voxel_hit_before_flat;
1006 std::vector<int> voxel_hit_after_flat;
1007 std::vector<int> voxel_hit_inside_flat;
1010 std::vector<float> voxel_individual_path_lengths_flat;
1011 std::vector<size_t> voxel_individual_path_offsets;
1012 std::vector<size_t> voxel_individual_path_counts;
1015 bool use_flat_arrays;
1018 bool voxel_data_initialized;
1028 float max_collision_distance;
1031 std::unordered_map<uint, helios::vec3> primitive_centroids_cache;
1039 uint *d_primitive_indices;
1042 bool gpu_memory_allocated;
1045 std::set<uint> last_processed_uuids;
1048 std::set<uint> last_processed_deleted_uuids;
1051 std::set<uint> static_geometry_cache;
1054 std::set<uint> last_bvh_geometry;
1063 bool automatic_bvh_rebuilds;
1066 mutable bool batch_mode_skip_bvh_check;
1071 bool hierarchical_bvh_enabled;
1074 std::vector<BVHNode> static_bvh_nodes;
1077 std::vector<uint> static_bvh_primitives;
1080 bool static_bvh_valid;
1083 std::set<uint> last_static_bvh_geometry;
1086 void updateHierarchicalBVH(
const std::set<uint> &requested_geometry,
bool force_rebuild);
1094 bool tree_based_bvh_enabled;
1097 float tree_isolation_distance;
1100 std::unordered_map<uint, uint> object_to_tree_map;
1103 std::vector<uint> static_obstacle_primitives;
1106 struct ObstacleSpatialGrid {
1108 std::unordered_map<int64_t, std::vector<uint>> grid_cells;
1110 [[nodiscard]] int64_t getGridKey(
float x,
float y)
const {
1111 auto grid_x =
static_cast<int32_t
>(std::floor(x / cell_size));
1112 auto grid_y =
static_cast<int32_t
>(std::floor(y / cell_size));
1113 return (
static_cast<int64_t
>(grid_x) << 32) |
static_cast<uint32_t
>(grid_y);
1116 [[nodiscard]] std::vector<uint> getRelevantObstacles(
const helios::vec3 &position,
float radius)
const;
1119 mutable ObstacleSpatialGrid obstacle_spatial_grid;
1120 bool obstacle_spatial_grid_initialized;
1139 float angular_distance;
1141 std::vector<int> sample_indices;
1147 struct SpatialHashGrid {
1152 std::vector<std::vector<Cell>> grid;
1153 int theta_resolution;
1158 explicit SpatialHashGrid(
int theta_res = 32,
int phi_res = 16) : theta_resolution(theta_res), phi_resolution(phi_res) {
1159 grid.resize(theta_resolution);
1160 for (
auto &row: grid) {
1161 row.resize(phi_resolution);
1163 theta_step =
M_PI / theta_resolution;
1164 phi_step = 2.0f *
M_PI / phi_resolution;
1168 for (
auto &row: grid) {
1169 for (
auto &cell: row) {
1170 cell.sample_indices.clear();
1175 [[nodiscard]] std::pair<int, int> getGridIndex(
const helios::vec3 &direction)
const {
1177 float theta = acosf(std::max(-1.0f, std::min(1.0f, direction.
z)));
1178 float phi = atan2f(direction.
y, direction.
x);
1182 int theta_idx = std::min((
int) (theta / theta_step), theta_resolution - 1);
1183 int phi_idx = std::min((
int) (phi / phi_step), phi_resolution - 1);
1185 return {theta_idx, phi_idx};
1188 void addSample(
size_t sample_idx,
const helios::vec3 &direction) {
1189 auto [theta_idx, phi_idx] = getGridIndex(direction);
1190 grid[theta_idx][phi_idx].sample_indices.push_back(sample_idx);
1193 [[nodiscard]] std::vector<size_t> getNearbyIndices(
const helios::vec3 &direction,
int radius = 1)
const {
1194 auto [center_theta, center_phi] = getGridIndex(direction);
1195 std::vector<size_t> nearby_indices;
1197 for (
int dt = -radius; dt <= radius; dt++) {
1198 for (
int dp = -radius; dp <= radius; dp++) {
1199 int theta_idx = center_theta + dt;
1200 int phi_idx = (center_phi + dp + phi_resolution) % phi_resolution;
1202 if (theta_idx >= 0 && theta_idx < theta_resolution) {
1203 const auto &cell = grid[theta_idx][phi_idx];
1204 nearby_indices.insert(nearby_indices.end(), cell.sample_indices.begin(), cell.sample_indices.end());
1209 return nearby_indices;
1217 uint tree_object_id;
1220 std::vector<BVHNode> nodes;
1221 std::vector<uint> primitive_indices;
1222 BVHNodesSoA soa_structure;
1225 TreeBVH() : tree_object_id(0), tree_center(0, 0, 0), tree_radius(0), soa_dirty(true) {
1230 primitive_indices.clear();
1231 soa_structure = BVHNodesSoA();
1237 std::unordered_map<uint, TreeBVH> tree_bvh_map;
1247 void ensureBVHCurrent();
1268 void buildBVHRecursive(
uint node_index,
size_t primitive_start,
size_t primitive_count,
int depth);
1278#ifdef HELIOS_CUDA_AVAILABLE
1331 void traverseBVHSIMD(
const helios::vec3 *ray_origins,
const helios::vec3 *ray_directions,
int count, HitResult *results);
1336 void traverseBVHSIMDImpl(
const helios::vec3 *ray_origins,
const helios::vec3 *ray_directions,
int count, HitResult *results);
1387 bool findNearestRayIntersection(
const helios::vec3 &origin,
const helios::vec3 &direction,
const std::set<uint> &candidate_UUIDs,
float &nearest_distance,
float max_distance = -1.0f);
1397 std::vector<helios::vec3> sampleDirectionsInCone(
const helios::vec3 &apex,
const helios::vec3 ¢ral_axis,
float half_angle,
int num_samples);
1410 std::vector<Gap> detectGapsInCone(
const helios::vec3 &apex,
const helios::vec3 ¢ral_axis,
float half_angle,
float height,
int num_samples);
1421 std::vector<uint> getCandidatePrimitivesInCone(
const helios::vec3 &apex,
const helios::vec3 ¢ral_axis,
float half_angle,
float height);
1432 std::vector<uint> getCandidatesUsingSpatialGrid(
const Cone &cone,
const helios::vec3 &apex,
const helios::vec3 ¢ral_axis,
float half_angle,
float height);
1440 float calculateGapAngularSize(
const std::vector<RaySample> &gap_samples,
const helios::vec3 ¢ral_axis);
1447 void scoreGapsByFishEyeMetric(std::vector<Gap> &gaps,
const helios::vec3 ¢ral_axis);
1472 void calculateVoxelRayPathLengths_CPU(
const std::vector<helios::vec3> &ray_origins,
const std::vector<helios::vec3> &ray_directions);
1479 void calculateVoxelRayPathLengths_GPU(
const std::vector<helios::vec3> &ray_origins,
const std::vector<helios::vec3> &ray_directions);
1486 bool validateVoxelIndices(
const helios::int3 &ijk)
const;
1502 std::vector<std::pair<helios::int3, float>> traverseVoxelGrid(
const helios::vec3 &ray_origin,
const helios::vec3 &ray_direction)
const;
1511 inline size_t flatIndex(
int i,
int j,
int k)
const {
1512 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);
1520 inline size_t flatIndex(
const helios::int3 &ijk)
const {
1521 return flatIndex(ijk.
x, ijk.
y, ijk.
z);
1524#ifdef HELIOS_CUDA_AVAILABLE
1528 void allocateGPUMemory();
1533 void freeGPUMemory();
1538 void transferBVHToGPU();
1544 void markBVHDirty();
1552 void incrementalUpdateBVH(
const std::set<uint> &added_geometry,
const std::set<uint> &removed_geometry,
const std::set<uint> &final_geometry);
1558 void updatePrimitiveAABBCache(
uint uuid);
1564 void optimizedRebuildBVH(
const std::set<uint> &final_geometry);
1571 bool validateUUIDs(
const std::vector<uint> &UUIDs)
const;
1589 void castRaysCPU(
const std::vector<RayQuery> &ray_queries, std::vector<HitResult> &results, RayTracingStats &stats);
1591#ifdef HELIOS_CUDA_AVAILABLE
1598 void castRaysGPU(
const std::vector<RayQuery> &ray_queries, std::vector<HitResult> &results, RayTracingStats &stats);
1607 void ensureOptimizedBVH();
1612 std::vector<HitResult> castRaysSoA(
const std::vector<RayQuery> &ray_queries, RayTracingStats &stats);
1617 HitResult castRaySoATraversal(
const RayQuery &query, RayTracingStats &stats);
1623 HitResult castRayBVHTraversal(
const RayQuery &query);
1628 inline bool aabbIntersectSoA(
const helios::vec3 &ray_origin,
const helios::vec3 &ray_direction,
float max_distance,
size_t node_index)
const;
1639 HitResult intersectPrimitive(
const RayQuery &query,
uint primitive_id);
1660 int calculateOptimalBinCount(
float cone_half_angle,
int geometry_count);
1668 std::vector<uint> filterPrimitivesParallel(
const Cone &cone,
const std::vector<uint> &primitive_uuids);
1676 void projectGeometryToBins(
const Cone &cone,
const std::vector<uint> &filtered_uuids, AngularBins &bins);
1684 std::vector<Gap> findGapsInCoverageMap(
const AngularBins &bins,
const Cone &cone);
1695 bool sphericalCoordsToBinIndices(
float theta,
float phi,
const AngularBins &bins,
int &theta_bin,
int &phi_bin);
1705 float cartesianToSphericalCone(
const helios::vec3 &vector,
const helios::vec3 &cone_axis,
float &theta,
float &phi);
1713 void projectGeometryToBinsSerial(
const Cone &cone,
const std::vector<uint> &filtered_uuids, AngularBins &bins);
1724 Gap floodFillGap(
const AngularBins &bins,
int start_theta,
int start_phi, std::vector<std::vector<bool>> &visited,
const Cone &cone);
1734 float calculateBinSolidAngle(
int theta_bin,
int phi_bin,
const AngularBins &bins,
float cone_half_angle);
1744 helios::vec3 binIndicesToCartesian(
int theta_bin,
int phi_bin,
const AngularBins &bins,
const Cone &cone);
1754 void addUnoccupiedNeighbors(
int theta,
int phi,
const AngularBins &bins, std::vector<std::vector<bool>> &visited, std::queue<std::pair<int, int>> &queue);