Lagrange
Loading...
Searching...
No Matches
project_particles_directional.h
1/*
2 * Copyright 2022 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 <lagrange/MeshTrait.h>
15#include <lagrange/create_mesh.h>
16#include <lagrange/legacy/inline.h>
17#include <lagrange/raycasting/Options.h>
18#include <lagrange/raycasting/create_ray_caster.h>
19#include <lagrange/utils/safe_cast.h>
20
21// clang-format off
22#include <lagrange/utils/warnoff.h>
23#include <igl/bounding_box_diagonal.h>
24#include <lagrange/utils/warnon.h>
25// clang-format on
26
27#include <tbb/parallel_for.h>
28
29#include <algorithm>
30#include <string>
31#include <type_traits>
32#include <vector>
33
34namespace lagrange {
35namespace raycasting {
36LAGRANGE_LEGACY_INLINE
37namespace legacy {
38
76template <
77 typename ParticleDataType,
78 typename MeshType,
79 typename DefaultScalar = typename ParticleDataType::value_type::Scalar,
80 typename MatrixType = typename Eigen::Matrix<DefaultScalar, 4, 4>,
81 typename VectorType = typename Eigen::Matrix<DefaultScalar, 3, 1>>
82void project_particles_directional(
83 const ParticleDataType& origins,
84 const MeshType& mesh_proj_on,
85 const VectorType& direction,
86 ParticleDataType& out_origins,
87 ParticleDataType& out_normals,
88 const MatrixType& parent_transforms = MatrixType::Identity(),
89 EmbreeRayCaster<ScalarOf<MeshType>>* ray_caster = nullptr,
90 bool has_normals = true)
91{
92 static_assert(MeshTrait<MeshType>::is_mesh(), "Mesh type is wrong");
93
94 // Typedef festival because templates...
95 using Scalar = DefaultScalar;
96 using Index = typename MeshType::Index;
97 using Point = typename EmbreeRayCaster<Scalar>::Point;
98 using Direction = typename EmbreeRayCaster<Scalar>::Direction;
99 using Scalar4 = typename EmbreeRayCaster<Scalar>::Scalar4;
100 using Index4 = typename EmbreeRayCaster<Scalar>::Index4;
101 using Point4 = typename EmbreeRayCaster<Scalar>::Point4;
102 using Direction4 = typename EmbreeRayCaster<Scalar>::Direction4;
103 using Mask4 = typename EmbreeRayCaster<Scalar>::Mask4;
104
105 // We need to convert to a shared_ptr AND the ray caster will make another copy of the data..
106 std::unique_ptr<EmbreeRayCaster<Scalar>> engine;
107 if (!ray_caster) {
108 auto mesh = lagrange::to_shared_ptr(
109 lagrange::create_mesh(mesh_proj_on.get_vertices(), mesh_proj_on.get_facets()));
110 // Robust mode gives slightly more accurate results...
111 engine = create_ray_caster<Scalar>(EMBREE_ROBUST, BUILD_QUALITY_HIGH);
112
113 // Gosh why do I need to specify a transform here?
114 engine->add_mesh(mesh, Eigen::Matrix<Scalar, 4, 4>::Identity());
115
116 // Do a dummy raycast to trigger scene update, otherwise `cast()` will not work in
117 // multithread mode... (this is why we need const-safety...)
118 engine->cast(Point(0, 0, 0), Direction(0, 0, 1));
119 ray_caster = engine.get();
120 } else {
121 logger().debug("Using provided ray-caster");
122 }
123
124 Index num_particles = safe_cast<Index>(origins.size());
125
126 // C^-1
127 MatrixType parent_transforms_inv = parent_transforms.inverse();
128 bool use_parent_transforms = !parent_transforms.isIdentity();
129
130 VectorType dir = direction.normalized();
131 Index num_ray_packets = (num_particles + 3) / 4;
132 Direction4 dirs;
133 dirs.row(0) = dir.transpose();
134 for (int i = 1; i < 4; ++i) {
135 dirs.row(i) = dirs.row(0);
136 }
137
138 std::vector<uint8_t> vector_hits(num_ray_packets, safe_cast<uint8_t>(0u));
139
140 ParticleDataType projected_origins;
141 ParticleDataType projected_normals;
142
143 out_origins.resize(num_particles);
144 if (has_normals) out_normals.resize(num_particles);
145
146 tbb::parallel_for(Index(0), num_ray_packets, [&](Index packet_index) {
147 uint32_t batchsize =
148 safe_cast<uint32_t>(std::min(num_particles - (packet_index << 2), safe_cast<Index>(4)));
149 Mask4 mask = Mask4::Constant(-1);
150 Point4 ray_origins;
151
152 for (uint32_t b = 0; b < batchsize; ++b) {
153 Index i = (packet_index << 2) + b;
154
155 // C * L
156 if (use_parent_transforms) {
157 ray_origins.row(b) =
158 (parent_transforms * origins[i].homogeneous()).hnormalized().transpose();
159 } else {
160 ray_origins.row(b) = origins[i].transpose();
161 }
162 }
163
164 for (Index b = batchsize; b < 4; ++b) {
165 mask(b) = 0;
166 }
167
168 Index4 mesh_indices;
169 Index4 instance_indices;
170 Index4 facet_indices;
171 Scalar4 ray_depths;
172 Point4 barys;
173 Point4 normals;
174 uint8_t hits = ray_caster->cast4(
175 batchsize,
176 ray_origins,
177 dirs,
178 mask,
179 mesh_indices,
180 instance_indices,
181 facet_indices,
182 ray_depths,
183 barys,
184 normals);
185
186 if (!hits) return;
187
188 vector_hits[packet_index] = hits;
189
190 for (uint32_t b = 0; b < batchsize; ++b) {
191 bool hit = hits & (1 << b);
192 if (hit) {
193 Index i = (packet_index << 2) + b;
194 if (has_normals) {
195 VectorType norm =
196 (ray_caster->get_transform(mesh_indices[b], instance_indices[b])
197 .template topLeftCorner<3, 3>() *
198 normals.row(b).transpose())
199 .normalized();
200 if (use_parent_transforms) {
201 norm = parent_transforms_inv.template topLeftCorner<3, 3>() * norm;
202 }
203 out_normals[i] = norm;
204 }
205
206 VectorType old_pos = ray_origins.row(b).transpose();
207 out_origins[i] = old_pos + dir * ray_depths(b);
208 if (use_parent_transforms) {
209 out_origins[i] =
210 (parent_transforms_inv * out_origins[i].homogeneous()).hnormalized();
211 }
212 }
213 }
214 });
215
216 // remove redundant output data
217 Index i = 0;
218 auto remove_func = [&](const VectorType&) -> bool {
219 Index packet_index = i >> 2;
220 Index b = i++ - (packet_index << 2);
221 return !(vector_hits[packet_index] & (1 << b));
222 };
223
224 out_origins.erase(
225 std::remove_if(out_origins.begin(), out_origins.end(), remove_func),
226 out_origins.end());
227
228 if (has_normals) {
229 i = 0;
230 out_normals.erase(
231 std::remove_if(out_normals.begin(), out_normals.end(), remove_func),
232 out_normals.end());
233 }
234}
235} // namespace legacy
236} // namespace raycasting
237} // namespace lagrange
A wrapper for Embree's raycasting API to compute ray intersections with (instances of) meshes.
Definition EmbreeRayCaster.h:59
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
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:22
Main namespace for Lagrange.
auto create_mesh(const Eigen::MatrixBase< DerivedV > &vertices, const Eigen::MatrixBase< DerivedF > &facets)
This function create a new mesh given the vertex and facet arrays by copying data into the Mesh objec...
Definition create_mesh.h:39
std::shared_ptr< T > to_shared_ptr(std::unique_ptr< T > &&ptr)
Helper for automatic type deduction for unique_ptr to shared_ptr conversion.
Definition common.h:88