14#include <lagrange/AttributeValueType.h>
15#include <lagrange/CameraTransforms.h>
16#include <lagrange/Logger.h>
17#include <lagrange/python/binding.h>
18#include <lagrange/python/tensor_utils.h>
19#include <lagrange/scene/Scene.h>
20#include <lagrange/scene/SimpleScene.h>
21#include <lagrange/scene/internal/scene_string_utils.h>
22#include <lagrange/scene/internal/shared_utils.h>
23#include <lagrange/scene/scene_convert.h>
24#include <lagrange/scene/scene_utils.h>
25#include <lagrange/utils/assert.h>
27#include "bind_value.h"
29namespace lagrange::python {
31namespace nb = nanobind;
33void bind_scene(nb::module_& m)
35 using namespace lagrange::scene;
37 using Index = uint32_t;
38 using SceneType = Scene<Scalar, Index>;
40 nb::bind_vector<SafeVector<ElementId>>(m,
"ElementIdList");
41 nb::bind_safe_vector<SafeVector<Node>>(m,
"NodeList");
42 nb::bind_safe_vector<SafeVector<SceneMeshInstance>>(m,
"SceneMeshInstanceList");
43 nb::bind_safe_vector<SafeVector<SurfaceMesh<Scalar, Index>>>(m,
"SurfaceMeshList");
44 nb::bind_safe_vector<SafeVector<ImageExperimental>>(m,
"ImageList");
45 nb::bind_safe_vector<SafeVector<Texture>>(m,
"TextureList");
46 nb::bind_safe_vector<SafeVector<MaterialExperimental>>(m,
"MaterialList");
47 nb::bind_safe_vector<SafeVector<Light>>(m,
"LightList");
48 nb::bind_safe_vector<SafeVector<Camera>>(m,
"CameraList");
49 nb::bind_safe_vector<SafeVector<Skeleton>>(m,
"SkeletonList");
50 nb::bind_safe_vector<SafeVector<Animation>>(m,
"AnimationList");
52 nb::class_<lagrange::scene::Extensions>(m,
"Extensions")
55 [](
const lagrange::scene::Extensions& self) {
56 return scene::internal::to_string(self);
58 .def_prop_ro(
"size", &Extensions::size)
59 .def_prop_ro(
"empty", &Extensions::empty)
63 nb::rv_policy::reference_internal,
64 "Raw data stored in this extension as a dict");
66 nb::class_<SceneMeshInstance>(
69 "Pairs a mesh with its materials (zero, one, or more)")
73 [](
const SceneMeshInstance& self) {
return scene::internal::to_string(self); })
76 [](SceneMeshInstance& self) -> std::optional<ElementId> {
77 if (self.mesh != invalid_element)
82 [](SceneMeshInstance& self, ElementId mesh) { self.mesh = mesh; },
83 "Mesh index. Has to be a valid index in the scene.meshes vector (None if invalid)")
86 &SceneMeshInstance::materials,
87 "Material indices in the scene.materials vector. This is typically a single material "
88 "index. When a single mesh uses multiple materials, the AttributeName::material_id "
89 "facet attribute should be defined.");
91 nb::class_<Node>(m,
"Node",
"Represents a node in the scene hierarchy")
93 .def(
"__repr__", [](
const Node& self) {
return scene::internal::to_string(self); })
94 .def_rw(
"name", &Node::name,
"Node name. May not be unique and can be empty")
98 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
99 node.transform.data(),
104 [](Node& node, nb::ndarray<nb::numpy, const float, nb::shape<4, 4>> t) ->
void {
105 auto view = t.view<float, nb::ndim<2>>();
107 for (
size_t i = 0; i < 4; i++) {
108 for (
size_t j = 0; j < 4; j++) {
109 node.transform.data()[i + j * 4] = view(i, j);
113 "Transform of the node, relative to its parent")
116 [](Node& node) -> std::optional<ElementId> {
117 if (node.parent != invalid_element)
122 [](Node& node, ElementId parent) { node.parent = parent; },
123 "Parent index. May be invalid if the node has no parent (e.g. the root)")
124 .def_rw(
"children", &Node::children,
"Children indices. May be empty")
125 .def_rw(
"meshes", &Node::meshes,
"List of meshes contained in this node")
126 .def_rw(
"cameras", &Node::cameras,
"List of cameras contained in this node")
127 .def_rw(
"lights", &Node::lights,
"List of lights contained in this node")
128 .def_rw(
"extensions", &Node::extensions);
130 nb::class_<ImageBufferExperimental> image_buffer(
133 "Minimalistic image data structure that stores the raw image data");
134 image_buffer.def(nb::init<>())
137 [](
const ImageBufferExperimental& self) {
return scene::internal::to_string(self); })
138 .def_ro(
"width", &ImageBufferExperimental::width,
"Image width")
139 .def_ro(
"height", &ImageBufferExperimental::height,
"Image height")
142 &ImageBufferExperimental::num_channels,
143 "Number of image channels (must be 1, 3, or 4)")
146 [](ImageBufferExperimental& self) {
149 case AttributeValueType::e_int8_t:
151 nb::ndarray<int8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
152 reinterpret_cast<int8_t*
>(self.
data.data()),
156 nb::rv_policy::reference_internal);
157 case AttributeValueType::e_uint8_t:
159 nb::ndarray<uint8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
160 reinterpret_cast<uint8_t*
>(self.
data.data()),
164 nb::rv_policy::reference_internal);
165 case AttributeValueType::e_int16_t:
167 nb::ndarray<int16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
168 reinterpret_cast<int16_t*
>(self.
data.data()),
172 nb::rv_policy::reference_internal);
173 case AttributeValueType::e_uint16_t:
175 nb::ndarray<uint16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
176 reinterpret_cast<uint16_t*
>(self.
data.data()),
180 nb::rv_policy::reference_internal);
181 case AttributeValueType::e_int32_t:
183 nb::ndarray<int32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
184 reinterpret_cast<int32_t*
>(self.
data.data()),
188 nb::rv_policy::reference_internal);
189 case AttributeValueType::e_uint32_t:
191 nb::ndarray<uint32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
192 reinterpret_cast<uint32_t*
>(self.
data.data()),
196 nb::rv_policy::reference_internal);
197 case AttributeValueType::e_int64_t:
199 nb::ndarray<int64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
200 reinterpret_cast<int64_t*
>(self.
data.data()),
204 nb::rv_policy::reference_internal);
205 case AttributeValueType::e_uint64_t:
207 nb::ndarray<uint64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
208 reinterpret_cast<uint64_t*
>(self.
data.data()),
212 nb::rv_policy::reference_internal);
213 case AttributeValueType::e_float:
215 nb::ndarray<float, nb::numpy, nb::c_contig, nb::device::cpu>(
216 reinterpret_cast<float*
>(self.
data.data()),
220 nb::rv_policy::reference_internal);
221 case AttributeValueType::e_double:
223 nb::ndarray<double, nb::numpy, nb::c_contig, nb::device::cpu>(
224 reinterpret_cast<double*
>(self.
data.data()),
228 nb::rv_policy::reference_internal);
229 default:
throw nb::type_error(
"Unsupported image buffer `dtype`!");
232 [](ImageBufferExperimental& self,
233 nb::ndarray<nb::numpy, nb::c_contig, nb::device::cpu> tensor) {
235 self.
width = tensor.shape(1);
236 self.
height = tensor.shape(0);
238 auto dtype = tensor.dtype();
239 if (dtype == nb::dtype<int8_t>()) {
241 }
else if (dtype == nb::dtype<uint8_t>()) {
243 }
else if (dtype == nb::dtype<int16_t>()) {
245 }
else if (dtype == nb::dtype<uint16_t>()) {
247 }
else if (dtype == nb::dtype<int32_t>()) {
249 }
else if (dtype == nb::dtype<uint32_t>()) {
251 }
else if (dtype == nb::dtype<int64_t>()) {
253 }
else if (dtype == nb::dtype<uint64_t>()) {
255 }
else if (dtype == nb::dtype<float>()) {
257 }
else if (dtype == nb::dtype<double>()) {
260 throw nb::type_error(
"Unsupported input tensor `dtype`!");
262 self.
data.resize(tensor.nbytes());
264 reinterpret_cast<uint8_t*
>(tensor.data()),
265 reinterpret_cast<uint8_t*
>(tensor.data()) + tensor.nbytes(),
268 "Raw buffer of size (width * height * num_channels * num_bits_per_element / 8) bytes "
269 "containing image data")
272 [](ImageBufferExperimental& self) -> std::optional<nb::type_object> {
273 auto np = nb::module_::import_(
"numpy");
275 case AttributeValueType::e_int8_t:
return np.attr(
"int8");
276 case AttributeValueType::e_int16_t:
return np.attr(
"int16");
277 case AttributeValueType::e_int32_t:
return np.attr(
"int32");
278 case AttributeValueType::e_int64_t:
return np.attr(
"int64");
279 case AttributeValueType::e_uint8_t:
return np.attr(
"uint8");
280 case AttributeValueType::e_uint16_t:
return np.attr(
"uint16");
281 case AttributeValueType::e_uint32_t:
return np.attr(
"uint32");
282 case AttributeValueType::e_uint64_t:
return np.attr(
"uint64");
283 case AttributeValueType::e_float:
return np.attr(
"float32");
284 case AttributeValueType::e_double:
return np.attr(
"float64");
285 default:
logger().warn(
"Image buffer has an unknown dtype.");
return std::nullopt;
288 "The scalar type of the elements in the buffer");
290 nb::class_<ImageExperimental> image(
293 "Image structure that can store either image data or reference to an image file");
294 image.def(nb::init<>())
297 [](
const ImageExperimental& self) {
return scene::internal::to_string(self); })
300 &ImageExperimental::name,
301 "Image name. Not guaranteed to be unique and can be empty")
302 .def_rw(
"image", &ImageExperimental::image,
"Image data")
305 [](
const ImageExperimental& self) -> std::optional<std::string> {
306 if (self.
uri.empty())
309 return self.
uri.string();
311 [](ImageExperimental& self, std::optional<std::string> uri) {
313 self.
uri = fs::path(uri.value());
315 self.
uri = fs::path();
317 "Image file path. This path is relative to the file that contains the scene. It is "
318 "only valid if image data should be mapped to an external file")
319 .def_rw(
"extensions", &ImageExperimental::extensions,
"Image extensions");
321 nb::class_<TextureInfo>(
324 "Pair of texture index (which texture to use) and texture coordinate index (which set of "
327 .def(
"__repr__", [](
const TextureInfo& self) {
return scene::internal::to_string(self); })
330 [](
const TextureInfo& self) -> std::optional<ElementId> {
331 if (self.
index != invalid_element)
336 [](TextureInfo& self, std::optional<ElementId> index) {
337 if (index.has_value())
338 self.
index = index.value();
340 self.
index = invalid_element;
342 "Texture index. Index in scene.textures vector. `None` if not set")
345 &TextureInfo::texcoord,
346 "Index of UV coordinates. Usually stored in the mesh as `texcoord_x` attribute where x "
347 "is this variable. This is typically 0");
349 nb::class_<MaterialExperimental> material(
352 "PBR material, based on the gltf specification. This is subject to change, to support more "
354 material.def(nb::init<>())
357 [](
const MaterialExperimental& self) {
return scene::internal::to_string(self); })
360 &MaterialExperimental::name,
361 "Material name. May not be unique, and can be empty")
362 .def_rw(
"base_color_value", &MaterialExperimental::base_color_value,
"Base color value")
364 "base_color_texture",
365 &MaterialExperimental::base_color_texture,
366 "Base color texture")
369 &MaterialExperimental::alpha_mode,
370 "The alpha mode specifies how to interpret the alpha value of the base color")
371 .def_rw(
"alpha_cutoff", &MaterialExperimental::alpha_cutoff,
"Alpha cutoff value")
372 .def_rw(
"emissive_value", &MaterialExperimental::emissive_value,
"Emissive color value")
373 .def_rw(
"emissive_texture", &MaterialExperimental::emissive_texture,
"Emissive texture")
374 .def_rw(
"metallic_value", &MaterialExperimental::metallic_value,
"Metallic value")
375 .def_rw(
"roughness_value", &MaterialExperimental::roughness_value,
"Roughness value")
377 "metallic_roughness_texture",
378 &MaterialExperimental::metallic_roughness_texture,
379 "Metalness and roughness are packed together in a single texture. Green channel has "
380 "roughness, blue channel has metalness")
381 .def_rw(
"normal_texture", &MaterialExperimental::normal_texture,
"Normal texture")
384 &MaterialExperimental::normal_scale,
385 "Normal scaling factor. normal = normalize(<sampled tex value> * 2 - 1) * vec3(scale, "
387 .def_rw(
"occlusion_texture", &MaterialExperimental::occlusion_texture,
"Occlusion texture")
389 "occlusion_strength",
390 &MaterialExperimental::occlusion_strength,
391 "Occlusion strength. color = lerp(color, color * <sampled tex value>, strength)")
394 &MaterialExperimental::double_sided,
395 "Whether the material is double-sided")
396 .def_rw(
"extensions", &MaterialExperimental::extensions,
"Material extensions");
398 nb::enum_<MaterialExperimental::AlphaMode>(material,
"AlphaMode",
"Alpha mode")
401 MaterialExperimental::AlphaMode::Opaque,
402 "Alpha is ignored, and rendered output is opaque")
405 MaterialExperimental::AlphaMode::Mask,
406 "Output is either opaque or transparent depending on the alpha value and the "
407 "alpha_cutoff value")
410 MaterialExperimental::AlphaMode::Blend,
411 "Alpha value is used to composite source and destination");
414 nb::class_<Texture> texture(m,
"Texture",
"Texture");
415 texture.def(nb::init<>())
416 .def(
"__repr__", [](
const Texture& self) {
return scene::internal::to_string(self); })
417 .def_rw(
"name", &Texture::name,
"Texture name")
420 [](Texture& self) -> std::optional<ElementId> {
421 if (self.image != invalid_element)
426 [](Texture& self, ElementId img) { self.image = img; },
427 "Index of image in scene.images vector (None if invalid)")
430 &Texture::mag_filter,
431 "Texture magnification filter, used when texture appears larger on screen than the "
435 &Texture::min_filter,
436 "Texture minification filter, used when the texture appears smaller on screen than the "
438 .def_rw(
"wrap_u", &Texture::wrap_u,
"Texture wrap mode for U coordinate")
439 .def_rw(
"wrap_v", &Texture::wrap_v,
"Texture wrap mode for V coordinate")
440 .def_rw(
"scale", &Texture::scale,
"Texture scale")
441 .def_rw(
"offset", &Texture::offset,
"Texture offset")
442 .def_rw(
"rotation", &Texture::rotation,
"Texture rotation")
443 .def_rw(
"extensions", &Texture::extensions,
"Texture extensions");
445 nb::enum_<Texture::WrapMode>(texture,
"WrapMode",
"Texture wrap mode")
446 .value(
"Wrap", Texture::WrapMode::Wrap,
"u|v becomes u%1|v%1")
449 Texture::WrapMode::Clamp,
450 "Coordinates outside [0, 1] are clamped to the nearest value")
453 Texture::WrapMode::Decal,
454 "If the texture coordinates for a pixel are outside [0, 1], the texture is not applied")
455 .value(
"Mirror", Texture::WrapMode::Mirror,
"Mirror wrap mode");
456 nb::enum_<Texture::TextureFilter>(texture,
"TextureFilter",
"Texture filter mode")
457 .value(
"Undefined", Texture::TextureFilter::Undefined,
"Undefined filter")
458 .value(
"Nearest", Texture::TextureFilter::Nearest,
"Nearest neighbor filtering")
459 .value(
"Linear", Texture::TextureFilter::Linear,
"Linear filtering")
461 "NearestMipmapNearest",
462 Texture::TextureFilter::NearestMipmapNearest,
463 "Nearest mipmap nearest filtering")
465 "LinearMipmapNearest",
466 Texture::TextureFilter::LinearMipmapNearest,
467 "Linear mipmap nearest filtering")
469 "NearestMipmapLinear",
470 Texture::TextureFilter::NearestMipmapLinear,
471 "Nearest mipmap linear filtering")
473 "LinearMipmapLinear",
474 Texture::TextureFilter::LinearMipmapLinear,
475 "Linear mipmap linear filtering");
477 nb::class_<Light> light(m,
"Light",
"Light");
478 light.def(nb::init<>())
479 .def(
"__repr__", [](
const Light& self) {
return scene::internal::to_string(self); })
480 .def_rw(
"name", &Light::name,
"Light name")
481 .def_rw(
"type", &Light::type,
"Light type")
485 "Light position. Note that the light is part of the scene graph, and has an associated "
486 "transform in its node. This value is relative to the coordinate system defined by the "
488 .def_rw(
"direction", &Light::direction,
"Light direction")
489 .def_rw(
"up", &Light::up,
"Light up vector")
490 .def_rw(
"intensity", &Light::intensity,
"Light intensity")
492 "attenuation_constant",
493 &Light::attenuation_constant,
494 "Attenuation constant. Intensity of light at a given distance 'd' is: intensity / "
495 "(attenuation_constant + attenuation_linear * d + attenuation_quadratic * d * d + "
496 "attenuation_cubic * d * d * d)")
497 .def_rw(
"attenuation_linear", &Light::attenuation_linear,
"Linear attenuation factor")
499 "attenuation_quadratic",
500 &Light::attenuation_quadratic,
501 "Quadratic attenuation factor")
502 .def_rw(
"attenuation_cubic", &Light::attenuation_cubic,
"Cubic attenuation factor")
506 "Range is defined for point and spot lights. It defines a distance cutoff at which the "
507 "light intensity is to be considered zero. When the value is 0, range is assumed to be "
509 .def_rw(
"color_diffuse", &Light::color_diffuse,
"Diffuse color")
510 .def_rw(
"color_specular", &Light::color_specular,
"Specular color")
511 .def_rw(
"color_ambient", &Light::color_ambient,
"Ambient color")
514 &Light::angle_inner_cone,
515 "Inner angle of a spot light's light cone. 2PI for point lights, undefined for "
516 "directional lights")
519 &Light::angle_outer_cone,
520 "Outer angle of a spot light's light cone. 2PI for point lights, undefined for "
521 "directional lights")
522 .def_rw(
"size", &Light::size,
"Size of area light source")
523 .def_rw(
"extensions", &Light::extensions,
"Light extensions");
525 nb::enum_<Light::Type>(light,
"Type",
"Light type")
526 .value(
"Undefined", Light::Type::Undefined,
"Undefined light type")
527 .value(
"Directional", Light::Type::Directional,
"Directional light")
528 .value(
"Point", Light::Type::Point,
"Point light")
529 .value(
"Spot", Light::Type::Spot,
"Spot light")
530 .value(
"Ambient", Light::Type::Ambient,
"Ambient light")
531 .value(
"Area", Light::Type::Area,
"Area light");
533 nb::class_<Camera> camera(m,
"Camera",
"Camera");
534 camera.def(nb::init<>())
535 .def(
"__repr__", [](
const Camera& self) {
return scene::internal::to_string(self); })
536 .def_rw(
"name", &Camera::name,
"Camera name")
540 "Camera position. Note that the camera is part of the scene graph, and has an "
541 "associated transform in its node. This value is relative to the coordinate system "
542 "defined by the node")
543 .def_rw(
"up", &Camera::up,
"Camera up vector")
544 .def_rw(
"look_at", &Camera::look_at,
"Camera look-at point")
548 "Distance of the near clipping plane. This value cannot be 0")
549 .def_rw(
"far_plane", &Camera::far_plane,
"Distance of the far clipping plane")
550 .def_rw(
"type", &Camera::type,
"Camera type")
553 &Camera::aspect_ratio,
554 "Screen aspect ratio. This is the value of width / height of the screen. aspect_ratio "
555 "= tan(horizontal_fov / 2) / tan(vertical_fov / 2)")
558 &Camera::horizontal_fov,
559 "Horizontal field of view angle, in radians. This is the angle between the left and "
560 "right borders of the viewport. It should not be greater than Pi. fov is only defined "
561 "when the camera type is perspective, otherwise it should be 0")
563 "orthographic_width",
564 &Camera::orthographic_width,
565 "Half width of the orthographic view box. Or horizontal magnification. This is only "
566 "defined when the camera type is orthographic, otherwise it should be 0")
569 &Camera::get_vertical_fov,
570 "Get the vertical field of view. Make sure aspect_ratio is set before calling this")
572 "set_horizontal_fov_from_vertical_fov",
573 &Camera::set_horizontal_fov_from_vertical_fov,
575 "Set horizontal fov from vertical fov. Make sure aspect_ratio is set before calling "
577 .def_rw(
"extensions", &Camera::extensions,
"Camera extensions");
579 nb::enum_<Camera::Type>(camera,
"Type",
"Camera type")
580 .value(
"Perspective", Camera::Type::Perspective,
"Perspective projection")
581 .value(
"Orthographic", Camera::Type::Orthographic,
"Orthographic projection");
583 nb::class_<Animation>(m,
"Animation",
"Animation")
585 .def(
"__repr__", [](
const Animation& self) {
return scene::internal::to_string(self); })
586 .def_rw(
"name", &Animation::name,
"Animation name")
587 .def_rw(
"extensions", &Animation::extensions,
"Animation extensions");
590 nb::class_<Skeleton>(m,
"Skeleton",
"Skeleton")
592 .def(
"__repr__", [](
const Skeleton& self) {
return scene::internal::to_string(self); })
596 "This skeleton is used to deform those meshes. This will typically contain one value, "
597 "but can have zero or multiple meshes. The value is the index in the scene meshes")
598 .def_rw(
"extensions", &Skeleton::extensions,
"Skeleton extensions");
601 nb::class_<SceneType>(m,
"Scene",
"A 3D scene")
603 .def(
"__repr__", [](
const SceneType& self) {
return scene::internal::to_string(self); })
604 .def_rw(
"name", &SceneType::name,
"Name of the scene")
608 "Scene nodes. This is a list of nodes, the hierarchy information is contained by each "
609 "node having a list of children as indices to this vector")
612 &SceneType::root_nodes,
613 "Root nodes. This is typically one. Must be at least one")
614 .def_rw(
"meshes", &SceneType::meshes,
"Scene meshes")
615 .def_rw(
"images", &SceneType::images,
"Images")
616 .def_rw(
"textures", &SceneType::textures,
"Textures. They can reference images")
617 .def_rw(
"materials", &SceneType::materials,
"Materials. They can reference textures")
618 .def_rw(
"lights", &SceneType::lights,
"Lights in the scene")
622 "Cameras. The first camera (if any) is the default camera view")
623 .def_rw(
"skeletons", &SceneType::skeletons,
"Scene skeletons")
624 .def_rw(
"animations", &SceneType::animations,
"Animations (unused for now)")
625 .def_rw(
"extensions", &SceneType::extensions,
"Scene extensions")
634 MaterialExperimental,
638 Animation> element) {
641 using T = std::decay_t<
decltype(value)>;
642 return self.add(std::forward<T>(value));
647 R
"(Add an element to the scene.
649:param element: The element to add to the scene. E.g. node, mesh, image, texture, material, light, camera, skeleton, or animation.
651:returns: The id of the added element.)")
654 &SceneType::add_child,
657 R
"(Add a child node to a parent node. The parent-child relationship will be updated for both nodes.
659:param parent_id: The parent node id.
660:param child_id: The child node id.
662:returns: The id of the added child node.)");
665 "compute_global_node_transform",
666 [](
const SceneType& scene,
size_t node_idx) {
667 auto t = utils::compute_global_node_transform<Scalar, Index>(scene, node_idx);
668 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
677 R
"(Compute the global transform associated with a node.
679:param scene: The input scene.
680:param node_idx: The index of the target node.
682:returns: The global transform of the target node, which is the combination of transforms from this node all the way to the root.
686 "camera_transforms_from_scene",
687 [](
const SceneType& scene) {
688 return scene::internal::camera_transforms_from_scene<Scalar, Index>(scene);
691 R
"(Extract view and projection transforms for every camera referenced by a node in the scene.
693:param scene: The input scene.
695:returns: A list of CameraTransforms, one per camera instance in the scene.)");
699 [](
const SceneType& scene,
700 bool normalize_normals,
701 bool normalize_tangents_bitangents,
702 bool preserve_attributes) {
703 TransformOptions transform_options;
706 return scene::scene_to_mesh(scene, transform_options, preserve_attributes);
709 "normalize_normals"_a = TransformOptions{}.normalize_normals,
710 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
711 "preserve_attributes"_a =
true,
712 R
"(Converts a scene into a concatenated mesh with all the transforms applied.
714:param scene: Scene to convert.
715:param normalize_normals: If enabled, normals are normalized after transformation.
716:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
717:param preserve_attributes: Preserve shared attributes and map them to the output mesh.
719:return: Concatenated mesh.)");
723 [](
const SceneType& scene,
bool normalize_normals,
bool normalize_tangents_bitangents) {
724 TransformOptions transform_options;
727 return scene::scene_to_meshes(scene, transform_options);
730 "normalize_normals"_a = TransformOptions{}.normalize_normals,
731 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
732 R
"(Converts a scene into a list of meshes with all the transforms applied.
734:param scene: Scene to convert.
735:param normalize_normals: If enabled, normals are normalized after transformation.
736:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
738:return: List of transformed meshes.)");
741 "scene_to_meshes_and_materials",
742 [](
const SceneType& scene,
bool normalize_normals,
bool normalize_tangents_bitangents)
743 -> std::pair<std::vector<SceneType::MeshType>, std::vector<std::vector<ElementId>>> {
744 TransformOptions transform_options;
747 auto [meshes, material_ids] =
748 scene::scene_to_meshes_and_materials(scene, transform_options);
749 return {std::move(meshes), std::move(material_ids)};
752 "normalize_normals"_a = TransformOptions{}.normalize_normals,
753 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
754 R
"(Converts a scene into a list of meshes with all the transforms applied and a list of material IDs.
756:param scene: Scene to convert.
757:param normalize_normals: If enabled, normals are normalized after transformation.
758:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
760:return: List of meshes with transforms applied and a list of material IDs.)");
766 R
"(Converts a single mesh into a scene with a single identity instance of the input mesh.
768:param mesh: Input mesh to convert.
770:return: Scene containing the input mesh.)");
774 [](std::vector<SceneType::MeshType> meshes) {
775 return scene::meshes_to_scene(std::move(meshes));
778 R
"(Converts a list of meshes into a scene with a single identity instance of each input mesh.
780:param meshes: Input meshes to convert.
782:return: Scene containing the input meshes.)");
784 using SimpleScene3D = scene::SimpleScene<Scalar, Index, 3>;
787 "scene_to_simple_scene",
788 [](
const SceneType& scene) {
return scene::scene_to_simple_scene(scene); },
790 R
"(Converts a Scene into a SimpleScene.
792The Scene's node hierarchy is flattened: each mesh instance in the scene becomes a
793MeshInstance in the SimpleScene with the accumulated world transform. Meshes are copied
794by index. Materials and other scene metadata (images, textures, cameras, lights) are not
795preserved in the SimpleScene.
797:param scene: Input scene to convert.
799:return: SimpleScene containing all mesh instances from the scene.)");
802 "simple_scene_to_scene",
803 [](
const SimpleScene3D& simple_scene) {
804 return scene::simple_scene_to_scene(simple_scene);
807 R
"(Converts a SimpleScene into a Scene.
809Each mesh instance in the SimpleScene becomes a node in the Scene with the instance
810transform. All nodes are direct children of a single root node. Meshes are copied
813:param simple_scene: Input simple scene to convert.
815:return: Scene containing the meshes and instances from the SimpleScene.)");
SurfaceMesh< Scalar, Index > MeshType
Definition SimpleScene.h:65
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.
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:175
size_t height
Image height.
Definition Scene.h:95
size_t width
Image width.
Definition Scene.h:92
AttributeValueType element_type
The scalar type of the elements in the buffer.
Definition Scene.h:101
std::vector< unsigned char > data
Raw buffer of size (width * height * num_channels * num_bits_per_element / 8) bytes containing image ...
Definition Scene.h:104
size_t num_channels
Number of image channels (must be 1, 3, or 4).
Definition Scene.h:98
fs::path uri
Image file path.
Definition Scene.h:125
ElementId index
Texture index. Index in scene.textures vector.
Definition Scene.h:137