1.3.64
 
Loading...
Searching...
No Matches
CollisionDetection.h
Go to the documentation of this file.
1
16#ifndef COLLISION_DETECTION_H
17#define COLLISION_DETECTION_H
18
19#include <queue>
20#include <set>
21#include <unordered_map>
22#include <unordered_set>
23#include "Context.h"
24
35public:
36 // -------- CONE COLLISION STRUCTURES --------
37
47
51 struct AngularBins {
55
56 // Coverage data using packed bits for memory efficiency
57 std::vector<uint8_t> coverage_bits;
58 std::vector<float> depth_values;
59
60 AngularBins(int theta_div, int phi_div) : theta_divisions(theta_div), phi_divisions(phi_div) {
61 int total_bins = theta_div * phi_div;
62 coverage_bits.resize((total_bins + 7) / 8, 0); // Packed bits
63 depth_values.resize(total_bins, std::numeric_limits<float>::max());
64 angular_resolution = (2.0f * M_PI) / float(theta_div * phi_div);
65 }
66
67 // Fast bit operations for coverage testing
68 bool isCovered(int theta, int phi) const {
69 int index = theta * phi_divisions + phi;
70 return coverage_bits[index >> 3] & (1 << (index & 7));
71 }
72
73 void setCovered(int theta, int phi, float depth) {
74 int index = theta * phi_divisions + phi;
75 coverage_bits[index >> 3] |= (1 << (index & 7));
76 depth_values[index] = std::min(depth_values[index], depth);
77 }
78
79 void clear() {
80 std::fill(coverage_bits.begin(), coverage_bits.end(), 0);
81 std::fill(depth_values.begin(), depth_values.end(), std::numeric_limits<float>::max());
82 }
83 };
84
85 // -------- GENERIC RAY-TRACING STRUCTURES --------
86
90 struct RayQuery {
94 std::vector<uint> target_UUIDs;
95
96 RayQuery() : origin(0, 0, 0), direction(0, 0, 1), max_distance(-1.0f) {
97 }
98 RayQuery(const helios::vec3 &ray_origin, const helios::vec3 &ray_direction, float max_dist = -1.0f, const std::vector<uint> &targets = {}) : origin(ray_origin), direction(ray_direction), max_distance(max_dist), target_UUIDs(targets) {
99 }
100 };
101
116
129
134 static constexpr size_t WARP_SIZE = 32;
135 static constexpr size_t RAY_BATCH_SIZE = 1024;
136
141 struct RayPacket {
142 // Ray data (Structure-of-Arrays layout)
143 std::vector<helios::vec3> origins;
144 std::vector<helios::vec3> directions;
145 std::vector<float> max_distances;
146 std::vector<std::vector<uint>> target_UUIDs;
147
148 // Results (output)
149 std::vector<HitResult> results;
150
151 size_t ray_count = 0;
152
153 RayPacket() = default;
154
158 void reserve(size_t capacity) {
159 origins.reserve(capacity);
160 directions.reserve(capacity);
161 max_distances.reserve(capacity);
162 target_UUIDs.reserve(capacity);
163 results.reserve(capacity);
164 }
165
169 void addRay(const RayQuery &query) {
170 origins.push_back(query.origin);
171 directions.push_back(query.direction);
172 max_distances.push_back(query.max_distance);
173 target_UUIDs.push_back(query.target_UUIDs);
174 results.emplace_back(); // Initialize empty result
175 ray_count++;
176 }
177
181 void clear() {
182 origins.clear();
183 directions.clear();
184 max_distances.clear();
185 target_UUIDs.clear();
186 results.clear();
187 ray_count = 0;
188 }
189
193 [[nodiscard]] std::vector<RayQuery> toRayQueries() const {
194 std::vector<RayQuery> queries;
195 queries.reserve(ray_count);
196 for (size_t i = 0; i < ray_count; ++i) {
197 queries.emplace_back(origins[i], directions[i], max_distances[i], target_UUIDs[i]);
198 }
199 return queries;
200 }
201
205 [[nodiscard]] size_t getMemoryUsage() const {
206 size_t base_memory = (origins.size() + directions.size()) * sizeof(helios::vec3) + max_distances.size() * sizeof(float) + results.size() * sizeof(HitResult);
207
208 // Add memory for target UUIDs
209 for (const auto &targets: target_UUIDs) {
210 base_memory += targets.size() * sizeof(uint);
211 }
212
213 return base_memory;
214 }
215 };
216
221 struct RayStream {
222 std::vector<RayPacket> packets;
223 size_t current_packet = 0;
224 size_t total_rays = 0;
225
229 void addRays(const std::vector<RayQuery> &queries) {
230 for (const auto &query: queries) {
231 // Create new packet if current one is full
232 if (packets.empty() || packets.back().ray_count >= RAY_BATCH_SIZE) {
233 packets.emplace_back();
234 packets.back().reserve(RAY_BATCH_SIZE);
235 }
236
237 packets.back().addRay(query);
238 total_rays++;
239 }
240 }
241
245 [[nodiscard]] std::vector<HitResult> getAllResults() const {
246 std::vector<HitResult> all_results;
247 all_results.reserve(total_rays);
248
249 for (const auto &packet: packets) {
250 all_results.insert(all_results.end(), packet.results.begin(), packet.results.end());
251 }
252
253 return all_results;
254 }
255
259 void clear() {
260 packets.clear();
261 current_packet = 0;
262 total_rays = 0;
263 }
264
268 [[nodiscard]] size_t getMemoryUsage() const {
269 size_t total_memory = 0;
270 for (const auto &packet: packets) {
271 total_memory += packet.getMemoryUsage();
272 }
273 return total_memory;
274 }
275 };
276
285
290 explicit CollisionDetection(helios::Context *context);
291
296
297 // -------- PRIMITIVE/OBJECT COLLISION DETECTION --------
298
305 std::vector<uint> findCollisions(uint UUID, bool allow_spatial_culling = true);
306
313 std::vector<uint> findCollisions(const std::vector<uint> &UUIDs, bool allow_spatial_culling = true);
314
322 std::vector<uint> findCollisions(const std::vector<uint> &primitive_UUIDs, const std::vector<uint> &object_IDs, bool allow_spatial_culling = true);
323
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);
334
335 // -------- GENERIC RAY-TRACING INTERFACE --------
336
342 HitResult castRay(const RayQuery &ray_query);
343
352 HitResult castRay(const helios::vec3 &origin, const helios::vec3 &direction, float max_distance = -1.0f, const std::vector<uint> &target_UUIDs = {});
353
360 std::vector<HitResult> castRays(const std::vector<RayQuery> &ray_queries, RayTracingStats *stats = nullptr);
361
362 // -------- OPTIMIZATION METHODS --------
363
369 };
370
376
382
389 std::vector<HitResult> castRaysOptimized(const std::vector<RayQuery> &ray_queries, RayTracingStats *stats = nullptr);
390
391#ifdef HELIOS_CUDA_AVAILABLE
399 std::vector<HitResult> castRaysGPU(const std::vector<RayQuery> &ray_queries, RayTracingStats &stats);
400#endif
401
408 bool processRayStream(RayStream &ray_stream, RayTracingStats *stats = nullptr);
409
415 size_t soa_memory_bytes = 0;
416 size_t quantized_memory_bytes = 0;
417 float quantized_reduction_percent = 0.0f; // Reduction vs SoA
418 };
419 MemoryUsageStats getBVHMemoryUsage() const;
420
429 std::vector<std::vector<std::vector<std::vector<HitResult>>>> performGridRayIntersection(const helios::vec3 &grid_center, const helios::vec3 &grid_size, const helios::int3 &grid_divisions, const std::vector<RayQuery> &ray_queries);
430
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);
444
454 void calculateRayPathLengthsDetailed(const helios::vec3 &grid_center, const helios::vec3 &grid_size, const helios::int3 &grid_divisions, const std::vector<helios::vec3> &ray_origins, const std::vector<helios::vec3> &ray_directions,
455 std::vector<HitResult> &hit_results);
456
457 // -------- CONE INTERSECTION QUERIES --------
458
468 OptimalPathResult findOptimalConePath(const helios::vec3 &apex, const helios::vec3 &centralAxis, float half_angle, float height = 0.0f, int initialSamples = 256);
469
470
471 // -------- GRID-BASED INTERSECTION --------
472
480 void calculateGridIntersection(const helios::vec3 &grid_center, const helios::vec3 &grid_size, const helios::int3 &grid_divisions, const std::vector<uint> &UUIDs = {});
481
486 std::vector<std::vector<std::vector<std::vector<uint>>>> getGridCells();
487
495 std::vector<uint> getGridIntersections(int i, int j, int k);
496
497 // -------- PRIMITIVE SLICING OPERATIONS --------
498
506 std::vector<uint> slicePrimitive(uint UUID, const std::vector<helios::vec3> &voxel_face_vertices, helios::WarningAggregator &warnings);
507
518 std::vector<uint> slicePrimitivesUsingGrid(const std::vector<uint> &UUIDs, const helios::vec3 &grid_center, const helios::vec3 &grid_size, const helios::int3 &grid_divisions);
519
527 void calculatePrimitiveVoxelIntersection(const std::vector<uint> &UUIDs = {});
528
529 // -------- GEOMETRIC UTILITY FUNCTIONS --------
530
539 helios::vec3 linesIntersection(const helios::vec3 &line1_point, const helios::vec3 &line1_direction, const helios::vec3 &line2_point, const helios::vec3 &line2_direction) const;
540
549 bool approxSame(float a, float b, float absTol, float relTol) const;
550
554 bool approxSame(const helios::vec3 &a, const helios::vec3 &b, float absTol) const;
555
565 helios::vec2 interpolate_texture_UV_to_slice_point(const helios::vec3 &p1, const helios::vec2 &uv1, const helios::vec3 &p2, const helios::vec2 &uv2, const helios::vec3 &ps) const;
566
567 // -------- VOXEL RAY PATH LENGTH CALCULATIONS --------
568
577 void calculateVoxelRayPathLengths(const helios::vec3 &grid_center, const helios::vec3 &grid_size, const helios::int3 &grid_divisions, const std::vector<helios::vec3> &ray_origins, const std::vector<helios::vec3> &ray_directions);
578
585 void setVoxelTransmissionProbability(int P_denom, int P_trans, const helios::int3 &ijk);
586
593 void getVoxelTransmissionProbability(const helios::int3 &ijk, int &P_denom, int &P_trans) const;
594
600 void setVoxelRbar(float r_bar, const helios::int3 &ijk);
601
607 float getVoxelRbar(const helios::int3 &ijk) const;
608
616 void getVoxelRayHitCounts(const helios::int3 &ijk, int &hit_before, int &hit_after, int &hit_inside) const;
617
623 std::vector<float> getVoxelRayPathLengths(const helios::int3 &ijk) const;
624
628 void clearVoxelData();
629
630 // -------- FEATURE 4: COLLISION MINIMIZATION --------
631
639 int optimizeLayout(const std::vector<uint> &UUIDs, float learning_rate = 0.01f, int max_iterations = 1000);
640
641 // -------- SPATIAL OPTIMIZATION --------
642
650 std::vector<std::pair<uint, uint>> findCollisionsWithinDistance(const std::vector<uint> &query_UUIDs, const std::vector<uint> &target_UUIDs, float max_distance);
651
656 void setMaxCollisionDistance(float distance);
657
662 [[nodiscard]] float getMaxCollisionDistance() const;
663
671 std::vector<uint> filterGeometryByDistance(const helios::vec3 &query_center, float max_radius, const std::vector<uint> &candidate_UUIDs = {});
672
687 bool findNearestPrimitiveDistance(const helios::vec3 &origin, const helios::vec3 &direction, const std::vector<uint> &candidate_UUIDs, float &distance, helios::vec3 &obstacle_direction);
688
705 bool findNearestSolidObstacleInCone(const helios::vec3 &apex, const helios::vec3 &axis, float half_angle, float height, const std::vector<uint> &candidate_UUIDs, float &distance, helios::vec3 &obstacle_direction, int num_rays = 64);
706
708
725 bool findNearestSolidObstacleInCone(const helios::vec3 &apex, const helios::vec3 &axis, float half_angle, float height, const std::vector<uint> &candidate_UUIDs, const std::vector<uint> &plant_primitives, float &distance,
726 helios::vec3 &obstacle_direction, int num_rays = 64);
727
728
729 // -------- BVH MANAGEMENT --------
730
735 void buildBVH(const std::vector<uint> &UUIDs = {});
736
742 void updateBVH(const std::vector<uint> &UUIDs, bool force_rebuild = false);
743
748 void setStaticGeometry(const std::vector<uint> &UUIDs);
749
753 void rebuildBVH();
754
759
764
769
774
781 void buildStaticBVH();
782
791 void enableTreeBasedBVH(float isolation_distance = 5.0f);
792
796 void disableTreeBasedBVH();
797
802 [[nodiscard]] bool isTreeBasedBVHEnabled() const;
803
808
817 void registerTree(uint tree_object_id, const std::vector<uint> &tree_primitives);
818
826 void setStaticObstacles(const std::vector<uint> &obstacle_primitives);
827
839 std::vector<uint> getRelevantGeometryForTree(const helios::vec3 &query_position, const std::vector<uint> &query_primitives = {}, float max_distance = 15.0f);
840
845 [[nodiscard]] bool isBVHValid() const;
846
847 // -------- GPU ACCELERATION CONTROL --------
848
853
858
863 [[nodiscard]] bool isGPUAccelerationEnabled() const;
864
865 // -------- UTILITY METHODS --------
866
870 void disableMessages();
871
875 void enableMessages();
876
881 [[nodiscard]] size_t getPrimitiveCount() const;
882
889 void getBVHStatistics(size_t &node_count, size_t &leaf_count, size_t &max_depth) const;
890
895 static int selfTest(int argc, char **argv);
896
897private:
899 helios::Context *context;
900
902 bool gpu_acceleration_enabled;
903
905 bool printmessages;
906
907 // -------- THREAD-SAFE PRIMITIVE CACHE --------
908
912 struct CachedPrimitive {
914 std::vector<helios::vec3> vertices;
915
916 CachedPrimitive() : type(helios::PRIMITIVE_TYPE_TRIANGLE) {
917 }
918 CachedPrimitive(helios::PrimitiveType t, const std::vector<helios::vec3> &v) : type(t), vertices(v) {
919 }
920 };
921
923 std::unordered_map<uint, CachedPrimitive> primitive_cache;
924
928 void buildPrimitiveCache();
929
933 HitResult intersectPrimitiveThreadSafe(const helios::vec3 &origin, const helios::vec3 &direction, uint primitive_id, float max_distance);
934
938 bool triangleIntersect(const helios::vec3 &origin, const helios::vec3 &direction, const helios::vec3 &v0, const helios::vec3 &v1, const helios::vec3 &v2, float &distance);
939
940 bool patchIntersect(const helios::vec3 &origin, const helios::vec3 &direction, const helios::vec3 &v0, const helios::vec3 &v1, const helios::vec3 &v2, const helios::vec3 &v3, float &distance);
941
942 // -------- BVH DATA STRUCTURES --------
943
947 struct BVHNode {
948 helios::vec3 aabb_min;
949 helios::vec3 aabb_max;
950 uint left_child;
951 uint right_child;
952 uint primitive_start;
953 uint primitive_count;
954 bool is_leaf;
955
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) {
957 }
958 };
959
964 struct BVHNodesSoA {
965 // Hot data: frequently accessed during traversal (cache-friendly grouping)
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;
970
971 // Cold data: accessed less frequently (separate for better cache utilization)
972 std::vector<uint32_t> primitive_starts;
973 std::vector<uint32_t> primitive_counts;
974 std::vector<uint8_t> is_leaf_flags;
975
976 // Metadata
977 size_t node_count = 0;
978
979 BVHNodesSoA() = default;
980
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);
992 }
993
997 void clear() {
998 aabb_mins.clear();
999 aabb_maxs.clear();
1000 left_children.clear();
1001 right_children.clear();
1002 primitive_starts.clear();
1003 primitive_counts.clear();
1004 is_leaf_flags.clear();
1005 node_count = 0;
1006 }
1007
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);
1013 }
1014 };
1015
1016
1018 std::vector<BVHNode> bvh_nodes;
1019
1021 size_t next_available_node_index;
1022
1024 BVHNodesSoA bvh_nodes_soa;
1025
1028
1030 std::vector<uint> primitive_indices;
1031
1033 std::unordered_map<uint, std::pair<helios::vec3, helios::vec3>> primitive_aabbs_cache;
1034
1036 std::unordered_set<uint> dirty_primitive_cache;
1037
1039 std::vector<std::vector<std::vector<std::vector<uint>>>> grid_cells;
1040
1042 helios::vec3 grid_center;
1043 helios::vec3 grid_size;
1044 helios::int3 grid_divisions;
1045
1046 // -------- VOXEL RAY STATISTICS DATA --------
1047
1049 std::vector<std::vector<std::vector<int>>> voxel_ray_counts;
1050
1052 std::vector<std::vector<std::vector<int>>> voxel_transmitted;
1053
1055 std::vector<std::vector<std::vector<float>>> voxel_path_lengths;
1056
1058 std::vector<std::vector<std::vector<int>>> voxel_hit_before;
1059
1061 std::vector<std::vector<std::vector<int>>> voxel_hit_after;
1062
1064 std::vector<std::vector<std::vector<int>>> voxel_hit_inside;
1065
1067 std::vector<std::vector<std::vector<std::vector<float>>>> voxel_individual_path_lengths;
1068
1069 // -------- OPTIMIZED FLAT ARRAY DATA (Structure-of-Arrays) --------
1070
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;
1078
1080 std::vector<float> voxel_individual_path_lengths_flat;
1081 std::vector<size_t> voxel_individual_path_offsets; // Start offset for each voxel
1082 std::vector<size_t> voxel_individual_path_counts; // Count for each voxel
1083
1085 bool use_flat_arrays;
1086
1088 bool voxel_data_initialized;
1089
1091 helios::vec3 voxel_grid_center;
1092 helios::vec3 voxel_grid_size;
1093 helios::int3 voxel_grid_divisions;
1094
1095 // -------- SPATIAL OPTIMIZATION DATA --------
1096
1098 float max_collision_distance;
1099
1101 std::unordered_map<uint, helios::vec3> primitive_centroids_cache;
1102
1103 // -------- GPU MEMORY POINTERS --------
1104
1106 void *d_bvh_nodes;
1107
1109 uint *d_primitive_indices;
1110
1112 bool gpu_memory_allocated;
1113
1115 std::set<uint> last_processed_uuids;
1116
1118 std::set<uint> last_processed_deleted_uuids;
1119
1121 std::set<uint> static_geometry_cache;
1122
1124 std::set<uint> last_bvh_geometry;
1125
1127 bool bvh_dirty;
1128
1130 bool soa_dirty;
1131
1133 bool automatic_bvh_rebuilds;
1134
1136 mutable bool batch_mode_skip_bvh_check;
1137
1138 // -------- HIERARCHICAL BVH DATA STRUCTURES --------
1139
1141 bool hierarchical_bvh_enabled;
1142
1144 std::vector<BVHNode> static_bvh_nodes;
1145
1147 std::vector<uint> static_bvh_primitives;
1148
1150 bool static_bvh_valid;
1151
1153 std::set<uint> last_static_bvh_geometry;
1154
1156 void updateHierarchicalBVH(const std::set<uint> &requested_geometry, bool force_rebuild);
1157
1158 // -------- TREE-BASED BVH DATA STRUCTURES --------
1159
1160 // Forward declaration
1161 struct TreeBVH;
1162
1164 bool tree_based_bvh_enabled;
1165
1167 float tree_isolation_distance;
1168
1170 std::unordered_map<uint, uint> object_to_tree_map;
1171
1173 std::vector<uint> static_obstacle_primitives;
1174
1176 struct ObstacleSpatialGrid {
1177 float cell_size;
1178 std::unordered_map<int64_t, std::vector<uint>> grid_cells;
1179
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);
1184 }
1185
1186 [[nodiscard]] std::vector<uint> getRelevantObstacles(const helios::vec3 &position, float radius) const;
1187 };
1188
1189 mutable ObstacleSpatialGrid obstacle_spatial_grid;
1190 bool obstacle_spatial_grid_initialized;
1191
1192 // -------- GAP DETECTION DATA STRUCTURES --------
1193
1197 struct RaySample {
1198 helios::vec3 direction;
1199 float distance;
1200 bool is_free;
1201 };
1202
1206 struct Gap {
1207 helios::vec3 center_direction;
1208 float angular_size;
1209 float angular_distance;
1210 float score;
1211 std::vector<int> sample_indices;
1212 };
1213
1217 struct SpatialHashGrid {
1218 struct Cell {
1219 std::vector<size_t> sample_indices;
1220 };
1221
1222 std::vector<std::vector<Cell>> grid;
1223 int theta_resolution;
1224 int phi_resolution;
1225 float theta_step;
1226 float phi_step;
1227
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);
1232 }
1233 theta_step = M_PI / theta_resolution;
1234 phi_step = 2.0f * M_PI / phi_resolution;
1235 }
1236
1237 void clear() {
1238 for (auto &row: grid) {
1239 for (auto &cell: row) {
1240 cell.sample_indices.clear();
1241 }
1242 }
1243 }
1244
1245 [[nodiscard]] std::pair<int, int> getGridIndex(const helios::vec3 &direction) const {
1246 // Convert direction to spherical coordinates
1247 float theta = acosf(std::max(-1.0f, std::min(1.0f, direction.z)));
1248 float phi = atan2f(direction.y, direction.x);
1249 if (phi < 0)
1250 phi += 2.0f * M_PI;
1251
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);
1254
1255 return {theta_idx, phi_idx};
1256 }
1257
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);
1261 }
1262
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;
1266
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;
1271
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());
1275 }
1276 }
1277 }
1278
1279 return nearby_indices;
1280 }
1281 };
1282
1286 struct TreeBVH {
1287 uint tree_object_id;
1288 helios::vec3 tree_center;
1289 float tree_radius;
1290 std::vector<BVHNode> nodes;
1291 std::vector<uint> primitive_indices;
1292 BVHNodesSoA soa_structure;
1293 bool soa_dirty;
1294
1295 TreeBVH() : tree_object_id(0), tree_center(0, 0, 0), tree_radius(0), soa_dirty(true) {
1296 }
1297
1298 void clear() {
1299 nodes.clear();
1300 primitive_indices.clear();
1301 soa_structure = BVHNodesSoA();
1302 soa_dirty = true;
1303 }
1304 };
1305
1307 std::unordered_map<uint, TreeBVH> tree_bvh_map;
1308
1309 // -------- PRIVATE HELPER METHODS --------
1310
1317 void ensureBVHCurrent();
1318
1325 void calculateAABB(const std::vector<uint> &primitives, helios::vec3 &aabb_min, helios::vec3 &aabb_max) const;
1326
1338 void buildBVHRecursive(uint node_index, size_t primitive_start, size_t primitive_count, int depth);
1339
1346 std::vector<uint> traverseBVH_CPU(const helios::vec3 &query_aabb_min, const helios::vec3 &query_aabb_max);
1347
1348#ifdef HELIOS_CUDA_AVAILABLE
1356 std::vector<uint> traverseBVH_GPU(const helios::vec3 &query_aabb_min, const helios::vec3 &query_aabb_max);
1357#endif
1358
1367 bool aabbIntersect(const helios::vec3 &min1, const helios::vec3 &max1, const helios::vec3 &min2, const helios::vec3 &max2);
1368
1379 bool rayAABBIntersect(const helios::vec3 &origin, const helios::vec3 &direction, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max, float &t_min, float &t_max) const;
1380
1392 uint32_t rayAABBIntersectSIMD(const helios::vec3 *ray_origins, const helios::vec3 *ray_directions, const helios::vec3 *aabb_mins, const helios::vec3 *aabb_maxs, float *t_mins, float *t_maxs, int count);
1393
1401 void traverseBVHSIMD(const helios::vec3 *ray_origins, const helios::vec3 *ray_directions, int count, HitResult *results);
1402
1406 void traverseBVHSIMDImpl(const helios::vec3 *ray_origins, const helios::vec3 *ray_directions, int count, HitResult *results);
1407
1415 bool coneAABBIntersect(const Cone &cone, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max);
1416
1425 bool coneAABBIntersectFast(const Cone &cone, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max);
1426
1437 bool coneAABBIntersect(const helios::vec3 &cone_origin, const helios::vec3 &cone_direction, float cone_angle, float max_distance, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max);
1438
1446 int countRayIntersections(const helios::vec3 &origin, const helios::vec3 &direction, float max_distance = -1.0f);
1447
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);
1458
1467 std::vector<helios::vec3> sampleDirectionsInCone(const helios::vec3 &apex, const helios::vec3 &central_axis, float half_angle, int num_samples);
1468
1469 // -------- GAP DETECTION HELPER METHODS --------
1470
1480 std::vector<Gap> detectGapsInCone(const helios::vec3 &apex, const helios::vec3 &central_axis, float half_angle, float height, int num_samples);
1481
1482
1491 std::vector<uint> getCandidatePrimitivesInCone(const helios::vec3 &apex, const helios::vec3 &central_axis, float half_angle, float height);
1492
1502 std::vector<uint> getCandidatesUsingSpatialGrid(const Cone &cone, const helios::vec3 &apex, const helios::vec3 &central_axis, float half_angle, float height);
1503
1510 float calculateGapAngularSize(const std::vector<RaySample> &gap_samples, const helios::vec3 &central_axis);
1511
1517 void scoreGapsByFishEyeMetric(std::vector<Gap> &gaps, const helios::vec3 &central_axis);
1518
1525 helios::vec3 findOptimalGapDirection(const std::vector<Gap> &gaps, const helios::vec3 &central_axis);
1526
1527 // -------- VOXEL RAY PATH LENGTH HELPER METHODS --------
1528
1535 void initializeVoxelData(const helios::vec3 &grid_center, const helios::vec3 &grid_size, const helios::int3 &grid_divisions);
1536
1542 void calculateVoxelRayPathLengths_CPU(const std::vector<helios::vec3> &ray_origins, const std::vector<helios::vec3> &ray_directions);
1543
1549 bool calculateVoxelRayPathLengths_GPU(const std::vector<helios::vec3> &ray_origins, const std::vector<helios::vec3> &ray_directions);
1550
1556 bool validateVoxelIndices(const helios::int3 &ijk) const;
1557
1564 void calculateVoxelAABB(const helios::int3 &ijk, helios::vec3 &voxel_min, helios::vec3 &voxel_max) const;
1565
1572 std::vector<std::pair<helios::int3, float>> traverseVoxelGrid(const helios::vec3 &ray_origin, const helios::vec3 &ray_direction) const;
1573
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);
1583 }
1584
1590 inline size_t flatIndex(const helios::int3 &ijk) const {
1591 return flatIndex(ijk.x, ijk.y, ijk.z);
1592 }
1593
1594#ifdef HELIOS_CUDA_AVAILABLE
1598 void allocateGPUMemory();
1599
1603 void freeGPUMemory();
1604
1608 void transferBVHToGPU();
1609#endif
1610
1614 void markBVHDirty();
1615
1622 void incrementalUpdateBVH(const std::set<uint> &added_geometry, const std::set<uint> &removed_geometry, const std::set<uint> &final_geometry);
1623
1628 void updatePrimitiveAABBCache(uint uuid);
1629
1634 void optimizedRebuildBVH(const std::set<uint> &final_geometry);
1635
1641 bool validateUUIDs(const std::vector<uint> &UUIDs) const;
1642
1651 bool rayPrimitiveIntersection(const helios::vec3 &origin, const helios::vec3 &direction, uint primitive_UUID, float &distance) const;
1652
1659 void castRaysCPU(const std::vector<RayQuery> &ray_queries, std::vector<HitResult> &results, RayTracingStats &stats);
1660
1661#ifdef HELIOS_CUDA_AVAILABLE
1668 void castRaysGPU(const std::vector<RayQuery> &ray_queries, std::vector<HitResult> &results, RayTracingStats &stats);
1669#endif
1670
1671 // -------- OPTIMIZED RAY TRACING PRIVATE METHODS --------
1672
1676 void convertBVHLayout(BVHOptimizationMode from_mode, BVHOptimizationMode to_mode);
1677 void ensureOptimizedBVH(); // Populate optimized BVH structures on demand
1678
1682 std::vector<HitResult> castRaysSoA(const std::vector<RayQuery> &ray_queries, RayTracingStats &stats);
1683
1687 HitResult castRaySoATraversal(const RayQuery &query, RayTracingStats &stats);
1688
1693 HitResult castRayBVHTraversal(const RayQuery &query);
1694
1698 inline bool aabbIntersectSoA(const helios::vec3 &ray_origin, const helios::vec3 &ray_direction, float max_distance, size_t node_index) const;
1699
1704 bool rayAABBIntersect(const helios::vec3 &ray_origin, const helios::vec3 &ray_direction, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max) const;
1705
1709 HitResult intersectPrimitive(const RayQuery &query, uint primitive_id);
1710
1720 bool rayAABBIntersectPrimitive(const helios::vec3 &origin, const helios::vec3 &direction, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max, float &distance);
1721
1722 // -------- RASTERIZATION-BASED COLLISION DETECTION --------
1723
1730 int calculateOptimalBinCount(float cone_half_angle, int geometry_count);
1731
1738 std::vector<uint> filterPrimitivesParallel(const Cone &cone, const std::vector<uint> &primitive_uuids);
1739
1746 void projectGeometryToBins(const Cone &cone, const std::vector<uint> &filtered_uuids, AngularBins &bins);
1747
1754 std::vector<Gap> findGapsInCoverageMap(const AngularBins &bins, const Cone &cone);
1755
1765 bool sphericalCoordsToBinIndices(float theta, float phi, const AngularBins &bins, int &theta_bin, int &phi_bin);
1766
1775 float cartesianToSphericalCone(const helios::vec3 &vector, const helios::vec3 &cone_axis, float &theta, float &phi);
1776
1783 void projectGeometryToBinsSerial(const Cone &cone, const std::vector<uint> &filtered_uuids, AngularBins &bins);
1784
1794 Gap floodFillGap(const AngularBins &bins, int start_theta, int start_phi, std::vector<std::vector<bool>> &visited, const Cone &cone);
1795
1804 float calculateBinSolidAngle(int theta_bin, int phi_bin, const AngularBins &bins, float cone_half_angle);
1805
1814 helios::vec3 binIndicesToCartesian(int theta_bin, int phi_bin, const AngularBins &bins, const Cone &cone);
1815
1824 void addUnoccupiedNeighbors(int theta, int phi, const AngularBins &bins, std::vector<std::vector<bool>> &visited, std::queue<std::pair<int, int>> &queue);
1825};
1826
1827#endif