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