14#include <lagrange/AttributeValueType.h>
15#include <lagrange/Logger.h>
16#include <lagrange/python/tensor_utils.h>
17#include <lagrange/scene/Scene.h>
18#include <lagrange/scene/scene_utils.h>
19#include <lagrange/utils/assert.h>
21#include "bind_value.h"
24#include <lagrange/utils/warnoff.h>
26#include <nanobind/nanobind.h>
27#include <nanobind/eigen/dense.h>
28#include <nanobind/stl/string.h>
29#include <nanobind/stl/filesystem.h>
30#include <nanobind/stl/array.h>
31#include <nanobind/stl/variant.h>
32#include <nanobind/stl/optional.h>
33#include <nanobind/stl/bind_vector.h>
34#include <nanobind/stl/bind_map.h>
35#include <nanobind/trampoline.h>
36#include <lagrange/utils/warnon.h>
40namespace lagrange::python {
42namespace nb = nanobind;
44#pragma GCC visibility push(hidden)
49 bool is_supported(
const std::string& key)
const override
51 NB_OVERRIDE_PURE(is_supported, key);
53 bool can_read(
const std::string& key)
const override { NB_OVERRIDE(can_read, key); }
54 bool can_write(
const std::string& key)
const override { NB_OVERRIDE(can_write, key); }
58 NB_OVERRIDE(write, value);
61#pragma GCC visibility pop
63void bind_scene(nb::module_& m)
65 using namespace lagrange::scene;
66 using Scalar = double;
67 using Index = uint32_t;
70 nb::bind_vector<std::vector<Node>>(m,
"NodeList");
71 nb::bind_vector<std::vector<ElementId>>(m,
"ElementIdList");
72 nb::bind_vector<std::vector<SceneMeshInstance>>(m,
"SceneMeshInstanceList");
73 nb::bind_vector<std::vector<SurfaceMesh<Scalar, Index>>>(m,
"SurfaceMeshList");
74 nb::bind_vector<std::vector<ImageExperimental>>(m,
"ImageList");
75 nb::bind_vector<std::vector<Texture>>(m,
"TextureList");
76 nb::bind_vector<std::vector<MaterialExperimental>>(m,
"MaterialList");
77 nb::bind_vector<std::vector<Light>>(m,
"LightList");
78 nb::bind_vector<std::vector<Camera>>(m,
"CameraList");
79 nb::bind_vector<std::vector<Skeleton>>(m,
"SkeletonList");
80 nb::bind_vector<std::vector<Animation>>(m,
"AnimationList");
83 nb::bind_vector<std::vector<lagrange::scene::Value>>(m,
"ValueList");
84 nb::bind_vector<std::vector<unsigned char>>(m,
"BufferList");
85 nb::bind_map<std::unordered_map<std::string, lagrange::scene::Value>>(m,
"ValueUnorderedMap");
86 nb::bind_map<std::map<std::string, lagrange::scene::Value>>(m,
"ValueMap");
90 nb::class_<lagrange::scene::Extensions>(m,
"Extensions")
91 .def_prop_ro(
"size", &Extensions::size)
92 .def_prop_ro(
"empty", &Extensions::empty)
93 .def_rw(
"data", &Extensions::data);
117 nb::class_<SceneMeshInstance>(m,
"SceneMeshInstance",
"Mesh and material index of a node")
119 .def_rw(
"mesh", &SceneMeshInstance::mesh)
120 .def_rw(
"materials", &SceneMeshInstance::materials);
122 nb::class_<Node>(m,
"Node")
124 .def(
"__repr__", [](
const Node& node) {
return fmt::format(
"Node('{}')", node.name); })
125 .def_rw(
"name", &Node::name)
129 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
130 node.transform.data(),
135 [](
Node& node, nb::ndarray<nb::numpy, const float, nb::shape<4, 4>> t) ->
void {
136 auto view = t.view<float, nb::ndim<2>>();
138 for (
size_t i = 0; i < 4; i++) {
139 for (
size_t j = 0; j < 4; j++) {
140 node.transform.data()[i + j * 4] = view(i, j);
144 "The affine transform associated with this node")
145 .def_rw(
"parent", &Node::parent)
146 .def_rw(
"children", &Node::children)
147 .def_rw(
"meshes", &Node::meshes)
148 .def_rw(
"cameras", &Node::cameras)
149 .def_rw(
"lights", &Node::lights)
150 .def_rw(
"extensions", &Node::extensions);
152 nb::class_<ImageBufferExperimental> image_buffer(m,
"ImageBuffer");
153 image_buffer.def(nb::init<>())
154 .def_ro(
"width", &ImageBufferExperimental::width,
"Image width")
155 .def_ro(
"height", &ImageBufferExperimental::height,
"Image height")
158 &ImageBufferExperimental::num_channels,
159 "Number of channels in each pixel")
165 case AttributeValueType::e_int8_t:
167 nb::ndarray<int8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
168 reinterpret_cast<int8_t*>(self.data.data()),
172 nb::rv_policy::reference_internal);
173 case AttributeValueType::e_uint8_t:
175 nb::ndarray<uint8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
176 reinterpret_cast<uint8_t*>(self.data.data()),
180 nb::rv_policy::reference_internal);
181 case AttributeValueType::e_int16_t:
183 nb::ndarray<int16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
184 reinterpret_cast<int16_t*>(self.data.data()),
188 nb::rv_policy::reference_internal);
189 case AttributeValueType::e_uint16_t:
191 nb::ndarray<uint16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
192 reinterpret_cast<uint16_t*>(self.data.data()),
196 nb::rv_policy::reference_internal);
197 case AttributeValueType::e_int32_t:
199 nb::ndarray<int32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
200 reinterpret_cast<int32_t*>(self.data.data()),
204 nb::rv_policy::reference_internal);
205 case AttributeValueType::e_uint32_t:
207 nb::ndarray<uint32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
208 reinterpret_cast<uint32_t*>(self.data.data()),
212 nb::rv_policy::reference_internal);
213 case AttributeValueType::e_int64_t:
215 nb::ndarray<int64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
216 reinterpret_cast<int64_t*>(self.data.data()),
220 nb::rv_policy::reference_internal);
221 case AttributeValueType::e_uint64_t:
223 nb::ndarray<uint64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
224 reinterpret_cast<uint64_t*>(self.data.data()),
228 nb::rv_policy::reference_internal);
229 case AttributeValueType::e_float:
231 nb::ndarray<float, nb::numpy, nb::c_contig, nb::device::cpu>(
232 reinterpret_cast<float*>(self.data.data()),
236 nb::rv_policy::reference_internal);
237 case AttributeValueType::e_double:
239 nb::ndarray<double, nb::numpy, nb::c_contig, nb::device::cpu>(
240 reinterpret_cast<double*>(self.data.data()),
244 nb::rv_policy::reference_internal);
245 default: throw nb::type_error(
"Unsupported image buffer `dtype`!");
249 nb::ndarray<nb::numpy, nb::c_contig, nb::device::cpu> tensor) {
251 self.
width = tensor.shape(1);
252 self.
height = tensor.shape(0);
254 auto dtype = tensor.dtype();
255 if (dtype == nb::dtype<int8_t>()) {
257 }
else if (dtype == nb::dtype<uint8_t>()) {
259 }
else if (dtype == nb::dtype<int16_t>()) {
261 }
else if (dtype == nb::dtype<uint16_t>()) {
263 }
else if (dtype == nb::dtype<int32_t>()) {
265 }
else if (dtype == nb::dtype<uint32_t>()) {
267 }
else if (dtype == nb::dtype<int64_t>()) {
269 }
else if (dtype == nb::dtype<uint64_t>()) {
271 }
else if (dtype == nb::dtype<float>()) {
273 }
else if (dtype == nb::dtype<double>()) {
276 throw nb::type_error(
"Unsupported input tensor `dtype`!");
278 self.
data.resize(tensor.nbytes());
280 reinterpret_cast<uint8_t*
>(tensor.data()),
281 reinterpret_cast<uint8_t*
>(tensor.data()) + tensor.nbytes(),
288 auto np = nb::module_::import_(
"numpy");
290 case AttributeValueType::e_int8_t:
return np.attr(
"int8");
291 case AttributeValueType::e_int16_t:
return np.attr(
"int16");
292 case AttributeValueType::e_int32_t:
return np.attr(
"int32");
293 case AttributeValueType::e_int64_t:
return np.attr(
"int64");
294 case AttributeValueType::e_uint8_t:
return np.attr(
"uint8");
295 case AttributeValueType::e_uint16_t:
return np.attr(
"uint16");
296 case AttributeValueType::e_uint32_t:
return np.attr(
"uint32");
297 case AttributeValueType::e_uint64_t:
return np.attr(
"uint64");
298 case AttributeValueType::e_float:
return np.attr(
"float32");
299 case AttributeValueType::e_double:
return np.attr(
"float64");
300 default:
logger().warn(
"Image buffer has an unknown dtype.");
return std::nullopt;
303 "The element data type of the image buffer.");
305 nb::class_<ImageExperimental> image(m,
"Image");
306 image.def(nb::init<>())
307 .def_rw(
"name", &ImageExperimental::name,
"Name of the image object")
308 .def_rw(
"image", &ImageExperimental::image,
"Image buffer")
309 .def_rw(
"uri", &ImageExperimental::uri,
"URI of the image file")
312 &ImageExperimental::extensions,
313 "Additional data associated with the image")
315 return fmt::format(
"Image ('{}')", self.
name);
318 nb::class_<TextureInfo>(m,
"TextureInfo")
320 .def_rw(
"index", &TextureInfo::index)
321 .def_rw(
"texcoord", &TextureInfo::texcoord);
323 nb::class_<MaterialExperimental> material(m,
"Material");
324 material.def(nb::init<>())
328 .def_rw(
"name", &MaterialExperimental::name)
329 .def_rw(
"base_color_value", &MaterialExperimental::base_color_value)
330 .def_rw(
"base_color_texture", &MaterialExperimental::base_color_texture)
331 .def_rw(
"alpha_mode", &MaterialExperimental::alpha_mode)
332 .def_rw(
"alpha_cutoff", &MaterialExperimental::alpha_cutoff)
333 .def_rw(
"emissive_value", &MaterialExperimental::emissive_value)
334 .def_rw(
"emissive_texture", &MaterialExperimental::emissive_texture)
335 .def_rw(
"metallic_value", &MaterialExperimental::metallic_value)
336 .def_rw(
"roughness_value", &MaterialExperimental::roughness_value)
337 .def_rw(
"metallic_roughness_texture", &MaterialExperimental::metallic_roughness_texture)
338 .def_rw(
"normal_texture", &MaterialExperimental::normal_texture)
339 .def_rw(
"normal_scale", &MaterialExperimental::normal_scale)
340 .def_rw(
"occlusion_texture", &MaterialExperimental::occlusion_texture)
341 .def_rw(
"occlusion_strength", &MaterialExperimental::occlusion_strength)
342 .def_rw(
"double_sided", &MaterialExperimental::double_sided)
343 .def_rw(
"extensions", &MaterialExperimental::extensions);
345 nb::enum_<MaterialExperimental::AlphaMode>(material,
"AlphaMode")
346 .value(
"Opaque", MaterialExperimental::AlphaMode::Opaque)
347 .value(
"Mask", MaterialExperimental::AlphaMode::Mask)
348 .value(
"Blend", MaterialExperimental::AlphaMode::Blend);
351 nb::class_<Texture> texture(m,
"Texture",
"Texture");
352 texture.def(nb::init<>())
353 .def(
"__repr__", [](
const Texture& t) {
return fmt::format(
"Texture('{}')", t.name); })
354 .def_rw(
"name", &Texture::name)
355 .def_rw(
"image", &Texture::image)
356 .def_rw(
"mag_filter", &Texture::mag_filter)
357 .def_rw(
"min_filter", &Texture::min_filter)
358 .def_rw(
"wrap_u", &Texture::wrap_u)
359 .def_rw(
"wrap_v", &Texture::wrap_v)
360 .def_rw(
"scale", &Texture::scale)
361 .def_rw(
"offset", &Texture::offset)
362 .def_rw(
"rotation", &Texture::rotation)
363 .def_rw(
"extensions", &Texture::extensions);
365 nb::enum_<Texture::WrapMode>(texture,
"WrapMode")
366 .value(
"Wrap", Texture::WrapMode::Wrap)
367 .value(
"Clamp", Texture::WrapMode::Clamp)
368 .value(
"Decal", Texture::WrapMode::Decal)
369 .value(
"Mirror", Texture::WrapMode::Mirror);
370 nb::enum_<Texture::TextureFilter>(texture,
"TextureFilter")
371 .value(
"Undefined", Texture::TextureFilter::Undefined)
372 .value(
"Nearest", Texture::TextureFilter::Nearest)
373 .value(
"Linear", Texture::TextureFilter::Linear)
374 .value(
"NearestMimpapNearest", Texture::TextureFilter::NearestMimpapNearest)
375 .value(
"LinearMipmapNearest", Texture::TextureFilter::LinearMipmapNearest)
376 .value(
"NearestMipmapLinear", Texture::TextureFilter::NearestMipmapLinear)
377 .value(
"LinearMipmapLinear", Texture::TextureFilter::LinearMipmapLinear);
379 nb::class_<Light> light(m,
"Light",
"Light");
380 light.def(nb::init<>())
381 .def(
"__repr__", [](
const Light& l) {
return fmt::format(
"Light('{}')", l.name); })
382 .def_rw(
"name", &Light::name)
383 .def_rw(
"type", &Light::type)
384 .def_rw(
"position", &Light::position)
385 .def_rw(
"direction", &Light::direction)
386 .def_rw(
"up", &Light::up)
387 .def_rw(
"intensity", &Light::intensity)
388 .def_rw(
"attenuation_constant", &Light::attenuation_constant)
389 .def_rw(
"attenuation_linear", &Light::attenuation_linear)
390 .def_rw(
"attenuation_quadratic", &Light::attenuation_quadratic)
391 .def_rw(
"attenuation_cubic", &Light::attenuation_cubic)
393 .def_rw(
"color_diffuse", &Light::color_diffuse)
394 .def_rw(
"color_specular", &Light::color_specular)
395 .def_rw(
"color_ambient", &Light::color_ambient)
396 .def_rw(
"angle_inner_cone", &Light::angle_inner_cone)
397 .def_rw(
"angle_outer_cone", &Light::angle_outer_cone)
398 .def_rw(
"size", &Light::size)
399 .def_rw(
"extensions", &Light::extensions);
401 nb::enum_<Light::Type>(light,
"Type")
402 .value(
"Undefined", Light::Type::Undefined)
403 .value(
"Directional", Light::Type::Directional)
404 .value(
"Point", Light::Type::Point)
405 .value(
"Spot", Light::Type::Spot)
406 .value(
"Ambient", Light::Type::Ambient)
407 .value(
"Area", Light::Type::Area);
409 nb::class_<Camera> camera(m,
"Camera",
"Camera");
410 camera.def(nb::init<>())
411 .def(
"__repr__", [](
const Camera& c) {
return fmt::format(
"Camera('{}')", c.name); })
412 .def_rw(
"name", &Camera::name)
413 .def_rw(
"position", &Camera::position)
414 .def_rw(
"up", &Camera::up)
415 .def_rw(
"look_at", &Camera::look_at)
416 .def_rw(
"near_plane", &Camera::near_plane)
417 .def_rw(
"far_plane", &Camera::far_plane)
418 .def_rw(
"type", &Camera::type)
419 .def_rw(
"aspect_ratio", &Camera::aspect_ratio)
420 .def_rw(
"horizontal_fov", &Camera::horizontal_fov)
421 .def_rw(
"orthographic_width", &Camera::orthographic_width)
422 .def_prop_ro(
"get_vertical_fov", &Camera::get_vertical_fov)
424 "set_horizontal_fov_from_vertical_fov",
425 &Camera::set_horizontal_fov_from_vertical_fov,
427 .def_rw(
"extensions", &Camera::extensions);
429 nb::enum_<Camera::Type>(camera,
"Type")
430 .value(
"Perspective", Camera::Type::Perspective)
431 .value(
"Orthographic", Camera::Type::Orthographic);
433 nb::class_<Animation>(m,
"Animation",
"")
435 .def(
"__repr__", [](
const Animation& a) {
return fmt::format(
"Animation('{}')", a.name); })
436 .def_rw(
"name", &Animation::name)
437 .def_rw(
"extensions", &Animation::extensions);
440 nb::class_<Skeleton>(m,
"Skeleton",
"")
442 .def_rw(
"meshes", &Skeleton::meshes)
443 .def_rw(
"extensions", &Skeleton::extensions);
446 nb::class_<SceneType>(m,
"Scene",
"A 3D scene")
448 .def(
"__repr__", [](
const SceneType& s) {
return fmt::format(
"Scene('{}')", s.name); })
449 .def_rw(
"name", &SceneType::name)
450 .def_rw(
"nodes", &SceneType::nodes)
451 .def_rw(
"root_nodes", &SceneType::root_nodes)
452 .def_rw(
"meshes", &SceneType::meshes)
453 .def_rw(
"images", &SceneType::images)
454 .def_rw(
"textures", &SceneType::textures)
455 .def_rw(
"materials", &SceneType::materials)
456 .def_rw(
"lights", &SceneType::lights)
457 .def_rw(
"cameras", &SceneType::cameras)
458 .def_rw(
"skeletons", &SceneType::skeletons)
459 .def_rw(
"animations", &SceneType::animations)
460 .def_rw(
"extensions", &SceneType::extensions)
476 using T = std::decay_t<
decltype(value)>;
477 return self.add(std::forward<T>(value));
482 R
"(Add an element to the scene.
484:param element: The element to add to the scene. E.g. node, mesh, image, texture, material, light, camera, skeleton, or animation.
486:returns: The id of the added element.)")
489 &SceneType::add_child,
492 R
"(Add a child node to a parent node.
494:param parent_id: The parent node id.
495:param child_id: The child node id.
497:returns: The id of the added child node.)");
500 "compute_global_node_transform",
501 [](
const SceneType& scene,
size_t node_idx) {
502 auto t = utils::compute_global_node_transform<Scalar, Index>(scene, node_idx);
503 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
512 R
"(Compute the global transform associated with a node.
514:param scene: The input node.
515:param node_idx: The index of the taget node.
517:returns: The global transform of the target node, which is the combination of transforms from this node all the way to the root.
Definition: SceneExtension.h:33
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:40
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition: range.h:176
Definition: bind_scene.h:46
Minimalistic image data structure that stores the raw image data.
Definition: Scene.h:87
size_t height
Image height.
Definition: Scene.h:92
size_t width
Image width.
Definition: Scene.h:89
AttributeValueType element_type
The scalar type of the elements in the buffer.
Definition: Scene.h:98
std::vector< unsigned char > data
Raw buffer of size (width * height * num_channels * num_bits_per_element / 8) bytes containing image ...
Definition: Scene.h:101
size_t num_channels
Number of image channels (must be 1, 3, or 4).
Definition: Scene.h:95
Image structure that can store either image data or reference to an image file.
Definition: Scene.h:113
std::string name
Image name. Not guaranteed to be unique and can be empty.
Definition: Scene.h:115
Definition: SceneExtension.h:182