Implement sculpt mesh and OBJ support (#43)
Some checks failed
JPEG 2000 feature / linux (push) Has been cancelled
JPEG 2000 feature / macos (push) Has been cancelled
JPEG 2000 feature / windows (push) Has been cancelled
Skia feature / linux (push) Has been cancelled
Skia feature / macos (push) Has been cancelled
Skia feature / windows (push) Has been cancelled
Some checks failed
JPEG 2000 feature / linux (push) Has been cancelled
JPEG 2000 feature / macos (push) Has been cancelled
JPEG 2000 feature / windows (push) Has been cancelled
Skia feature / linux (push) Has been cancelled
Skia feature / macos (push) Has been cancelled
Skia feature / windows (push) Has been cancelled
This commit is contained in:
167
crates/libremetaverse-prim-mesher/src/vertex_indexer.rs
Normal file
167
crates/libremetaverse-prim-mesher/src/vertex_indexer.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
//! Viewer-face indexing compatible with `VertexIndexer.cs`.
|
||||
|
||||
#![allow(clippy::missing_errors_doc)] // Result shapes are fixed by the compatibility map.
|
||||
#![allow(clippy::must_use_candidate)] // Attributes are not part of the mapped C# surface.
|
||||
#![allow(clippy::needless_pass_by_value)] // PrimMesh ownership matches the mapped constructor.
|
||||
|
||||
use crate::{Coord, Error, PrimMesh, UVCoord};
|
||||
use std::collections::HashMap;
|
||||
|
||||
const MAX_PRIM_FACES: usize = 65_536;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct ViewerVertex {
|
||||
pub n: Coord,
|
||||
pub uv: UVCoord,
|
||||
pub v: Coord,
|
||||
}
|
||||
|
||||
impl ViewerVertex {
|
||||
pub fn new(coord: Coord, normal: Coord, uv: UVCoord) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
n: normal,
|
||||
uv,
|
||||
v: coord,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct ViewerPolygon {
|
||||
pub v1: i32,
|
||||
pub v2: i32,
|
||||
pub v3: i32,
|
||||
}
|
||||
|
||||
impl ViewerPolygon {
|
||||
pub fn new(v1: i32, v2: i32, v3: i32) -> Result<Self, Error> {
|
||||
Ok(Self { v1, v2, v3 })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct VertexIndexer {
|
||||
pub num_prim_faces: i32,
|
||||
pub viewer_polygons: Vec<Option<Vec<ViewerPolygon>>>,
|
||||
pub viewer_vertices: Vec<Vec<ViewerVertex>>,
|
||||
}
|
||||
|
||||
impl VertexIndexer {
|
||||
pub fn new_with_constructor() -> Result<Self, Error> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
pub fn new_with_prim_mesh(prim_mesh: PrimMesh) -> Result<Self, Error> {
|
||||
Self::from_viewer_faces(&prim_mesh.viewer_faces)
|
||||
}
|
||||
|
||||
pub(crate) fn from_viewer_faces(faces: &[crate::ViewerFace]) -> Result<Self, Error> {
|
||||
let max_face = faces
|
||||
.iter()
|
||||
.map(|face| face.prim_face_number)
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let num_prim_faces = max_face.checked_add(1).ok_or(Error::Argument)?;
|
||||
let face_count = usize::try_from(num_prim_faces).map_err(|_| Error::Argument)?;
|
||||
if face_count > MAX_PRIM_FACES {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let mut viewer_vertices: Vec<Vec<ViewerVertex>> =
|
||||
(0..face_count).map(|_| Vec::new()).collect();
|
||||
let mut viewer_polygons: Vec<Option<Vec<ViewerPolygon>>> =
|
||||
(0..face_count).map(|_| Some(Vec::new())).collect();
|
||||
let mut indices: Vec<HashMap<i32, i32>> = (0..face_count).map(|_| HashMap::new()).collect();
|
||||
|
||||
for face in faces {
|
||||
let face_index =
|
||||
usize::try_from(face.prim_face_number).map_err(|_| Error::IndexOutOfRange)?;
|
||||
if face_index >= face_count {
|
||||
return Err(Error::IndexOutOfRange);
|
||||
}
|
||||
let vertices = &mut viewer_vertices[face_index];
|
||||
let map = &mut indices[face_index];
|
||||
let v1 = indexed_vertex(map, vertices, face.coord_index1, face.v1, face.n1, face.uv1)?;
|
||||
let v2 = indexed_vertex(map, vertices, face.coord_index2, face.v2, face.n2, face.uv2)?;
|
||||
let v3 = indexed_vertex(map, vertices, face.coord_index3, face.v3, face.n3, face.uv3)?;
|
||||
viewer_polygons[face_index]
|
||||
.as_mut()
|
||||
.ok_or(Error::InvalidOperation)?
|
||||
.push(ViewerPolygon { v1, v2, v3 });
|
||||
}
|
||||
Ok(Self {
|
||||
num_prim_faces,
|
||||
viewer_polygons,
|
||||
viewer_vertices,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn indexed_vertex(
|
||||
indices: &mut HashMap<i32, i32>,
|
||||
vertices: &mut Vec<ViewerVertex>,
|
||||
coord_index: i32,
|
||||
v: Coord,
|
||||
n: Coord,
|
||||
uv: UVCoord,
|
||||
) -> Result<i32, Error> {
|
||||
if coord_index < 0 {
|
||||
return Err(Error::IndexOutOfRange);
|
||||
}
|
||||
if let Some(index) = indices.get(&coord_index) {
|
||||
return Ok(*index);
|
||||
}
|
||||
let index = i32::try_from(vertices.len()).map_err(|_| Error::Argument)?;
|
||||
vertices.push(ViewerVertex { n, uv, v });
|
||||
indices.insert(coord_index, index);
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ViewerFace;
|
||||
|
||||
#[test]
|
||||
fn direct_constructor_deduplicates_coordinate_indices_per_prim_face() {
|
||||
let mut first = ViewerFace::new(0).expect("face");
|
||||
first.coord_index1 = 0;
|
||||
first.coord_index2 = 1;
|
||||
first.coord_index3 = 2;
|
||||
first.v1 = Coord {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
};
|
||||
first.v2 = Coord {
|
||||
x: 1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
};
|
||||
first.v3 = Coord {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.0,
|
||||
};
|
||||
let mut second = first;
|
||||
second.coord_index2 = 2;
|
||||
second.coord_index3 = 3;
|
||||
let indexer = VertexIndexer::from_viewer_faces(&[first, second]).expect("indexer");
|
||||
assert_eq!(indexer.num_prim_faces, 1);
|
||||
assert_eq!(indexer.viewer_vertices[0].len(), 4);
|
||||
assert_eq!(
|
||||
indexer.viewer_polygons[0].as_ref().expect("polygons"),
|
||||
&vec![
|
||||
ViewerPolygon {
|
||||
v1: 0,
|
||||
v2: 1,
|
||||
v3: 2
|
||||
},
|
||||
ViewerPolygon {
|
||||
v1: 0,
|
||||
v2: 2,
|
||||
v3: 3
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user