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.",
90 LA_SAFE_VECTOR_SETTER(
"materials",
"ElementIdList | collections.abc.Sequence[int]"));
92 nb::class_<Node>(m,
"Node",
"Represents a node in the scene hierarchy")
94 .def(
"__repr__", [](
const Node& self) {
return scene::internal::to_string(self); })
95 .def_rw(
"name", &Node::name,
"Node name. May not be unique and can be empty")
99 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
100 node.transform.data(),
105 [](Node& node, nb::ndarray<nb::numpy, const float, nb::shape<4, 4>> t) ->
void {
106 auto view = t.view<float, nb::ndim<2>>();
108 for (
size_t i = 0; i < 4; i++) {
109 for (
size_t j = 0; j < 4; j++) {
110 node.transform.data()[i + j * 4] = view(i, j);
114 "Transform of the node, relative to its parent",
115 nb::for_setter(nb::sig(
"def transform(self, arg: numpy.typing.ArrayLike, /) -> None")))
118 [](Node& node) -> std::optional<ElementId> {
119 if (node.parent != invalid_element)
124 [](Node& node, ElementId parent) { node.parent = parent; },
125 "Parent index. May be invalid if the node has no parent (e.g. the root)")
126 .def_rw(
"children", &Node::children,
"Children indices. May be empty")
130 "List of meshes contained in this node",
131 LA_SAFE_VECTOR_SETTER(
"meshes",
"collections.abc.Sequence[SceneMeshInstance]"))
132 .def_rw(
"cameras", &Node::cameras,
"List of cameras contained in this node")
133 .def_rw(
"lights", &Node::lights,
"List of lights contained in this node")
134 .def_rw(
"extensions", &Node::extensions);
136 nb::class_<ImageBufferExperimental> image_buffer(
139 "Minimalistic image data structure that stores the raw image data");
140 image_buffer.def(nb::init<>())
143 [](
const ImageBufferExperimental& self) {
return scene::internal::to_string(self); })
144 .def_ro(
"width", &ImageBufferExperimental::width,
"Image width")
145 .def_ro(
"height", &ImageBufferExperimental::height,
"Image height")
148 &ImageBufferExperimental::num_channels,
149 "Number of image channels (must be 1, 3, or 4)")
152 [](ImageBufferExperimental& self) {
155 case AttributeValueType::e_int8_t:
157 nb::ndarray<int8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
158 reinterpret_cast<int8_t*
>(self.
data.data()),
162 nb::rv_policy::reference_internal);
163 case AttributeValueType::e_uint8_t:
165 nb::ndarray<uint8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
166 reinterpret_cast<uint8_t*
>(self.
data.data()),
170 nb::rv_policy::reference_internal);
171 case AttributeValueType::e_int16_t:
173 nb::ndarray<int16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
174 reinterpret_cast<int16_t*
>(self.
data.data()),
178 nb::rv_policy::reference_internal);
179 case AttributeValueType::e_uint16_t:
181 nb::ndarray<uint16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
182 reinterpret_cast<uint16_t*
>(self.
data.data()),
186 nb::rv_policy::reference_internal);
187 case AttributeValueType::e_int32_t:
189 nb::ndarray<int32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
190 reinterpret_cast<int32_t*
>(self.
data.data()),
194 nb::rv_policy::reference_internal);
195 case AttributeValueType::e_uint32_t:
197 nb::ndarray<uint32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
198 reinterpret_cast<uint32_t*
>(self.
data.data()),
202 nb::rv_policy::reference_internal);
203 case AttributeValueType::e_int64_t:
205 nb::ndarray<int64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
206 reinterpret_cast<int64_t*
>(self.
data.data()),
210 nb::rv_policy::reference_internal);
211 case AttributeValueType::e_uint64_t:
213 nb::ndarray<uint64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
214 reinterpret_cast<uint64_t*
>(self.
data.data()),
218 nb::rv_policy::reference_internal);
219 case AttributeValueType::e_float:
221 nb::ndarray<float, nb::numpy, nb::c_contig, nb::device::cpu>(
222 reinterpret_cast<float*
>(self.
data.data()),
226 nb::rv_policy::reference_internal);
227 case AttributeValueType::e_double:
229 nb::ndarray<double, nb::numpy, nb::c_contig, nb::device::cpu>(
230 reinterpret_cast<double*
>(self.
data.data()),
234 nb::rv_policy::reference_internal);
235 default:
throw nb::type_error(
"Unsupported image buffer `dtype`!");
238 [](ImageBufferExperimental& self,
239 nb::ndarray<nb::numpy, nb::c_contig, nb::device::cpu> tensor) {
241 self.
width = tensor.shape(1);
242 self.
height = tensor.shape(0);
244 auto dtype = tensor.dtype();
245 if (dtype == nb::dtype<int8_t>()) {
247 }
else if (dtype == nb::dtype<uint8_t>()) {
249 }
else if (dtype == nb::dtype<int16_t>()) {
251 }
else if (dtype == nb::dtype<uint16_t>()) {
253 }
else if (dtype == nb::dtype<int32_t>()) {
255 }
else if (dtype == nb::dtype<uint32_t>()) {
257 }
else if (dtype == nb::dtype<int64_t>()) {
259 }
else if (dtype == nb::dtype<uint64_t>()) {
261 }
else if (dtype == nb::dtype<float>()) {
263 }
else if (dtype == nb::dtype<double>()) {
266 throw nb::type_error(
"Unsupported input tensor `dtype`!");
268 self.
data.resize(tensor.nbytes());
270 reinterpret_cast<uint8_t*
>(tensor.data()),
271 reinterpret_cast<uint8_t*
>(tensor.data()) + tensor.nbytes(),
274 "Raw buffer of size (width * height * num_channels * num_bits_per_element / 8) bytes "
275 "containing image data",
277 nb::sig(
"def data(self) -> Annotated[NDArray, dict(order='C', device='cpu')]")))
280 [](ImageBufferExperimental& self) -> std::optional<nb::type_object> {
281 auto np = nb::module_::import_(
"numpy");
283 case AttributeValueType::e_int8_t:
return np.attr(
"int8");
284 case AttributeValueType::e_int16_t:
return np.attr(
"int16");
285 case AttributeValueType::e_int32_t:
return np.attr(
"int32");
286 case AttributeValueType::e_int64_t:
return np.attr(
"int64");
287 case AttributeValueType::e_uint8_t:
return np.attr(
"uint8");
288 case AttributeValueType::e_uint16_t:
return np.attr(
"uint16");
289 case AttributeValueType::e_uint32_t:
return np.attr(
"uint32");
290 case AttributeValueType::e_uint64_t:
return np.attr(
"uint64");
291 case AttributeValueType::e_float:
return np.attr(
"float32");
292 case AttributeValueType::e_double:
return np.attr(
"float64");
293 default:
logger().warn(
"Image buffer has an unknown dtype.");
return std::nullopt;
296 "The scalar type of the elements in the buffer");
298 nb::class_<ImageExperimental> image(
301 "Image structure that can store either image data or reference to an image file");
302 image.def(nb::init<>())
305 [](
const ImageExperimental& self) {
return scene::internal::to_string(self); })
308 &ImageExperimental::name,
309 "Image name. Not guaranteed to be unique and can be empty")
310 .def_rw(
"image", &ImageExperimental::image,
"Image data")
313 [](
const ImageExperimental& self) -> std::optional<std::string> {
314 if (self.
uri.empty())
317 return self.
uri.string();
319 [](ImageExperimental& self, std::optional<std::string> uri) {
321 self.
uri = fs::path(uri.value());
323 self.
uri = fs::path();
325 "Image file path. This path is relative to the file that contains the scene. It is "
326 "only valid if image data should be mapped to an external file")
327 .def_rw(
"extensions", &ImageExperimental::extensions,
"Image extensions");
329 nb::class_<TextureInfo>(
332 "Pair of texture index (which texture to use) and texture coordinate index (which set of "
335 .def(
"__repr__", [](
const TextureInfo& self) {
return scene::internal::to_string(self); })
338 [](
const TextureInfo& self) -> std::optional<ElementId> {
339 if (self.
index != invalid_element)
344 [](TextureInfo& self, std::optional<ElementId> index) {
345 if (index.has_value())
346 self.
index = index.value();
348 self.
index = invalid_element;
350 "Texture index. Index in scene.textures vector. `None` if not set")
353 &TextureInfo::texcoord,
354 "Index of UV coordinates. Usually stored in the mesh as `texcoord_x` attribute where x "
355 "is this variable. This is typically 0");
357 nb::class_<MaterialExperimental> material(
360 "PBR material, based on the gltf specification. This is subject to change, to support more "
362 material.def(nb::init<>())
365 [](
const MaterialExperimental& self) {
return scene::internal::to_string(self); })
368 &MaterialExperimental::name,
369 "Material name. May not be unique, and can be empty")
370 .def_rw(
"base_color_value", &MaterialExperimental::base_color_value,
"Base color value")
372 "base_color_texture",
373 &MaterialExperimental::base_color_texture,
374 "Base color texture")
377 &MaterialExperimental::alpha_mode,
378 "The alpha mode specifies how to interpret the alpha value of the base color")
379 .def_rw(
"alpha_cutoff", &MaterialExperimental::alpha_cutoff,
"Alpha cutoff value")
380 .def_rw(
"emissive_value", &MaterialExperimental::emissive_value,
"Emissive color value")
381 .def_rw(
"emissive_texture", &MaterialExperimental::emissive_texture,
"Emissive texture")
382 .def_rw(
"metallic_value", &MaterialExperimental::metallic_value,
"Metallic value")
383 .def_rw(
"roughness_value", &MaterialExperimental::roughness_value,
"Roughness value")
385 "metallic_roughness_texture",
386 &MaterialExperimental::metallic_roughness_texture,
387 "Metalness and roughness are packed together in a single texture. Green channel has "
388 "roughness, blue channel has metalness")
389 .def_rw(
"normal_texture", &MaterialExperimental::normal_texture,
"Normal texture")
392 &MaterialExperimental::normal_scale,
393 "Normal scaling factor. normal = normalize(<sampled tex value> * 2 - 1) * vec3(scale, "
395 .def_rw(
"occlusion_texture", &MaterialExperimental::occlusion_texture,
"Occlusion texture")
397 "occlusion_strength",
398 &MaterialExperimental::occlusion_strength,
399 "Occlusion strength. color = lerp(color, color * <sampled tex value>, strength)")
402 &MaterialExperimental::double_sided,
403 "Whether the material is double-sided")
404 .def_rw(
"extensions", &MaterialExperimental::extensions,
"Material extensions");
406 nb::enum_<MaterialExperimental::AlphaMode>(material,
"AlphaMode",
"Alpha mode")
409 MaterialExperimental::AlphaMode::Opaque,
410 "Alpha is ignored, and rendered output is opaque")
413 MaterialExperimental::AlphaMode::Mask,
414 "Output is either opaque or transparent depending on the alpha value and the "
415 "alpha_cutoff value")
418 MaterialExperimental::AlphaMode::Blend,
419 "Alpha value is used to composite source and destination");
422 nb::class_<Texture> texture(m,
"Texture",
"Texture");
423 texture.def(nb::init<>())
424 .def(
"__repr__", [](
const Texture& self) {
return scene::internal::to_string(self); })
425 .def_rw(
"name", &Texture::name,
"Texture name")
428 [](Texture& self) -> std::optional<ElementId> {
429 if (self.image != invalid_element)
434 [](Texture& self, ElementId img) { self.image = img; },
435 "Index of image in scene.images vector (None if invalid)")
438 &Texture::mag_filter,
439 "Texture magnification filter, used when texture appears larger on screen than the "
443 &Texture::min_filter,
444 "Texture minification filter, used when the texture appears smaller on screen than the "
446 .def_rw(
"wrap_u", &Texture::wrap_u,
"Texture wrap mode for U coordinate")
447 .def_rw(
"wrap_v", &Texture::wrap_v,
"Texture wrap mode for V coordinate")
448 .def_rw(
"scale", &Texture::scale,
"Texture scale")
449 .def_rw(
"offset", &Texture::offset,
"Texture offset")
450 .def_rw(
"rotation", &Texture::rotation,
"Texture rotation")
451 .def_rw(
"extensions", &Texture::extensions,
"Texture extensions");
453 nb::enum_<Texture::WrapMode>(texture,
"WrapMode",
"Texture wrap mode")
454 .value(
"Wrap", Texture::WrapMode::Wrap,
"u|v becomes u%1|v%1")
457 Texture::WrapMode::Clamp,
458 "Coordinates outside [0, 1] are clamped to the nearest value")
461 Texture::WrapMode::Decal,
462 "If the texture coordinates for a pixel are outside [0, 1], the texture is not applied")
463 .value(
"Mirror", Texture::WrapMode::Mirror,
"Mirror wrap mode");
464 nb::enum_<Texture::TextureFilter>(texture,
"TextureFilter",
"Texture filter mode")
465 .value(
"Undefined", Texture::TextureFilter::Undefined,
"Undefined filter")
466 .value(
"Nearest", Texture::TextureFilter::Nearest,
"Nearest neighbor filtering")
467 .value(
"Linear", Texture::TextureFilter::Linear,
"Linear filtering")
469 "NearestMipmapNearest",
470 Texture::TextureFilter::NearestMipmapNearest,
471 "Nearest mipmap nearest filtering")
473 "LinearMipmapNearest",
474 Texture::TextureFilter::LinearMipmapNearest,
475 "Linear mipmap nearest filtering")
477 "NearestMipmapLinear",
478 Texture::TextureFilter::NearestMipmapLinear,
479 "Nearest mipmap linear filtering")
481 "LinearMipmapLinear",
482 Texture::TextureFilter::LinearMipmapLinear,
483 "Linear mipmap linear filtering");
485 nb::class_<Light> light(m,
"Light",
"Light");
486 light.def(nb::init<>())
487 .def(
"__repr__", [](
const Light& self) {
return scene::internal::to_string(self); })
488 .def_rw(
"name", &Light::name,
"Light name")
489 .def_rw(
"type", &Light::type,
"Light type")
493 "Light position. Note that the light is part of the scene graph, and has an associated "
494 "transform in its node. This value is relative to the coordinate system defined by the "
496 .def_rw(
"direction", &Light::direction,
"Light direction")
497 .def_rw(
"up", &Light::up,
"Light up vector")
498 .def_rw(
"intensity", &Light::intensity,
"Light intensity")
500 "attenuation_constant",
501 &Light::attenuation_constant,
502 "Attenuation constant. Intensity of light at a given distance 'd' is: intensity / "
503 "(attenuation_constant + attenuation_linear * d + attenuation_quadratic * d * d + "
504 "attenuation_cubic * d * d * d)")
505 .def_rw(
"attenuation_linear", &Light::attenuation_linear,
"Linear attenuation factor")
507 "attenuation_quadratic",
508 &Light::attenuation_quadratic,
509 "Quadratic attenuation factor")
510 .def_rw(
"attenuation_cubic", &Light::attenuation_cubic,
"Cubic attenuation factor")
514 "Range is defined for point and spot lights. It defines a distance cutoff at which the "
515 "light intensity is to be considered zero. When the value is 0, range is assumed to be "
517 .def_rw(
"color_diffuse", &Light::color_diffuse,
"Diffuse color")
518 .def_rw(
"color_specular", &Light::color_specular,
"Specular color")
519 .def_rw(
"color_ambient", &Light::color_ambient,
"Ambient color")
522 &Light::angle_inner_cone,
523 "Inner angle of a spot light's light cone. 2PI for point lights, undefined for "
524 "directional lights")
527 &Light::angle_outer_cone,
528 "Outer angle of a spot light's light cone. 2PI for point lights, undefined for "
529 "directional lights")
530 .def_rw(
"size", &Light::size,
"Size of area light source")
531 .def_rw(
"extensions", &Light::extensions,
"Light extensions");
533 nb::enum_<Light::Type>(light,
"Type",
"Light type")
534 .value(
"Undefined", Light::Type::Undefined,
"Undefined light type")
535 .value(
"Directional", Light::Type::Directional,
"Directional light")
536 .value(
"Point", Light::Type::Point,
"Point light")
537 .value(
"Spot", Light::Type::Spot,
"Spot light")
538 .value(
"Ambient", Light::Type::Ambient,
"Ambient light")
539 .value(
"Area", Light::Type::Area,
"Area light");
541 nb::class_<Camera> camera(m,
"Camera",
"Camera");
542 camera.def(nb::init<>())
543 .def(
"__repr__", [](
const Camera& self) {
return scene::internal::to_string(self); })
544 .def_rw(
"name", &Camera::name,
"Camera name")
548 "Camera position. Note that the camera is part of the scene graph, and has an "
549 "associated transform in its node. This value is relative to the coordinate system "
550 "defined by the node")
551 .def_rw(
"up", &Camera::up,
"Camera up vector")
552 .def_rw(
"look_at", &Camera::look_at,
"Camera look-at point")
556 "Distance of the near clipping plane. This value cannot be 0")
557 .def_rw(
"far_plane", &Camera::far_plane,
"Distance of the far clipping plane")
558 .def_rw(
"type", &Camera::type,
"Camera type")
561 &Camera::aspect_ratio,
562 "Screen aspect ratio. This is the value of width / height of the screen. aspect_ratio "
563 "= tan(horizontal_fov / 2) / tan(vertical_fov / 2)")
566 &Camera::horizontal_fov,
567 "Horizontal field of view angle, in radians. This is the angle between the left and "
568 "right borders of the viewport. It should not be greater than Pi. fov is only defined "
569 "when the camera type is perspective, otherwise it should be 0")
571 "orthographic_width",
572 &Camera::orthographic_width,
573 "Half width of the orthographic view box. Or horizontal magnification. This is only "
574 "defined when the camera type is orthographic, otherwise it should be 0")
577 &Camera::get_vertical_fov,
578 "Get the vertical field of view. Make sure aspect_ratio is set before calling this")
580 "set_horizontal_fov_from_vertical_fov",
581 &Camera::set_horizontal_fov_from_vertical_fov,
583 "Set horizontal fov from vertical fov. Make sure aspect_ratio is set before calling "
585 .def_rw(
"extensions", &Camera::extensions,
"Camera extensions");
587 nb::enum_<Camera::Type>(camera,
"Type",
"Camera type")
588 .value(
"Perspective", Camera::Type::Perspective,
"Perspective projection")
589 .value(
"Orthographic", Camera::Type::Orthographic,
"Orthographic projection");
591 nb::class_<Animation>(m,
"Animation",
"Animation")
593 .def(
"__repr__", [](
const Animation& self) {
return scene::internal::to_string(self); })
594 .def_rw(
"name", &Animation::name,
"Animation name")
595 .def_rw(
"extensions", &Animation::extensions,
"Animation extensions");
598 nb::class_<Skeleton>(m,
"Skeleton",
"Skeleton")
600 .def(
"__repr__", [](
const Skeleton& self) {
return scene::internal::to_string(self); })
604 "This skeleton is used to deform those meshes. This will typically contain one value, "
605 "but can have zero or multiple meshes. The value is the index in the scene meshes")
606 .def_rw(
"extensions", &Skeleton::extensions,
"Skeleton extensions");
609 nb::class_<SceneType>(m,
"Scene",
"A 3D scene")
611 .def(
"__repr__", [](
const SceneType& self) {
return scene::internal::to_string(self); })
612 .def_rw(
"name", &SceneType::name,
"Name of the scene")
616 "Scene nodes. This is a list of nodes, the hierarchy information is contained by each "
617 "node having a list of children as indices to this vector")
620 &SceneType::root_nodes,
621 "Root nodes. This is typically one. Must be at least one")
626 LA_SAFE_VECTOR_SETTER(
"meshes",
"collections.abc.Sequence[lagrange.core.SurfaceMesh]"))
627 .def_rw(
"images", &SceneType::images,
"Images")
628 .def_rw(
"textures", &SceneType::textures,
"Textures. They can reference images")
629 .def_rw(
"materials", &SceneType::materials,
"Materials. They can reference textures")
630 .def_rw(
"lights", &SceneType::lights,
"Lights in the scene")
634 "Cameras. The first camera (if any) is the default camera view")
635 .def_rw(
"skeletons", &SceneType::skeletons,
"Scene skeletons")
636 .def_rw(
"animations", &SceneType::animations,
"Animations (unused for now)")
637 .def_rw(
"extensions", &SceneType::extensions,
"Scene extensions")
646 MaterialExperimental,
650 Animation> element) {
653 using T = std::decay_t<
decltype(value)>;
654 return self.add(std::forward<T>(value));
659 R
"(Add an element to the scene.
661:param element: The element to add to the scene. E.g. node, mesh, image, texture, material, light, camera, skeleton, or animation.
663:returns: The id of the added element.)")
666 &SceneType::add_child,
669 R
"(Add a child node to a parent node. The parent-child relationship will be updated for both nodes.
671:param parent_id: The parent node id.
672:param child_id: The child node id.
674:returns: The id of the added child node.)");
677 "compute_global_node_transform",
678 [](
const SceneType& scene,
size_t node_idx) {
679 auto t = utils::compute_global_node_transform<Scalar, Index>(scene, node_idx);
680 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
689 R
"(Compute the global transform associated with a node.
691:param scene: The input scene.
692:param node_idx: The index of the target node.
694:returns: The global transform of the target node, which is the combination of transforms from this node all the way to the root.
698 "camera_transforms_from_scene",
699 [](
const SceneType& scene) {
700 return scene::internal::camera_transforms_from_scene<Scalar, Index>(scene);
703 R
"(Extract view and projection transforms for every camera referenced by a node in the scene.
705:param scene: The input scene.
707:returns: A list of CameraTransforms, one per camera instance in the scene.)");
711 [](
const SceneType& scene,
712 bool normalize_normals,
713 bool normalize_tangents_bitangents,
715 bool preserve_attributes) {
716 TransformOptions transform_options;
719 transform_options.
reorient = reorient;
720 return scene::scene_to_mesh(scene, transform_options, preserve_attributes);
724 "normalize_normals"_a = TransformOptions{}.normalize_normals,
725 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
726 "reorient"_a = TransformOptions{}.reorient,
727 "preserve_attributes"_a =
true,
728 R
"(Converts a scene into a concatenated mesh with all the transforms applied.
730:param scene: Scene to convert.
731:param normalize_normals: If enabled, normals are normalized after transformation.
732:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
733:param reorient: If enabled, flip facets and reorient attributes for instances with a negative-determinant transform.
734:param preserve_attributes: Preserve shared attributes and map them to the output mesh.
736:return: Concatenated mesh.)");
740 [](
const SceneType& scene,
741 bool normalize_normals,
742 bool normalize_tangents_bitangents,
744 TransformOptions transform_options;
747 transform_options.
reorient = reorient;
748 return scene::scene_to_meshes(scene, transform_options);
752 "normalize_normals"_a = TransformOptions{}.normalize_normals,
753 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
754 "reorient"_a = TransformOptions{}.reorient,
755 R
"(Converts a scene into a list of meshes with all the transforms applied.
757:param scene: Scene to convert.
758:param normalize_normals: If enabled, normals are normalized after transformation.
759:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
760:param reorient: If enabled, flip facets and reorient attributes for instances with a negative-determinant transform.
762:return: List of transformed meshes.)");
765 "scene_to_meshes_and_materials",
766 [](
const SceneType& scene,
767 bool normalize_normals,
768 bool normalize_tangents_bitangents,
770 -> std::pair<std::vector<SceneType::MeshType>, std::vector<std::vector<ElementId>>> {
771 TransformOptions transform_options;
774 transform_options.
reorient = reorient;
775 auto [meshes, material_ids] =
776 scene::scene_to_meshes_and_materials(scene, transform_options);
777 return {std::move(meshes), std::move(material_ids)};
781 "normalize_normals"_a = TransformOptions{}.normalize_normals,
782 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
783 "reorient"_a = TransformOptions{}.reorient,
784 R
"(Converts a scene into a list of meshes with all the transforms applied and a list of material IDs.
786:param scene: Scene to convert.
787:param normalize_normals: If enabled, normals are normalized after transformation.
788:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
789:param reorient: If enabled, flip facets and reorient attributes for instances with a negative-determinant transform.
791:return: List of meshes with transforms applied and a list of material IDs.)");
797 R
"(Converts a single mesh into a scene with a single identity instance of the input mesh.
799:param mesh: Input mesh to convert.
801:return: Scene containing the input mesh.)");
805 [](std::vector<SceneType::MeshType> meshes) {
806 return scene::meshes_to_scene(std::move(meshes));
809 R
"(Converts a list of meshes into a scene with a single identity instance of each input mesh.
811:param meshes: Input meshes to convert.
813:return: Scene containing the input meshes.)");
815 using SimpleScene3D = scene::SimpleScene<Scalar, Index, 3>;
818 "scene_to_simple_scene",
819 [](
const SceneType& scene) {
return scene::scene_to_simple_scene(scene); },
821 R
"(Converts a Scene into a SimpleScene.
823The Scene's node hierarchy is flattened: each mesh instance in the scene becomes a
824MeshInstance in the SimpleScene with the accumulated world transform. Meshes are copied
825by index. Materials and other scene metadata (images, textures, cameras, lights) are not
826preserved in the SimpleScene.
828:param scene: Input scene to convert.
830:return: SimpleScene containing all mesh instances from the scene.)");
833 "simple_scene_to_scene",
834 [](
const SimpleScene3D& simple_scene) {
835 return scene::simple_scene_to_scene(simple_scene);
838 R
"(Converts a SimpleScene into a Scene.
840Each mesh instance in the SimpleScene becomes a node in the Scene with the instance
841transform. All nodes are direct children of a single root node. Meshes are copied
844:param simple_scene: Input simple scene to convert.
846: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:177
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