Lagrange
Loading...
Searching...
No Matches
bind_simple_scene.h
1/*
2 * Copyright 2023 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/python/tensor_utils.h>
15#include <lagrange/scene/SimpleScene.h>
16#include <lagrange/scene/compute_mesh_weights.h>
17#include <lagrange/scene/filter_instances.h>
18#include <lagrange/scene/simple_scene_convert.h>
19#include <lagrange/utils/assert.h>
20
21#include <Eigen/Core>
22
23#include <functional>
24#include <type_traits>
25
26namespace lagrange::python {
27
28namespace nb = nanobind;
29
30template <typename Scalar, typename Index>
31void bind_simple_scene(nb::module_& m)
32{
33 using MeshInstance3D = lagrange::scene::MeshInstance<Scalar, Index, 3>;
34 nb::class_<MeshInstance3D>(m, "MeshInstance3D", "A single mesh instance in a scene")
35 .def(
36 nb::init<>(),
37 "Creates a new mesh instance with identity transform and mesh_index of 0.")
38 .def_rw(
39 "mesh_index",
40 &MeshInstance3D::mesh_index,
41 "Index of the mesh in the scene's mesh array.")
42 .def_prop_rw(
43 "transform",
44 [](MeshInstance3D& self) {
45 auto& M = self.transform.matrix();
46 using MatrixType = std::decay_t<decltype(M)>;
47 static_assert(!MatrixType::IsRowMajor, "Transformation matrix is not column major");
48
49 span<Scalar> data(M.data(), M.size());
50 size_t shape[2]{static_cast<size_t>(M.rows()), static_cast<size_t>(M.cols())};
51 int64_t stride[2]{1, 4};
52 return span_to_tensor(data, shape, stride, nb::cast(&self));
53 },
54 [](MeshInstance3D& self, Tensor<Scalar> tensor) {
55 auto [values, shape, stride] = tensor_to_span(tensor);
56 auto& M = self.transform.matrix();
57 using MatrixType = std::decay_t<decltype(M)>;
58 static_assert(!MatrixType::IsRowMajor, "Transformation matrix is not column major");
59
60 la_runtime_assert(is_dense(shape, stride));
61 la_runtime_assert(check_shape(shape, 4, 4));
62 if (stride[0] == 1) {
63 // Tensor is col major.
64 std::copy(values.begin(), values.end(), M.data());
65 } else {
66 // Tensor is row major.
67 M(0, 0) = values[0];
68 M(0, 1) = values[1];
69 M(0, 2) = values[2];
70 M(0, 3) = values[3];
71
72 M(1, 0) = values[4];
73 M(1, 1) = values[5];
74 M(1, 2) = values[6];
75 M(1, 3) = values[7];
76
77 M(2, 0) = values[8];
78 M(2, 1) = values[9];
79 M(2, 2) = values[10];
80 M(2, 3) = values[11];
81
82 M(3, 0) = values[12];
83 M(3, 1) = values[13];
84 M(3, 2) = values[14];
85 M(3, 3) = values[15];
86 }
87 },
88 R"(4x4 transformation matrix for this instance.
89
90The transformation matrix is stored in column-major order. Both row-major and column-major
91input tensors are supported for setting the transform.)",
92 nb::for_setter(nb::sig("def transform(self, arg: numpy.typing.ArrayLike, /) -> None")));
93
94 using SimpleScene3D = lagrange::scene::SimpleScene<Scalar, Index, 3>;
95 nb::class_<SimpleScene3D>(m, "SimpleScene3D", "Simple scene container for instanced meshes")
96 .def(nb::init<>(), "Creates an empty scene with no meshes or instances.")
97 .def_prop_ro("num_meshes", &SimpleScene3D::get_num_meshes, "Number of meshes in the scene")
98 .def(
99 "num_instances",
100 &SimpleScene3D::get_num_instances,
101 "mesh_index"_a,
102 R"(Gets the number of instances for a specific mesh.
103
104:param mesh_index: Index of the mesh.
105
106:return: Number of instances of the specified mesh.)")
107 .def_prop_ro(
108 "total_num_instances",
109 &SimpleScene3D::compute_num_instances,
110 "Total number of instances for all meshes in the scene")
111 .def(
112 "get_mesh",
113 &SimpleScene3D::get_mesh,
114 "mesh_index"_a,
115 R"(Gets a copy of the mesh at the specified index.
116
117:param mesh_index: Index of the mesh.
118
119:return: Copy of the mesh.)")
120 .def(
121 "ref_mesh",
122 &SimpleScene3D::ref_mesh,
123 "mesh_index"_a,
124 R"(Gets a reference to the mesh at the specified index.
125
126:param mesh_index: Index of the mesh.
127
128:return: Reference to the mesh.)")
129 .def(
130 "get_instance",
131 &SimpleScene3D::get_instance,
132 "mesh_index"_a,
133 "instance_index"_a,
134 R"(Gets a specific instance of a mesh.
135
136:param mesh_index: Index of the mesh.
137:param instance_index: Index of the instance for that mesh.
138
139:return: The mesh instance.)")
140 .def(
141 "reserve_meshes",
142 &SimpleScene3D::reserve_meshes,
143 "num_meshes"_a,
144 R"(Reserves storage for meshes.
145
146:param num_meshes: Number of meshes to reserve space for.)")
147 .def(
148 "add_mesh",
149 &SimpleScene3D::add_mesh,
150 "mesh"_a,
151 R"(Adds a mesh to the scene.
152
153:param mesh: Mesh to add.
154
155:return: Index of the newly added mesh.)")
156 .def(
157 "reserve_instances",
158 &SimpleScene3D::reserve_instances,
159 "mesh_index"_a,
160 "num_instances"_a,
161 R"(Reserves storage for instances of a specific mesh.
162
163:param mesh_index: Index of the mesh.
164:param num_instances: Number of instances to reserve space for.)")
165 .def(
166 "add_instance",
167 &SimpleScene3D::add_instance,
168 "instance"_a,
169 R"(Adds an instance to the scene.
170
171:param instance: Mesh instance to add.
172
173:return: Index of the newly added instance for its mesh.)");
174
175 // Mesh to scene + scene to mesh
176
177 m.def(
178 "simple_scene_to_mesh",
179 [](const SimpleScene3D& scene,
180 bool normalize_normals,
181 bool normalize_tangents_bitangents,
182 bool reorient,
183 bool preserve_attributes) {
184 TransformOptions transform_options;
185 transform_options.normalize_normals = normalize_normals;
186 transform_options.normalize_tangents_bitangents = normalize_tangents_bitangents;
187 transform_options.reorient = reorient;
188 return scene::simple_scene_to_mesh(scene, transform_options, preserve_attributes);
189 },
190 "scene"_a,
191 nb::kw_only(),
192 "normalize_normals"_a = TransformOptions{}.normalize_normals,
193 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
194 "reorient"_a = TransformOptions{}.reorient,
195 "preserve_attributes"_a = true,
196 R"(Converts a scene into a concatenated mesh with all the transforms applied.
197
198:param scene: Scene to convert.
199:param normalize_normals: If enabled, normals are normalized after transformation.
200:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
201:param reorient: If enabled, flip facets and reorient attributes for instances with a negative-determinant transform.
202:param preserve_attributes: Preserve shared attributes and map them to the output mesh.
203
204:return: Concatenated mesh.)");
205
206 m.def(
207 "simple_scene_to_meshes",
208 [](const SimpleScene3D& scene,
209 bool normalize_normals,
210 bool normalize_tangents_bitangents,
211 bool reorient) {
212 TransformOptions transform_options;
213 transform_options.normalize_normals = normalize_normals;
214 transform_options.normalize_tangents_bitangents = normalize_tangents_bitangents;
215 transform_options.reorient = reorient;
216 return scene::simple_scene_to_meshes(scene, transform_options);
217 },
218 "scene"_a,
219 nb::kw_only(),
220 "normalize_normals"_a = TransformOptions{}.normalize_normals,
221 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
222 "reorient"_a = TransformOptions{}.reorient,
223 R"(Converts a scene into a list of meshes with all the transforms applied.
224
225:param scene: Scene to convert.
226:param normalize_normals: If enabled, normals are normalized after transformation.
227:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
228:param reorient: If enabled, flip facets and reorient attributes for instances with a negative-determinant transform.
229
230:return: List of transformed meshes.)");
231
232 using MeshType = lagrange::SurfaceMesh<Scalar, Index>;
233 m.def(
234 "mesh_to_simple_scene",
235 [](const MeshType& mesh) { return scene::mesh_to_simple_scene<3>(mesh); },
236 "mesh"_a,
237 R"(Converts a single mesh into a simple scene with a single identity instance of the input mesh.
238
239:param mesh: Input mesh to convert.
240
241:return: Simple scene containing the input mesh.)");
242
243 m.def(
244 "meshes_to_simple_scene",
245 [](std::vector<MeshType> meshes) {
246 return scene::meshes_to_simple_scene<3>(std::move(meshes));
247 },
248 "meshes"_a,
249 R"(Converts a list of meshes into a simple scene with a single identity instance of each input mesh.
250
251:param meshes: Input meshes to convert.
252
253:return: Simple scene containing the input meshes.)");
254
255 m.def(
256 "compute_mesh_weights",
257 [](const SimpleScene3D& scene, scene::FacetAllocationStrategy facet_allocation_strategy) {
258 return scene::compute_mesh_weights(scene, facet_allocation_strategy);
259 },
260 "scene"_a,
261 "facet_allocation_strategy"_a = scene::FacetAllocationStrategy::EvenSplit,
262 R"(Computes mesh weights of a scene.
263
264:param scene: Input scene. Must contain at least one mesh. For
265 ``RelativeToMeshArea``, if the scene contains no instances (or only
266 degenerate transforms) the total transformed area is zero and all returned
267 weights are zero. For ``RelativeToNumFacets`` the total facet count must be
268 positive, otherwise the returned weights will contain non-finite values.
269:param facet_allocation_strategy: Strategy used to compute the weights distribution. Defaults to
270 ``FacetAllocationStrategy.EvenSplit``. ``FacetAllocationStrategy.Synchronized``
271 is not supported by this function and will raise :class:`RuntimeError`.
272
273:return: Weights for each mesh of the scene that sum to unity, each in [0, 1].)");
274
275 m.def(
276 "filter_instances",
277 [](const SimpleScene3D& s, std::function<bool(Index, Index)> keep) {
278 return lagrange::scene::filter_instances<Scalar, Index, 3>(s, keep);
279 },
280 "scene"_a,
281 "keep"_a,
282 R"(Build a new scene keeping only instances for which ``keep(mesh_index, instance_index)``
283returns True. Meshes with no remaining instances are dropped; mesh indices are compacted.
284
285:param scene: Input scene.
286:param keep: Callable ``(mesh_index, instance_index) -> bool``.
287
288:return: Filtered scene.)");
289}
290
291} // namespace lagrange::python
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:177
::nonstd::span< T, Extent > span
A bounds-safe view for sequences of objects.
Definition span.h:27