Lagrange
Loading...
Searching...
No Matches
EmbreeRayCaster.h
1/*
2 * Copyright 2017 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <embree3/rtcore.h>
15#include <embree3/rtcore_geometry.h>
16#include <embree3/rtcore_ray.h>
17#include <lagrange/common.h>
18#include <lagrange/raycasting/ClosestPointResult.h>
19#include <lagrange/raycasting/EmbreeHelper.h>
20#include <lagrange/raycasting/RayCasterMesh.h>
21#include <lagrange/raycasting/embree_closest_point.h>
22#include <lagrange/utils/safe_cast.h>
23
24#include <cassert>
25#include <cmath>
26#include <exception>
27#include <functional>
28#include <iostream>
29#include <limits>
30#include <list>
31#include <memory>
32#include <sstream>
33#include <string>
34#include <vector>
35
36namespace lagrange {
37namespace raycasting {
38
45template <typename ScalarType>
47{
48public:
49 using Scalar = ScalarType;
50 using Transform = Eigen::Matrix<Scalar, 4, 4>;
51 using Point = Eigen::Matrix<Scalar, 3, 1>;
52 using Direction = Eigen::Matrix<Scalar, 3, 1>;
53 using Index = size_t;
54 using ClosestPoint = ClosestPointResult<Scalar>;
55 using TransformVector = std::vector<Transform>;
56
57 using Point4 = Eigen::Matrix<Scalar, 4, 3>;
58 using Direction4 = Eigen::Matrix<Scalar, 4, 3>;
59 using Index4 = Eigen::Matrix<size_t, 4, 1>;
60 using Scalar4 = Eigen::Matrix<Scalar, 4, 1>;
61 using Mask4 = Eigen::Matrix<std::int32_t, 4, 1>;
62
63 using FloatData = std::vector<float>;
64 using IntData = std::vector<unsigned>;
65
80 using FilterFunction = std::function<void(
81 const EmbreeRayCaster* obj,
82 const Index* mesh_index,
83 const Index* instance_index,
84 const RTCFilterFunctionNArguments* args)>;
85
86private:
88 enum { FILTER_INTERSECT = 0, FILTER_OCCLUDED = 1 };
89
90public:
93 RTCSceneFlags scene_flags = RTC_SCENE_FLAG_DYNAMIC,
94 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_LOW)
95 {
96// Embree strongly recommend to have the Flush to Zero and
97// Denormals are Zero mode of the MXCSR control and status
98// register enabled for each thread before calling the
99// rtcIntersect and rtcOccluded functions.
100#ifdef _MM_SET_FLUSH_ZERO_MODE
101 _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
102#endif
103#ifdef _MM_SET_DENORMALS_ZERO_MODE
104 _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
105#endif
106
107 m_scene_flags = scene_flags;
108 m_build_quality = build_quality;
109 m_device = rtcNewDevice(NULL);
110 ensure_no_errors_internal();
111 m_embree_world_scene = rtcNewScene(m_device);
112 ensure_no_errors_internal();
113 m_instance_index_ranges.push_back(safe_cast<Index>(0));
114
115 m_need_rebuild = true;
116 m_need_commit = false;
117 }
118
121 {
123 rtcReleaseDevice(m_device);
124 }
125
126 EmbreeRayCaster(const EmbreeRayCaster&) = delete;
127 void operator=(const EmbreeRayCaster&) = delete;
128
129public:
131 Index get_num_meshes() const { return safe_cast<Index>(m_meshes.size()); }
132
134 Index get_num_instances() const { return m_instance_index_ranges.back(); }
135
137 Index get_num_instances(Index mesh_index) const
138 {
139 la_debug_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
140 return safe_cast<Index>(
141 m_instance_index_ranges[mesh_index + 1] - m_instance_index_ranges[mesh_index]);
142 }
143
148 template <typename MeshType>
149 std::shared_ptr<MeshType> get_mesh(Index index) const
150 {
151 la_runtime_assert(index < safe_cast<Index>(m_meshes.size()));
152 la_runtime_assert(m_meshes[index] != nullptr);
153 return dynamic_cast<RaycasterMeshDerived<MeshType>&>(*m_meshes[index]).get_mesh_ptr();
154 }
155
164 Index get_mesh_for_instance(Index cumulative_instance_index) const
165 {
166 la_runtime_assert(cumulative_instance_index < get_num_instances());
167 return m_instance_to_user_mesh[cumulative_instance_index];
168 }
169
176 template <typename MeshType>
177 Index add_mesh(
178 std::shared_ptr<MeshType> mesh,
179 const Transform& trans = Transform::Identity(),
180 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
181 {
182 return add_raycasting_mesh(
183 std::make_unique<RaycasterMeshDerived<MeshType>>(mesh),
184 trans,
185 build_quality);
186 }
187
194 template <typename MeshType>
196 std::shared_ptr<MeshType> mesh,
197 const TransformVector& trans_vector,
198 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
199 {
200 m_meshes.push_back(std::move(std::make_unique<RaycasterMeshDerived<MeshType>>(mesh)));
201 m_transforms.insert(m_transforms.end(), trans_vector.begin(), trans_vector.end());
202 m_mesh_build_qualities.push_back(build_quality);
203 m_visibility.resize(m_visibility.size() + trans_vector.size(), true);
204 for (auto& f : m_filters) { // per-mesh, not per-instance
205 f.push_back(nullptr);
206 }
207 Index mesh_index = safe_cast<Index>(m_meshes.size() - 1);
208 la_runtime_assert(m_instance_index_ranges.size() > 0);
209 Index instance_index = m_instance_index_ranges.back();
210 la_runtime_assert(instance_index == safe_cast<Index>(m_instance_to_user_mesh.size()));
211 Index new_instance_size = instance_index + trans_vector.size();
212 m_instance_index_ranges.push_back(new_instance_size);
213 m_instance_to_user_mesh.resize(new_instance_size, mesh_index);
214 m_need_rebuild = true;
215 return mesh_index;
216 }
217
224 template <typename MeshType>
226 Index index,
227 std::shared_ptr<MeshType> mesh,
228 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
229 {
230 update_raycasting_mesh(
231 index,
232 std::make_unique<RaycasterMeshDerived<MeshType>>(mesh),
233 build_quality);
234 }
235
241 void update_mesh_vertices(Index index)
242 {
243 la_runtime_assert(index < safe_cast<Index>(m_meshes.size()));
244 if (m_need_rebuild) return;
245
246 la_runtime_assert(index < safe_cast<Index>(m_embree_mesh_scenes.size()));
247 auto geom = rtcGetGeometry(m_embree_mesh_scenes[index], 0);
248
249 // Update the vertex buffer in Embree
250 auto const& mesh = m_meshes[index];
252 safe_cast<Index>(mesh->get_num_vertices()) == m_mesh_vertex_counts[index]);
253
254 auto vbuf =
255 reinterpret_cast<float*>(rtcGetGeometryBufferData(geom, RTC_BUFFER_TYPE_VERTEX, 0));
256 la_runtime_assert(vbuf);
257 mesh->vertices_to_float(vbuf);
258 rtcUpdateGeometryBuffer(geom, RTC_BUFFER_TYPE_VERTEX, 0);
259
260 // Re-commit the mesh geometry and scene
261 rtcCommitGeometry(geom);
262 rtcCommitScene(m_embree_mesh_scenes[index]);
263
264 // Re-commit every instance of the mesh
265 for (Index instance_index = m_instance_index_ranges[index];
266 instance_index < m_instance_index_ranges[index + 1];
267 ++instance_index) {
268 Index rtc_inst_id = m_instance_index_ranges[index] + instance_index;
269 auto geom_inst =
270 rtcGetGeometry(m_embree_world_scene, static_cast<unsigned>(rtc_inst_id));
271 rtcCommitGeometry(geom_inst);
272 }
273
274 // Mark the world scene as needing a re-commit (will be called lazily)
275 m_need_commit = true;
276 }
277
279 Transform get_transform(Index mesh_index, Index instance_index) const
280 {
281 la_runtime_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
282 Index index = m_instance_index_ranges[mesh_index] + instance_index;
283 la_runtime_assert(index < m_instance_index_ranges[mesh_index + 1]);
284 la_runtime_assert(index < safe_cast<Index>(m_transforms.size()));
285 return m_transforms[index];
286 }
287
289 void update_transformation(Index mesh_index, Index instance_index, const Transform& trans)
290 {
291 la_runtime_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
292 Index index = m_instance_index_ranges[mesh_index] + instance_index;
293 la_runtime_assert(index < m_instance_index_ranges[mesh_index + 1]);
294 la_runtime_assert(index < safe_cast<Index>(m_transforms.size()));
295 m_transforms[index] = trans;
296 if (!m_need_rebuild) {
297 auto geom = rtcGetGeometry(m_embree_world_scene, static_cast<unsigned>(index));
298 Eigen::Matrix<float, 4, 4> T = trans.template cast<float>();
299 rtcSetGeometryTransform(geom, 0, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, T.eval().data());
300 rtcCommitGeometry(geom);
301 m_need_commit = true;
302 }
303 }
304
306 bool get_visibility(Index mesh_index, Index instance_index) const
307 {
308 la_runtime_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
309 Index index = m_instance_index_ranges[mesh_index] + instance_index;
310 la_runtime_assert(index < m_instance_index_ranges[mesh_index + 1]);
311 la_runtime_assert(index < safe_cast<Index>(m_visibility.size()));
312 return m_visibility[index];
313 }
314
316 void update_visibility(Index mesh_index, Index instance_index, bool visible)
317 {
318 la_runtime_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
319 Index index = m_instance_index_ranges[mesh_index] + instance_index;
320 la_runtime_assert(index < m_instance_index_ranges[mesh_index + 1]);
321 la_runtime_assert(index < safe_cast<Index>(m_visibility.size()));
322 m_visibility[index] = visible;
323 if (!m_need_rebuild &&
324 rtcGetDeviceProperty(m_device, RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED)) {
325 // ^^^ else, visibility will be checked by the already bound filter
326
327 auto geom = rtcGetGeometry(m_embree_world_scene, static_cast<unsigned>(index));
328 rtcSetGeometryMask(geom, visible ? 0xFFFFFFFF : 0x00000000);
329 rtcCommitGeometry(geom);
330 m_need_commit = true;
331 }
332 }
333
350 void set_intersection_filter(Index mesh_index, FilterFunction filter)
351 {
352 la_runtime_assert(mesh_index < m_filters[FILTER_INTERSECT].size());
353 m_filters[FILTER_INTERSECT][mesh_index] = filter;
354 m_need_rebuild = true;
355 }
356
364 {
365 la_runtime_assert(mesh_index < m_filters[FILTER_INTERSECT].size());
366 return m_filters[FILTER_INTERSECT][mesh_index];
367 }
368
385 void set_occlusion_filter(Index mesh_index, FilterFunction filter)
386 {
387 la_runtime_assert(mesh_index < m_filters[FILTER_OCCLUDED].size());
388 m_filters[FILTER_OCCLUDED][mesh_index] = filter;
389 m_need_rebuild = true;
390 }
391
398 FilterFunction get_occlusion_filter(Index mesh_index) const
399 {
400 la_runtime_assert(mesh_index < m_filters[FILTER_OCCLUDED].size());
401 return m_filters[FILTER_OCCLUDED][mesh_index];
402 }
403
412 {
413 if (!m_need_commit) return;
414
415 rtcCommitScene(m_embree_world_scene);
416 m_need_commit = false;
417 }
418
420 void ensure_no_errors() const { EmbreeHelper::ensure_no_errors(m_device); }
421
426 uint32_t cast4(
427 uint32_t batch_size,
428 const Point4& origin,
429 const Direction4& direction,
430 const Mask4& mask,
431 Index4& mesh_index,
432 Index4& instance_index,
433 Index4& facet_index,
434 Scalar4& ray_depth,
435 Point4& barycentric_coord,
436 Point4& normal,
437 const Scalar4& tmin = Scalar4::Zero(),
438 const Scalar4& tmax = Scalar4::Constant(std::numeric_limits<Scalar>::infinity()))
439 {
440 la_debug_assert(batch_size <= 4);
441
443
444 RTCRayHit4 embree_raypacket;
445 for (int i = 0; i < static_cast<int>(batch_size); ++i) {
446 // Set ray origins
447 embree_raypacket.ray.org_x[i] = static_cast<float>(origin(i, 0));
448 embree_raypacket.ray.org_y[i] = static_cast<float>(origin(i, 1));
449 embree_raypacket.ray.org_z[i] = static_cast<float>(origin(i, 2));
450
451 // Set ray directions
452 embree_raypacket.ray.dir_x[i] = static_cast<float>(direction(i, 0));
453 embree_raypacket.ray.dir_y[i] = static_cast<float>(direction(i, 1));
454 embree_raypacket.ray.dir_z[i] = static_cast<float>(direction(i, 2));
455
456 // Misc
457 embree_raypacket.ray.tnear[i] = static_cast<float>(tmin[i]);
458 embree_raypacket.ray.tfar[i] = std::isinf(tmax[i]) ? std::numeric_limits<float>::max()
459 : static_cast<float>(tmax[i]);
460 embree_raypacket.ray.mask[i] = 0xFFFFFFFF;
461 embree_raypacket.ray.id[i] = static_cast<unsigned>(i);
462 embree_raypacket.ray.flags[i] = 0;
463
464 // Required initialization of the hit substructure
465 embree_raypacket.hit.geomID[i] = RTC_INVALID_GEOMETRY_ID;
466 embree_raypacket.hit.primID[i] = RTC_INVALID_GEOMETRY_ID;
467 embree_raypacket.hit.instID[0][i] = RTC_INVALID_GEOMETRY_ID;
468 }
469
470 // Modify the mask to make 100% sure extra rays in the packet will be ignored
471 auto packet_mask = mask;
472 for (int i = static_cast<int>(batch_size); i < 4; ++i) packet_mask[i] = 0;
473
474 ensure_no_errors_internal();
475 {
476 RTCIntersectContext context;
477 rtcInitIntersectContext(&context);
478 rtcIntersect4(packet_mask.data(), m_embree_world_scene, &context, &embree_raypacket);
479 }
480 ensure_no_errors_internal();
481
482 uint32_t is_hits = 0;
483 for (int i = 0; i < static_cast<int>(batch_size); ++i) {
484 if (embree_raypacket.hit.geomID[i] != RTC_INVALID_GEOMETRY_ID) {
485 Index rtc_inst_id = embree_raypacket.hit.instID[0][i];
486 Index rtc_mesh_id = (rtc_inst_id == RTC_INVALID_GEOMETRY_ID)
487 ? embree_raypacket.hit.geomID[i]
488 : rtc_inst_id;
489 assert(rtc_mesh_id < m_instance_to_user_mesh.size());
490 assert(m_visibility[rtc_mesh_id]);
491 mesh_index[i] = m_instance_to_user_mesh[rtc_mesh_id];
492 assert(mesh_index[i] + 1 < m_instance_index_ranges.size());
493 assert(mesh_index[i] < safe_cast<Index>(m_meshes.size()));
494 instance_index[i] = rtc_mesh_id - m_instance_index_ranges[mesh_index[i]];
495 facet_index[i] = embree_raypacket.hit.primID[i];
496 ray_depth[i] = embree_raypacket.ray.tfar[i];
497 barycentric_coord(i, 0) =
498 1.0f - embree_raypacket.hit.u[i] - embree_raypacket.hit.v[i];
499 barycentric_coord(i, 1) = embree_raypacket.hit.u[i];
500 barycentric_coord(i, 2) = embree_raypacket.hit.v[i];
501 normal(i, 0) = embree_raypacket.hit.Ng_x[i];
502 normal(i, 1) = embree_raypacket.hit.Ng_y[i];
503 normal(i, 2) = embree_raypacket.hit.Ng_z[i];
504 is_hits = is_hits | (1 << i);
505 }
506 }
507
508 return is_hits;
509 }
510
515 uint32_t cast4(
516 uint32_t batch_size,
517 const Point4& origin,
518 const Direction4& direction,
519 const Mask4& mask,
520 Index4& mesh_index,
521 Index4& facet_index,
522 Scalar4& ray_depth,
523 Point4& barycentric_coord,
524 const Scalar4& tmin = Scalar4::Zero(),
525 const Scalar4& tmax = Scalar4::Constant(std::numeric_limits<Scalar>::infinity()))
526 {
527 Index4 instance_index;
528 Point4 normal;
529 return cast4(
530 batch_size,
531 origin,
532 direction,
533 mask,
534 mesh_index,
535 instance_index,
536 facet_index,
537 ray_depth,
538 barycentric_coord,
539 normal,
540 tmin,
541 tmax);
542 }
543
547 uint32_t cast4(
548 uint32_t batch_size,
549 const Point4& origin,
550 const Direction4& direction,
551 const Mask4& mask,
552 const Scalar4& tmin = Scalar4::Zero(),
553 const Scalar4& tmax = Scalar4::Constant(std::numeric_limits<Scalar>::infinity()))
554 {
555 la_debug_assert(batch_size <= 4);
556
558
559 RTCRay4 embree_raypacket;
560 for (int i = 0; i < static_cast<int>(batch_size); ++i) {
561 // Set ray origins
562 embree_raypacket.org_x[i] = static_cast<float>(origin(i, 0));
563 embree_raypacket.org_y[i] = static_cast<float>(origin(i, 1));
564 embree_raypacket.org_z[i] = static_cast<float>(origin(i, 2));
565
566 // Set ray directions
567 embree_raypacket.dir_x[i] = static_cast<float>(direction(i, 0));
568 embree_raypacket.dir_y[i] = static_cast<float>(direction(i, 1));
569 embree_raypacket.dir_z[i] = static_cast<float>(direction(i, 2));
570
571 // Misc
572 embree_raypacket.tnear[i] = static_cast<float>(tmin[i]);
573 embree_raypacket.tfar[i] = std::isinf(tmax[i]) ? std::numeric_limits<float>::max()
574 : static_cast<float>(tmax[i]);
575 embree_raypacket.mask[i] = 0xFFFFFFFF;
576 embree_raypacket.id[i] = static_cast<unsigned>(i);
577 embree_raypacket.flags[i] = 0;
578 }
579
580 // Modify the mask to make 100% sure extra rays in the packet will be ignored
581 auto packet_mask = mask;
582 for (int i = static_cast<int>(batch_size); i < 4; ++i) packet_mask[i] = 0;
583
584 ensure_no_errors_internal();
585 {
586 RTCIntersectContext context;
587 rtcInitIntersectContext(&context);
588 rtcOccluded4(packet_mask.data(), m_embree_world_scene, &context, &embree_raypacket);
589 }
590 ensure_no_errors_internal();
591
592 // If hit, the tfar field will be set to -inf.
593 uint32_t is_hits = 0;
594 for (uint32_t i = 0; i < batch_size; ++i)
595 if (!std::isfinite(embree_raypacket.tfar[i])) is_hits = is_hits | (1 << i);
596
597 return is_hits;
598 }
599
604 bool cast(
605 const Point& origin,
606 const Direction& direction,
607 Index& mesh_index,
608 Index& instance_index,
609 Index& facet_index,
610 Scalar& ray_depth,
611 Point& barycentric_coord,
612 Point& normal,
613 Scalar tmin = 0,
614 Scalar tmax = std::numeric_limits<Scalar>::infinity())
615 {
616 // Overloaded when specializing tnear and tfar
617
619
620 RTCRayHit embree_rayhit;
621 embree_rayhit.ray.org_x = static_cast<float>(origin.x());
622 embree_rayhit.ray.org_y = static_cast<float>(origin.y());
623 embree_rayhit.ray.org_z = static_cast<float>(origin.z());
624 embree_rayhit.ray.dir_x = static_cast<float>(direction.x());
625 embree_rayhit.ray.dir_y = static_cast<float>(direction.y());
626 embree_rayhit.ray.dir_z = static_cast<float>(direction.z());
627 embree_rayhit.ray.tnear = static_cast<float>(tmin);
628 embree_rayhit.ray.tfar =
629 std::isinf(tmax) ? std::numeric_limits<float>::max() : static_cast<float>(tmax);
630 embree_rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
631 embree_rayhit.hit.primID = RTC_INVALID_GEOMETRY_ID;
632 embree_rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;
633 embree_rayhit.ray.mask = 0xFFFFFFFF;
634 embree_rayhit.ray.id = 0;
635 embree_rayhit.ray.flags = 0;
636 ensure_no_errors_internal();
637 {
638 RTCIntersectContext context;
639 rtcInitIntersectContext(&context);
640 rtcIntersect1(m_embree_world_scene, &context, &embree_rayhit);
641 }
642 ensure_no_errors_internal();
643
644 if (embree_rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) {
645 Index rtc_inst_id = embree_rayhit.hit.instID[0];
646 Index rtc_mesh_id =
647 (rtc_inst_id == RTC_INVALID_GEOMETRY_ID) ? embree_rayhit.hit.geomID : rtc_inst_id;
648 assert(rtc_mesh_id < m_instance_to_user_mesh.size());
649 assert(m_visibility[rtc_mesh_id]);
650 mesh_index = m_instance_to_user_mesh[rtc_mesh_id];
651 assert(mesh_index + 1 < m_instance_index_ranges.size());
652 assert(mesh_index < safe_cast<Index>(m_meshes.size()));
653 instance_index = rtc_mesh_id - m_instance_index_ranges[mesh_index];
654 facet_index = embree_rayhit.hit.primID;
655 ray_depth = embree_rayhit.ray.tfar;
656 barycentric_coord[0] = 1.0f - embree_rayhit.hit.u - embree_rayhit.hit.v;
657 barycentric_coord[1] = embree_rayhit.hit.u;
658 barycentric_coord[2] = embree_rayhit.hit.v;
659 normal[0] = embree_rayhit.hit.Ng_x;
660 normal[1] = embree_rayhit.hit.Ng_y;
661 normal[2] = embree_rayhit.hit.Ng_z;
662 return true;
663 } else {
664 // Ray missed.
665 mesh_index = invalid<Index>();
666 instance_index = invalid<Index>();
667 facet_index = invalid<Index>();
668 return false;
669 }
670 }
671
676 bool cast(
677 const Point& origin,
678 const Direction& direction,
679 Index& mesh_index,
680 Index& facet_index,
681 Scalar& ray_depth,
682 Point& barycentric_coord,
683 Scalar tmin = 0,
684 Scalar tmax = std::numeric_limits<Scalar>::infinity())
685 {
686 Index instance_index;
687 Point normal;
688 return cast(
689 origin,
690 direction,
691 mesh_index,
692 instance_index,
693 facet_index,
694 ray_depth,
695 barycentric_coord,
696 normal,
697 tmin,
698 tmax);
699 }
700
702 bool cast(
703 const Point& origin,
704 const Direction& direction,
705 Scalar tmin = 0,
706 Scalar tmax = std::numeric_limits<Scalar>::infinity())
707 {
709
710 RTCRay embree_ray;
711 embree_ray.org_x = static_cast<float>(origin.x());
712 embree_ray.org_y = static_cast<float>(origin.y());
713 embree_ray.org_z = static_cast<float>(origin.z());
714 embree_ray.dir_x = static_cast<float>(direction.x());
715 embree_ray.dir_y = static_cast<float>(direction.y());
716 embree_ray.dir_z = static_cast<float>(direction.z());
717 embree_ray.tnear = static_cast<float>(tmin);
718 embree_ray.tfar =
719 std::isinf(tmax) ? std::numeric_limits<float>::max() : static_cast<float>(tmax);
720 embree_ray.mask = 0xFFFFFFFF;
721 embree_ray.id = 0;
722 embree_ray.flags = 0;
723
724 ensure_no_errors_internal();
725 {
726 RTCIntersectContext context;
727 rtcInitIntersectContext(&context);
728 rtcOccluded1(m_embree_world_scene, &context, &embree_ray);
729 }
730 ensure_no_errors_internal();
731
732 // If hit, the tfar field will be set to -inf.
733 return !std::isfinite(embree_ray.tfar);
734 }
735
737 ClosestPoint query_closest_point(const Point& p) const;
738
741 std::unique_ptr<RaycasterMesh> mesh,
742 const Transform& trans = Transform::Identity(),
743 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
744 {
745 m_meshes.push_back(std::move(mesh));
746 m_transforms.push_back(trans);
747 m_mesh_build_qualities.push_back(build_quality);
748 m_visibility.push_back(true);
749 for (auto& f : m_filters) { // per-mesh, not per-instance
750 f.push_back(nullptr);
751 }
752 Index mesh_index = safe_cast<Index>(m_meshes.size() - 1);
753 la_runtime_assert(m_instance_index_ranges.size() > 0);
754 Index instance_index = m_instance_index_ranges.back();
755 la_runtime_assert(instance_index == safe_cast<Index>(m_instance_to_user_mesh.size()));
756 m_instance_index_ranges.push_back(instance_index + 1);
757 m_instance_to_user_mesh.resize(instance_index + 1, mesh_index);
758 m_need_rebuild = true;
759 return mesh_index;
760 }
761
762 void update_raycasting_mesh(
763 Index index,
764 std::unique_ptr<RaycasterMesh> mesh,
765 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
766 {
767 la_runtime_assert(mesh->get_dim() == 3);
768 la_runtime_assert(mesh->get_vertex_per_facet() == 3);
769 la_runtime_assert(index < safe_cast<Index>(m_meshes.size()));
770 m_meshes[index] = std::move(mesh);
771 m_mesh_build_qualities[index] = build_quality;
772 m_need_rebuild = true; // TODO: Make this more fine-grained so only the affected part of
773 // the Embree scene is updated
774 }
775
776protected:
779 {
780 for (auto& s : m_embree_mesh_scenes) {
781 rtcReleaseScene(s);
782 }
783 rtcReleaseScene(m_embree_world_scene);
784 }
785
787 virtual RTCSceneFlags get_scene_flags() const { return m_scene_flags; }
788
790 virtual RTCBuildQuality get_scene_build_quality() const { return m_build_quality; }
791
794 {
795 if (m_need_rebuild)
796 generate_scene(); // full rebuild
797 else if (m_need_commit)
798 commit_scene_changes(); // just call rtcCommitScene()
799 }
800
808 {
809 if (!m_need_rebuild) return;
810
811 // Scene needs to be updated
813 m_embree_world_scene = rtcNewScene(m_device);
814 auto scene_flags = get_scene_flags(); // FIXME: or just m_scene_flags?
815 auto scene_build_quality = get_scene_build_quality(); // FIXME: or just m_build_quality?
816 rtcSetSceneFlags(
817 m_embree_world_scene,
818 scene_flags); // TODO: maybe also set RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION
819 rtcSetSceneBuildQuality(m_embree_world_scene, scene_build_quality);
820 m_float_data.clear();
821 m_int_data.clear();
822 const auto num_meshes = m_meshes.size();
823 la_runtime_assert(num_meshes + 1 == m_instance_index_ranges.size());
824 m_embree_mesh_scenes.resize(num_meshes);
825 m_mesh_vertex_counts.resize(num_meshes);
826 ensure_no_errors_internal();
827
828 bool is_mask_supported =
829 rtcGetDeviceProperty(m_device, RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED);
830 for (size_t i = 0; i < num_meshes; i++) {
831 // Initialize RTC meshes
832 const auto& mesh = m_meshes[i];
833 // const auto& vertices = mesh->get_vertices();
834 // const auto& facets = mesh->get_facets();
835 const Index num_vertices = m_mesh_vertex_counts[i] =
836 safe_cast<Index>(mesh->get_num_vertices());
837 const Index num_facets = safe_cast<Index>(mesh->get_num_facets());
838
839 auto& embree_mesh_scene = m_embree_mesh_scenes[i];
840 embree_mesh_scene = rtcNewScene(m_device);
841
842 rtcSetSceneFlags(embree_mesh_scene, scene_flags);
843 rtcSetSceneBuildQuality(embree_mesh_scene, scene_build_quality);
844 ensure_no_errors_internal();
845
846 RTCGeometry geom = rtcNewGeometry(
847 m_device,
848 RTC_GEOMETRY_TYPE_TRIANGLE); // EMBREE_FIXME: check if geometry gets properly
849 // committed
850 rtcSetGeometryBuildQuality(geom, m_mesh_build_qualities[i]);
851 // rtcSetGeometryTimeStepCount(geom, 1);
852
853 const float* vertex_data = extract_float_data(*mesh);
854 const unsigned* facet_data = extract_int_data(*mesh);
855
856 rtcSetSharedGeometryBuffer(
857 geom,
858 RTC_BUFFER_TYPE_VERTEX,
859 0,
860 RTC_FORMAT_FLOAT3,
861 vertex_data,
862 0,
863 sizeof(float) * 3,
864 num_vertices);
865 rtcSetSharedGeometryBuffer(
866 geom,
867 RTC_BUFFER_TYPE_INDEX,
868 0,
869 RTC_FORMAT_UINT3,
870 facet_data,
871 0,
872 sizeof(int) * 3,
873 num_facets);
874
875 set_intersection_filter(geom, m_filters[FILTER_INTERSECT][i], is_mask_supported);
876 set_occlusion_filter(geom, m_filters[FILTER_OCCLUDED][i], is_mask_supported);
877
878 rtcCommitGeometry(geom);
879 rtcAttachGeometry(embree_mesh_scene, geom);
880 rtcReleaseGeometry(geom);
881 ensure_no_errors_internal();
882
883 // Initialize RTC instances
884 for (Index instance_index = m_instance_index_ranges[i];
885 instance_index < m_instance_index_ranges[i + 1];
886 ++instance_index) {
887 const auto& trans = m_transforms[instance_index];
888
889 RTCGeometry geom_inst = rtcNewGeometry(
890 m_device,
891 RTC_GEOMETRY_TYPE_INSTANCE); // EMBREE_FIXME: check if geometry gets properly
892 // committed
893 rtcSetGeometryInstancedScene(geom_inst, embree_mesh_scene);
894 rtcSetGeometryTimeStepCount(geom_inst, 1);
895
896 Eigen::Matrix<float, 4, 4> T = trans.template cast<float>();
897 rtcSetGeometryTransform(geom_inst, 0, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, T.data());
898 ensure_no_errors_internal();
899
900 if (is_mask_supported) {
901 rtcSetGeometryMask(
902 geom_inst,
903 m_visibility[instance_index] ? 0xFFFFFFFF : 0x00000000);
904 }
905 ensure_no_errors_internal();
906
907 rtcCommitGeometry(geom_inst);
908 unsigned rtc_instance_id = rtcAttachGeometry(m_embree_world_scene, geom_inst);
909 rtcReleaseGeometry(geom_inst);
910 la_runtime_assert(safe_cast<Index>(rtc_instance_id) == instance_index);
911 ensure_no_errors_internal();
912 }
913
914 rtcCommitScene(embree_mesh_scene);
915 ensure_no_errors_internal();
916 }
917 rtcCommitScene(m_embree_world_scene);
918 ensure_no_errors_internal();
919
920 m_need_rebuild = m_need_commit = false;
921 }
922
924 const float* extract_float_data(const RaycasterMesh& mesh)
925 {
926 auto float_data = mesh.vertices_to_float();
927 // Due to Embree bug, we have to have at least one more entry
928 // after the bound. Sigh...
929 // See https://github.com/embree/embree/issues/124
930 float_data.push_back(0.0);
931 m_float_data.emplace_back(std::move(float_data));
932 return m_float_data.back().data();
933 }
934
936 const unsigned* extract_int_data(const RaycasterMesh& mesh)
937 {
938 auto int_data = mesh.indices_to_int();
939 // Due to Embree bug, we have to have at least one more entry
940 // after the bound. Sigh...
941 // See https://github.com/embree/embree/issues/124
942 int_data.push_back(0);
943 m_int_data.emplace_back(std::move(int_data));
944 return m_int_data.back().data();
945 }
946
947private:
952 void set_intersection_filter(RTCGeometry geom, FilterFunction filter, bool is_mask_supported)
953 {
954 if (is_mask_supported) {
955 if (filter) {
956 rtcSetGeometryUserData(geom, this);
957 rtcSetGeometryIntersectFilterFunction(geom, &wrap_filter<FILTER_INTERSECT>);
958 } else {
959 rtcSetGeometryIntersectFilterFunction(geom, nullptr);
960 }
961 } else {
962 rtcSetGeometryUserData(geom, this);
963 rtcSetGeometryIntersectFilterFunction(geom, &wrap_filter_and_mask<FILTER_INTERSECT>);
964 }
965 }
966
971 void set_occlusion_filter(RTCGeometry geom, FilterFunction filter, bool is_mask_supported)
972 {
973 if (is_mask_supported) {
974 if (filter) {
975 rtcSetGeometryUserData(geom, this);
976 rtcSetGeometryOccludedFilterFunction(geom, &wrap_filter<FILTER_OCCLUDED>);
977 } else {
978 rtcSetGeometryOccludedFilterFunction(geom, nullptr);
979 }
980 } else {
981 rtcSetGeometryUserData(geom, this);
982 rtcSetGeometryOccludedFilterFunction(geom, &wrap_filter_and_mask<FILTER_OCCLUDED>);
983 }
984 }
985
990 template <int IntersectionOrOcclusion> // 0: intersection, 1: occlusion
991 static void wrap_filter(const RTCFilterFunctionNArguments* args)
992 {
993 // Embree never actually calls a filter callback with different geometry or instance IDs
994 // So we can assume they are the same for all the hits in this batch. Also, every single
995 // mesh in this class is instanced (never used raw), so we can ignore geomID.
996 const auto* obj = reinterpret_cast<EmbreeRayCaster*>(args->geometryUserPtr);
997 auto rtc_inst_id = RTCHitN_instID(args->hit, args->N, 0, 0);
998 assert(rtc_inst_id < obj->m_instance_to_user_mesh.size());
999
1000 auto filter = obj->m_filters[IntersectionOrOcclusion][rtc_inst_id];
1001 if (!filter) {
1002 return;
1003 }
1004
1005 Index mesh_index = obj->m_instance_to_user_mesh[rtc_inst_id];
1006 assert(mesh_index + 1 < obj->m_instance_index_ranges.size());
1007 assert(mesh_index < safe_cast<Index>(obj->m_meshes.size()));
1008 Index instance_index = rtc_inst_id - obj->m_instance_index_ranges[mesh_index];
1009
1010 // In case Embree's implementation changes in the future, the callback should be written
1011 // generally, without assuming the single geometry/instance condition above.
1012 Index4 mesh_index4;
1013 mesh_index4.fill(mesh_index);
1014 Index4 instance_index4;
1015 instance_index4.fill(instance_index);
1016
1017 // Call the wrapped filter with the indices specific to this object
1018 filter(obj, mesh_index4.data(), instance_index4.data(), args);
1019 }
1020
1026 template <int IntersectionOrOcclusion> // 0: intersection, 1: occlusion
1027 static void wrap_filter_and_mask(const RTCFilterFunctionNArguments* args)
1028 {
1029 // Embree never actually calls a filter callback with different geometry or instance IDs
1030 // So we can assume they are the same for all the hits in this batch. Also, every single
1031 // mesh in this class is instanced (never used raw), so we can ignore geomID.
1032 const auto* obj = reinterpret_cast<EmbreeRayCaster*>(args->geometryUserPtr);
1033 auto rtc_inst_id = RTCHitN_instID(args->hit, args->N, 0, 0);
1034 if (!obj->m_visibility[rtc_inst_id]) {
1035 // Object is invisible. Make the hits of all the rays with this object invalid.
1036 std::fill(args->valid, args->valid + args->N, 0);
1037 return;
1038 }
1039
1040 // Delegate to the regular filtering after having checked visibility
1041 wrap_filter<IntersectionOrOcclusion>(args);
1042 }
1043
1044protected:
1045 RTCSceneFlags m_scene_flags;
1046 RTCBuildQuality m_build_quality;
1047 RTCDevice m_device;
1048 RTCScene m_embree_world_scene;
1049 bool m_need_rebuild; // full rebuild of the scene?
1050 bool m_need_commit; // just call rtcCommitScene() on the scene?
1051
1052 // Data reservoirs for holding temporary/casted/per-geometry data.
1053 // Length = Number of polygonal meshes
1054 std::vector<FloatData> m_float_data;
1055 std::vector<IntData> m_int_data;
1056 std::vector<std::unique_ptr<RaycasterMesh>> m_meshes;
1057 std::vector<RTCBuildQuality> m_mesh_build_qualities;
1058 std::vector<RTCScene> m_embree_mesh_scenes;
1059 std::vector<Index> m_mesh_vertex_counts; // for bounds-checking of buffer updates
1060 std::vector<FilterFunction> m_filters[2]; // 0: intersection filters, 1: occlusion filters
1061
1062 // Ranges of instance indices corresponding to a specific
1063 // Mesh. For example, in a scenario with 3 meshes each of
1064 // which has 1, 2, 5 instances, this array would be
1065 // [0, 1, 3, 8].
1066 // Length = Number of user meshes + 1
1067 std::vector<Index> m_instance_index_ranges;
1068
1069 // Mapping from (RTC-)instanced mesh to user mesh. For
1070 // example, in a scenario with 3 meshes each of
1071 // which has 1, 2, 5 instances, this array would be
1072 // [0, 1, 1, 2, 2, 2, 2, 2]
1073 // Length = Number of instanced meshes
1074 // Note: This array is only used internally. We shouldn't
1075 // allow the users to access anything with the indices
1076 // used in those RTC functions.
1077 std::vector<Index> m_instance_to_user_mesh;
1078
1079 // Data reservoirs for holding instanced mesh data
1080 // Length = Number of (world-space) instanced meshes
1081 std::vector<Transform> m_transforms;
1082 std::vector<bool> m_visibility;
1083
1084 // error checking function used internally
1085 void ensure_no_errors_internal() const
1086 {
1087#ifdef LAGRANGE_EMBREE_DEBUG
1088 EmbreeHelper::ensure_no_errors(m_device);
1089#endif
1090 }
1091};
1092
1094// IMPLEMENTATION
1096
1097template <typename Scalar>
1098auto EmbreeRayCaster<Scalar>::query_closest_point(const Point& p) const -> ClosestPoint
1099{
1100 RTCPointQuery query;
1101 query.x = (float)(p.x());
1102 query.y = (float)(p.y());
1103 query.z = (float)(p.z());
1104 query.radius = std::numeric_limits<float>::max();
1105 query.time = 0.f;
1106 ensure_no_errors_internal();
1107
1108 ClosestPoint result;
1109
1110 // Callback to retrieve triangle corner positions
1111 result.populate_triangle =
1112 [&](unsigned mesh_index, unsigned facet_index, Point& v0, Point& v1, Point& v2) {
1113 // TODO: There's no way to call this->get_mesh<> since we need to template the function
1114 // by the (derived) type, which we don't know here... This means our only choice is so
1115 // use the float data instead of the (maybe) double point coordinate if available. Note
1116 // that we could also call rtcSetGeometryPointQueryFunction() when we register our mesh,
1117 // since we know the derived type at this point.
1118 const unsigned* face = m_int_data[mesh_index].data() + 3 * facet_index;
1119 const float* vertices = m_float_data[mesh_index].data();
1120 v0 = Point(vertices[3 * face[0]], vertices[3 * face[0] + 1], vertices[3 * face[0] + 2]);
1121 v1 = Point(vertices[3 * face[1]], vertices[3 * face[1] + 1], vertices[3 * face[1] + 2]);
1122 v2 = Point(vertices[3 * face[2]], vertices[3 * face[2] + 1], vertices[3 * face[2] + 2]);
1123 };
1124
1125 {
1126 RTCPointQueryContext context;
1127 rtcInitPointQueryContext(&context);
1128 rtcPointQuery(
1129 m_embree_world_scene,
1130 &query,
1131 &context,
1132 &embree_closest_point<Scalar>,
1133 reinterpret_cast<void*>(&result));
1134 assert(
1135 result.mesh_index != RTC_INVALID_GEOMETRY_ID ||
1136 result.facet_index != RTC_INVALID_GEOMETRY_ID);
1137 assert(
1138 result.mesh_index != invalid<unsigned>() || result.facet_index != invalid<unsigned>());
1139 }
1140 ensure_no_errors_internal();
1141
1142 return result;
1143}
1144
1145} // namespace raycasting
1146} // namespace lagrange
A wrapper for Embree's raycasting API to compute ray intersections with (instances of) meshes.
Definition EmbreeRayCaster.h:47
Index add_meshes(std::shared_ptr< MeshType > mesh, const TransformVector &trans_vector, RTCBuildQuality build_quality=RTC_BUILD_QUALITY_MEDIUM)
Add multiple instances of a single mesh to the scene, with given transformations.
Definition EmbreeRayCaster.h:195
Index add_raycasting_mesh(std::unique_ptr< RaycasterMesh > mesh, const Transform &trans=Transform::Identity(), RTCBuildQuality build_quality=RTC_BUILD_QUALITY_MEDIUM)
Add raycasting utilities.
Definition EmbreeRayCaster.h:740
Index add_mesh(std::shared_ptr< MeshType > mesh, const Transform &trans=Transform::Identity(), RTCBuildQuality build_quality=RTC_BUILD_QUALITY_MEDIUM)
Add an instance of a mesh to the scene, with a given transformation.
Definition EmbreeRayCaster.h:177
uint32_t cast4(uint32_t batch_size, const Point4 &origin, const Direction4 &direction, const Mask4 &mask, const Scalar4 &tmin=Scalar4::Zero(), const Scalar4 &tmax=Scalar4::Constant(std::numeric_limits< Scalar >::infinity()))
Cast a packet of up to 4 rays through the scene and check whether they hit anything or not.
Definition EmbreeRayCaster.h:547
FilterFunction get_intersection_filter(Index mesh_index) const
Get the intersection filter function currently bound to a given mesh.
Definition EmbreeRayCaster.h:363
Index get_num_instances(Index mesh_index) const
Get the number of instances of a particular mesh.
Definition EmbreeRayCaster.h:137
void update_mesh(Index index, std::shared_ptr< MeshType > mesh, RTCBuildQuality build_quality=RTC_BUILD_QUALITY_MEDIUM)
Update a particular mesh with a new mesh object.
Definition EmbreeRayCaster.h:225
void set_intersection_filter(Index mesh_index, FilterFunction filter)
Set an intersection filter that is called for every hit on (every instance of) a mesh during an inter...
Definition EmbreeRayCaster.h:350
bool cast(const Point &origin, const Direction &direction, Index &mesh_index, Index &instance_index, Index &facet_index, Scalar &ray_depth, Point &barycentric_coord, Point &normal, Scalar tmin=0, Scalar tmax=std::numeric_limits< Scalar >::infinity())
Cast a single ray through the scene, returning full data of the closest intersection including the no...
Definition EmbreeRayCaster.h:604
virtual ~EmbreeRayCaster()
Destructor.
Definition EmbreeRayCaster.h:120
void generate_scene()
Build the whole Embree scene from the specified meshes, instances, etc.
Definition EmbreeRayCaster.h:807
bool get_visibility(Index mesh_index, Index instance_index) const
Get the visibility flag of a given mesh instance.
Definition EmbreeRayCaster.h:306
virtual RTCSceneFlags get_scene_flags() const
Get the Embree scene flags.
Definition EmbreeRayCaster.h:787
const float * extract_float_data(const RaycasterMesh &mesh)
Get the vertex data of a mesh as an array of floats.
Definition EmbreeRayCaster.h:924
void update_transformation(Index mesh_index, Index instance_index, const Transform &trans)
Update the transform applied to a given mesh instance.
Definition EmbreeRayCaster.h:289
void update_mesh_vertices(Index index)
Update the object to reflect external changes to the vertices of a particular mesh which is already i...
Definition EmbreeRayCaster.h:241
uint32_t cast4(uint32_t batch_size, const Point4 &origin, const Direction4 &direction, const Mask4 &mask, Index4 &mesh_index, Index4 &instance_index, Index4 &facet_index, Scalar4 &ray_depth, Point4 &barycentric_coord, Point4 &normal, const Scalar4 &tmin=Scalar4::Zero(), const Scalar4 &tmax=Scalar4::Constant(std::numeric_limits< Scalar >::infinity()))
Cast a packet of up to 4 rays through the scene, returning full data of the closest intersections inc...
Definition EmbreeRayCaster.h:426
bool cast(const Point &origin, const Direction &direction, Index &mesh_index, Index &facet_index, Scalar &ray_depth, Point &barycentric_coord, Scalar tmin=0, Scalar tmax=std::numeric_limits< Scalar >::infinity())
Cast a single ray through the scene, returning data of the closest intersection excluding the normal ...
Definition EmbreeRayCaster.h:676
uint32_t cast4(uint32_t batch_size, const Point4 &origin, const Direction4 &direction, const Mask4 &mask, Index4 &mesh_index, Index4 &facet_index, Scalar4 &ray_depth, Point4 &barycentric_coord, const Scalar4 &tmin=Scalar4::Zero(), const Scalar4 &tmax=Scalar4::Constant(std::numeric_limits< Scalar >::infinity()))
Cast a packet of up to 4 rays through the scene, returning data of the closest intersections excludin...
Definition EmbreeRayCaster.h:515
Index get_num_instances() const
Get the total number of mesh instances.
Definition EmbreeRayCaster.h:134
void update_internal()
Update all internal structures based on the current dirty flags.
Definition EmbreeRayCaster.h:793
FilterFunction get_occlusion_filter(Index mesh_index) const
Get the occlusion filter function currently bound to a given mesh.
Definition EmbreeRayCaster.h:398
virtual RTCBuildQuality get_scene_build_quality() const
Get the Embree geometry build quality.
Definition EmbreeRayCaster.h:790
std::function< void( const EmbreeRayCaster *obj, const Index *mesh_index, const Index *instance_index, const RTCFilterFunctionNArguments *args)> FilterFunction
Interface for a hit filter function.
Definition EmbreeRayCaster.h:80
void ensure_no_errors() const
Throw an exception if an Embree error has occurred.
Definition EmbreeRayCaster.h:420
std::shared_ptr< MeshType > get_mesh(Index index) const
Get the mesh with a given index.
Definition EmbreeRayCaster.h:149
EmbreeRayCaster(RTCSceneFlags scene_flags=RTC_SCENE_FLAG_DYNAMIC, RTCBuildQuality build_quality=RTC_BUILD_QUALITY_LOW)
Constructor.
Definition EmbreeRayCaster.h:92
void commit_scene_changes()
Call rtcCommitScene() on the overall scene, if it has been marked as modified.
Definition EmbreeRayCaster.h:411
bool cast(const Point &origin, const Direction &direction, Scalar tmin=0, Scalar tmax=std::numeric_limits< Scalar >::infinity())
Cast a single ray through the scene and check whether it hits anything or not.
Definition EmbreeRayCaster.h:702
ClosestPoint query_closest_point(const Point &p) const
Use the underlying BVH to find the point closest to a query point.
Definition EmbreeRayCaster.h:1098
void update_visibility(Index mesh_index, Index instance_index, bool visible)
Update the visibility of a given mesh index (true for visible, false for invisible).
Definition EmbreeRayCaster.h:316
void set_occlusion_filter(Index mesh_index, FilterFunction filter)
Set an occlusion filter that is called for every hit on (every instance of) a mesh during an occlusio...
Definition EmbreeRayCaster.h:385
Index get_num_meshes() const
Get the total number of meshes (not instances).
Definition EmbreeRayCaster.h:131
void release_scenes()
Release internal Embree scenes.
Definition EmbreeRayCaster.h:778
Transform get_transform(Index mesh_index, Index instance_index) const
Get the transform applied to a given mesh instance.
Definition EmbreeRayCaster.h:279
const unsigned * extract_int_data(const RaycasterMesh &mesh)
Get the index data of a mesh as an array of integers.
Definition EmbreeRayCaster.h:936
Index get_mesh_for_instance(Index cumulative_instance_index) const
Get the index of the mesh corresponding to a given instance, where the instances are indexed sequenti...
Definition EmbreeRayCaster.h:164
Definition RayCasterMesh.h:43
Definition RayCasterMesh.h:22
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
#define la_debug_assert(...)
Debug assertion check.
Definition assert.h:194
constexpr T invalid()
You can use invalid<T>() to get a value that can represent "invalid" values, such as invalid indices ...
Definition invalid.h:40
constexpr auto safe_cast(SourceType value) -> std::enable_if_t<!std::is_same< SourceType, TargetType >::value, TargetType >
Perform safe cast from SourceType to TargetType, where "safe" means:
Definition safe_cast.h:50
Raycasting operations.
Definition ClosestPointResult.h:21
Main namespace for Lagrange.
Definition ClosestPointResult.h:25