1.3.77
 
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
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
386 void castRaysSoA(const helios::vec3 *origins, const helios::vec3 *directions, size_t count, float max_distance, float *out_distance, helios::vec3 *out_normal, uint *out_primitive_UUID, RayTracingStats *stats = nullptr);
387
389
412 void castRaysSoA_packets(const helios::vec3 *origins, const helios::vec3 *directions, size_t count, size_t packet_size, float max_distance, float *out_distance, helios::vec3 *out_normal, uint *out_primitive_UUID,
413 RayTracingStats *stats = nullptr);
414
415 // -------- OPTIMIZATION METHODS --------
416
422 };
423
429
435
442 std::vector<HitResult> castRaysOptimized(const std::vector<RayQuery> &ray_queries, RayTracingStats *stats = nullptr);
443
444#ifdef HELIOS_CUDA_AVAILABLE
452 std::vector<HitResult> castRaysGPU(const std::vector<RayQuery> &ray_queries, RayTracingStats &stats);
453#endif
454
461 bool processRayStream(RayStream &ray_stream, RayTracingStats *stats = nullptr);
462
468 size_t soa_memory_bytes = 0;
469 size_t quantized_memory_bytes = 0;
470 float quantized_reduction_percent = 0.0f; // Reduction vs SoA
471 };
472 MemoryUsageStats getBVHMemoryUsage() const;
473
482 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);
483
496 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);
497
507 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,
508 std::vector<HitResult> &hit_results);
509
510 // -------- CONE INTERSECTION QUERIES --------
511
521 OptimalPathResult findOptimalConePath(const helios::vec3 &apex, const helios::vec3 &centralAxis, float half_angle, float height = 0.0f, int initialSamples = 256);
522
523
524 // -------- GRID-BASED INTERSECTION --------
525
533 void calculateGridIntersection(const helios::vec3 &grid_center, const helios::vec3 &grid_size, const helios::int3 &grid_divisions, const std::vector<uint> &UUIDs = {});
534
539 std::vector<std::vector<std::vector<std::vector<uint>>>> getGridCells();
540
548 std::vector<uint> getGridIntersections(int i, int j, int k);
549
550 // -------- PRIMITIVE SLICING OPERATIONS --------
551
559 std::vector<uint> slicePrimitive(uint UUID, const std::vector<helios::vec3> &voxel_face_vertices, helios::WarningAggregator &warnings);
560
571 std::vector<uint> slicePrimitivesUsingGrid(const std::vector<uint> &UUIDs, const helios::vec3 &grid_center, const helios::vec3 &grid_size, const helios::int3 &grid_divisions);
572
580 void calculatePrimitiveVoxelIntersection(const std::vector<uint> &UUIDs = {});
581
582 // -------- GEOMETRIC UTILITY FUNCTIONS --------
583
592 helios::vec3 linesIntersection(const helios::vec3 &line1_point, const helios::vec3 &line1_direction, const helios::vec3 &line2_point, const helios::vec3 &line2_direction) const;
593
602 bool approxSame(float a, float b, float absTol, float relTol) const;
603
607 bool approxSame(const helios::vec3 &a, const helios::vec3 &b, float absTol) const;
608
618 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;
619
620 // -------- VOXEL RAY PATH LENGTH CALCULATIONS --------
621
630 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);
631
638 void setVoxelTransmissionProbability(int P_denom, int P_trans, const helios::int3 &ijk);
639
646 void getVoxelTransmissionProbability(const helios::int3 &ijk, int &P_denom, int &P_trans) const;
647
653 void setVoxelRbar(float r_bar, const helios::int3 &ijk);
654
660 float getVoxelRbar(const helios::int3 &ijk) const;
661
669 void getVoxelRayHitCounts(const helios::int3 &ijk, int &hit_before, int &hit_after, int &hit_inside) const;
670
676 std::vector<float> getVoxelRayPathLengths(const helios::int3 &ijk) const;
677
681 void clearVoxelData();
682
683 // -------- FEATURE 4: COLLISION MINIMIZATION --------
684
692 int optimizeLayout(const std::vector<uint> &UUIDs, float learning_rate = 0.01f, int max_iterations = 1000);
693
694 // -------- SPATIAL OPTIMIZATION --------
695
703 std::vector<std::pair<uint, uint>> findCollisionsWithinDistance(const std::vector<uint> &query_UUIDs, const std::vector<uint> &target_UUIDs, float max_distance);
704
709 void setMaxCollisionDistance(float distance);
710
715 [[nodiscard]] float getMaxCollisionDistance() const;
716
724 std::vector<uint> filterGeometryByDistance(const helios::vec3 &query_center, float max_radius, const std::vector<uint> &candidate_UUIDs = {});
725
740 bool findNearestPrimitiveDistance(const helios::vec3 &origin, const helios::vec3 &direction, const std::vector<uint> &candidate_UUIDs, float &distance, helios::vec3 &obstacle_direction);
741
758 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);
759
761
778 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,
779 helios::vec3 &obstacle_direction, int num_rays = 64);
780
781
782 // -------- BVH MANAGEMENT --------
783
788 void buildBVH(const std::vector<uint> &UUIDs = {});
789
795 void updateBVH(const std::vector<uint> &UUIDs, bool force_rebuild = false);
796
801 void setStaticGeometry(const std::vector<uint> &UUIDs);
802
806 void rebuildBVH();
807
812
817
822
827
834 void buildStaticBVH();
835
844 void enableTreeBasedBVH(float isolation_distance = 5.0f);
845
849 void disableTreeBasedBVH();
850
855 [[nodiscard]] bool isTreeBasedBVHEnabled() const;
856
861
870 void registerTree(uint tree_object_id, const std::vector<uint> &tree_primitives);
871
879 void setStaticObstacles(const std::vector<uint> &obstacle_primitives);
880
892 std::vector<uint> getRelevantGeometryForTree(const helios::vec3 &query_position, const std::vector<uint> &query_primitives = {}, float max_distance = 15.0f);
893
898 [[nodiscard]] bool isBVHValid() const;
899
900 // -------- GPU ACCELERATION CONTROL --------
901
906
911
916 [[nodiscard]] bool isGPUAccelerationEnabled() const;
917
931 [[nodiscard]] static bool isGPUAvailable();
932
933 // -------- UTILITY METHODS --------
934
938 void disableMessages();
939
943 void enableMessages();
944
956 void setCancelFlag(volatile int *flag);
957
962 [[nodiscard]] size_t getPrimitiveCount() const;
963
970 void getBVHStatistics(size_t &node_count, size_t &leaf_count, size_t &max_depth) const;
971
976 static int selfTest(int argc, char **argv);
977
978private:
981
983 bool gpu_acceleration_enabled;
984
986 bool printmessages;
987
990 volatile int *cancel_flag = nullptr;
991
992 // -------- THREAD-SAFE PRIMITIVE CACHE --------
993
997 struct CachedPrimitive {
999 std::vector<helios::vec3> vertices;
1000
1003 uint UUID = 0xFFFFFFFFu;
1004
1007 const std::vector<std::vector<bool>> *transparency_mask = nullptr;
1009 helios::int2 texture_size = helios::make_int2(0, 0);
1011 std::vector<helios::vec2> uv;
1012
1013 CachedPrimitive() : type(helios::PRIMITIVE_TYPE_TRIANGLE) {
1014 }
1015 CachedPrimitive(helios::PrimitiveType t, const std::vector<helios::vec3> &v) : type(t), vertices(v) {
1016 }
1017 };
1018
1022 std::unordered_map<uint, CachedPrimitive> primitive_cache;
1023
1028 std::vector<CachedPrimitive> primitive_cache_dense;
1029
1033 void buildPrimitiveCache();
1034
1041 void rebuildDensePrimitiveCache();
1042
1049 void ensurePrimitiveCacheCurrent();
1050
1054 HitResult intersectPrimitiveThreadSafe(const helios::vec3 &origin, const helios::vec3 &direction, uint primitive_id, float max_distance);
1055
1064 HitResult intersectCachedPrimitive(const helios::vec3 &origin, const helios::vec3 &direction, const CachedPrimitive &cached, float max_distance) const;
1065
1078 [[nodiscard]] bool isHitTexelOpaque(const CachedPrimitive &cached, const helios::vec3 &hit_point) const;
1079
1083 bool triangleIntersect(const helios::vec3 &origin, const helios::vec3 &direction, const helios::vec3 &v0, const helios::vec3 &v1, const helios::vec3 &v2, float &distance) const;
1084
1085 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) const;
1086
1087 // -------- BVH DATA STRUCTURES --------
1088
1092 struct BVHNode {
1093 helios::vec3 aabb_min;
1094 helios::vec3 aabb_max;
1095 uint left_child;
1096 uint right_child;
1097 uint primitive_start;
1098 uint primitive_count;
1099 bool is_leaf;
1100
1101 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) {
1102 }
1103 };
1104
1109 struct BVHNodesSoA {
1110 // Hot data: frequently accessed during traversal (cache-friendly grouping)
1111 std::vector<helios::vec3> aabb_mins;
1112 std::vector<helios::vec3> aabb_maxs;
1113 std::vector<uint32_t> left_children;
1114 std::vector<uint32_t> right_children;
1115
1116 // Cold data: accessed less frequently (separate for better cache utilization)
1117 std::vector<uint32_t> primitive_starts;
1118 std::vector<uint32_t> primitive_counts;
1119 std::vector<uint8_t> is_leaf_flags;
1120
1121 // Metadata
1122 size_t node_count = 0;
1123
1124 BVHNodesSoA() = default;
1125
1129 void reserve(size_t capacity) {
1130 aabb_mins.reserve(capacity);
1131 aabb_maxs.reserve(capacity);
1132 left_children.reserve(capacity);
1133 right_children.reserve(capacity);
1134 primitive_starts.reserve(capacity);
1135 primitive_counts.reserve(capacity);
1136 is_leaf_flags.reserve(capacity);
1137 }
1138
1142 void clear() {
1143 aabb_mins.clear();
1144 aabb_maxs.clear();
1145 left_children.clear();
1146 right_children.clear();
1147 primitive_starts.clear();
1148 primitive_counts.clear();
1149 is_leaf_flags.clear();
1150 node_count = 0;
1151 }
1152
1156 [[nodiscard]] size_t getMemoryUsage() const {
1157 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);
1158 }
1159 };
1160
1161
1163 std::vector<BVHNode> bvh_nodes;
1164
1166 size_t next_available_node_index;
1167
1169 BVHNodesSoA bvh_nodes_soa;
1170
1173
1175 std::vector<uint> primitive_indices;
1176
1178 std::unordered_map<uint, std::pair<helios::vec3, helios::vec3>> primitive_aabbs_cache;
1179
1181 std::unordered_set<uint> dirty_primitive_cache;
1182
1184 std::vector<std::vector<std::vector<std::vector<uint>>>> grid_cells;
1185
1187 helios::vec3 grid_center;
1188 helios::vec3 grid_size;
1189 helios::int3 grid_divisions;
1190
1191 // -------- VOXEL RAY STATISTICS DATA --------
1192
1194 std::vector<std::vector<std::vector<int>>> voxel_ray_counts;
1195
1197 std::vector<std::vector<std::vector<int>>> voxel_transmitted;
1198
1200 std::vector<std::vector<std::vector<float>>> voxel_path_lengths;
1201
1203 std::vector<std::vector<std::vector<int>>> voxel_hit_before;
1204
1206 std::vector<std::vector<std::vector<int>>> voxel_hit_after;
1207
1209 std::vector<std::vector<std::vector<int>>> voxel_hit_inside;
1210
1212 std::vector<std::vector<std::vector<std::vector<float>>>> voxel_individual_path_lengths;
1213
1214 // -------- OPTIMIZED FLAT ARRAY DATA (Structure-of-Arrays) --------
1215
1217 std::vector<int> voxel_ray_counts_flat;
1218 std::vector<int> voxel_transmitted_flat;
1219 std::vector<float> voxel_path_lengths_flat;
1220 std::vector<int> voxel_hit_before_flat;
1221 std::vector<int> voxel_hit_after_flat;
1222 std::vector<int> voxel_hit_inside_flat;
1223
1225 std::vector<float> voxel_individual_path_lengths_flat;
1226 std::vector<size_t> voxel_individual_path_offsets; // Start offset for each voxel
1227 std::vector<size_t> voxel_individual_path_counts; // Count for each voxel
1228
1230 bool use_flat_arrays;
1231
1233 bool voxel_data_initialized;
1234
1236 helios::vec3 voxel_grid_center;
1237 helios::vec3 voxel_grid_size;
1238 helios::int3 voxel_grid_divisions;
1239
1240 // -------- SPATIAL OPTIMIZATION DATA --------
1241
1243 float max_collision_distance;
1244
1246 std::unordered_map<uint, helios::vec3> primitive_centroids_cache;
1247
1248 // -------- GPU MEMORY POINTERS --------
1249
1251 void *d_bvh_nodes;
1252
1254 uint *d_primitive_indices;
1255
1257 int *d_primitive_types;
1258
1260 void *d_primitive_vertices;
1261
1263 uint *d_vertex_offsets;
1264
1269 void *d_mask_data;
1271 uint *d_mask_offsets;
1273 int *d_mask_sizes;
1275 int *d_mask_IDs;
1277 void *d_uv_data;
1279 int *d_uv_IDs;
1281 bool d_gpu_has_masks;
1282
1284 int d_gpu_node_count;
1285 int d_gpu_primitive_count;
1286 int d_gpu_total_vertex_count;
1287
1289 bool gpu_memory_allocated;
1290
1292 std::set<uint> last_processed_uuids;
1293
1295 std::set<uint> last_processed_deleted_uuids;
1296
1298 std::set<uint> static_geometry_cache;
1299
1301 std::set<uint> last_bvh_geometry;
1302
1304 bool bvh_dirty;
1305
1307 bool soa_dirty;
1308
1310 bool automatic_bvh_rebuilds;
1311
1313 mutable bool batch_mode_skip_bvh_check;
1314
1315 // -------- HIERARCHICAL BVH DATA STRUCTURES --------
1316
1318 bool hierarchical_bvh_enabled;
1319
1321 std::vector<BVHNode> static_bvh_nodes;
1322
1324 std::vector<uint> static_bvh_primitives;
1325
1327 bool static_bvh_valid;
1328
1330 std::set<uint> last_static_bvh_geometry;
1331
1333 void updateHierarchicalBVH(const std::set<uint> &requested_geometry, bool force_rebuild);
1334
1335 // -------- TREE-BASED BVH DATA STRUCTURES --------
1336
1337 // Forward declaration
1338 struct TreeBVH;
1339
1341 bool tree_based_bvh_enabled;
1342
1344 float tree_isolation_distance;
1345
1347 std::unordered_map<uint, uint> object_to_tree_map;
1348
1350 std::vector<uint> static_obstacle_primitives;
1351
1353 struct ObstacleSpatialGrid {
1354 float cell_size;
1355 std::unordered_map<int64_t, std::vector<uint>> grid_cells;
1356
1357 [[nodiscard]] int64_t getGridKey(float x, float y) const {
1358 auto grid_x = static_cast<int32_t>(std::floor(x / cell_size));
1359 auto grid_y = static_cast<int32_t>(std::floor(y / cell_size));
1360 return (static_cast<int64_t>(grid_x) << 32) | static_cast<uint32_t>(grid_y);
1361 }
1362
1363 [[nodiscard]] std::vector<uint> getRelevantObstacles(const helios::vec3 &position, float radius) const;
1364 };
1365
1366 mutable ObstacleSpatialGrid obstacle_spatial_grid;
1367 bool obstacle_spatial_grid_initialized;
1368
1369 // -------- GAP DETECTION DATA STRUCTURES --------
1370
1374 struct RaySample {
1375 helios::vec3 direction;
1376 float distance;
1377 bool is_free;
1378 };
1379
1383 struct Gap {
1384 helios::vec3 center_direction;
1385 float angular_size;
1386 float angular_distance;
1387 float score;
1388 std::vector<int> sample_indices;
1389 };
1390
1394 struct SpatialHashGrid {
1395 struct Cell {
1396 std::vector<size_t> sample_indices;
1397 };
1398
1399 std::vector<std::vector<Cell>> grid;
1400 int theta_resolution;
1401 int phi_resolution;
1402 float theta_step;
1403 float phi_step;
1404
1405 explicit SpatialHashGrid(int theta_res = 32, int phi_res = 16) : theta_resolution(theta_res), phi_resolution(phi_res) {
1406 grid.resize(theta_resolution);
1407 for (auto &row: grid) {
1408 row.resize(phi_resolution);
1409 }
1410 theta_step = M_PI / theta_resolution;
1411 phi_step = 2.0f * M_PI / phi_resolution;
1412 }
1413
1414 void clear() {
1415 for (auto &row: grid) {
1416 for (auto &cell: row) {
1417 cell.sample_indices.clear();
1418 }
1419 }
1420 }
1421
1422 [[nodiscard]] std::pair<int, int> getGridIndex(const helios::vec3 &direction) const {
1423 // Convert direction to spherical coordinates
1424 float theta = acosf(std::max(-1.0f, std::min(1.0f, direction.z)));
1425 float phi = atan2f(direction.y, direction.x);
1426 if (phi < 0)
1427 phi += 2.0f * M_PI;
1428
1429 int theta_idx = std::min((int) (theta / theta_step), theta_resolution - 1);
1430 int phi_idx = std::min((int) (phi / phi_step), phi_resolution - 1);
1431
1432 return {theta_idx, phi_idx};
1433 }
1434
1435 void addSample(size_t sample_idx, const helios::vec3 &direction) {
1436 auto [theta_idx, phi_idx] = getGridIndex(direction);
1437 grid[theta_idx][phi_idx].sample_indices.push_back(sample_idx);
1438 }
1439
1440 [[nodiscard]] std::vector<size_t> getNearbyIndices(const helios::vec3 &direction, int radius = 1) const {
1441 auto [center_theta, center_phi] = getGridIndex(direction);
1442 std::vector<size_t> nearby_indices;
1443
1444 for (int dt = -radius; dt <= radius; dt++) {
1445 for (int dp = -radius; dp <= radius; dp++) {
1446 int theta_idx = center_theta + dt;
1447 int phi_idx = (center_phi + dp + phi_resolution) % phi_resolution;
1448
1449 if (theta_idx >= 0 && theta_idx < theta_resolution) {
1450 const auto &cell = grid[theta_idx][phi_idx];
1451 nearby_indices.insert(nearby_indices.end(), cell.sample_indices.begin(), cell.sample_indices.end());
1452 }
1453 }
1454 }
1455
1456 return nearby_indices;
1457 }
1458 };
1459
1463 struct TreeBVH {
1464 uint tree_object_id;
1465 helios::vec3 tree_center;
1466 float tree_radius;
1467 std::vector<BVHNode> nodes;
1468 std::vector<uint> primitive_indices;
1469 BVHNodesSoA soa_structure;
1470 bool soa_dirty;
1471
1472 TreeBVH() : tree_object_id(0), tree_center(0, 0, 0), tree_radius(0), soa_dirty(true) {
1473 }
1474
1475 void clear() {
1476 nodes.clear();
1477 primitive_indices.clear();
1478 soa_structure = BVHNodesSoA();
1479 soa_dirty = true;
1480 }
1481 };
1482
1484 std::unordered_map<uint, TreeBVH> tree_bvh_map;
1485
1486 // -------- PRIVATE HELPER METHODS --------
1487
1494 void ensureBVHCurrent();
1495
1502 void calculateAABB(const std::vector<uint> &primitives, helios::vec3 &aabb_min, helios::vec3 &aabb_max) const;
1503
1515 void buildBVHRecursive(uint node_index, size_t primitive_start, size_t primitive_count, int depth);
1516
1523 std::vector<uint> traverseBVH_CPU(const helios::vec3 &query_aabb_min, const helios::vec3 &query_aabb_max);
1524
1525#ifdef HELIOS_CUDA_AVAILABLE
1533 std::vector<uint> traverseBVH_GPU(const helios::vec3 &query_aabb_min, const helios::vec3 &query_aabb_max);
1534#endif
1535
1544 bool aabbIntersect(const helios::vec3 &min1, const helios::vec3 &max1, const helios::vec3 &min2, const helios::vec3 &max2);
1545
1556 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;
1557
1569 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);
1570
1578 void traverseBVHSIMD(const helios::vec3 *ray_origins, const helios::vec3 *ray_directions, int count, HitResult *results);
1579
1583 void traverseBVHSIMDImpl(const helios::vec3 *ray_origins, const helios::vec3 *ray_directions, int count, HitResult *results);
1584
1592 bool coneAABBIntersect(const Cone &cone, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max);
1593
1602 bool coneAABBIntersectFast(const Cone &cone, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max);
1603
1614 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);
1615
1623 int countRayIntersections(const helios::vec3 &origin, const helios::vec3 &direction, float max_distance = -1.0f);
1624
1634 bool findNearestRayIntersection(const helios::vec3 &origin, const helios::vec3 &direction, const std::set<uint> &candidate_UUIDs, float &nearest_distance, float max_distance = -1.0f);
1635
1644 std::vector<helios::vec3> sampleDirectionsInCone(const helios::vec3 &apex, const helios::vec3 &central_axis, float half_angle, int num_samples);
1645
1646 // -------- GAP DETECTION HELPER METHODS --------
1647
1657 std::vector<Gap> detectGapsInCone(const helios::vec3 &apex, const helios::vec3 &central_axis, float half_angle, float height, int num_samples);
1658
1659
1668 std::vector<uint> getCandidatePrimitivesInCone(const helios::vec3 &apex, const helios::vec3 &central_axis, float half_angle, float height);
1669
1679 std::vector<uint> getCandidatesUsingSpatialGrid(const Cone &cone, const helios::vec3 &apex, const helios::vec3 &central_axis, float half_angle, float height);
1680
1687 float calculateGapAngularSize(const std::vector<RaySample> &gap_samples, const helios::vec3 &central_axis);
1688
1694 void scoreGapsByFishEyeMetric(std::vector<Gap> &gaps, const helios::vec3 &central_axis);
1695
1702 helios::vec3 findOptimalGapDirection(const std::vector<Gap> &gaps, const helios::vec3 &central_axis);
1703
1704 // -------- VOXEL RAY PATH LENGTH HELPER METHODS --------
1705
1712 void initializeVoxelData(const helios::vec3 &grid_center, const helios::vec3 &grid_size, const helios::int3 &grid_divisions);
1713
1719 void calculateVoxelRayPathLengths_CPU(const std::vector<helios::vec3> &ray_origins, const std::vector<helios::vec3> &ray_directions);
1720
1726 bool calculateVoxelRayPathLengths_GPU(const std::vector<helios::vec3> &ray_origins, const std::vector<helios::vec3> &ray_directions);
1727
1733 bool validateVoxelIndices(const helios::int3 &ijk) const;
1734
1741 void calculateVoxelAABB(const helios::int3 &ijk, helios::vec3 &voxel_min, helios::vec3 &voxel_max) const;
1742
1749 std::vector<std::pair<helios::int3, float>> traverseVoxelGrid(const helios::vec3 &ray_origin, const helios::vec3 &ray_direction) const;
1750
1758 inline size_t flatIndex(int i, int j, int k) const {
1759 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);
1760 }
1761
1767 inline size_t flatIndex(const helios::int3 &ijk) const {
1768 return flatIndex(ijk.x, ijk.y, ijk.z);
1769 }
1770
1771#ifdef HELIOS_CUDA_AVAILABLE
1775 void allocateGPUMemory();
1776
1780 void freeGPUMemory();
1781
1785 void transferBVHToGPU();
1786
1808 void buildGPUGeometrySoA(std::vector<int> &primitive_types, std::vector<float> &primitive_vertices_xyz, std::vector<unsigned int> &vertex_offsets, std::vector<unsigned char> &mask_data, std::vector<unsigned int> &mask_offsets,
1809 std::vector<int> &mask_sizes, std::vector<int> &mask_IDs, std::vector<float> &uv_data, std::vector<int> &uv_IDs);
1810#endif
1811
1822 [[nodiscard]] bool shouldUseGPU(size_t ray_count) const;
1823
1827 void markBVHDirty();
1828
1835 void incrementalUpdateBVH(const std::set<uint> &added_geometry, const std::set<uint> &removed_geometry, const std::set<uint> &final_geometry);
1836
1841 void updatePrimitiveAABBCache(uint uuid);
1842
1847 void optimizedRebuildBVH(const std::set<uint> &final_geometry);
1848
1854 bool validateUUIDs(const std::vector<uint> &UUIDs) const;
1855
1864 bool rayPrimitiveIntersection(const helios::vec3 &origin, const helios::vec3 &direction, uint primitive_UUID, float &distance) const;
1865
1872 void castRaysCPU(const std::vector<RayQuery> &ray_queries, std::vector<HitResult> &results, RayTracingStats &stats);
1873
1874#ifdef HELIOS_CUDA_AVAILABLE
1881 void castRaysGPU(const std::vector<RayQuery> &ray_queries, std::vector<HitResult> &results, RayTracingStats &stats);
1882#endif
1883
1884 // -------- OPTIMIZED RAY TRACING PRIVATE METHODS --------
1885
1889 void convertBVHLayout(BVHOptimizationMode from_mode, BVHOptimizationMode to_mode);
1890 void ensureOptimizedBVH(); // Populate optimized BVH structures on demand
1891
1895 std::vector<HitResult> castRaysSoA(const std::vector<RayQuery> &ray_queries, RayTracingStats &stats);
1896
1900 HitResult castRaySoATraversal(const RayQuery &query, RayTracingStats &stats);
1901
1910 void castPacketSoATraversal(const helios::vec3 *origins, const helios::vec3 *directions, size_t begin, size_t end, float max_distance, float *out_distance, helios::vec3 *out_normal, uint *out_primitive_UUID, RayTracingStats &stats);
1911
1916 HitResult castRayBVHTraversal(const RayQuery &query);
1917
1921 inline bool aabbIntersectSoA(const helios::vec3 &ray_origin, const helios::vec3 &ray_direction, float max_distance, size_t node_index) const;
1922
1929 float aabbEntryDistanceSoA(const helios::vec3 &ray_origin, const helios::vec3 &ray_direction, size_t node_index) const;
1930
1935 bool rayAABBIntersect(const helios::vec3 &ray_origin, const helios::vec3 &ray_direction, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max) const;
1936
1940 HitResult intersectPrimitive(const RayQuery &query, uint primitive_id);
1941
1951 bool rayAABBIntersectPrimitive(const helios::vec3 &origin, const helios::vec3 &direction, const helios::vec3 &aabb_min, const helios::vec3 &aabb_max, float &distance);
1952
1953 // -------- RASTERIZATION-BASED COLLISION DETECTION --------
1954
1961 int calculateOptimalBinCount(float cone_half_angle, int geometry_count);
1962
1969 std::vector<uint> filterPrimitivesParallel(const Cone &cone, const std::vector<uint> &primitive_uuids);
1970
1977 void projectGeometryToBins(const Cone &cone, const std::vector<uint> &filtered_uuids, AngularBins &bins);
1978
1985 std::vector<Gap> findGapsInCoverageMap(const AngularBins &bins, const Cone &cone);
1986
1996 bool sphericalCoordsToBinIndices(float theta, float phi, const AngularBins &bins, int &theta_bin, int &phi_bin);
1997
2006 float cartesianToSphericalCone(const helios::vec3 &vector, const helios::vec3 &cone_axis, float &theta, float &phi);
2007
2014 void projectGeometryToBinsSerial(const Cone &cone, const std::vector<uint> &filtered_uuids, AngularBins &bins);
2015
2025 Gap floodFillGap(const AngularBins &bins, int start_theta, int start_phi, std::vector<std::vector<bool>> &visited, const Cone &cone);
2026
2035 float calculateBinSolidAngle(int theta_bin, int phi_bin, const AngularBins &bins, float cone_half_angle);
2036
2045 helios::vec3 binIndicesToCartesian(int theta_bin, int phi_bin, const AngularBins &bins, const Cone &cone);
2046
2055 void addUnoccupiedNeighbors(int theta, int phi, const AngularBins &bins, std::vector<std::vector<bool>> &visited, std::queue<std::pair<int, int>> &queue);
2056};
2057
2058#endif