14#include <lagrange/Logger.h>
15#include <lagrange/internal/constants.h>
16#include <lagrange/raycasting/RayCaster.h>
17#include <lagrange/scene/SimpleScene.h>
18#include <lagrange/utils/AdjacencyList.h>
19#include <lagrange/utils/geometry3d.h>
20#include <lagrange/utils/range.h>
21#include <lagrange/views.h>
24#include <Eigen/Geometry>
35namespace lagrange::raycasting::detail {
37enum class SamplingMode {
48template <
typename Scalar>
49Eigen::Array4<Scalar> generate_vec4(
const size_t i)
51 constexpr double phi_1 = 1.1673039782614186842560459;
52 constexpr double phi_2 = phi_1 * phi_1;
53 constexpr double phi_3 = phi_2 * phi_1;
54 constexpr double phi_4 = phi_2 * phi_2;
56 const Eigen::Array4<Scalar> shifted =
57 (
static_cast<double>(i) / Eigen::Array4d(phi_1, phi_2, phi_3, phi_4)).
cast<Scalar>() +
59 return shifted - shifted.floor();
62template <
typename Scalar>
67 Eigen::RowVector2<Scalar> bary;
68 Eigen::RowVector3<Scalar> direction;
71 explicit RaySampler(
const Eigen::RowVector3<Scalar>& normal)
74 Eigen::Vector3<Scalar> x_col, y_col;
76 m_x_axis = x_col.transpose();
77 m_y_axis = y_col.transpose();
83 : m_normal(other.m_normal)
84 , m_x_axis(other.m_x_axis)
85 , m_y_axis(other.m_y_axis)
86 , m_sample_index(other.m_sample_index.load(std::memory_order_relaxed))
92 const Eigen::RowVector3<Scalar>& get_normal()
const {
return m_normal; }
99 generate_vec4<Scalar>(m_sample_index.fetch_add(1, std::memory_order_relaxed));
100 const Scalar phi = 2 *
static_cast<Scalar>(lagrange::internal::pi) * vec4[0];
101 const Scalar r2 = vec4[1];
102 const Scalar r = std::sqrt(r2);
103 const Scalar x = r * std::cos(phi);
104 const Scalar y = r * std::sin(phi);
107 result.direction = x * m_x_axis + y * m_y_axis + z * m_normal;
108 result.bary = vec4.template tail<2>();
109 if (result.bary[0] + result.bary[1] > 1) {
111 result.direction = -result.direction;
112 result.bary = Eigen::RowVector2<Scalar>::Ones() - result.bary;
117 Sample jitter(
Scalar jitter_sigma)
119 auto vec4 = generate_vec4<Scalar>(m_sample_index.fetch_add(1, std::memory_order_relaxed));
121 const Scalar r = jitter_sigma * std::sqrt(-2 * std::log(1 - vec4[0]));
122 const Scalar phi = 2 *
static_cast<Scalar>(lagrange::internal::pi) * vec4[1];
124 result.direction = r * (std::cos(phi) * m_x_axis + std::sin(phi) * m_y_axis);
125 result.bary = vec4.template tail<2>();
126 if (result.bary[0] + result.bary[1] > 1) {
128 result.bary = Eigen::RowVector2<Scalar>::Ones() - result.bary;
134 const Eigen::RowVector3<Scalar> m_normal;
135 Eigen::RowVector3<Scalar> m_x_axis;
136 Eigen::RowVector3<Scalar> m_y_axis;
137 std::atomic<size_t> m_sample_index{0};
142template <
typename Scalar,
typename Vertices,
typename Facets,
typename Index>
143Eigen::RowVector3<Scalar> barycentric_position(
144 const Eigen::RowVector2<Scalar>& bary,
145 const Vertices& vertices,
146 const Facets& facets,
150 return (bary(0) * vertices.col(facets(f, 0)) + bary(1) * vertices.col(facets(f, 1)) +
151 b2 * vertices.col(facets(f, 2)))
158 static constexpr size_t capacity = 16;
160 RayCaster::Point16f origins;
161 RayCaster::Direction16f directions;
162 RayCaster::Float16 tmins;
165 template <
typename Origin,
typename Direction>
166 void push(
const Origin& origin,
const Direction& direction,
float tmin = 1e-6f)
168 origins.row(count) = origin.template cast<float>();
169 directions.row(count) = direction.template cast<float>();
174 bool full()
const {
return count == capacity; }
175 bool empty()
const {
return count == 0; }
176 void clear() { count = 0; }
178 uint32_t cast(
const RayCaster& caster)
const
180 return caster.
occluded16(origins, directions, count, tmins);
188template <
typename Scalar,
typename Index>
192 Eigen::Transform<Scalar, 3, Eigen::Affine> transform;
193 std::vector<Scalar> facet_areas;
194 std::vector<RaySampler<Scalar>> ray_samplers;
198template <
typename Scalar,
typename Index>
204 std::vector<Index> active_local_facets;
219template <
typename Derived,
typename Scalar,
typename Index>
225 Derived& derived() {
return static_cast<Derived&
>(*this); }
226 const Derived& derived()
const {
return static_cast<const Derived&
>(*this); }
228 size_t num_instances()
const {
return derived().m_instances.size(); }
229 Index mesh_index(
size_t i)
const {
return derived().m_instances[i].mesh_index; }
230 const auto& instance_transform(
size_t i)
const {
return derived().m_instances[i].transform; }
232 Scalar compute_total_active_weight()
const
235 return std::accumulate(r.begin(), r.end(),
Scalar(0), [&](
Scalar s,
size_t i) {
236 return s + derived().instance_active_weight(i);
240 void run_batch(uint64_t num_rays)
242 const Scalar total = compute_total_active_weight();
243 if (total <= 0)
return;
245 const auto t_start = std::chrono::steady_clock::now();
248 const Scalar inst_weight = derived().instance_active_weight(i);
249 if (inst_weight <= 0)
continue;
251 const uint64_t inst_rays =
static_cast<uint64_t
>(
252 std::round(
static_cast<double>(num_rays) * inst_weight / total));
253 if (inst_rays == 0)
continue;
255 const auto& mesh = m_scene.get_mesh(mesh_index(i));
256 const auto vertices =
257 (instance_transform(i) *
vertex_view(mesh).transpose().template topRows<3>())
261 derived().process_instance(i, inst_rays, inst_weight, vertices, facets);
264 derived().end_batch();
266 const auto t_end = std::chrono::steady_clock::now();
267 const auto batch_ms =
268 std::chrono::duration_cast<std::chrono::milliseconds>(t_end - t_start).count();
274template <
typename Sampler,
typename Index>
275std::pair<uint64_t, uint64_t> sum_progress(
const Sampler& sampler, Index count)
277 uint64_t num_visible = 0;
278 uint64_t total_rays = 0;
279 for (Index i = 0; i < count; ++i) {
280 if (sampler.is_visible(i)) ++num_visible;
281 total_rays += sampler.num_rays_cast(i);
283 return {num_visible, total_rays};
A ray caster built on top of Embree that operates directly on SurfaceMesh and SimpleScene objects.
Definition RayCaster.h:196
uint32_t occluded16(const Point16f &origins, const Direction16f &directions, const std::variant< Mask16, size_t > &active=size_t(16), const Float16 &tmin=Float16::Zero(), const Float16 &tmax=Float16::Constant(std::numeric_limits< float >::infinity())) const
Test a packet of up to 16 rays for occlusion.
Definition RayCaster.cpp:1640
Simple scene container for instanced meshes.
Definition SimpleScene.h:62
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
SurfaceMesh< ToScalar, ToIndex > cast(const SurfaceMesh< FromScalar, FromIndex > &source_mesh, const AttributeFilter &convertible_attributes={}, std::vector< std::string > *converted_attributes_names=nullptr)
Cast a mesh to a mesh of different scalar and/or index type.
ConstRowMatrixView< Scalar > vertex_view(const SurfaceMesh< Scalar, Index > &mesh)
Returns a read-only view of the mesh vertices in the form of an Eigen matrix.
Definition views.cpp:156
ConstRowMatrixView< Index > facet_view(const SurfaceMesh< Scalar, Index > &mesh)
Returns a read-only view of a mesh facets in the form of an Eigen matrix.
Definition views.cpp:170
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition range.h:176
void orthogonal_frame(const Eigen::Matrix< Scalar, 3, 1 > &x, Eigen::Matrix< Scalar, 3, 1 > &y, Eigen::Matrix< Scalar, 3, 1 > &z)
Build an orthogonal frame given a single vector.
Definition geometry3d.h:124
Per-instance data for OccludedFacetSampler; gates each facet individually.
Definition occluded_sampler_common.h:200
std::vector< Scalar > facet_weights
Per-facet ray-distribution weight = cbrt(area).
Definition occluded_sampler_common.h:203
std::vector< std::optional< Eigen::Vector3< Scalar > > > facet_escape_snapshot
Pre-batch snapshot of facet_escape_directions.
Definition occluded_sampler_common.h:216
std::vector< std::optional< Eigen::Vector3< Scalar > > > facet_escape_directions
World-space unit-length escape direction recorded the first time each facet becomes visible; std::nul...
Definition occluded_sampler_common.h:213
std::optional< AdjacencyList< Index > > facet_neighbors
1-ring edge-neighbors (mesh dual graph).
Definition occluded_sampler_common.h:208
Definition occluded_sampler_common.h:221
Per-instance precomputation shared by both samplers.
Definition occluded_sampler_common.h:190
16-ray SIMD packet for RayCaster::occluded16. push widens to float on entry.
Definition occluded_sampler_common.h:157
uint32_t occupied_mask() const
Low count bits set — masks off undefined slots in a partial packet's cast mask.
Definition occluded_sampler_common.h:184
Definition occluded_sampler_common.h:66
Definition occluded_sampler_common.h:64
Sample operator()()
Bi-hemisphere sample via Malley's method: when bary reflection fires (u+v > 1), the direction is also...
Definition occluded_sampler_common.h:96