1use std::path::Path;
2use std::collections::HashSet;
3use chrono::Utc;
4use tracing::info;
5use blake3::Hasher;
6use nalgebra as na;
7use anyhow::{Result, ensure, bail};
8use serde::{Deserialize, Serialize};
9use ndarray::{prelude::*, Slice, concatenate};
10use tobj::{load_obj, GPU_LOAD_OPTIONS};
11use super::message::*;
12
13#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
14struct TriMesh {
15 transform_v: Array2<f32>,
17 transform_vt: Array2<f32>,
19 trimesh_data: Hash,
21}
22
23fn area_cross(uv_a: na::Vector2<f32>, uv_b: na::Vector2<f32>, uv_o: na::Vector2<f32>) -> f32 {
24 (uv_a[0] - uv_o[0]) * (uv_b[1] - uv_o[1]) - (uv_a[1] - uv_o[1]) * (uv_b[0] - uv_o[0])
25}
26
27fn dist_dot(uv_a: na::Vector2<f32>, uv_o: na::Vector2<f32>) -> f32 {
28 f32::sqrt((uv_a - uv_o).dot(&(uv_a - uv_o)))
29}
30
31pub fn uv_xyz(vertex: ArrayView3<f32>, texture_resolution: usize, padding: usize) -> Result<Array3<f32>> {
35 let size_geometry = vertex.shape()[2];
36 assert_eq!(vertex.shape(), &[8, 3, size_geometry]);
37 let mut xyz = Array3::zeros((4, texture_resolution, texture_resolution));
38 xyz.index_axis_mut(Axis(0), 3).fill(padding as f32);
39 for vertex in vertex.axis_iter(Axis(2)) {
40 let v_curr = vertex.slice_axis(Axis(0), Slice::from(0..3)).flatten().to_vec();
42 let v_curr = na::Matrix3::<f32>::from_vec(v_curr);
43 let vt_curr = vertex.slice_axis(Axis(0), Slice::from(6..8)).flatten().to_vec();
44 let vt_curr = texture_resolution as f32 * na::Matrix3x2::<f32>::from_vec(vt_curr);
45 let area = area_cross(vt_curr.row(1).transpose(), vt_curr.row(2).transpose(), vt_curr.row(0).transpose());
47 if f32::abs(area) < 1e-12 {
48 continue;
49 }
50 let u_min = f32::max(0., vt_curr.column(0).min() - padding as f32) as usize;
51 let u_max = f32::min(texture_resolution as f32, vt_curr.column(0).max() + padding as f32 + 1.) as usize;
52 let v_min = f32::max(0., vt_curr.column(1).min() - padding as f32) as usize;
53 let v_max = f32::min(texture_resolution as f32, vt_curr.column(1).max() + padding as f32 + 1.) as usize;
54 for u in u_min..u_max {
55 for v in v_min..v_max {
56 let xyz_index = (texture_resolution - 1 - v, u);
57 let center = na::Vector2::new(u as f32 + 0.5, v as f32 + 0.5);
58 let area_0 = area_cross(center, vt_curr.row(2).transpose(), vt_curr.row(1).transpose());
59 let dist_0 = area_0 / dist_dot(vt_curr.row(2).transpose(), vt_curr.row(1).transpose());
60 let area_1 = area_cross(center, vt_curr.row(0).transpose(), vt_curr.row(2).transpose());
61 let dist_1 = area_1 / dist_dot(vt_curr.row(0).transpose(), vt_curr.row(2).transpose());
62 let area_2 = area_cross(center, vt_curr.row(1).transpose(), vt_curr.row(0).transpose());
63 let dist_2 = area_2 / dist_dot(vt_curr.row(1).transpose(), vt_curr.row(0).transpose());
64 let w = f32::max(1e-6, f32::min(1. - 1e-6, -area_2 / area));
65 let v = f32::max(1e-6, f32::min(1. - 1e-6 - w, -area_1 / area));
66 let u = 1. - 1e-6 - w - v;
67 let dist_max = f32::max(dist_0, f32::max(dist_1, dist_2));
68 let xyz_curr = v_curr.row(0) * u + v_curr.row(1) * v + v_curr.row(2) * w;
69 if xyz[(3, xyz_index.0, xyz_index.1)] > dist_max {
70 xyz[(0, xyz_index.0, xyz_index.1)] = xyz_curr[0];
71 xyz[(1, xyz_index.0, xyz_index.1)] = xyz_curr[1];
72 xyz[(2, xyz_index.0, xyz_index.1)] = xyz_curr[2];
73 xyz[(3, xyz_index.0, xyz_index.1)] = dist_max;
74 }
75 }
76 }
77 }
80 Ok(xyz)
88}
89
90#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
94pub struct Geometry {
95 trimesh: Vec<TriMesh>,
96}
97
98impl Default for Geometry {
99 fn default() -> Self {
100 Geometry::new()
101 }
102}
103
104impl Geometry {
105 pub fn new() -> Self {
107 Geometry {
108 trimesh: Vec::new(),
109 }
110 }
111
112 pub fn size(&self, data_cache: &DataCache) -> Result<usize> {
114 let mut size = 0;
115 let data_cache = data_cache.lock().expect("No task should panic");
116 for trimesh_data_hash in self.required_data() {
117 data_cache.get(&trimesh_data_hash).map_or_else(|| {
118 bail!("Geometry::size: Hash {trimesh_data_hash} not found");
119 }, |entry| {
120 let data = &entry.1;
121 if let Data::TriMeshData(vertex) = data {
122 size += vertex.shape()[2];
123 } else {
124 bail!("Geometry::size: Wrong data {trimesh_data_hash} {data}");
125 }
126 Ok(())
127 })?;
128 }
129 Ok(size)
130 }
131
132 pub fn hash(&self) -> Hash {
136 let mut hasher = Hasher::new();
137 let msg = postcard::to_stdvec(self).expect("Internal Data Struct");
138 hasher.update(&msg);
139 Hash(*hasher.finalize().as_bytes())
140 }
141
142 pub fn required_data(&self) -> HashSet<Hash> {
146 HashSet::from_iter(self.trimesh.iter().map(|trimesh| trimesh.trimesh_data.to_owned()))
147 }
148
149 pub fn add_vertex_hash (
159 &mut self,
160 vertex_hash: Hash,
161 transform_v: Array2<f32>,
162 transform_vt: Array2<f32>,
163 data_cache: &DataCache) -> Result<Hash> {
164 {
165 let data_cache = data_cache.lock().expect("No task should panic");
166 ensure!(data_cache.contains_key(&vertex_hash),
167 "Geometry::add_vertex_hash: vertex hash {vertex_hash} doesn't exist in data_cache");
168 }
169 let trimesh = TriMesh {
170 transform_v,
171 transform_vt,
172 trimesh_data: vertex_hash.to_owned(),
173 };
174 self.trimesh.push(trimesh);
175 Ok(vertex_hash)
176 }
177
178 pub fn add_trimesh(
188 &mut self,
189 vertex: Array3<f32>,
190 transform_v: Array2<f32>,
191 transform_vt: Array2<f32>,
192 data_cache: &DataCache) -> Result<Hash> {
193 let nf = vertex.shape()[2];
194 ensure!(vertex.shape() == [8, 3, nf], "Geometry::add_trimesh: vertex.shape {:?}, expected {:?}",
195 vertex.shape(), [8, 3, nf]);
196 ensure!(transform_v.shape() == [4, 4], "Geometry::add_trimesh: transform_v.shape {:?}, expected {:?}",
197 transform_v.shape(), [4, 4]);
198 ensure!(transform_vt.shape() == [3, 3], "Geometry::add_trimesh: transform_vt.shape {:?}, expected {:?}",
199 transform_vt.shape(), [3, 3]);
200 let trimesh_data = Data::TriMeshData(vertex);
201 let trimesh_data_hash = trimesh_data.hash();
202 {
203 let mut data_cache = data_cache.lock().expect("No task should panic");
204 data_cache.insert(trimesh_data_hash.to_owned(), (Utc::now().timestamp(), trimesh_data));
205 }
206 self.add_vertex_hash(trimesh_data_hash, transform_v, transform_vt, data_cache)
207 }
208
209 pub fn add_obj(
219 &mut self,
220 filename: &Path,
221 transform_v: Array2<f32>,
222 transform_vt: Array2<f32>,
223 data_cache: &DataCache) -> Result<Hash> {
224 info!("Geometry::add_obj: loading {}", filename.display());
225 let obj = load_obj(filename, &GPU_LOAD_OPTIONS)?.0;
226 let mut vertex: Array3<f32> = Array3::default((8, 3, 0));
227 for model in obj {
228 let mesh = model.mesh;
229 ensure!(!mesh.indices.is_empty(), "Geometry::add_obj: obj file {} is empty", filename.display());
230 ensure!(!mesh.positions.is_empty(), "Geometry::add_obj: obj file {} is empty", filename.display());
231 ensure!(!mesh.normals.is_empty(), "Geometry::add_obj: obj file {} has no vertex normal",
232 filename.display());
233 ensure!(!mesh.texcoords.is_empty(), "Geometry::add_obj: obj file {} has no texture coordinate",
234 filename.display());
235 let nf = mesh.indices.len() / 3;
237 let nv = mesh.positions.len() / 3;
239 let mesh_indices = Array::from_shape_vec((nf, 3), mesh.indices)?;
240 let mesh_positions = Array::from_shape_vec((nv, 3), mesh.positions)?;
241 let mesh_normals = Array::from_shape_vec((nv, 3), mesh.normals)?;
242 let mesh_texcoords = Array::from_shape_vec((nv, 2), mesh.texcoords)?;
243 for indices in mesh_indices.axis_iter(Axis(0)) {
244 let mut curr_vertex: Array2<f32> = Array2::default((8, 0));
245 for j in 0..3 {
246 curr_vertex.push(Axis(1), concatenate![Axis(0),
248 mesh_positions.row(indices[j] as usize),
249 mesh_normals.row(indices[j] as usize),
250 mesh_texcoords.row(indices[j] as usize)].view())?;
251 }
252 vertex.push(Axis(2), curr_vertex.view())?;
253 }
254 }
255 self.add_trimesh(vertex, transform_v, transform_vt, data_cache)
256 }
257
258 pub fn combine(&self, data_cache: &DataCache) -> Result<Array3<f32>> {
260 let curr_timestamp = Utc::now().timestamp();
261 let mut vertex: Array3<f32> = Array3::default((8, 3, 0));
262 for trimesh in &self.trimesh {
263 let mut data_cache = data_cache.lock().expect("No task should panic");
264 let trimesh_data = data_cache.get_mut(&trimesh.trimesh_data).map_or_else(|| {
265 bail!("Geometry::combine: Hash {} not found", trimesh.trimesh_data)
266 }, |trimesh_data| {
267 trimesh_data.0 = curr_timestamp;
268 if let Data::TriMeshData(trimesh_data) = &trimesh_data.1 {
269 Ok(trimesh_data)
270 } else {
271 bail!("Geometry::combine: Wrong data for `trimesh`: {}", &trimesh_data.1);
272 }
273 })?;
274 let nf = trimesh_data.shape()[2];
275 ensure!(trimesh_data.shape() == [8, 3, nf],
276 "Geometry::combine: Wrong data shape {:?}, expected {:?}",
277 trimesh_data.shape(), [8, 3, nf]);
278 let mut v = trimesh_data.slice(s![..3, .., ..]).to_shape([3, 3 * nf])?.into_owned();
279 v.push(Axis(0), Array1::ones(3 * nf).view())?;
280 let v = trimesh.transform_v.dot(&v).slice(s![..3, ..]).to_shape([3, 3, nf])?.into_owned();
281 let vn = trimesh_data.slice(s![3..6, .., ..]).to_shape([3, 3 * nf])?.into_owned();
282 let vn = trimesh.transform_v.slice(s![..3, ..3]).dot(&vn).to_shape([3, 3, nf])?.into_owned();
283 let mut vt = trimesh_data.slice(s![6..8, .., ..]).to_shape([2, 3 * nf])?.into_owned();
284 vt.push(Axis(0), Array1::ones(3 * nf).view())?;
285 let vt = trimesh.transform_vt.dot(&vt).slice(s![..2, ..]).to_shape([2, 3, nf])?.into_owned();
286 vertex.append(Axis(2), concatenate![Axis(0), v, vn, vt].view())?;
287 }
288 Ok(vertex)
289 }
290
291 pub fn uv_xyz_cached(&self, texture_resolution: usize, padding: usize, data_cache: &DataCache) -> Result<Array3<f32>> {
293 let mut hasher = Hasher::new();
294 let msg = postcard::to_stdvec(&(&self, texture_resolution, padding)).expect("Internal Data Struct");
295 hasher.update(&msg);
296 let hash = Hash(*hasher.finalize().as_bytes());
297 {
298 let mut data_cache = data_cache.lock().expect("No task should panic");
299 if let Some(data) = data_cache.get_mut(&hash) {
300 data.0 = Utc::now().timestamp();
301 if let Data::TriMeshUVXYZ(uv_xyz) = &data.1 {
302 return Ok(uv_xyz.to_owned());
303 } else {
304 bail!("Geometry::uv_xyz_cached: Wrong data for `uv_xyz`: {}", &data.1);
305 }
306 }
307 }
308 let vertex = self.combine(data_cache)?.as_standard_layout().into_owned();
309 let uv_xyz = uv_xyz(vertex.view(), texture_resolution, padding)?;
310 {
311 let mut data_cache = data_cache.lock().expect("No task should panic");
312 data_cache.insert(hash, (Utc::now().timestamp(), Data::TriMeshUVXYZ(uv_xyz.to_owned())));
313 }
314 Ok(uv_xyz)
315 }
316}