Lagrange
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;
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
132 {
133 return safe_cast<Index>(m_meshes.size());
134 }
135
137 Index get_num_instances() const
138 {
139 return m_instance_index_ranges.back();
140 }
141
143 Index get_num_instances(Index mesh_index) const
144 {
145 la_debug_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
146 return safe_cast<Index>(
147 m_instance_index_ranges[mesh_index + 1] - m_instance_index_ranges[mesh_index]);
148 }
149
154 template <typename MeshType>
155 std::shared_ptr<MeshType> get_mesh(Index index) const
156 {
157 la_runtime_assert(index < safe_cast<Index>(m_meshes.size()));
158 la_runtime_assert(m_meshes[index] != nullptr);
159 return dynamic_cast<RaycasterMeshDerived<MeshType>&>(*m_meshes[index]).get_mesh_ptr();
160 }
161
170 Index get_mesh_for_instance(Index cumulative_instance_index) const
171 {
172 la_runtime_assert(cumulative_instance_index < get_num_instances());
173 return m_instance_to_user_mesh[cumulative_instance_index];
174 }
175
182 template <typename MeshType>
183 Index add_mesh(
184 std::shared_ptr<MeshType> mesh,
185 const Transform& trans = Transform::Identity(),
186 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
187 {
188 return add_raycasting_mesh(
189 std::make_unique<RaycasterMeshDerived<MeshType>>(mesh),
190 trans,
191 build_quality);
192 }
193
200 template <typename MeshType>
202 std::shared_ptr<MeshType> mesh,
203 const TransformVector& trans_vector,
204 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
205 {
206 m_meshes.push_back(std::move(std::make_unique<RaycasterMeshDerived<MeshType>>(mesh)));
207 m_transforms.insert(m_transforms.end(), trans_vector.begin(), trans_vector.end());
208 m_mesh_build_qualities.push_back(build_quality);
209 m_visibility.resize(m_visibility.size() + trans_vector.size(), true);
210 for (auto& f : m_filters) { // per-mesh, not per-instance
211 f.push_back(nullptr);
212 }
213 Index mesh_index = safe_cast<Index>(m_meshes.size() - 1);
214 la_runtime_assert(m_instance_index_ranges.size() > 0);
215 Index instance_index = m_instance_index_ranges.back();
216 la_runtime_assert(instance_index == safe_cast<Index>(m_instance_to_user_mesh.size()));
217 Index new_instance_size = instance_index + trans_vector.size();
218 m_instance_index_ranges.push_back(new_instance_size);
219 m_instance_to_user_mesh.resize(new_instance_size, mesh_index);
220 m_need_rebuild = true;
221 return mesh_index;
222 }
223
230 template <typename MeshType>
232 Index index,
233 std::shared_ptr<MeshType> mesh,
234 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
235 {
236 update_raycasting_mesh(
237 index,
238 std::make_unique<RaycasterMeshDerived<MeshType>>(mesh),
239 build_quality);
240 }
241
247 void update_mesh_vertices(Index index)
248 {
249 la_runtime_assert(index < safe_cast<Index>(m_meshes.size()));
250 if (m_need_rebuild) return;
251
252 la_runtime_assert(index < safe_cast<Index>(m_embree_mesh_scenes.size()));
253 auto geom = rtcGetGeometry(m_embree_mesh_scenes[index], 0);
254
255 // Update the vertex buffer in Embree
256 auto const& mesh = m_meshes[index];
258 safe_cast<Index>(mesh->get_num_vertices()) == m_mesh_vertex_counts[index]);
259
260 auto vbuf =
261 reinterpret_cast<float*>(rtcGetGeometryBufferData(geom, RTC_BUFFER_TYPE_VERTEX, 0));
262 la_runtime_assert(vbuf);
263 mesh->vertices_to_float(vbuf);
264 rtcUpdateGeometryBuffer(geom, RTC_BUFFER_TYPE_VERTEX, 0);
265
266 // Re-commit the mesh geometry and scene
267 rtcCommitGeometry(geom);
268 rtcCommitScene(m_embree_mesh_scenes[index]);
269
270 // Re-commit every instance of the mesh
271 for (Index instance_index = m_instance_index_ranges[index];
272 instance_index < m_instance_index_ranges[index + 1];
273 ++instance_index) {
274 Index rtc_inst_id = m_instance_index_ranges[index] + instance_index;
275 auto geom_inst =
276 rtcGetGeometry(m_embree_world_scene, static_cast<unsigned>(rtc_inst_id));
277 rtcCommitGeometry(geom_inst);
278 }
279
280 // Mark the world scene as needing a re-commit (will be called lazily)
281 m_need_commit = true;
282 }
283
285 Transform get_transform(Index mesh_index, Index instance_index) const
286 {
287 la_runtime_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
288 Index index = m_instance_index_ranges[mesh_index] + instance_index;
289 la_runtime_assert(index < m_instance_index_ranges[mesh_index + 1]);
290 la_runtime_assert(index < safe_cast<Index>(m_transforms.size()));
291 return m_transforms[index];
292 }
293
295 void update_transformation(Index mesh_index, Index instance_index, const Transform& trans)
296 {
297 la_runtime_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
298 Index index = m_instance_index_ranges[mesh_index] + instance_index;
299 la_runtime_assert(index < m_instance_index_ranges[mesh_index + 1]);
300 la_runtime_assert(index < safe_cast<Index>(m_transforms.size()));
301 m_transforms[index] = trans;
302 if (!m_need_rebuild) {
303 auto geom = rtcGetGeometry(m_embree_world_scene, static_cast<unsigned>(index));
304 Eigen::Matrix<float, 4, 4> T = trans.template cast<float>();
305 rtcSetGeometryTransform(geom, 0, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, T.eval().data());
306 rtcCommitGeometry(geom);
307 m_need_commit = true;
308 }
309 }
310
312 bool get_visibility(Index mesh_index, Index instance_index) const
313 {
314 la_runtime_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
315 Index index = m_instance_index_ranges[mesh_index] + instance_index;
316 la_runtime_assert(index < m_instance_index_ranges[mesh_index + 1]);
317 la_runtime_assert(index < safe_cast<Index>(m_visibility.size()));
318 return m_visibility[index];
319 }
320
322 void update_visibility(Index mesh_index, Index instance_index, bool visible)
323 {
324 la_runtime_assert(mesh_index + 1 < safe_cast<Index>(m_instance_index_ranges.size()));
325 Index index = m_instance_index_ranges[mesh_index] + instance_index;
326 la_runtime_assert(index < m_instance_index_ranges[mesh_index + 1]);
327 la_runtime_assert(index < safe_cast<Index>(m_visibility.size()));
328 m_visibility[index] = visible;
329 if (!m_need_rebuild &&
330 rtcGetDeviceProperty(m_device, RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED)) {
331 // ^^^ else, visibility will be checked by the already bound filter
332
333 auto geom = rtcGetGeometry(m_embree_world_scene, static_cast<unsigned>(index));
334 rtcSetGeometryMask(geom, visible ? 0xFFFFFFFF : 0x00000000);
335 rtcCommitGeometry(geom);
336 m_need_commit = true;
337 }
338 }
339
356 void set_intersection_filter(Index mesh_index, FilterFunction filter)
357 {
358 la_runtime_assert(mesh_index < m_filters[FILTER_INTERSECT].size());
359 m_filters[FILTER_INTERSECT][mesh_index] = filter;
360 m_need_rebuild = true;
361 }
362
370 {
371 la_runtime_assert(mesh_index < m_filters[FILTER_INTERSECT].size());
372 return m_filters[FILTER_INTERSECT][mesh_index];
373 }
374
391 void set_occlusion_filter(Index mesh_index, FilterFunction filter)
392 {
393 la_runtime_assert(mesh_index < m_filters[FILTER_OCCLUDED].size());
394 m_filters[FILTER_OCCLUDED][mesh_index] = filter;
395 m_need_rebuild = true;
396 }
397
404 FilterFunction get_occlusion_filter(Index mesh_index) const
405 {
406 la_runtime_assert(mesh_index < m_filters[FILTER_OCCLUDED].size());
407 return m_filters[FILTER_OCCLUDED][mesh_index];
408 }
409
418 {
419 if (!m_need_commit) return;
420
421 rtcCommitScene(m_embree_world_scene);
422 m_need_commit = false;
423 }
424
426 void ensure_no_errors() const
427 {
428 EmbreeHelper::ensure_no_errors(m_device);
429 }
430
435 uint32_t cast4(
436 uint32_t batch_size,
437 const Point4& origin,
438 const Direction4& direction,
439 const Mask4& mask,
440 Index4& mesh_index,
441 Index4& instance_index,
442 Index4& facet_index,
443 Scalar4& ray_depth,
444 Point4& barycentric_coord,
445 Point4& normal,
446 const Scalar4& tmin = Scalar4::Zero(),
447 const Scalar4& tmax = Scalar4::Constant(std::numeric_limits<Scalar>::infinity()))
448 {
449 la_debug_assert(batch_size <= 4);
450
452
453 RTCRayHit4 embree_raypacket;
454 for (int i = 0; i < static_cast<int>(batch_size); ++i) {
455 // Set ray origins
456 embree_raypacket.ray.org_x[i] = static_cast<float>(origin(i, 0));
457 embree_raypacket.ray.org_y[i] = static_cast<float>(origin(i, 1));
458 embree_raypacket.ray.org_z[i] = static_cast<float>(origin(i, 2));
459
460 // Set ray directions
461 embree_raypacket.ray.dir_x[i] = static_cast<float>(direction(i, 0));
462 embree_raypacket.ray.dir_y[i] = static_cast<float>(direction(i, 1));
463 embree_raypacket.ray.dir_z[i] = static_cast<float>(direction(i, 2));
464
465 // Misc
466 embree_raypacket.ray.tnear[i] = static_cast<float>(tmin[i]);
467 embree_raypacket.ray.tfar[i] = std::isinf(tmax[i]) ? std::numeric_limits<float>::max()
468 : static_cast<float>(tmax[i]);
469 embree_raypacket.ray.mask[i] = 0xFFFFFFFF;
470 embree_raypacket.ray.id[i] = static_cast<unsigned>(i);
471 embree_raypacket.ray.flags[i] = 0;
472
473 // Required initialization of the hit substructure
474 embree_raypacket.hit.geomID[i] = RTC_INVALID_GEOMETRY_ID;
475 embree_raypacket.hit.primID[i] = RTC_INVALID_GEOMETRY_ID;
476 embree_raypacket.hit.instID[0][i] = RTC_INVALID_GEOMETRY_ID;
477 }
478
479 // Modify the mask to make 100% sure extra rays in the packet will be ignored
480 auto packet_mask = mask;
481 for (int i = static_cast<int>(batch_size); i < 4; ++i) packet_mask[i] = 0;
482
483 ensure_no_errors_internal();
484 {
485 RTCIntersectContext context;
486 rtcInitIntersectContext(&context);
487 rtcIntersect4(packet_mask.data(), m_embree_world_scene, &context, &embree_raypacket);
488 }
489 ensure_no_errors_internal();
490
491 uint32_t is_hits = 0;
492 for (int i = 0; i < static_cast<int>(batch_size); ++i) {
493 if (embree_raypacket.hit.geomID[i] != RTC_INVALID_GEOMETRY_ID) {
494 Index rtc_inst_id = embree_raypacket.hit.instID[0][i];
495 Index rtc_mesh_id = (rtc_inst_id == RTC_INVALID_GEOMETRY_ID)
496 ? embree_raypacket.hit.geomID[i]
497 : rtc_inst_id;
498 assert(rtc_mesh_id < m_instance_to_user_mesh.size());
499 assert(m_visibility[rtc_mesh_id]);
500 mesh_index[i] = m_instance_to_user_mesh[rtc_mesh_id];
501 assert(mesh_index[i] + 1 < m_instance_index_ranges.size());
502 assert(mesh_index[i] < safe_cast<Index>(m_meshes.size()));
503 instance_index[i] = rtc_mesh_id - m_instance_index_ranges[mesh_index[i]];
504 facet_index[i] = embree_raypacket.hit.primID[i];
505 ray_depth[i] = embree_raypacket.ray.tfar[i];
506 barycentric_coord(i, 0) =
507 1.0f - embree_raypacket.hit.u[i] - embree_raypacket.hit.v[i];
508 barycentric_coord(i, 1) = embree_raypacket.hit.u[i];
509 barycentric_coord(i, 2) = embree_raypacket.hit.v[i];
510 normal(i, 0) = embree_raypacket.hit.Ng_x[i];
511 normal(i, 1) = embree_raypacket.hit.Ng_y[i];
512 normal(i, 2) = embree_raypacket.hit.Ng_z[i];
513 is_hits = is_hits | (1 << i);
514 }
515 }
516
517 return is_hits;
518 }
519
524 uint32_t cast4(
525 uint32_t batch_size,
526 const Point4& origin,
527 const Direction4& direction,
528 const Mask4& mask,
529 Index4& mesh_index,
530 Index4& facet_index,
531 Scalar4& ray_depth,
532 Point4& barycentric_coord,
533 const Scalar4& tmin = Scalar4::Zero(),
534 const Scalar4& tmax = Scalar4::Constant(std::numeric_limits<Scalar>::infinity()))
535 {
536 Index4 instance_index;
537 Point4 normal;
538 return cast4(
539 batch_size,
540 origin,
541 direction,
542 mask,
543 mesh_index,
544 instance_index,
545 facet_index,
546 ray_depth,
547 barycentric_coord,
548 normal,
549 tmin,
550 tmax);
551 }
552
556 uint32_t cast4(
557 uint32_t batch_size,
558 const Point4& origin,
559 const Direction4& direction,
560 const Mask4& mask,
561 const Scalar4& tmin = Scalar4::Zero(),
562 const Scalar4& tmax = Scalar4::Constant(std::numeric_limits<Scalar>::infinity()))
563 {
564 la_debug_assert(batch_size <= 4);
565
567
568 RTCRay4 embree_raypacket;
569 for (int i = 0; i < static_cast<int>(batch_size); ++i) {
570 // Set ray origins
571 embree_raypacket.org_x[i] = static_cast<float>(origin(i, 0));
572 embree_raypacket.org_y[i] = static_cast<float>(origin(i, 1));
573 embree_raypacket.org_z[i] = static_cast<float>(origin(i, 2));
574
575 // Set ray directions
576 embree_raypacket.dir_x[i] = static_cast<float>(direction(i, 0));
577 embree_raypacket.dir_y[i] = static_cast<float>(direction(i, 1));
578 embree_raypacket.dir_z[i] = static_cast<float>(direction(i, 2));
579
580 // Misc
581 embree_raypacket.tnear[i] = static_cast<float>(tmin[i]);
582 embree_raypacket.tfar[i] = std::isinf(tmax[i]) ? std::numeric_limits<float>::max()
583 : static_cast<float>(tmax[i]);
584 embree_raypacket.mask[i] = 0xFFFFFFFF;
585 embree_raypacket.id[i] = static_cast<unsigned>(i);
586 embree_raypacket.flags[i] = 0;
587 }
588
589 // Modify the mask to make 100% sure extra rays in the packet will be ignored
590 auto packet_mask = mask;
591 for (int i = static_cast<int>(batch_size); i < 4; ++i) packet_mask[i] = 0;
592
593 ensure_no_errors_internal();
594 {
595 RTCIntersectContext context;
596 rtcInitIntersectContext(&context);
597 rtcOccluded4(packet_mask.data(), m_embree_world_scene, &context, &embree_raypacket);
598 }
599 ensure_no_errors_internal();
600
601 // If hit, the tfar field will be set to -inf.
602 uint32_t is_hits = 0;
603 for (uint32_t i = 0; i < batch_size; ++i)
604 if (!std::isfinite(embree_raypacket.tfar[i])) is_hits = is_hits | (1 << i);
605
606 return is_hits;
607 }
608
613 bool cast(
614 const Point& origin,
615 const Direction& direction,
616 Index& mesh_index,
617 Index& instance_index,
618 Index& facet_index,
619 Scalar& ray_depth,
620 Point& barycentric_coord,
621 Point& normal,
622 Scalar tmin = 0,
623 Scalar tmax = std::numeric_limits<Scalar>::infinity())
624 {
625 // Overloaded when specializing tnear and tfar
626
628
629 RTCRayHit embree_rayhit;
630 embree_rayhit.ray.org_x = static_cast<float>(origin.x());
631 embree_rayhit.ray.org_y = static_cast<float>(origin.y());
632 embree_rayhit.ray.org_z = static_cast<float>(origin.z());
633 embree_rayhit.ray.dir_x = static_cast<float>(direction.x());
634 embree_rayhit.ray.dir_y = static_cast<float>(direction.y());
635 embree_rayhit.ray.dir_z = static_cast<float>(direction.z());
636 embree_rayhit.ray.tnear = static_cast<float>(tmin);
637 embree_rayhit.ray.tfar =
638 std::isinf(tmax) ? std::numeric_limits<float>::max() : static_cast<float>(tmax);
639 embree_rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
640 embree_rayhit.hit.primID = RTC_INVALID_GEOMETRY_ID;
641 embree_rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;
642 embree_rayhit.ray.mask = 0xFFFFFFFF;
643 embree_rayhit.ray.id = 0;
644 embree_rayhit.ray.flags = 0;
645 ensure_no_errors_internal();
646 {
647 RTCIntersectContext context;
648 rtcInitIntersectContext(&context);
649 rtcIntersect1(m_embree_world_scene, &context, &embree_rayhit);
650 }
651 ensure_no_errors_internal();
652
653 if (embree_rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) {
654 Index rtc_inst_id = embree_rayhit.hit.instID[0];
655 Index rtc_mesh_id =
656 (rtc_inst_id == RTC_INVALID_GEOMETRY_ID) ? embree_rayhit.hit.geomID : rtc_inst_id;
657 assert(rtc_mesh_id < m_instance_to_user_mesh.size());
658 assert(m_visibility[rtc_mesh_id]);
659 mesh_index = m_instance_to_user_mesh[rtc_mesh_id];
660 assert(mesh_index + 1 < m_instance_index_ranges.size());
661 assert(mesh_index < safe_cast<Index>(m_meshes.size()));
662 instance_index = rtc_mesh_id - m_instance_index_ranges[mesh_index];
663 facet_index = embree_rayhit.hit.primID;
664 ray_depth = embree_rayhit.ray.tfar;
665 barycentric_coord[0] = 1.0f - embree_rayhit.hit.u - embree_rayhit.hit.v;
666 barycentric_coord[1] = embree_rayhit.hit.u;
667 barycentric_coord[2] = embree_rayhit.hit.v;
668 normal[0] = embree_rayhit.hit.Ng_x;
669 normal[1] = embree_rayhit.hit.Ng_y;
670 normal[2] = embree_rayhit.hit.Ng_z;
671 return true;
672 } else {
673 // Ray missed.
674 mesh_index = invalid<Index>();
675 instance_index = invalid<Index>();
676 facet_index = invalid<Index>();
677 return false;
678 }
679 }
680
685 bool cast(
686 const Point& origin,
687 const Direction& direction,
688 Index& mesh_index,
689 Index& facet_index,
690 Scalar& ray_depth,
691 Point& barycentric_coord,
692 Scalar tmin = 0,
693 Scalar tmax = std::numeric_limits<Scalar>::infinity())
694 {
695 Index instance_index;
696 Point normal;
697 return cast(
698 origin,
699 direction,
700 mesh_index,
701 instance_index,
702 facet_index,
703 ray_depth,
704 barycentric_coord,
705 normal,
706 tmin,
707 tmax);
708 }
709
711 bool cast(
712 const Point& origin,
713 const Direction& direction,
714 Scalar tmin = 0,
715 Scalar tmax = std::numeric_limits<Scalar>::infinity())
716 {
718
719 RTCRay embree_ray;
720 embree_ray.org_x = static_cast<float>(origin.x());
721 embree_ray.org_y = static_cast<float>(origin.y());
722 embree_ray.org_z = static_cast<float>(origin.z());
723 embree_ray.dir_x = static_cast<float>(direction.x());
724 embree_ray.dir_y = static_cast<float>(direction.y());
725 embree_ray.dir_z = static_cast<float>(direction.z());
726 embree_ray.tnear = static_cast<float>(tmin);
727 embree_ray.tfar =
728 std::isinf(tmax) ? std::numeric_limits<float>::max() : static_cast<float>(tmax);
729 embree_ray.mask = 0xFFFFFFFF;
730 embree_ray.id = 0;
731 embree_ray.flags = 0;
732
733 ensure_no_errors_internal();
734 {
735 RTCIntersectContext context;
736 rtcInitIntersectContext(&context);
737 rtcOccluded1(m_embree_world_scene, &context, &embree_ray);
738 }
739 ensure_no_errors_internal();
740
741 // If hit, the tfar field will be set to -inf.
742 return !std::isfinite(embree_ray.tfar);
743 }
744
746 ClosestPoint query_closest_point(const Point& p) const;
747
750 std::unique_ptr<RaycasterMesh> mesh,
751 const Transform& trans = Transform::Identity(),
752 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
753 {
754 m_meshes.push_back(std::move(mesh));
755 m_transforms.push_back(trans);
756 m_mesh_build_qualities.push_back(build_quality);
757 m_visibility.push_back(true);
758 for (auto& f : m_filters) { // per-mesh, not per-instance
759 f.push_back(nullptr);
760 }
761 Index mesh_index = safe_cast<Index>(m_meshes.size() - 1);
762 la_runtime_assert(m_instance_index_ranges.size() > 0);
763 Index instance_index = m_instance_index_ranges.back();
764 la_runtime_assert(instance_index == safe_cast<Index>(m_instance_to_user_mesh.size()));
765 m_instance_index_ranges.push_back(instance_index + 1);
766 m_instance_to_user_mesh.resize(instance_index + 1, mesh_index);
767 m_need_rebuild = true;
768 return mesh_index;
769 }
770
771 void update_raycasting_mesh(
772 Index index,
773 std::unique_ptr<RaycasterMesh> mesh,
774 RTCBuildQuality build_quality = RTC_BUILD_QUALITY_MEDIUM)
775 {
776 la_runtime_assert(mesh->get_dim() == 3);
777 la_runtime_assert(mesh->get_vertex_per_facet() == 3);
778 la_runtime_assert(index < safe_cast<Index>(m_meshes.size()));
779 m_meshes[index] = std::move(mesh);
780 m_mesh_build_qualities[index] = build_quality;
781 m_need_rebuild = true; // TODO: Make this more fine-grained so only the affected part of
782 // the Embree scene is updated
783 }
784
785protected:
788 for (auto& s : m_embree_mesh_scenes) {
789 rtcReleaseScene(s);
790 }
791 rtcReleaseScene(m_embree_world_scene);
792 }
793
795 virtual RTCSceneFlags get_scene_flags() const
796 {
797 return m_scene_flags;
798 }
799
801 virtual RTCBuildQuality get_scene_build_quality() const
802 {
803 return m_build_quality;
804 }
805
808 {
809 if (m_need_rebuild)
810 generate_scene(); // full rebuild
811 else if (m_need_commit)
812 commit_scene_changes(); // just call rtcCommitScene()
813 }
814
822 {
823 if (!m_need_rebuild) return;
824
825 // Scene needs to be updated
827 m_embree_world_scene = rtcNewScene(m_device);
828 auto scene_flags = get_scene_flags(); // FIXME: or just m_scene_flags?
829 auto scene_build_quality = get_scene_build_quality(); // FIXME: or just m_build_quality?
830 rtcSetSceneFlags(
831 m_embree_world_scene,
832 scene_flags); // TODO: maybe also set RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION
833 rtcSetSceneBuildQuality(m_embree_world_scene, scene_build_quality);
834 m_float_data.clear();
835 m_int_data.clear();
836 const auto num_meshes = m_meshes.size();
837 la_runtime_assert(num_meshes + 1 == m_instance_index_ranges.size());
838 m_embree_mesh_scenes.resize(num_meshes);
839 m_mesh_vertex_counts.resize(num_meshes);
840 ensure_no_errors_internal();
841
842 bool is_mask_supported =
843 rtcGetDeviceProperty(m_device, RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED);
844 for (size_t i = 0; i < num_meshes; i++) {
845 // Initialize RTC meshes
846 const auto& mesh = m_meshes[i];
847 // const auto& vertices = mesh->get_vertices();
848 // const auto& facets = mesh->get_facets();
849 const Index num_vertices = m_mesh_vertex_counts[i] =
850 safe_cast<Index>(mesh->get_num_vertices());
851 const Index num_facets = safe_cast<Index>(mesh->get_num_facets());
852
853 auto& embree_mesh_scene = m_embree_mesh_scenes[i];
854 embree_mesh_scene = rtcNewScene(m_device);
855
856 rtcSetSceneFlags(embree_mesh_scene, scene_flags);
857 rtcSetSceneBuildQuality(embree_mesh_scene, scene_build_quality);
858 ensure_no_errors_internal();
859
860 RTCGeometry geom = rtcNewGeometry(
861 m_device,
862 RTC_GEOMETRY_TYPE_TRIANGLE); // EMBREE_FIXME: check if geometry gets properly
863 // committed
864 rtcSetGeometryBuildQuality(geom, m_mesh_build_qualities[i]);
865 // rtcSetGeometryTimeStepCount(geom, 1);
866
867 const float* vertex_data = extract_float_data(*mesh);
868 const unsigned* facet_data = extract_int_data(*mesh);
869
870 rtcSetSharedGeometryBuffer(
871 geom,
872 RTC_BUFFER_TYPE_VERTEX,
873 0,
874 RTC_FORMAT_FLOAT3,
875 vertex_data,
876 0,
877 sizeof(float) * 3,
878 num_vertices);
879 rtcSetSharedGeometryBuffer(
880 geom,
881 RTC_BUFFER_TYPE_INDEX,
882 0,
883 RTC_FORMAT_UINT3,
884 facet_data,
885 0,
886 sizeof(int) * 3,
887 num_facets);
888
889 set_intersection_filter(geom, m_filters[FILTER_INTERSECT][i], is_mask_supported);
890 set_occlusion_filter(geom, m_filters[FILTER_OCCLUDED][i], is_mask_supported);
891
892 rtcCommitGeometry(geom);
893 rtcAttachGeometry(embree_mesh_scene, geom);
894 rtcReleaseGeometry(geom);
895 ensure_no_errors_internal();
896
897 // Initialize RTC instances
898 for (Index instance_index = m_instance_index_ranges[i];
899 instance_index < m_instance_index_ranges[i + 1];
900 ++instance_index) {
901 const auto& trans = m_transforms[instance_index];
902
903 RTCGeometry geom_inst = rtcNewGeometry(
904 m_device,
905 RTC_GEOMETRY_TYPE_INSTANCE); // EMBREE_FIXME: check if geometry gets properly
906 // committed
907 rtcSetGeometryInstancedScene(geom_inst, embree_mesh_scene);
908 rtcSetGeometryTimeStepCount(geom_inst, 1);
909
910 Eigen::Matrix<float, 4, 4> T = trans.template cast<float>();
911 rtcSetGeometryTransform(geom_inst, 0, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, T.data());
912 ensure_no_errors_internal();
913
914 if (is_mask_supported) {
915 rtcSetGeometryMask(
916 geom_inst,
917 m_visibility[instance_index] ? 0xFFFFFFFF : 0x00000000);
918 }
919 ensure_no_errors_internal();
920
921 rtcCommitGeometry(geom_inst);
922 unsigned rtc_instance_id = rtcAttachGeometry(m_embree_world_scene, geom_inst);
923 rtcReleaseGeometry(geom_inst);
924 la_runtime_assert(safe_cast<Index>(rtc_instance_id) == instance_index);
925 ensure_no_errors_internal();
926 }
927
928 rtcCommitScene(embree_mesh_scene);
929 ensure_no_errors_internal();
930 }
931 rtcCommitScene(m_embree_world_scene);
932 ensure_no_errors_internal();
933
934 m_need_rebuild = m_need_commit = false;
935 }
936
938 const float* extract_float_data(const RaycasterMesh& mesh)
939 {
940 auto float_data = mesh.vertices_to_float();
941 // Due to Embree bug, we have to have at least one more entry
942 // after the bound. Sigh...
943 // See https://github.com/embree/embree/issues/124
944 float_data.push_back(0.0);
945 m_float_data.emplace_back(std::move(float_data));
946 return m_float_data.back().data();
947 }
948
950 const unsigned* extract_int_data(const RaycasterMesh& mesh)
951 {
952 auto int_data = mesh.indices_to_int();
953 // Due to Embree bug, we have to have at least one more entry
954 // after the bound. Sigh...
955 // See https://github.com/embree/embree/issues/124
956 int_data.push_back(0);
957 m_int_data.emplace_back(std::move(int_data));
958 return m_int_data.back().data();
959 }
960
961private:
966 void set_intersection_filter(RTCGeometry geom, FilterFunction filter, bool is_mask_supported)
967 {
968 if (is_mask_supported) {
969 if (filter) {
970 rtcSetGeometryUserData(geom, this);
971 rtcSetGeometryIntersectFilterFunction(geom, &wrap_filter<FILTER_INTERSECT>);
972 } else {
973 rtcSetGeometryIntersectFilterFunction(geom, nullptr);
974 }
975 } else {
976 rtcSetGeometryUserData(geom, this);
977 rtcSetGeometryIntersectFilterFunction(geom, &wrap_filter_and_mask<FILTER_INTERSECT>);
978 }
979 }
980
985 void set_occlusion_filter(RTCGeometry geom, FilterFunction filter, bool is_mask_supported)
986 {
987 if (is_mask_supported) {
988 if (filter) {
989 rtcSetGeometryUserData(geom, this);
990 rtcSetGeometryOccludedFilterFunction(geom, &wrap_filter<FILTER_OCCLUDED>);
991 } else {
992 rtcSetGeometryOccludedFilterFunction(geom, nullptr);
993 }
994 } else {
995 rtcSetGeometryUserData(geom, this);
996 rtcSetGeometryOccludedFilterFunction(geom, &wrap_filter_and_mask<FILTER_OCCLUDED>);
997 }
998 }
999
1004 template <int IntersectionOrOcclusion> // 0: intersection, 1: occlusion
1005 static void wrap_filter(const RTCFilterFunctionNArguments* args)
1006 {
1007 // Embree never actually calls a filter callback with different geometry or instance IDs
1008 // So we can assume they are the same for all the hits in this batch. Also, every single
1009 // mesh in this class is instanced (never used raw), so we can ignore geomID.
1010 const auto* obj = reinterpret_cast<EmbreeRayCaster*>(args->geometryUserPtr);
1011 auto rtc_inst_id = RTCHitN_instID(args->hit, args->N, 0, 0);
1012 assert(rtc_inst_id < obj->m_instance_to_user_mesh.size());
1013
1014 auto filter = obj->m_filters[IntersectionOrOcclusion][rtc_inst_id];
1015 if (!filter) {
1016 return;
1017 }
1018
1019 Index mesh_index = obj->m_instance_to_user_mesh[rtc_inst_id];
1020 assert(mesh_index + 1 < obj->m_instance_index_ranges.size());
1021 assert(mesh_index < safe_cast<Index>(obj->m_meshes.size()));
1022 Index instance_index = rtc_inst_id - obj->m_instance_index_ranges[mesh_index];
1023
1024 // In case Embree's implementation changes in the future, the callback should be written
1025 // generally, without assuming the single geometry/instance condition above.
1026 Index4 mesh_index4;
1027 mesh_index4.fill(mesh_index);
1028 Index4 instance_index4;
1029 instance_index4.fill(instance_index);
1030
1031 // Call the wrapped filter with the indices specific to this object
1032 filter(obj, mesh_index4.data(), instance_index4.data(), args);
1033 }
1034
1040 template <int IntersectionOrOcclusion> // 0: intersection, 1: occlusion
1041 static void wrap_filter_and_mask(const RTCFilterFunctionNArguments* args)
1042 {
1043 // Embree never actually calls a filter callback with different geometry or instance IDs
1044 // So we can assume they are the same for all the hits in this batch. Also, every single
1045 // mesh in this class is instanced (never used raw), so we can ignore geomID.
1046 const auto* obj = reinterpret_cast<EmbreeRayCaster*>(args->geometryUserPtr);
1047 auto rtc_inst_id = RTCHitN_instID(args->hit, args->N, 0, 0);
1048 if (!obj->m_visibility[rtc_inst_id]) {
1049 // Object is invisible. Make the hits of all the rays with this object invalid.
1050 std::fill(args->valid, args->valid + args->N, 0);
1051 return;
1052 }
1053
1054 // Delegate to the regular filtering after having checked visibility
1055 wrap_filter<IntersectionOrOcclusion>(args);
1056 }
1057
1058protected:
1059 RTCSceneFlags m_scene_flags;
1060 RTCBuildQuality m_build_quality;
1061 RTCDevice m_device;
1062 RTCScene m_embree_world_scene;
1063 bool m_need_rebuild; // full rebuild of the scene?
1064 bool m_need_commit; // just call rtcCommitScene() on the scene?
1065
1066 // Data reservoirs for holding temporary/casted/per-geometry data.
1067 // Length = Number of polygonal meshes
1068 std::vector<FloatData> m_float_data;
1069 std::vector<IntData> m_int_data;
1070 std::vector<std::unique_ptr<RaycasterMesh>> m_meshes;
1071 std::vector<RTCBuildQuality> m_mesh_build_qualities;
1072 std::vector<RTCScene> m_embree_mesh_scenes;
1073 std::vector<Index> m_mesh_vertex_counts; // for bounds-checking of buffer updates
1074 std::vector<FilterFunction> m_filters[2]; // 0: intersection filters, 1: occlusion filters
1075
1076 // Ranges of instance indices corresponding to a specific
1077 // Mesh. For example, in a scenario with 3 meshes each of
1078 // which has 1, 2, 5 instances, this array would be
1079 // [0, 1, 3, 8].
1080 // Length = Number of user meshes + 1
1081 std::vector<Index> m_instance_index_ranges;
1082
1083 // Mapping from (RTC-)instanced mesh to user mesh. For
1084 // example, in a scenario with 3 meshes each of
1085 // which has 1, 2, 5 instances, this array would be
1086 // [0, 1, 1, 2, 2, 2, 2, 2]
1087 // Length = Number of instanced meshes
1088 // Note: This array is only used internally. We shouldn't
1089 // allow the users to access anything with the indices
1090 // used in those RTC functions.
1091 std::vector<Index> m_instance_to_user_mesh;
1092
1093 // Data reservoirs for holding instanced mesh data
1094 // Length = Number of (world-space) instanced meshes
1095 std::vector<Transform> m_transforms;
1096 std::vector<bool> m_visibility;
1097
1098 // error checking function used internally
1099 void ensure_no_errors_internal() const
1100 {
1101#ifdef LAGRANGE_EMBREE_DEBUG
1102 EmbreeHelper::ensure_no_errors(m_device);
1103#endif
1104 }
1105};
1106
1108// IMPLEMENTATION
1110
1111template <typename Scalar>
1113{
1114 RTCPointQuery query;
1115 query.x = (float)(p.x());
1116 query.y = (float)(p.y());
1117 query.z = (float)(p.z());
1118 query.radius = std::numeric_limits<float>::max();
1119 query.time = 0.f;
1120 ensure_no_errors_internal();
1121
1122 ClosestPoint result;
1123
1124 // Callback to retrieve triangle corner positions
1125 result.populate_triangle =
1126 [&](unsigned mesh_index, unsigned facet_index, Point& v0, Point& v1, Point& v2) {
1127 // TODO: There's no way to call this->get_mesh<> since we need to template the function
1128 // by the (derived) type, which we don't know here... This means our only choice is so
1129 // use the float data instead of the (maybe) double point coordinate if available. Note
1130 // that we could also call rtcSetGeometryPointQueryFunction() when we register our mesh,
1131 // since we know the derived type at this point.
1132 const unsigned* face = m_int_data[mesh_index].data() + 3 * facet_index;
1133 const float* vertices = m_float_data[mesh_index].data();
1134 v0 = Point(vertices[3 * face[0]], vertices[3 * face[0] + 1], vertices[3 * face[0] + 2]);
1135 v1 = Point(vertices[3 * face[1]], vertices[3 * face[1] + 1], vertices[3 * face[1] + 2]);
1136 v2 = Point(vertices[3 * face[2]], vertices[3 * face[2] + 1], vertices[3 * face[2] + 2]);
1137 };
1138
1139 {
1140 RTCPointQueryContext context;
1141 rtcInitPointQueryContext(&context);
1142 rtcPointQuery(
1143 m_embree_world_scene,
1144 &query,
1145 &context,
1146 &embree_closest_point<Scalar>,
1147 reinterpret_cast<void*>(&result));
1148 assert(
1149 result.mesh_index != RTC_INVALID_GEOMETRY_ID ||
1150 result.facet_index != RTC_INVALID_GEOMETRY_ID);
1151 assert(
1152 result.mesh_index != invalid<unsigned>() || result.facet_index != invalid<unsigned>());
1153 }
1154 ensure_no_errors_internal();
1155
1156 return result;
1157}
1158
1159} // namespace raycasting
1160} // 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:201
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:749
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:183
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:556
FilterFunction get_intersection_filter(Index mesh_index) const
Get the intersection filter function currently bound to a given mesh.
Definition: EmbreeRayCaster.h:369
Index get_num_instances(Index mesh_index) const
Get the number of instances of a particular mesh.
Definition: EmbreeRayCaster.h:143
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:231
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:356
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:613
virtual ~EmbreeRayCaster()
Destructor.
Definition: EmbreeRayCaster.h:120
void generate_scene()
Build the whole Embree scene from the specified meshes, instances, etc.
Definition: EmbreeRayCaster.h:821
bool get_visibility(Index mesh_index, Index instance_index) const
Get the visibility flag of a given mesh instance.
Definition: EmbreeRayCaster.h:312
virtual RTCSceneFlags get_scene_flags() const
Get the Embree scene flags.
Definition: EmbreeRayCaster.h:795
const float * extract_float_data(const RaycasterMesh &mesh)
Get the vertex data of a mesh as an array of floats.
Definition: EmbreeRayCaster.h:938
void update_transformation(Index mesh_index, Index instance_index, const Transform &trans)
Update the transform applied to a given mesh instance.
Definition: EmbreeRayCaster.h:295
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:247
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:435
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:685
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:524
Index get_num_instances() const
Get the total number of mesh instances.
Definition: EmbreeRayCaster.h:137
void update_internal()
Update all internal structures based on the current dirty flags.
Definition: EmbreeRayCaster.h:807
FilterFunction get_occlusion_filter(Index mesh_index) const
Get the occlusion filter function currently bound to a given mesh.
Definition: EmbreeRayCaster.h:404
virtual RTCBuildQuality get_scene_build_quality() const
Get the Embree geometry build quality.
Definition: EmbreeRayCaster.h:801
void ensure_no_errors() const
Throw an exception if an Embree error has occurred.
Definition: EmbreeRayCaster.h:426
std::shared_ptr< MeshType > get_mesh(Index index) const
Get the mesh with a given index.
Definition: EmbreeRayCaster.h:155
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:417
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:711
ClosestPoint query_closest_point(const Point &p) const
Use the underlying BVH to find the point closest to a query point.
Definition: EmbreeRayCaster.h:1112
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:322
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:391
Index get_num_meshes() const
Get the total number of meshes (not instances).
Definition: EmbreeRayCaster.h:131
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:84
void release_scenes()
Release internal Embree scenes.
Definition: EmbreeRayCaster.h:787
Transform get_transform(Index mesh_index, Index instance_index) const
Get the transform applied to a given mesh instance.
Definition: EmbreeRayCaster.h:285
const unsigned * extract_int_data(const RaycasterMesh &mesh)
Get the index data of a mesh as an array of integers.
Definition: EmbreeRayCaster.h:950
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:170
Definition: RayCasterMesh.h:43
Definition: RayCasterMesh.h:22
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
#define la_debug_assert(...)
Debug assertion check.
Definition: assert.h:189
Main namespace for Lagrange.
Definition: AABBIGL.h:30
Definition: ClosestPointResult.h:25