1use crate::{errors::Result, utils::is_url};
2use k::nalgebra as na;
3use kiss3d::scene::SceneNode;
4use std::{cell::RefCell, ffi::OsStr, io, path::Path, rc::Rc};
5use tracing::*;
6
7#[cfg(feature = "assimp")]
8#[cfg(not(target_family = "wasm"))]
9fn load_mesh_assimp(
10 file_string: &str,
11 scale: na::Vector3<f32>,
12 opt_urdf_color: &Option<na::Point3<f32>>,
13 group: &mut SceneNode,
14 use_texture: bool,
15) -> Result<SceneNode> {
16 use crate::assimp_utils::*;
17
18 let filename = Path::new(file_string);
19
20 let mut base = group.add_group();
21 let mut importer = assimp::Importer::new();
22 importer.pre_transform_vertices(|x| x.enable = true);
23 importer.collada_ignore_up_direction(true);
24 importer.triangulate(true);
25 let (meshes, textures, colors) =
26 convert_assimp_scene_to_kiss3d_mesh(&importer.read_file(file_string)?);
27 info!(
28 "num mesh, texture, colors = {} {} {}",
29 meshes.len(),
30 textures.len(),
31 colors.len()
32 );
33 let mesh_scenes = meshes
34 .into_iter()
35 .map(|mesh| {
36 let mut scene = base.add_mesh(mesh, scale);
37 if let Some(urdf_color) = *opt_urdf_color {
39 scene.set_color(urdf_color[0], urdf_color[1], urdf_color[2]);
40 }
41 scene
42 })
43 .collect::<Vec<_>>();
44 let is_collada = matches!(
46 filename.extension().and_then(OsStr::to_str),
47 Some("dae" | "DAE")
48 );
49 if !use_texture || !is_collada {
51 return Ok(base);
52 }
53
54 if mesh_scenes.len() == colors.len() {
56 for (count, (mut mesh_scene, color)) in
57 mesh_scenes.into_iter().zip(colors.into_iter()).enumerate()
58 {
59 mesh_scene.set_color(color[0], color[1], color[2]);
60 if count < textures.len() {
62 let mut texture_path = filename.to_path_buf();
63 texture_path.set_file_name(textures[count].clone());
64 debug!("using texture={}", texture_path.display());
65 if texture_path.exists() {
66 mesh_scene.set_texture_from_file(&texture_path, texture_path.to_str().unwrap());
67 }
68 }
69 }
70 } else {
71 for mut mesh_scene in mesh_scenes {
74 if !textures.is_empty() {
75 let mut texture_path = filename.to_path_buf();
76 texture_path.set_file_name(textures[0].clone());
77 debug!("texture={}", texture_path.display());
78 if texture_path.exists() {
79 mesh_scene.set_texture_from_file(&texture_path, texture_path.to_str().unwrap());
80 }
81 }
82 if !colors.is_empty() {
83 let color = colors[0];
84 mesh_scene.set_color(color[0], color[1], color[2]);
85 }
86 }
87 }
88 Ok(base)
89}
90
91#[cfg(not(target_family = "wasm"))]
92pub fn load_mesh(
93 filename: impl AsRef<str>,
94 scale: na::Vector3<f32>,
95 opt_color: &Option<na::Point3<f32>>,
96 group: &mut SceneNode,
97 use_texture: bool,
98 #[allow(unused_variables)] use_assimp: bool,
99) -> Result<SceneNode> {
100 let file_string = filename.as_ref();
101
102 #[cfg(feature = "assimp")]
103 if use_assimp {
104 if is_url(file_string) {
105 let file = crate::utils::fetch_tempfile(file_string)?;
106 let path = file.path().to_str().unwrap();
107 return load_mesh_assimp(path, scale, opt_color, group, use_texture);
108 }
109 return load_mesh_assimp(file_string, scale, opt_color, group, use_texture);
110 }
111
112 let ext = Path::new(file_string).extension().and_then(OsStr::to_str);
113 debug!("load {ext:?}: path = {file_string}");
114 load_with_mesh_loader(
115 &fetch_or_read(file_string)?,
116 file_string,
117 scale,
118 opt_color,
119 group,
120 use_texture,
121 )
122}
123
124#[cfg(not(target_family = "wasm"))]
125fn fetch_or_read(filename: &str) -> Result<Vec<u8>> {
126 use std::io::Read;
127
128 const RESPONSE_SIZE_LIMIT: usize = 10 * 1_024 * 1_024;
129
130 if is_url(filename) {
131 let mut buf = Vec::with_capacity(128);
132 ureq::get(filename)
133 .call()
134 .map_err(|e| crate::Error::Other(e.to_string()))?
135 .into_reader()
136 .take((RESPONSE_SIZE_LIMIT + 1) as u64)
137 .read_to_end(&mut buf)?;
138 if buf.len() > RESPONSE_SIZE_LIMIT {
139 return Err(crate::errors::Error::Other(format!(
140 "{filename} is too big"
141 )));
142 }
143 Ok(buf)
144 } else {
145 Ok(std::fs::read(filename)?)
146 }
147}
148
149#[cfg(target_family = "wasm")]
152pub fn load_mesh(
153 data: impl AsRef<str>,
154 scale: na::Vector3<f32>,
155 opt_color: &Option<na::Point3<f32>>,
156 group: &mut SceneNode,
157 _use_texture: bool,
158 _use_assimp: bool,
159) -> Result<SceneNode> {
160 let data = crate::utils::Mesh::decode(data.as_ref())?;
161 let ext = Path::new(&data.path).extension().and_then(OsStr::to_str);
162 debug!("load {ext:?}: path = {}", data.path);
163 let use_texture = false;
164 load_with_mesh_loader(
165 data.bytes().unwrap(),
166 &data.path,
167 scale,
168 opt_color,
169 group,
170 use_texture,
171 )
172}
173
174fn load_with_mesh_loader(
175 bytes: &[u8],
176 file_string: &str,
177 scale: na::Vector3<f32>,
178 opt_color: &Option<na::Point3<f32>>,
179 group: &mut SceneNode,
180 mut use_texture: bool,
181) -> Result<SceneNode> {
182 let mut base = group.add_group();
183 let mut loader = mesh_loader::Loader::default();
184 use_texture &= !is_url(file_string);
185 if use_texture {
186 } else {
193 loader = loader.custom_reader(|_| Err(io::Error::other("texture rendering disabled")));
194 }
195 let scene = loader.load_from_slice(bytes, file_string).map_err(|e| {
196 if e.kind() == io::ErrorKind::Unsupported {
197 crate::errors::Error::from(format!(
198 "{file_string} is not supported, because assimp feature is disabled"
199 ))
200 } else {
201 e.into()
202 }
203 })?;
204
205 for (mesh, material) in scene.meshes.into_iter().zip(scene.materials) {
206 let coords = mesh.vertices.into_iter().map(Into::into).collect();
207 let faces = mesh
208 .faces
209 .into_iter()
210 .map(|f| na::Point3::new(f[0] as u16, f[1] as u16, f[2] as u16))
212 .collect();
213
214 let kiss3d_mesh = Rc::new(RefCell::new(kiss3d::resource::Mesh::new(
215 coords, faces, None, None, false,
216 )));
217 let mut kiss3d_scene = base.add_mesh(kiss3d_mesh, scale);
218 if use_texture {
219 if let Some(color) = material.color.diffuse {
220 kiss3d_scene.set_color(color[0], color[1], color[2]);
221 }
222 if let Some(path) = &material.texture.diffuse {
223 let path_string = path.to_str().unwrap();
224 kiss3d_scene.set_texture_from_file(path, path_string);
228 }
229 if let Some(path) = &material.texture.ambient {
230 let path_string = path.to_str().unwrap();
231 kiss3d_scene.set_texture_from_file(path, path_string);
235 }
236 }
237 }
238 if let Some(color) = *opt_color {
239 base.set_color(color[0], color[1], color[2]);
240 }
241 Ok(base)
242}