Skip to content

Commit d1152ed

Browse files
authored
Read surface mesh from PLY file (#31)
1 parent a5e5497 commit d1152ed

File tree

4 files changed

+193
-0
lines changed

4 files changed

+193
-0
lines changed

data/.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*.vtk filter=lfs diff=lfs merge=lfs -text
22
*.xyz filter=lfs diff=lfs merge=lfs -text
3+
*.ply filter=lfs diff=lfs merge=lfs -text

data/cube.ply

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
ply
2+
format ascii 1.0
3+
comment Created by Blender 2.82 (sub 7) - www.blender.org, source file: ''
4+
element vertex 24
5+
property float x
6+
property float y
7+
property float z
8+
property float nx
9+
property float ny
10+
property float nz
11+
property float s
12+
property float t
13+
element face 12
14+
property list uchar uint vertex_indices
15+
end_header
16+
-1.000000 1.000000 1.000000 0.000000 0.000000 1.000000 0.875000 0.500000
17+
1.000000 -1.000000 1.000000 0.000000 0.000000 1.000000 0.625000 0.750000
18+
1.000000 1.000000 1.000000 0.000000 0.000000 1.000000 0.625000 0.500000
19+
1.000000 -1.000000 1.000000 0.000000 -1.000000 0.000000 0.625000 0.750000
20+
-1.000000 -1.000000 -1.000000 0.000000 -1.000000 0.000000 0.375000 1.000000
21+
1.000000 -1.000000 -1.000000 0.000000 -1.000000 0.000000 0.375000 0.750000
22+
-1.000000 -1.000000 1.000000 -1.000000 0.000000 0.000000 0.625000 0.000000
23+
-1.000000 1.000000 -1.000000 -1.000000 0.000000 0.000000 0.375000 0.250000
24+
-1.000000 -1.000000 -1.000000 -1.000000 0.000000 0.000000 0.375000 0.000000
25+
1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 0.375000 0.500000
26+
-1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 0.125000 0.750000
27+
-1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 0.125000 0.500000
28+
1.000000 1.000000 1.000000 1.000000 0.000000 -0.000000 0.625000 0.500000
29+
1.000000 -1.000000 -1.000000 1.000000 0.000000 -0.000000 0.375000 0.750000
30+
1.000000 1.000000 -1.000000 1.000000 0.000000 -0.000000 0.375000 0.500000
31+
-1.000000 1.000000 1.000000 0.000000 1.000000 -0.000000 0.625000 0.250000
32+
1.000000 1.000000 -1.000000 0.000000 1.000000 -0.000000 0.375000 0.500000
33+
-1.000000 1.000000 -1.000000 0.000000 1.000000 -0.000000 0.375000 0.250000
34+
-1.000000 -1.000000 1.000000 0.000000 -0.000000 1.000000 0.875000 0.750000
35+
-1.000000 -1.000000 1.000000 0.000000 -1.000000 0.000000 0.625000 1.000000
36+
-1.000000 1.000000 1.000000 -1.000000 0.000000 0.000000 0.625000 0.250000
37+
1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 0.375000 0.750000
38+
1.000000 -1.000000 1.000000 1.000000 0.000000 0.000000 0.625000 0.750000
39+
1.000000 1.000000 1.000000 0.000000 1.000000 -0.000000 0.625000 0.500000
40+
3 0 1 2
41+
3 3 4 5
42+
3 6 7 8
43+
3 9 10 11
44+
3 12 13 14
45+
3 15 16 17
46+
3 0 18 1
47+
3 3 19 4
48+
3 6 20 7
49+
3 9 21 10
50+
3 12 22 13
51+
3 15 23 16

splashsurf/src/io.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ pub fn read_surface_mesh<R: Real, P: AsRef<Path>>(
143143

144144
match extension.to_lowercase().as_str() {
145145
"vtk" => vtk_format::surface_mesh_from_vtk(&input_file)?,
146+
"ply" => ply_format::surface_mesh_from_ply(&input_file)?,
146147
_ => {
147148
return Err(anyhow!(
148149
"Unsupported file format extension \"{}\" for reading surface meshes",

splashsurf/src/io/ply_format.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ use anyhow::{anyhow, Context};
44
use ply_rs as ply;
55
use ply_rs::ply::Property;
66

7+
use splashsurf_lib::mesh::AttributeData;
8+
use splashsurf_lib::mesh::MeshAttribute;
9+
use splashsurf_lib::mesh::MeshWithData;
10+
use splashsurf_lib::mesh::TriMesh3d;
711
use splashsurf_lib::nalgebra::Vector3;
812
use splashsurf_lib::Real;
913

@@ -49,3 +53,139 @@ pub fn particles_from_ply<R: Real, P: AsRef<Path>>(
4953

5054
Ok(particles)
5155
}
56+
57+
/// Tries to read a surface mesh from the VTK file at the given path
58+
pub fn surface_mesh_from_ply<R: Real, P: AsRef<Path>>(
59+
ply_file: P,
60+
) -> Result<MeshWithData<R, TriMesh3d<R>>, anyhow::Error> {
61+
let mut ply_file = std::fs::File::open(ply_file).unwrap();
62+
let parser = ply::parser::Parser::<ply::ply::DefaultElement>::new();
63+
64+
let ply = parser
65+
.read_ply(&mut ply_file)
66+
.context("Failed to read PLY file")?;
67+
let vertices_normals = ply
68+
.payload
69+
.get("vertex")
70+
.ok_or(anyhow!("PLY file is missing a 'vertex' element"))?;
71+
72+
let vertices_normals: Vec<(Vector3<_>, Vector3<_>)> = vertices_normals
73+
.into_iter()
74+
.map(|e| {
75+
let vertex = (
76+
e.get("x").unwrap(),
77+
e.get("y").unwrap(),
78+
e.get("z").unwrap(),
79+
e.get("nx").unwrap(),
80+
e.get("ny").unwrap(),
81+
e.get("nz").unwrap(),
82+
);
83+
84+
let v = match vertex {
85+
(
86+
Property::Float(x),
87+
Property::Float(y),
88+
Property::Float(z),
89+
Property::Float(nx),
90+
Property::Float(ny),
91+
Property::Float(nz),
92+
) => (
93+
Vector3::new(
94+
R::from_f32(*x).unwrap(),
95+
R::from_f32(*y).unwrap(),
96+
R::from_f32(*z).unwrap(),
97+
),
98+
Vector3::new(
99+
R::from_f32(*nx).unwrap(),
100+
R::from_f32(*ny).unwrap(),
101+
R::from_f32(*nz).unwrap(),
102+
),
103+
),
104+
_ => {
105+
return Err(anyhow!(
106+
"Vertex properties have wrong PLY data type (expected float)"
107+
))
108+
}
109+
};
110+
111+
Ok(v)
112+
})
113+
.map(|vn| vn.unwrap())
114+
.collect();
115+
116+
let vertices: Vec<Vector3<_>> = vertices_normals.iter().map(|vn| vn.0.clone()).collect();
117+
let normals: Vec<Vector3<_>> = vertices_normals.iter().map(|vn| vn.1.clone()).collect();
118+
119+
let faces = ply
120+
.payload
121+
.get("face")
122+
.ok_or(anyhow!("PLY file is missing a 'face' element"))?;
123+
124+
let triangles = faces
125+
.into_iter()
126+
.map(|e| {
127+
// This is as per what blender creates for a
128+
let indices = e.get("vertex_indices");
129+
if let Some(indices) = indices {
130+
if let Property::ListUInt(indices) = indices {
131+
if indices.len() == 3 {
132+
return Ok([
133+
indices[0] as usize,
134+
indices[1] as usize,
135+
indices[2] as usize,
136+
]);
137+
} else {
138+
return Err(anyhow!(
139+
"Invalid number of vertex indices per cell: {}",
140+
indices.len()
141+
));
142+
}
143+
} else {
144+
return Err(anyhow!(
145+
"Index properties have wrong PLY data type (expected uint)"
146+
));
147+
}
148+
} else {
149+
return Err(anyhow!(
150+
"Vertex properties have wrong PLY data type (expected uint)"
151+
));
152+
}
153+
})
154+
.map(|e| e.unwrap())
155+
.collect();
156+
157+
let normals = MeshAttribute::new("normals", AttributeData::Vector3Real(normals));
158+
Ok(MeshWithData::new(TriMesh3d {
159+
vertices,
160+
triangles,
161+
})
162+
.with_point_data(normals))
163+
}
164+
165+
#[cfg(test)]
166+
pub mod test {
167+
use super::*;
168+
169+
#[test]
170+
fn test_convert_cube() -> Result<(), anyhow::Error> {
171+
let input_file = Path::new("../data/cube.ply");
172+
173+
let mesh: MeshWithData<f32, _> = surface_mesh_from_ply(input_file).with_context(|| {
174+
format!(
175+
"Failed to load surface mesh from file \"{}\"",
176+
input_file.display()
177+
)
178+
})?;
179+
180+
assert_eq!(mesh.mesh.vertices.len(), 24);
181+
assert_eq!(mesh.mesh.triangles.len(), 12);
182+
let normals = mesh.point_attributes.iter().find(|a| a.name == "normals");
183+
if let Some(MeshAttribute { data, .. }) = normals {
184+
if let AttributeData::Vector3Real(normals) = data {
185+
assert_eq!(normals.len(), 24)
186+
}
187+
}
188+
189+
Ok(())
190+
}
191+
}

0 commit comments

Comments
 (0)