Skip to content

Commit 6a6c381

Browse files
committed
add ply support
1 parent 9ee45cd commit 6a6c381

9 files changed

Lines changed: 542 additions & 33 deletions

File tree

Cargo.lock

Lines changed: 322 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ repository = "https://github.com/ventengine/Modelz"
1414
actively-developed = {}
1515

1616
[features]
17-
default = ["obj", "gltf", "stl"]
17+
default = ["obj", "gltf", "stl", "ply"]
1818
obj = ["dep:tobj"]
1919
gltf = ["dep:gltf"]
2020
stl = ["dep:stl_io"]
21+
ply = ["dep:ply-rs"]
22+
2123

2224
[dependencies]
2325
log = "0.4"
@@ -28,4 +30,6 @@ gltf = {version = "1.4.0", optional = true }
2830
# OBJ Wavefront
2931
tobj = { version = "4.0.0", features = ["async"], optional = true }
3032
# STL
31-
stl_io = { version = "0.7.0", optional = true }
33+
stl_io = { version = "0.7.0", optional = true }
34+
# PLY
35+
ply-rs = { version = "0.1.3", optional = true }

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ let model = Model3D::from_format("model", ModelFormat::OBJ).except("Failed to lo
2121
- [x] Wavefront OBJ
2222
- [x] glTF 2.0
2323
- [x] STL
24+
- [x] PLY
2425

2526
### Contributing
2627

src/gltf.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use gltf::Mesh;
99
use crate::{Indices, Model3D, ModelError, Vertex};
1010

1111
pub fn load(path: &Path) -> Result<Model3D, ModelError> {
12-
let gltf = gltf::Gltf::from_reader(fs::File::open(path).unwrap()).unwrap();
12+
let gltf = gltf::Gltf::from_reader(
13+
fs::File::open(path).map_err(|e| ModelError::OpenFile(e.to_string()))?,
14+
)
15+
.map_err(|e| ModelError::ModelParsing(e.to_string()))?;
1316

1417
let path = path.parent().unwrap_or_else(|| Path::new("./"));
1518

src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ mod gltf;
55
#[cfg(feature = "obj")]
66
mod obj;
77
#[cfg(feature = "stl")]
8+
mod ply;
9+
#[cfg(feature = "ply")]
810
mod stl;
911

1012
pub struct Model3D {
@@ -62,6 +64,8 @@ impl Model3D {
6264
ModelFormat::GLTF => return gltf::load(path.as_ref()),
6365
#[cfg(feature = "stl")]
6466
ModelFormat::STL => return stl::load(path.as_ref()),
67+
#[cfg(feature = "ply")]
68+
ModelFormat::PLY => return ply::load(path.as_ref()),
6569
}
6670
}
6771
}
@@ -75,9 +79,12 @@ pub enum ModelFormat {
7579
#[cfg(feature = "gltf")]
7680
// gltf 2.0, .gltf | .glb
7781
GLTF,
78-
// STL .stl
7982
#[cfg(feature = "stl")]
83+
// STL .stl
8084
STL,
85+
#[cfg(feature = "ply")]
86+
// Polygon File Format .ply
87+
PLY,
8188
}
8289

8390
#[derive(Debug)]
@@ -86,6 +93,8 @@ pub enum ModelError {
8693
UnknowFormat,
8794
// Given file does not exist
8895
FileNotExists,
96+
// Failed to open file
97+
OpenFile(String),
8998
// Error while loading general 3D File
9099
ModelParsing(String),
91100
// Error loading Material

src/ply.rs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
use std::{fs::File, path::Path};
2+
3+
extern crate ply_rs;
4+
use ply_rs::{
5+
self as ply,
6+
ply::{Property, PropertyAccess},
7+
};
8+
9+
use crate::{Model3D, ModelError};
10+
11+
#[derive(Debug, Default, Clone, Copy)]
12+
struct Vertex {
13+
x: f32,
14+
y: f32,
15+
z: f32,
16+
x_norm: Option<f32>,
17+
y_norm: Option<f32>,
18+
z_norm: Option<f32>,
19+
tex_x: Option<f32>,
20+
tex_y: Option<f32>,
21+
}
22+
23+
impl PropertyAccess for Vertex {
24+
fn new() -> Self {
25+
Vertex {
26+
..Default::default()
27+
}
28+
}
29+
fn set_property(&mut self, key: String, property: Property) {
30+
match (key.as_ref(), property) {
31+
("x", Property::Float(v)) => self.x = v,
32+
("y", Property::Float(v)) => self.y = v,
33+
("z", Property::Float(v)) => self.z = v,
34+
35+
("nx", Property::Float(v)) => self.x_norm = Some(v),
36+
("ny", Property::Float(v)) => self.y_norm = Some(v),
37+
("nz", Property::Float(v)) => self.z_norm = Some(v),
38+
// NOTE: Blender3D exports texture coordinates as s,t tuples
39+
("u" | "s" | "tx" | "texture_u", Property::Float(v)) => self.tex_x = Some(v),
40+
("v" | "t" | "ty" | "texture_v", Property::Float(v)) => self.tex_y = Some(v),
41+
(k, _) => eprintln!("Vertex: Unexpected key/value combination: key: {}", k),
42+
}
43+
}
44+
}
45+
46+
#[derive(Debug)]
47+
struct Face {
48+
vertex_index: Vec<u32>,
49+
}
50+
51+
impl PropertyAccess for Face {
52+
fn new() -> Self {
53+
Face {
54+
vertex_index: Vec::new(),
55+
}
56+
}
57+
fn set_property(&mut self, key: String, property: Property) {
58+
match (key.as_ref(), property) {
59+
("vertex_index" | "vertex_indices", Property::ListUInt(vec)) => self.vertex_index = vec,
60+
(k, _) => eprintln!("Face: Unexpected key/value combination: key: {}", k),
61+
}
62+
}
63+
}
64+
65+
pub fn load(path: &Path) -> Result<Model3D, ModelError> {
66+
let mut file = File::open(path).map_err(|e| ModelError::OpenFile(e.to_string()))?;
67+
let mut reader = std::io::BufReader::new(&mut file);
68+
69+
// Create a parser for each struct. Parsers are cheap objects.
70+
let vertex_parser = ply::parser::Parser::<Vertex>::new();
71+
let face_parser = ply::parser::Parser::<Face>::new();
72+
73+
// lets first consume the header
74+
// We also could use `face_parser`, The configuration is a parser's only state.
75+
// The reading position only depends on `f`.
76+
let header = vertex_parser.read_header(&mut reader).unwrap();
77+
78+
// Depending on the header, read the data into our structs..
79+
let mut vertex_list = Vec::new();
80+
let mut face_list = Vec::new();
81+
for (_ignore_key, element) in &header.elements {
82+
// we could also just parse them in sequence, but the file format might change
83+
match element.name.as_ref() {
84+
"vertex" => {
85+
vertex_list = vertex_parser
86+
.read_payload_for_element(&mut reader, element, &header)
87+
.unwrap();
88+
}
89+
"face" => {
90+
face_list = face_parser
91+
.read_payload_for_element(&mut reader, element, &header)
92+
.unwrap();
93+
}
94+
_ => panic!("Enexpeced element!"),
95+
}
96+
}
97+
let mut vertices = Vec::new();
98+
for face in face_list {
99+
// Every face (triangle) has 3 Vertices
100+
let vertex = vertex_list[face.vertex_index[0] as usize];
101+
let vertex1 = vertex_list[face.vertex_index[1] as usize];
102+
let vertex2 = vertex_list[face.vertex_index[2] as usize];
103+
104+
let mut normal = None;
105+
let mut tex_coord = None;
106+
if let Some(x) = vertex.x_norm {
107+
if let Some(y) = vertex.z_norm {
108+
if let Some(z) = vertex.z_norm {
109+
normal = Some([x, y, z])
110+
}
111+
}
112+
}
113+
if let Some(x) = vertex.tex_x {
114+
if let Some(y) = vertex.tex_y {
115+
tex_coord = Some([x, y])
116+
}
117+
}
118+
119+
let v1 = crate::Vertex {
120+
position: [vertex.x, vertex.y, vertex.z],
121+
tex_coord,
122+
color: None,
123+
normal,
124+
};
125+
if let Some(x) = vertex1.x_norm {
126+
if let Some(y) = vertex1.z_norm {
127+
if let Some(z) = vertex1.z_norm {
128+
normal = Some([x, y, z])
129+
}
130+
}
131+
}
132+
if let Some(x) = vertex1.tex_x {
133+
if let Some(y) = vertex1.tex_y {
134+
tex_coord = Some([x, y])
135+
}
136+
}
137+
let v2 = crate::Vertex {
138+
position: [vertex1.x, vertex1.y, vertex1.z],
139+
tex_coord,
140+
color: None,
141+
normal,
142+
};
143+
if let Some(x) = vertex2.x_norm {
144+
if let Some(y) = vertex2.z_norm {
145+
if let Some(z) = vertex2.z_norm {
146+
normal = Some([x, y, z])
147+
}
148+
}
149+
}
150+
if let Some(x) = vertex2.tex_x {
151+
if let Some(y) = vertex2.tex_y {
152+
tex_coord = Some([x, y])
153+
}
154+
}
155+
let v3 = crate::Vertex {
156+
position: [vertex2.x, vertex2.y, vertex2.z],
157+
tex_coord,
158+
color: None,
159+
normal,
160+
};
161+
162+
vertices.push(v1);
163+
vertices.push(v2);
164+
vertices.push(v3);
165+
}
166+
let mesh = crate::Mesh {
167+
vertices,
168+
indices: None,
169+
material_index: None,
170+
mode: crate::RenderMode::TriangleFan,
171+
name: None,
172+
};
173+
174+
Ok(Model3D {
175+
meshes: vec![mesh],
176+
materials: vec![],
177+
format: crate::ModelFormat::PLY,
178+
})
179+
}

src/stl.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use std::{fs::File, path::Path};
33
use crate::{Model3D, ModelError, Vertex};
44

55
pub fn load(path: &Path) -> Result<Model3D, ModelError> {
6-
let stl = stl_io::read_stl(&mut File::open(path).unwrap()).unwrap();
6+
let stl =
7+
stl_io::read_stl(&mut File::open(path).map_err(|e| ModelError::OpenFile(e.to_string()))?)
8+
.map_err(|e| ModelError::ModelParsing(e.to_string()))?;
79

810
let mut vertices = Vec::new();
911
for face in stl.faces {

tests/cube.ply

628 Bytes
Binary file not shown.

tests/ply.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#[cfg(test)]
2+
mod ply {
3+
use modelz::Model3D;
4+
5+
#[test]
6+
fn load_ply() {
7+
let model_path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/cube.ply");
8+
9+
let model = Model3D::from_format(model_path, modelz::ModelFormat::PLY)
10+
.expect("Failed to load ply model");
11+
for mesh in model.meshes {
12+
for vert in mesh.vertices {
13+
println!("{:?}", vert)
14+
}
15+
}
16+
}
17+
}

0 commit comments

Comments
 (0)