14#include <lagrange/AttributeValueType.h>
15#include <lagrange/CameraTransforms.h>
16#include <lagrange/Logger.h>
17#include <lagrange/image/Array3D.h>
18#include <lagrange/image/View3D.h>
19#include <lagrange/scene/Scene.h>
20#include <lagrange/scene/scene_utils.h>
21#include <lagrange/transform_mesh.h>
22#include <lagrange/utils/fmt/format.h>
27namespace lagrange::scene::internal {
29using Array3Df = image::experimental::Array3D<float>;
30using View3Df = image::experimental::View3D<float>;
31using ConstView3Df = image::experimental::View3D<const float>;
34inline Array3Df convert_from(
const ImageBufferExperimental& image)
36 size_t nc = std::min(image.num_channels,
size_t(3));
39 auto copy_buffer = [&](
auto scalar) {
40 using T = std::decay_t<
decltype(scalar)>;
41 constexpr bool IsChar = std::is_integral_v<T> &&
sizeof(T) == 1;
43 auto rawbuf =
reinterpret_cast<const T*
>(image.data.data());
44 for (
size_t y = 0, i = 0; y < image.height; ++y) {
45 for (
size_t x = 0; x < image.width; ++x) {
46 for (
size_t c = 0; c < image.num_channels; ++c) {
51 if constexpr (IsChar) {
52 result(x, y, c) =
static_cast<float>(rawbuf[i++]) / 255.f;
54 result(x, y, c) = rawbuf[i++];
61 switch (image.element_type) {
62 case AttributeValueType::e_uint8_t: copy_buffer(uint8_t());
break;
63 case AttributeValueType::e_int8_t: copy_buffer(int8_t());
break;
64 case AttributeValueType::e_uint32_t: copy_buffer(uint32_t());
break;
65 case AttributeValueType::e_int32_t: copy_buffer(int32_t());
break;
66 case AttributeValueType::e_float: copy_buffer(
float());
break;
67 case AttributeValueType::e_double: copy_buffer(
double());
break;
68 default:
throw std::runtime_error(
"Unsupported image scalar type");
77inline ImageBufferExperimental convert_to(
const ConstView3Df& image)
79 ImageBufferExperimental result;
80 result.width = image.extent(0);
81 result.height = image.extent(1);
82 const size_t num_channels = image.extent(2);
84 num_channels == 1 || num_channels == 3 || num_channels == 4,
85 "ImageBufferExperimental requires 1, 3, or 4 channels");
86 result.num_channels = num_channels;
87 result.element_type = AttributeValueType::e_uint8_t;
88 result.data.resize(result.width * result.height * result.num_channels);
89 for (
size_t y = 0, i = 0; y < result.height; ++y) {
90 for (
size_t x = 0; x < result.width; ++x) {
91 for (
size_t c = 0; c < result.num_channels; ++c) {
93 static_cast<unsigned char>(std::clamp(image(x, y, c), 0.0f, 1.0f) * 255.0f);
102 MaterialExperimental::AlphaMode alpha_mode = MaterialExperimental::AlphaMode::Opaque;
103 float alpha_cutoff = 0.5f;
108template <
typename Scalar,
typename Index>
111 const ConstView3Df&
image,
116 auto mesh_id = scene.
add(std::move(mesh));
119 scene_image.
name =
"base_color";
121 auto image_id = scene.add(std::move(scene_image));
124 texture.name =
"base_color";
125 texture.image = image_id;
126 auto texture_id = scene.add(std::move(texture));
129 material.name =
"material";
130 material.alpha_mode = options.alpha_mode;
131 material.alpha_cutoff = options.alpha_cutoff;
132 material.base_color_texture.
index = texture_id;
133 material.base_color_texture.
texcoord = 0;
134 auto material_id = scene.add(std::move(material));
139 instance.mesh = mesh_id;
140 instance.materials.push_back(material_id);
141 node.meshes.push_back(std::move(instance));
142 auto node_id = scene.add(std::move(node));
143 scene.root_nodes.push_back(node_id);
149template <
typename Scalar,
typename Index>
150std::tuple<SurfaceMesh<Scalar, Index>, std::optional<Array3Df>> single_mesh_from_scene(
151 const Scene<Scalar, Index>& scene)
153 using ElementId = scene::ElementId;
156 std::vector<ElementId> mesh_node_ids;
157 for (ElementId node_id = 0; node_id < scene.nodes.size(); ++node_id) {
158 const auto& node = scene.nodes[node_id];
159 if (!node.meshes.empty()) {
160 mesh_node_ids.push_back(node_id);
164 if (mesh_node_ids.size() != 1) {
165 throw std::runtime_error(format(
166 "Input scene contains {} mesh nodes. Expected exactly 1 mesh node.",
167 mesh_node_ids.size()));
169 const auto& mesh_node = scene.nodes[mesh_node_ids.front()];
171 if (mesh_node.meshes.size() != 1) {
172 throw std::runtime_error(format(
173 "Input scene has a mesh node with {} instance per node. Expected "
174 "exactly 1 instance per node",
175 mesh_node.meshes.size()));
177 const auto& mesh_instance = mesh_node.meshes.front();
179 [[maybe_unused]]
const auto mesh_id = mesh_instance.mesh;
181 SurfaceMesh<Scalar, Index> mesh = scene.meshes[mesh_instance.mesh];
184 auto world_from_mesh = utils::compute_global_node_transform(scene, mesh_node_ids.front())
185 .template cast<Scalar>();
190 if (
auto num_mats = mesh_instance.materials.size(); num_mats != 1) {
192 "Mesh node has {} materials. Expected exactly 1 material. Ignoring materials.",
194 return {mesh, std::nullopt};
196 const auto& material = scene.materials[mesh_instance.materials.front()];
197 if (material.base_color_texture.
texcoord != 0) {
199 "Mesh node material texcoord is {} != 0. Expected 0. Ignoring texcoord.",
200 material.base_color_texture.
texcoord);
202 const auto texture_id = material.base_color_texture.
index;
204 const auto& texture = scene.textures[texture_id];
206 const auto image_id = texture.image;
208 const auto& image_ = scene.images[image_id].image;
209 Array3Df image = convert_from(image_);
211 return {mesh, image};
215template <
typename Scalar,
typename Index>
216std::vector<CameraTransforms> camera_transforms_from_scene(
const Scene<Scalar, Index>& scene)
218 using ElementId = scene::ElementId;
219 std::vector<CameraTransforms> cameras;
220 for (ElementId node_id = 0; node_id < scene.nodes.size(); ++node_id) {
221 const auto& node = scene.nodes[node_id];
222 if (!node.cameras.empty()) {
223 auto world_from_node = utils::compute_global_node_transform(scene, node_id);
224 for (
auto camera_id : node.cameras) {
225 const auto& scene_camera = scene.cameras[camera_id];
226 CameraTransforms camera;
227 camera.view = utils::camera_view_transform(scene_camera, world_from_node);
228 camera.projection = utils::camera_projection_transform(scene_camera);
229 cameras.push_back(camera);
A general purpose polygonal mesh class.
Definition SurfaceMesh.h:73
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
void transform_mesh(SurfaceMesh< Scalar, Index > &mesh, const Eigen::Transform< Scalar, Dimension, Eigen::Affine > &transform, const TransformOptions &options={})
Apply an affine transform to a mesh in-place.
Definition transform_mesh.cpp:137
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:175
#define la_debug_assert(...)
Debug assertion check.
Definition assert.h:195
Array3D< T > create_image(size_t width, size_t height, size_t num_channels)
Create an image with the given dimensions and number of channels.
Definition Array3D.h:46
Basic image data structure.
Image structure that can store either image data or reference to an image file.
Definition Scene.h:116
ImageBufferExperimental image
Image data.
Definition Scene.h:121
std::string name
Image name. Not guaranteed to be unique and can be empty.
Definition Scene.h:118
ElementId add(T &&value)
Add an element to the scene.
Definition Scene.h:417
int texcoord
Index of UV coordinates.
Definition Scene.h:141
ElementId index
Texture index. Index in scene.textures vector.
Definition Scene.h:137
Definition shared_utils.h:101