Lagrange
bind_scene.h
1/*
2 * Copyright 2023 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <lagrange/AttributeValueType.h>
15#include <lagrange/Logger.h>
16#include <lagrange/python/tensor_utils.h>
17#include <lagrange/scene/Scene.h>
18#include <lagrange/scene/internal/scene_string_utils.h>
19#include <lagrange/scene/scene_utils.h>
20#include <lagrange/utils/assert.h>
21
22#include "bind_value.h"
23
24// clang-format off
25#include <lagrange/utils/warnoff.h>
26#include <Eigen/Core>
27#include <nanobind/eigen/dense.h>
28#include <nanobind/eval.h>
29#include <nanobind/nanobind.h>
30#include <nanobind/stl/array.h>
31#include <nanobind/stl/bind_map.h>
32#include <nanobind/stl/bind_vector.h>
33#include <nanobind/stl/filesystem.h>
34#include <nanobind/stl/optional.h>
35#include <nanobind/stl/string.h>
36#include <nanobind/stl/variant.h>
37#include <nanobind/trampoline.h>
38#include <lagrange/utils/warnon.h>
39// clang-format on
40
41
42namespace lagrange::python {
43
44namespace nb = nanobind;
45
46void bind_scene(nb::module_& m)
47{
48 using namespace lagrange::scene;
49 using Scalar = double;
50 using Index = uint32_t;
51 using SceneType = Scene<Scalar, Index>;
52
53 nb::bind_vector<std::vector<Node>>(m, "NodeList");
54 nb::bind_vector<std::vector<ElementId>>(m, "ElementIdList");
55 nb::bind_vector<std::vector<SceneMeshInstance>>(m, "SceneMeshInstanceList");
56 nb::bind_vector<std::vector<SurfaceMesh<Scalar, Index>>>(m, "SurfaceMeshList");
57 nb::bind_vector<std::vector<ImageExperimental>>(m, "ImageList");
58 nb::bind_vector<std::vector<Texture>>(m, "TextureList");
59 nb::bind_vector<std::vector<MaterialExperimental>>(m, "MaterialList");
60 nb::bind_vector<std::vector<Light>>(m, "LightList");
61 nb::bind_vector<std::vector<Camera>>(m, "CameraList");
62 nb::bind_vector<std::vector<Skeleton>>(m, "SkeletonList");
63 nb::bind_vector<std::vector<Animation>>(m, "AnimationList");
64
65
66 nb::bind_vector<std::vector<lagrange::scene::Value>>(m, "ValueList");
67 nb::bind_vector<std::vector<unsigned char>>(m, "BufferList");
68 nb::bind_map<std::unordered_map<std::string, lagrange::scene::Value>>(m, "ValueUnorderedMap");
69 nb::bind_map<std::map<std::string, lagrange::scene::Value>>(m, "ValueMap");
70
71
72 nb::class_<lagrange::scene::Extensions>(m, "Extensions")
73 .def("__repr__", [](const lagrange::scene::Extensions& self) { return scene::internal::to_string(self); })
74 .def_prop_ro("size", &Extensions::size)
75 .def_prop_ro("empty", &Extensions::empty)
76 .def_rw("data", &Extensions::data);
77
78 nb::class_<SceneMeshInstance>(m, "SceneMeshInstance", "Mesh and material index of a node")
79 .def(nb::init<>())
80 .def(
81 "__repr__",
82 [](const SceneMeshInstance& self) { return scene::internal::to_string(self); })
83 .def_prop_rw(
84 "mesh",
85 [](SceneMeshInstance& self) -> std::optional<ElementId> {
86 if (self.mesh != invalid_element)
87 return self.mesh;
88 else
89 return {};
90 },
91 [](SceneMeshInstance& self, ElementId mesh) { self.mesh = mesh; },
92 "Mesh index in the scene.meshes vector (None if invalid)")
93 .def_rw("materials", &SceneMeshInstance::materials);
94
95 nb::class_<Node>(m, "Node")
96 .def(nb::init<>())
97 .def("__repr__", [](const Node& self) { return scene::internal::to_string(self); })
98 .def_rw("name", &Node::name)
99 .def_prop_rw(
100 "transform",
101 [](Node& node) {
102 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
103 node.transform.data(),
104 {4, 4},
105 nb::find(node),
106 {1, 4});
107 },
108 [](Node& node, nb::ndarray<nb::numpy, const float, nb::shape<4, 4>> t) -> void {
109 auto view = t.view<float, nb::ndim<2>>();
110 // Explicit 2D indexing because the input ndarray can be either row or column major.
111 for (size_t i = 0; i < 4; i++) {
112 for (size_t j = 0; j < 4; j++) {
113 node.transform.data()[i + j * 4] = view(i, j);
114 }
115 }
116 },
117 "The affine transform associated with this node")
118 .def_prop_rw(
119 "parent",
120 [](Node& node) -> std::optional<ElementId> {
121 if (node.parent != invalid_element)
122 return node.parent;
123 else
124 return {};
125 },
126 [](Node& node, ElementId parent) { node.parent = parent; },
127 "Parent node index (None if no parent)")
128 .def_rw("children", &Node::children)
129 .def_rw("meshes", &Node::meshes)
130 .def_rw("cameras", &Node::cameras)
131 .def_rw("lights", &Node::lights)
132 .def_rw("extensions", &Node::extensions);
133
134 nb::class_<ImageBufferExperimental> image_buffer(m, "ImageBuffer");
135 image_buffer.def(nb::init<>())
136 .def(
137 "__repr__",
138 [](const ImageBufferExperimental& self) { return scene::internal::to_string(self); })
139 .def_ro("width", &ImageBufferExperimental::width, "Image width")
140 .def_ro("height", &ImageBufferExperimental::height, "Image height")
141 .def_ro(
142 "num_channels",
143 &ImageBufferExperimental::num_channels,
144 "Number of channels in each pixel")
145 .def_prop_rw(
146 "data",
147 [](ImageBufferExperimental& self) {
148 size_t shape[3] = {self.height, self.width, self.num_channels};
149 switch (self.element_type) {
150 case AttributeValueType::e_int8_t:
151 return nb::cast(
152 nb::ndarray<int8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
153 reinterpret_cast<int8_t*>(self.data.data()),
154 3,
155 shape,
156 nb::find(self)),
157 nb::rv_policy::reference_internal);
158 case AttributeValueType::e_uint8_t:
159 return nb::cast(
160 nb::ndarray<uint8_t, nb::numpy, nb::c_contig, nb::device::cpu>(
161 reinterpret_cast<uint8_t*>(self.data.data()),
162 3,
163 shape,
164 nb::find(self)),
165 nb::rv_policy::reference_internal);
166 case AttributeValueType::e_int16_t:
167 return nb::cast(
168 nb::ndarray<int16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
169 reinterpret_cast<int16_t*>(self.data.data()),
170 3,
171 shape,
172 nb::find(self)),
173 nb::rv_policy::reference_internal);
174 case AttributeValueType::e_uint16_t:
175 return nb::cast(
176 nb::ndarray<uint16_t, nb::numpy, nb::c_contig, nb::device::cpu>(
177 reinterpret_cast<uint16_t*>(self.data.data()),
178 3,
179 shape,
180 nb::find(self)),
181 nb::rv_policy::reference_internal);
182 case AttributeValueType::e_int32_t:
183 return nb::cast(
184 nb::ndarray<int32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
185 reinterpret_cast<int32_t*>(self.data.data()),
186 3,
187 shape,
188 nb::find(self)),
189 nb::rv_policy::reference_internal);
190 case AttributeValueType::e_uint32_t:
191 return nb::cast(
192 nb::ndarray<uint32_t, nb::numpy, nb::c_contig, nb::device::cpu>(
193 reinterpret_cast<uint32_t*>(self.data.data()),
194 3,
195 shape,
196 nb::find(self)),
197 nb::rv_policy::reference_internal);
198 case AttributeValueType::e_int64_t:
199 return nb::cast(
200 nb::ndarray<int64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
201 reinterpret_cast<int64_t*>(self.data.data()),
202 3,
203 shape,
204 nb::find(self)),
205 nb::rv_policy::reference_internal);
206 case AttributeValueType::e_uint64_t:
207 return nb::cast(
208 nb::ndarray<uint64_t, nb::numpy, nb::c_contig, nb::device::cpu>(
209 reinterpret_cast<uint64_t*>(self.data.data()),
210 3,
211 shape,
212 nb::find(self)),
213 nb::rv_policy::reference_internal);
214 case AttributeValueType::e_float:
215 return nb::cast(
216 nb::ndarray<float, nb::numpy, nb::c_contig, nb::device::cpu>(
217 reinterpret_cast<float*>(self.data.data()),
218 3,
219 shape,
220 nb::find(self)),
221 nb::rv_policy::reference_internal);
222 case AttributeValueType::e_double:
223 return nb::cast(
224 nb::ndarray<double, nb::numpy, nb::c_contig, nb::device::cpu>(
225 reinterpret_cast<double*>(self.data.data()),
226 3,
227 shape,
228 nb::find(self)),
229 nb::rv_policy::reference_internal);
230 default: throw nb::type_error("Unsupported image buffer `dtype`!");
231 }
232 },
234 nb::ndarray<nb::numpy, nb::c_contig, nb::device::cpu> tensor) {
235 la_runtime_assert(tensor.ndim() == 3);
236 self.width = tensor.shape(1);
237 self.height = tensor.shape(0);
238 self.num_channels = tensor.shape(2);
239 auto dtype = tensor.dtype();
240 if (dtype == nb::dtype<int8_t>()) {
241 self.element_type = AttributeValueType::e_int8_t;
242 } else if (dtype == nb::dtype<uint8_t>()) {
243 self.element_type = AttributeValueType::e_uint8_t;
244 } else if (dtype == nb::dtype<int16_t>()) {
245 self.element_type = AttributeValueType::e_int16_t;
246 } else if (dtype == nb::dtype<uint16_t>()) {
247 self.element_type = AttributeValueType::e_uint16_t;
248 } else if (dtype == nb::dtype<int32_t>()) {
249 self.element_type = AttributeValueType::e_int32_t;
250 } else if (dtype == nb::dtype<uint32_t>()) {
251 self.element_type = AttributeValueType::e_uint32_t;
252 } else if (dtype == nb::dtype<int64_t>()) {
253 self.element_type = AttributeValueType::e_int64_t;
254 } else if (dtype == nb::dtype<uint64_t>()) {
255 self.element_type = AttributeValueType::e_uint64_t;
256 } else if (dtype == nb::dtype<float>()) {
257 self.element_type = AttributeValueType::e_float;
258 } else if (dtype == nb::dtype<double>()) {
259 self.element_type = AttributeValueType::e_double;
260 } else {
261 throw nb::type_error("Unsupported input tensor `dtype`!");
262 }
263 self.data.resize(tensor.nbytes());
264 std::copy(
265 reinterpret_cast<uint8_t*>(tensor.data()),
266 reinterpret_cast<uint8_t*>(tensor.data()) + tensor.nbytes(),
267 self.data.data());
268 },
269 "Raw image data.")
270 .def_prop_ro(
271 "dtype",
272 [](ImageBufferExperimental& self) -> std::optional<nb::type_object> {
273 auto np = nb::module_::import_("numpy");
274 switch (self.element_type) {
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;
286 }
287 },
288 "The element data type of the image buffer.");
289
290 nb::class_<ImageExperimental> image(m, "Image");
291 image.def(nb::init<>())
292 .def(
293 "__repr__",
294 [](const ImageExperimental& self) { return scene::internal::to_string(self); })
295 .def_rw("name", &ImageExperimental::name, "Name of the image object")
296 .def_rw("image", &ImageExperimental::image, "Image buffer")
297 .def_prop_rw(
298 "uri",
299 [](const ImageExperimental& self) -> std::optional<std::string> {
300 if (self.uri.empty())
301 return {};
302 else
303 return self.uri.string();
304 },
305 [](ImageExperimental& self, std::optional<std::string> uri) {
306 if (uri.has_value())
307 self.uri = fs::path(uri.value());
308 else
309 self.uri = fs::path();
310 },
311 "URI of the image file")
312 .def_rw(
313 "extensions",
314 &ImageExperimental::extensions,
315 "Additional data associated with the image");
316
317 nb::class_<TextureInfo>(m, "TextureInfo")
318 .def(nb::init<>())
319 .def("__repr__", [](const TextureInfo& self) { return scene::internal::to_string(self); })
320 .def_prop_rw(
321 "index",
322 [](const TextureInfo& self) -> std::optional<ElementId> {
323 if (self.index != invalid_element)
324 return self.index;
325 else
326 return {};
327 },
328 [](TextureInfo& self, std::optional<ElementId> index) {
329 if (index.has_value())
330 self.index = index.value();
331 else
332 self.index = invalid_element;
333 },
334 "Texture index in scene.textures vector. `None` if not set.")
335 .def_rw("texcoord", &TextureInfo::texcoord);
336
337 nb::class_<MaterialExperimental> material(m, "Material");
338 material.def(nb::init<>())
339 .def(
340 "__repr__",
341 [](const MaterialExperimental& self) { return scene::internal::to_string(self); })
342 .def_rw("name", &MaterialExperimental::name)
343 .def_rw("base_color_value", &MaterialExperimental::base_color_value)
344 .def_rw("base_color_texture", &MaterialExperimental::base_color_texture)
345 .def_rw("alpha_mode", &MaterialExperimental::alpha_mode)
346 .def_rw("alpha_cutoff", &MaterialExperimental::alpha_cutoff)
347 .def_rw("emissive_value", &MaterialExperimental::emissive_value)
348 .def_rw("emissive_texture", &MaterialExperimental::emissive_texture)
349 .def_rw("metallic_value", &MaterialExperimental::metallic_value)
350 .def_rw("roughness_value", &MaterialExperimental::roughness_value)
351 .def_rw("metallic_roughness_texture", &MaterialExperimental::metallic_roughness_texture)
352 .def_rw("normal_texture", &MaterialExperimental::normal_texture)
353 .def_rw("normal_scale", &MaterialExperimental::normal_scale)
354 .def_rw("occlusion_texture", &MaterialExperimental::occlusion_texture)
355 .def_rw("occlusion_strength", &MaterialExperimental::occlusion_strength)
356 .def_rw("double_sided", &MaterialExperimental::double_sided)
357 .def_rw("extensions", &MaterialExperimental::extensions);
358
359 nb::enum_<MaterialExperimental::AlphaMode>(material, "AlphaMode", "Alpha mode")
360 .value("Opaque", MaterialExperimental::AlphaMode::Opaque)
361 .value("Mask", MaterialExperimental::AlphaMode::Mask)
362 .value("Blend", MaterialExperimental::AlphaMode::Blend);
363
364
365 nb::class_<Texture> texture(m, "Texture", "Texture");
366 texture.def(nb::init<>())
367 .def("__repr__", [](const Texture& self) { return scene::internal::to_string(self); })
368 .def_rw("name", &Texture::name)
369 .def_prop_rw(
370 "image",
371 [](Texture& self) -> std::optional<ElementId> {
372 if (self.image != invalid_element)
373 return self.image;
374 else
375 return {};
376 },
377 [](Texture& self, ElementId img) { self.image = img; },
378 "Texture image index in scene.images vector (None if invalid)")
379 .def_rw("mag_filter", &Texture::mag_filter)
380 .def_rw("min_filter", &Texture::min_filter)
381 .def_rw("wrap_u", &Texture::wrap_u)
382 .def_rw("wrap_v", &Texture::wrap_v)
383 .def_rw("scale", &Texture::scale)
384 .def_rw("offset", &Texture::offset)
385 .def_rw("rotation", &Texture::rotation)
386 .def_rw("extensions", &Texture::extensions);
387
388 nb::enum_<Texture::WrapMode>(texture, "WrapMode", "Texture wrap mode")
389 .value("Wrap", Texture::WrapMode::Wrap)
390 .value("Clamp", Texture::WrapMode::Clamp)
391 .value("Decal", Texture::WrapMode::Decal)
392 .value("Mirror", Texture::WrapMode::Mirror);
393 nb::enum_<Texture::TextureFilter>(texture, "TextureFilter", "Texture filter mode")
394 .value("Undefined", Texture::TextureFilter::Undefined)
395 .value("Nearest", Texture::TextureFilter::Nearest)
396 .value("Linear", Texture::TextureFilter::Linear)
397 .value("NearestMipmapNearest", Texture::TextureFilter::NearestMipmapNearest)
398 .value("LinearMipmapNearest", Texture::TextureFilter::LinearMipmapNearest)
399 .value("NearestMipmapLinear", Texture::TextureFilter::NearestMipmapLinear)
400 .value("LinearMipmapLinear", Texture::TextureFilter::LinearMipmapLinear);
401
402 nb::class_<Light> light(m, "Light", "Light");
403 light.def(nb::init<>())
404 .def("__repr__", [](const Light& self) { return scene::internal::to_string(self); })
405 .def_rw("name", &Light::name)
406 .def_rw("type", &Light::type)
407 .def_rw("position", &Light::position)
408 .def_rw("direction", &Light::direction)
409 .def_rw("up", &Light::up)
410 .def_rw("intensity", &Light::intensity)
411 .def_rw("attenuation_constant", &Light::attenuation_constant)
412 .def_rw("attenuation_linear", &Light::attenuation_linear)
413 .def_rw("attenuation_quadratic", &Light::attenuation_quadratic)
414 .def_rw("attenuation_cubic", &Light::attenuation_cubic)
415 .def_rw("range", &Light::range)
416 .def_rw("color_diffuse", &Light::color_diffuse)
417 .def_rw("color_specular", &Light::color_specular)
418 .def_rw("color_ambient", &Light::color_ambient)
419 .def_rw("angle_inner_cone", &Light::angle_inner_cone)
420 .def_rw("angle_outer_cone", &Light::angle_outer_cone)
421 .def_rw("size", &Light::size)
422 .def_rw("extensions", &Light::extensions);
423
424 nb::enum_<Light::Type>(light, "Type", "Light type")
425 .value("Undefined", Light::Type::Undefined)
426 .value("Directional", Light::Type::Directional)
427 .value("Point", Light::Type::Point)
428 .value("Spot", Light::Type::Spot)
429 .value("Ambient", Light::Type::Ambient)
430 .value("Area", Light::Type::Area);
431
432 nb::class_<Camera> camera(m, "Camera", "Camera");
433 camera.def(nb::init<>())
434 .def("__repr__", [](const Camera& self) { return scene::internal::to_string(self); })
435 .def_rw("name", &Camera::name)
436 .def_rw("position", &Camera::position)
437 .def_rw("up", &Camera::up)
438 .def_rw("look_at", &Camera::look_at)
439 .def_rw("near_plane", &Camera::near_plane)
440 .def_rw("far_plane", &Camera::far_plane)
441 .def_rw("type", &Camera::type)
442 .def_rw("aspect_ratio", &Camera::aspect_ratio)
443 .def_rw("horizontal_fov", &Camera::horizontal_fov)
444 .def_rw("orthographic_width", &Camera::orthographic_width)
445 .def_prop_ro("get_vertical_fov", &Camera::get_vertical_fov)
446 .def_prop_ro(
447 "set_horizontal_fov_from_vertical_fov",
448 &Camera::set_horizontal_fov_from_vertical_fov,
449 "vfov"_a)
450 .def_rw("extensions", &Camera::extensions);
451
452 nb::enum_<Camera::Type>(camera, "Type", "Camera type")
453 .value("Perspective", Camera::Type::Perspective)
454 .value("Orthographic", Camera::Type::Orthographic);
455
456 nb::class_<Animation>(m, "Animation", "")
457 .def(nb::init<>())
458 .def("__repr__", [](const Animation& self) { return scene::internal::to_string(self); })
459 .def_rw("name", &Animation::name)
460 .def_rw("extensions", &Animation::extensions);
461
462
463 nb::class_<Skeleton>(m, "Skeleton", "")
464 .def(nb::init<>())
465 .def("__repr__", [](const Skeleton& self) { return scene::internal::to_string(self); })
466 .def_rw("meshes", &Skeleton::meshes)
467 .def_rw("extensions", &Skeleton::extensions);
468
469
470 nb::class_<SceneType>(m, "Scene", "A 3D scene")
471 .def(nb::init<>())
472 .def(
473 "__repr__",
474 [](const SceneType& self) { return scene::internal::to_string(self); })
475 .def_rw("name", &SceneType::name)
476 .def_rw("nodes", &SceneType::nodes)
477 .def_rw("root_nodes", &SceneType::root_nodes)
478 .def_rw("meshes", &SceneType::meshes)
479 .def_rw("images", &SceneType::images)
480 .def_rw("textures", &SceneType::textures)
481 .def_rw("materials", &SceneType::materials)
482 .def_rw("lights", &SceneType::lights)
483 .def_rw("cameras", &SceneType::cameras)
484 .def_rw("skeletons", &SceneType::skeletons)
485 .def_rw("animations", &SceneType::animations)
486 .def_rw("extensions", &SceneType::extensions)
487 .def(
488 "add",
489 [](SceneType& self,
490 std::variant<
491 Node,
492 SceneType::MeshType,
494 Texture,
496 Light,
497 Camera,
498 Skeleton,
499 Animation> element) {
500 return std::visit(
501 [&](auto&& value) {
502 using T = std::decay_t<decltype(value)>;
503 return self.add(std::forward<T>(value));
504 },
505 element);
506 },
507 "element"_a,
508 R"(Add an element to the scene.
509
510:param element: The element to add to the scene. E.g. node, mesh, image, texture, material, light, camera, skeleton, or animation.
511
512:returns: The id of the added element.)")
513 .def(
514 "add_child",
515 &SceneType::add_child,
516 "parent_id"_a,
517 "child_id"_a,
518 R"(Add a child node to a parent node.
519
520:param parent_id: The parent node id.
521:param child_id: The child node id.
522
523:returns: The id of the added child node.)");
524
525 m.def(
526 "compute_global_node_transform",
527 [](const SceneType& scene, size_t node_idx) {
528 auto t = utils::compute_global_node_transform<Scalar, Index>(scene, node_idx);
529 return nb::ndarray<nb::numpy, float, nb::f_contig, nb::shape<4, 4>>(
530 t.data(),
531 {4, 4},
532 nb::handle(), // owner
533 {1, 4});
534 },
535 nb::rv_policy::copy,
536 "scene"_a,
537 "node_idx"_a,
538 R"(Compute the global transform associated with a node.
539
540:param scene: The input node.
541:param node_idx: The index of the target node.
542
543:returns: The global transform of the target node, which is the combination of transforms from this node all the way to the root.
544 )");
545}
546
547} // namespace lagrange::python
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:40
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:169
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition: range.h:176
Definition: Scene.h:329
Definition: Scene.h:271
Definition: SceneExtension.h:192
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
fs::path uri
Image file path.
Definition: Scene.h:122
Definition: Scene.h:225
Definition: Scene.h:55
Definition: Scene.h:349
Definition: Scene.h:337
Definition: Scene.h:185
Definition: Scene.h:132
ElementId index
Texture index. Index in scene.textures vector.
Definition: Scene.h:134