14#include <lagrange/AttributeValueType.h>
15#include <lagrange/Logger.h>
16#include <lagrange/python/binding.h>
17#include <lagrange/python/tensor_utils.h>
18#include <lagrange/scene/Scene.h>
19#include <lagrange/scene/SimpleScene.h>
20#include <lagrange/scene/internal/scene_string_utils.h>
21#include <lagrange/scene/scene_convert.h>
22#include <lagrange/scene/scene_utils.h>
23#include <lagrange/utils/assert.h>
25#include "bind_value.h"
27namespace lagrange::python {
29namespace nb = nanobind;
31void bind_scene(nb::module_& m)
33 using namespace lagrange::scene;
35 using Index = uint32_t;
36 using SceneType = Scene<Scalar, Index>;
38 nb::bind_vector<SafeVector<ElementId>>(m,
"ElementIdList");
39 nb::bind_safe_vector<SafeVector<Node>>(m,
"NodeList");
40 nb::bind_safe_vector<SafeVector<SceneMeshInstance>>(m,
"SceneMeshInstanceList");
41 nb::bind_safe_vector<SafeVector<SurfaceMesh<Scalar, Index>>>(m,
"SurfaceMeshList");
42 nb::bind_safe_vector<SafeVector<ImageExperimental>>(m,
"ImageList");
43 nb::bind_safe_vector<SafeVector<Texture>>(m,
"TextureList");
44 nb::bind_safe_vector<SafeVector<MaterialExperimental>>(m,
"MaterialList");
45 nb::bind_safe_vector<SafeVector<Light>>(m,
"LightList");
46 nb::bind_safe_vector<SafeVector<Camera>>(m,
"CameraList");
47 nb::bind_safe_vector<SafeVector<Skeleton>>(m,
"SkeletonList");
48 nb::bind_safe_vector<SafeVector<Animation>>(m,
"AnimationList");
50 nb::class_<lagrange::scene::Extensions>(m,
"Extensions")
53 [](
const lagrange::scene::Extensions& self) {
54 return scene::internal::to_string(self);
56 .def_prop_ro(
"size", &Extensions::size)
57 .def_prop_ro(
"empty", &Extensions::empty)
61 nb::rv_policy::reference_internal,
62 "Raw data stored in this extension as a dict");
64 nb::class_<SceneMeshInstance>(
67 "Pairs a mesh with its materials (zero, one, or more)")
71 [](
const SceneMeshInstance& self) {
return scene::internal::to_string(self); })
74 [](SceneMeshInstance& self) -> std::optional<ElementId> {
75 if (self.mesh != invalid_element)
80 [](SceneMeshInstance& self, ElementId mesh) { self.mesh = mesh; },
81 "Mesh index. Has to be a valid index in the scene.meshes vector (None if invalid)")
84 &SceneMeshInstance::materials,
85 "Material indices in the scene.materials vector. This is typically a single material "
86 "index. When a single mesh uses multiple materials, the AttributeName::material_id "
87 "facet attribute should be defined.");
89 nb::class_<Node>(m,
"Node",
"Represents a node in the scene hierarchy")
91 .def(
"__repr__", [](
const Node& self) {
return scene::internal::to_string(self); })
92 .def_rw(
"name", &Node::name,
"Node name. May not be unique and can be empty")
96 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
97 node.transform.data(),
102 [](Node& node, nb::ndarray<nb::numpy, const float, nb::shape<4, 4>> t) ->
void {
103 auto view = t.view<float, nb::ndim<2>>();
105 for (
size_t i = 0; i < 4; i++) {
106 for (
size_t j = 0; j < 4; j++) {
107 node.transform.data()[i + j * 4] = view(i, j);
111 "Transform of the node, relative to its parent")
114 [](Node& node) -> std::optional<ElementId> {
115 if (node.parent != invalid_element)
120 [](Node& node, ElementId parent) { node.parent = parent; },
121 "Parent index. May be invalid if the node has no parent (e.g. the root)")
122 .def_rw(
"children", &Node::children,
"Children indices. May be empty")
123 .def_rw(
"meshes", &Node::meshes,
"List of meshes contained in this node")
124 .def_rw(
"cameras", &Node::cameras,
"List of cameras contained in this node")
125 .def_rw(
"lights", &Node::lights,
"List of lights contained in this node")
126 .def_rw(
"extensions", &Node::extensions);
128 nb::class_<ImageBufferExperimental> image_buffer(
131 "Minimalistic image data structure that stores the raw image data");
132 image_buffer.def(nb::init<>())
135 [](
const ImageBufferExperimental& self) {
return scene::internal::to_string(self); })
136 .def_ro(
"width", &ImageBufferExperimental::width,
"Image width")
137 .def_ro(
"height", &ImageBufferExperimental::height,
"Image height")
140 &ImageBufferExperimental::num_channels,
141 "Number of image channels (must be 1, 3, or 4)")
144 [](ImageBufferExperimental& self) {
147 case AttributeValueType::e_int8_t:
149 nb::ndarray<int8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
150 reinterpret_cast<int8_t*
>(self.
data.data()),
154 nb::rv_policy::reference_internal);
155 case AttributeValueType::e_uint8_t:
157 nb::ndarray<uint8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
158 reinterpret_cast<uint8_t*
>(self.
data.data()),
162 nb::rv_policy::reference_internal);
163 case AttributeValueType::e_int16_t:
165 nb::ndarray<int16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
166 reinterpret_cast<int16_t*
>(self.
data.data()),
170 nb::rv_policy::reference_internal);
171 case AttributeValueType::e_uint16_t:
173 nb::ndarray<uint16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
174 reinterpret_cast<uint16_t*
>(self.
data.data()),
178 nb::rv_policy::reference_internal);
179 case AttributeValueType::e_int32_t:
181 nb::ndarray<int32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
182 reinterpret_cast<int32_t*
>(self.
data.data()),
186 nb::rv_policy::reference_internal);
187 case AttributeValueType::e_uint32_t:
189 nb::ndarray<uint32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
190 reinterpret_cast<uint32_t*
>(self.
data.data()),
194 nb::rv_policy::reference_internal);
195 case AttributeValueType::e_int64_t:
197 nb::ndarray<int64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
198 reinterpret_cast<int64_t*
>(self.
data.data()),
202 nb::rv_policy::reference_internal);
203 case AttributeValueType::e_uint64_t:
205 nb::ndarray<uint64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
206 reinterpret_cast<uint64_t*
>(self.
data.data()),
210 nb::rv_policy::reference_internal);
211 case AttributeValueType::e_float:
213 nb::ndarray<float, nb::numpy, nb::c_contig, nb::device::cpu>(
214 reinterpret_cast<float*
>(self.
data.data()),
218 nb::rv_policy::reference_internal);
219 case AttributeValueType::e_double:
221 nb::ndarray<double, nb::numpy, nb::c_contig, nb::device::cpu>(
222 reinterpret_cast<double*
>(self.
data.data()),
226 nb::rv_policy::reference_internal);
227 default:
throw nb::type_error(
"Unsupported image buffer `dtype`!");
230 [](ImageBufferExperimental& self,
231 nb::ndarray<nb::numpy, nb::c_contig, nb::device::cpu> tensor) {
233 self.
width = tensor.shape(1);
234 self.
height = tensor.shape(0);
236 auto dtype = tensor.dtype();
237 if (dtype == nb::dtype<int8_t>()) {
239 }
else if (dtype == nb::dtype<uint8_t>()) {
241 }
else if (dtype == nb::dtype<int16_t>()) {
243 }
else if (dtype == nb::dtype<uint16_t>()) {
245 }
else if (dtype == nb::dtype<int32_t>()) {
247 }
else if (dtype == nb::dtype<uint32_t>()) {
249 }
else if (dtype == nb::dtype<int64_t>()) {
251 }
else if (dtype == nb::dtype<uint64_t>()) {
253 }
else if (dtype == nb::dtype<float>()) {
255 }
else if (dtype == nb::dtype<double>()) {
258 throw nb::type_error(
"Unsupported input tensor `dtype`!");
260 self.
data.resize(tensor.nbytes());
262 reinterpret_cast<uint8_t*
>(tensor.data()),
263 reinterpret_cast<uint8_t*
>(tensor.data()) + tensor.nbytes(),
266 "Raw buffer of size (width * height * num_channels * num_bits_per_element / 8) bytes "
267 "containing image data")
270 [](ImageBufferExperimental& self) -> std::optional<nb::type_object> {
271 auto np = nb::module_::import_(
"numpy");
273 case AttributeValueType::e_int8_t:
return np.attr(
"int8");
274 case AttributeValueType::e_int16_t:
return np.attr(
"int16");
275 case AttributeValueType::e_int32_t:
return np.attr(
"int32");
276 case AttributeValueType::e_int64_t:
return np.attr(
"int64");
277 case AttributeValueType::e_uint8_t:
return np.attr(
"uint8");
278 case AttributeValueType::e_uint16_t:
return np.attr(
"uint16");
279 case AttributeValueType::e_uint32_t:
return np.attr(
"uint32");
280 case AttributeValueType::e_uint64_t:
return np.attr(
"uint64");
281 case AttributeValueType::e_float:
return np.attr(
"float32");
282 case AttributeValueType::e_double:
return np.attr(
"float64");
283 default:
logger().warn(
"Image buffer has an unknown dtype.");
return std::nullopt;
286 "The scalar type of the elements in the buffer");
288 nb::class_<ImageExperimental> image(
291 "Image structure that can store either image data or reference to an image file");
292 image.def(nb::init<>())
295 [](
const ImageExperimental& self) {
return scene::internal::to_string(self); })
298 &ImageExperimental::name,
299 "Image name. Not guaranteed to be unique and can be empty")
300 .def_rw(
"image", &ImageExperimental::image,
"Image data")
303 [](
const ImageExperimental& self) -> std::optional<std::string> {
304 if (self.
uri.empty())
307 return self.
uri.string();
309 [](ImageExperimental& self, std::optional<std::string> uri) {
311 self.
uri = fs::path(uri.value());
313 self.
uri = fs::path();
315 "Image file path. This path is relative to the file that contains the scene. It is "
316 "only valid if image data should be mapped to an external file")
317 .def_rw(
"extensions", &ImageExperimental::extensions,
"Image extensions");
319 nb::class_<TextureInfo>(
322 "Pair of texture index (which texture to use) and texture coordinate index (which set of "
325 .def(
"__repr__", [](
const TextureInfo& self) {
return scene::internal::to_string(self); })
328 [](
const TextureInfo& self) -> std::optional<ElementId> {
329 if (self.
index != invalid_element)
334 [](TextureInfo& self, std::optional<ElementId> index) {
335 if (index.has_value())
336 self.
index = index.value();
338 self.
index = invalid_element;
340 "Texture index. Index in scene.textures vector. `None` if not set")
343 &TextureInfo::texcoord,
344 "Index of UV coordinates. Usually stored in the mesh as `texcoord_x` attribute where x "
345 "is this variable. This is typically 0");
347 nb::class_<MaterialExperimental> material(
350 "PBR material, based on the gltf specification. This is subject to change, to support more "
352 material.def(nb::init<>())
355 [](
const MaterialExperimental& self) {
return scene::internal::to_string(self); })
358 &MaterialExperimental::name,
359 "Material name. May not be unique, and can be empty")
360 .def_rw(
"base_color_value", &MaterialExperimental::base_color_value,
"Base color value")
362 "base_color_texture",
363 &MaterialExperimental::base_color_texture,
364 "Base color texture")
367 &MaterialExperimental::alpha_mode,
368 "The alpha mode specifies how to interpret the alpha value of the base color")
369 .def_rw(
"alpha_cutoff", &MaterialExperimental::alpha_cutoff,
"Alpha cutoff value")
370 .def_rw(
"emissive_value", &MaterialExperimental::emissive_value,
"Emissive color value")
371 .def_rw(
"emissive_texture", &MaterialExperimental::emissive_texture,
"Emissive texture")
372 .def_rw(
"metallic_value", &MaterialExperimental::metallic_value,
"Metallic value")
373 .def_rw(
"roughness_value", &MaterialExperimental::roughness_value,
"Roughness value")
375 "metallic_roughness_texture",
376 &MaterialExperimental::metallic_roughness_texture,
377 "Metalness and roughness are packed together in a single texture. Green channel has "
378 "roughness, blue channel has metalness")
379 .def_rw(
"normal_texture", &MaterialExperimental::normal_texture,
"Normal texture")
382 &MaterialExperimental::normal_scale,
383 "Normal scaling factor. normal = normalize(<sampled tex value> * 2 - 1) * vec3(scale, "
385 .def_rw(
"occlusion_texture", &MaterialExperimental::occlusion_texture,
"Occlusion texture")
387 "occlusion_strength",
388 &MaterialExperimental::occlusion_strength,
389 "Occlusion strength. color = lerp(color, color * <sampled tex value>, strength)")
392 &MaterialExperimental::double_sided,
393 "Whether the material is double-sided")
394 .def_rw(
"extensions", &MaterialExperimental::extensions,
"Material extensions");
396 nb::enum_<MaterialExperimental::AlphaMode>(material,
"AlphaMode",
"Alpha mode")
399 MaterialExperimental::AlphaMode::Opaque,
400 "Alpha is ignored, and rendered output is opaque")
403 MaterialExperimental::AlphaMode::Mask,
404 "Output is either opaque or transparent depending on the alpha value and the "
405 "alpha_cutoff value")
408 MaterialExperimental::AlphaMode::Blend,
409 "Alpha value is used to composite source and destination");
412 nb::class_<Texture> texture(m,
"Texture",
"Texture");
413 texture.def(nb::init<>())
414 .def(
"__repr__", [](
const Texture& self) {
return scene::internal::to_string(self); })
415 .def_rw(
"name", &Texture::name,
"Texture name")
418 [](Texture& self) -> std::optional<ElementId> {
419 if (self.image != invalid_element)
424 [](Texture& self, ElementId img) { self.image = img; },
425 "Index of image in scene.images vector (None if invalid)")
428 &Texture::mag_filter,
429 "Texture magnification filter, used when texture appears larger on screen than the "
433 &Texture::min_filter,
434 "Texture minification filter, used when the texture appears smaller on screen than the "
436 .def_rw(
"wrap_u", &Texture::wrap_u,
"Texture wrap mode for U coordinate")
437 .def_rw(
"wrap_v", &Texture::wrap_v,
"Texture wrap mode for V coordinate")
438 .def_rw(
"scale", &Texture::scale,
"Texture scale")
439 .def_rw(
"offset", &Texture::offset,
"Texture offset")
440 .def_rw(
"rotation", &Texture::rotation,
"Texture rotation")
441 .def_rw(
"extensions", &Texture::extensions,
"Texture extensions");
443 nb::enum_<Texture::WrapMode>(texture,
"WrapMode",
"Texture wrap mode")
444 .value(
"Wrap", Texture::WrapMode::Wrap,
"u|v becomes u%1|v%1")
447 Texture::WrapMode::Clamp,
448 "Coordinates outside [0, 1] are clamped to the nearest value")
451 Texture::WrapMode::Decal,
452 "If the texture coordinates for a pixel are outside [0, 1], the texture is not applied")
453 .value(
"Mirror", Texture::WrapMode::Mirror,
"Mirror wrap mode");
454 nb::enum_<Texture::TextureFilter>(texture,
"TextureFilter",
"Texture filter mode")
455 .value(
"Undefined", Texture::TextureFilter::Undefined,
"Undefined filter")
456 .value(
"Nearest", Texture::TextureFilter::Nearest,
"Nearest neighbor filtering")
457 .value(
"Linear", Texture::TextureFilter::Linear,
"Linear filtering")
459 "NearestMipmapNearest",
460 Texture::TextureFilter::NearestMipmapNearest,
461 "Nearest mipmap nearest filtering")
463 "LinearMipmapNearest",
464 Texture::TextureFilter::LinearMipmapNearest,
465 "Linear mipmap nearest filtering")
467 "NearestMipmapLinear",
468 Texture::TextureFilter::NearestMipmapLinear,
469 "Nearest mipmap linear filtering")
471 "LinearMipmapLinear",
472 Texture::TextureFilter::LinearMipmapLinear,
473 "Linear mipmap linear filtering");
475 nb::class_<Light> light(m,
"Light",
"Light");
476 light.def(nb::init<>())
477 .def(
"__repr__", [](
const Light& self) {
return scene::internal::to_string(self); })
478 .def_rw(
"name", &Light::name,
"Light name")
479 .def_rw(
"type", &Light::type,
"Light type")
483 "Light position. Note that the light is part of the scene graph, and has an associated "
484 "transform in its node. This value is relative to the coordinate system defined by the "
486 .def_rw(
"direction", &Light::direction,
"Light direction")
487 .def_rw(
"up", &Light::up,
"Light up vector")
488 .def_rw(
"intensity", &Light::intensity,
"Light intensity")
490 "attenuation_constant",
491 &Light::attenuation_constant,
492 "Attenuation constant. Intensity of light at a given distance 'd' is: intensity / "
493 "(attenuation_constant + attenuation_linear * d + attenuation_quadratic * d * d + "
494 "attenuation_cubic * d * d * d)")
495 .def_rw(
"attenuation_linear", &Light::attenuation_linear,
"Linear attenuation factor")
497 "attenuation_quadratic",
498 &Light::attenuation_quadratic,
499 "Quadratic attenuation factor")
500 .def_rw(
"attenuation_cubic", &Light::attenuation_cubic,
"Cubic attenuation factor")
504 "Range is defined for point and spot lights. It defines a distance cutoff at which the "
505 "light intensity is to be considered zero. When the value is 0, range is assumed to be "
507 .def_rw(
"color_diffuse", &Light::color_diffuse,
"Diffuse color")
508 .def_rw(
"color_specular", &Light::color_specular,
"Specular color")
509 .def_rw(
"color_ambient", &Light::color_ambient,
"Ambient color")
512 &Light::angle_inner_cone,
513 "Inner angle of a spot light's light cone. 2PI for point lights, undefined for "
514 "directional lights")
517 &Light::angle_outer_cone,
518 "Outer angle of a spot light's light cone. 2PI for point lights, undefined for "
519 "directional lights")
520 .def_rw(
"size", &Light::size,
"Size of area light source")
521 .def_rw(
"extensions", &Light::extensions,
"Light extensions");
523 nb::enum_<Light::Type>(light,
"Type",
"Light type")
524 .value(
"Undefined", Light::Type::Undefined,
"Undefined light type")
525 .value(
"Directional", Light::Type::Directional,
"Directional light")
526 .value(
"Point", Light::Type::Point,
"Point light")
527 .value(
"Spot", Light::Type::Spot,
"Spot light")
528 .value(
"Ambient", Light::Type::Ambient,
"Ambient light")
529 .value(
"Area", Light::Type::Area,
"Area light");
531 nb::class_<Camera> camera(m,
"Camera",
"Camera");
532 camera.def(nb::init<>())
533 .def(
"__repr__", [](
const Camera& self) {
return scene::internal::to_string(self); })
534 .def_rw(
"name", &Camera::name,
"Camera name")
538 "Camera position. Note that the camera is part of the scene graph, and has an "
539 "associated transform in its node. This value is relative to the coordinate system "
540 "defined by the node")
541 .def_rw(
"up", &Camera::up,
"Camera up vector")
542 .def_rw(
"look_at", &Camera::look_at,
"Camera look-at point")
546 "Distance of the near clipping plane. This value cannot be 0")
547 .def_rw(
"far_plane", &Camera::far_plane,
"Distance of the far clipping plane")
548 .def_rw(
"type", &Camera::type,
"Camera type")
551 &Camera::aspect_ratio,
552 "Screen aspect ratio. This is the value of width / height of the screen. aspect_ratio "
553 "= tan(horizontal_fov / 2) / tan(vertical_fov / 2)")
556 &Camera::horizontal_fov,
557 "Horizontal field of view angle, in radians. This is the angle between the left and "
558 "right borders of the viewport. It should not be greater than Pi. fov is only defined "
559 "when the camera type is perspective, otherwise it should be 0")
561 "orthographic_width",
562 &Camera::orthographic_width,
563 "Half width of the orthographic view box. Or horizontal magnification. This is only "
564 "defined when the camera type is orthographic, otherwise it should be 0")
567 &Camera::get_vertical_fov,
568 "Get the vertical field of view. Make sure aspect_ratio is set before calling this")
570 "set_horizontal_fov_from_vertical_fov",
571 &Camera::set_horizontal_fov_from_vertical_fov,
573 "Set horizontal fov from vertical fov. Make sure aspect_ratio is set before calling "
575 .def_rw(
"extensions", &Camera::extensions,
"Camera extensions");
577 nb::enum_<Camera::Type>(camera,
"Type",
"Camera type")
578 .value(
"Perspective", Camera::Type::Perspective,
"Perspective projection")
579 .value(
"Orthographic", Camera::Type::Orthographic,
"Orthographic projection");
581 nb::class_<Animation>(m,
"Animation",
"Animation")
583 .def(
"__repr__", [](
const Animation& self) {
return scene::internal::to_string(self); })
584 .def_rw(
"name", &Animation::name,
"Animation name")
585 .def_rw(
"extensions", &Animation::extensions,
"Animation extensions");
588 nb::class_<Skeleton>(m,
"Skeleton",
"Skeleton")
590 .def(
"__repr__", [](
const Skeleton& self) {
return scene::internal::to_string(self); })
594 "This skeleton is used to deform those meshes. This will typically contain one value, "
595 "but can have zero or multiple meshes. The value is the index in the scene meshes")
596 .def_rw(
"extensions", &Skeleton::extensions,
"Skeleton extensions");
599 nb::class_<SceneType>(m,
"Scene",
"A 3D scene")
601 .def(
"__repr__", [](
const SceneType& self) {
return scene::internal::to_string(self); })
602 .def_rw(
"name", &SceneType::name,
"Name of the scene")
606 "Scene nodes. This is a list of nodes, the hierarchy information is contained by each "
607 "node having a list of children as indices to this vector")
610 &SceneType::root_nodes,
611 "Root nodes. This is typically one. Must be at least one")
612 .def_rw(
"meshes", &SceneType::meshes,
"Scene meshes")
613 .def_rw(
"images", &SceneType::images,
"Images")
614 .def_rw(
"textures", &SceneType::textures,
"Textures. They can reference images")
615 .def_rw(
"materials", &SceneType::materials,
"Materials. They can reference textures")
616 .def_rw(
"lights", &SceneType::lights,
"Lights in the scene")
620 "Cameras. The first camera (if any) is the default camera view")
621 .def_rw(
"skeletons", &SceneType::skeletons,
"Scene skeletons")
622 .def_rw(
"animations", &SceneType::animations,
"Animations (unused for now)")
623 .def_rw(
"extensions", &SceneType::extensions,
"Scene extensions")
632 MaterialExperimental,
636 Animation> element) {
639 using T = std::decay_t<
decltype(value)>;
640 return self.add(std::forward<T>(value));
645 R
"(Add an element to the scene.
647:param element: The element to add to the scene. E.g. node, mesh, image, texture, material, light, camera, skeleton, or animation.
649:returns: The id of the added element.)")
652 &SceneType::add_child,
655 R
"(Add a child node to a parent node. The parent-child relationship will be updated for both nodes.
657:param parent_id: The parent node id.
658:param child_id: The child node id.
660:returns: The id of the added child node.)");
663 "compute_global_node_transform",
664 [](
const SceneType& scene,
size_t node_idx) {
665 auto t = utils::compute_global_node_transform<Scalar, Index>(scene, node_idx);
666 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
675 R
"(Compute the global transform associated with a node.
677:param scene: The input scene.
678:param node_idx: The index of the target node.
680:returns: The global transform of the target node, which is the combination of transforms from this node all the way to the root.
685 [](
const SceneType& scene,
686 bool normalize_normals,
687 bool normalize_tangents_bitangents,
688 bool preserve_attributes) {
689 TransformOptions transform_options;
692 return scene::scene_to_mesh(scene, transform_options, preserve_attributes);
695 "normalize_normals"_a = TransformOptions{}.normalize_normals,
696 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
697 "preserve_attributes"_a =
true,
698 R
"(Converts a scene into a concatenated mesh with all the transforms applied.
700:param scene: Scene to convert.
701:param normalize_normals: If enabled, normals are normalized after transformation.
702:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
703:param preserve_attributes: Preserve shared attributes and map them to the output mesh.
705:return: Concatenated mesh.)");
709 [](
const SceneType& scene,
bool normalize_normals,
bool normalize_tangents_bitangents) {
710 TransformOptions transform_options;
713 return scene::scene_to_meshes(scene, transform_options);
716 "normalize_normals"_a = TransformOptions{}.normalize_normals,
717 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
718 R
"(Converts a scene into a list of meshes with all the transforms applied.
720:param scene: Scene to convert.
721:param normalize_normals: If enabled, normals are normalized after transformation.
722:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
724:return: List of transformed meshes.)");
727 "scene_to_meshes_and_materials",
728 [](
const SceneType& scene,
bool normalize_normals,
bool normalize_tangents_bitangents)
729 -> std::pair<std::vector<SceneType::MeshType>, std::vector<std::vector<ElementId>>> {
730 TransformOptions transform_options;
733 auto [meshes, material_ids] =
734 scene::scene_to_meshes_and_materials(scene, transform_options);
735 return {std::move(meshes), std::move(material_ids)};
738 "normalize_normals"_a = TransformOptions{}.normalize_normals,
739 "normalize_tangents_bitangents"_a = TransformOptions{}.normalize_tangents_bitangents,
740 R
"(Converts a scene into a list of meshes with all the transforms applied and a list of material IDs.
742:param scene: Scene to convert.
743:param normalize_normals: If enabled, normals are normalized after transformation.
744:param normalize_tangents_bitangents: If enabled, tangents and bitangents are normalized after transformation.
746:return: List of meshes with transforms applied and a list of material IDs.)");
750 [](
const SceneType::MeshType& mesh) {
return scene::mesh_to_scene(mesh); },
752 R
"(Converts a single mesh into a scene with a single identity instance of the input mesh.
754:param mesh: Input mesh to convert.
756:return: Scene containing the input mesh.)");
760 [](std::vector<SceneType::MeshType> meshes) {
761 return scene::meshes_to_scene(std::move(meshes));
764 R
"(Converts a list of meshes into a scene with a single identity instance of each input mesh.
766:param meshes: Input meshes to convert.
768:return: Scene containing the input meshes.)");
770 using SimpleScene3D = scene::SimpleScene<Scalar, Index, 3>;
773 "scene_to_simple_scene",
774 [](
const SceneType& scene) {
return scene::scene_to_simple_scene(scene); },
776 R
"(Converts a Scene into a SimpleScene.
778The Scene's node hierarchy is flattened: each mesh instance in the scene becomes a
779MeshInstance in the SimpleScene with the accumulated world transform. Meshes are copied
780by index. Materials and other scene metadata (images, textures, cameras, lights) are not
781preserved in the SimpleScene.
783:param scene: Input scene to convert.
785:return: SimpleScene containing all mesh instances from the scene.)");
788 "simple_scene_to_scene",
789 [](
const SimpleScene3D& simple_scene) {
790 return scene::simple_scene_to_scene(simple_scene);
793 R
"(Converts a SimpleScene into a Scene.
795Each mesh instance in the SimpleScene becomes a node in the Scene with the instance
796transform. All nodes are direct children of a single root node. Meshes are copied
799:param simple_scene: Input simple scene to convert.
801:return: Scene containing the meshes and instances from the SimpleScene.)");
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:174
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