mirror of
https://github.com/IllusionMan1212/3dobs.git
synced 2026-10-24 10:12:35 +00:00
importer/stl: finish ascii stl loader
This commit is contained in:
@@ -28,5 +28,7 @@
|
|||||||
- [x] Use filename for the root node
|
- [x] Use filename for the root node
|
||||||
- [x] Ship shaders directly in the executable
|
- [x] Ship shaders directly in the executable
|
||||||
- [ ] Have the program accept model files as CLI args
|
- [ ] Have the program accept model files as CLI args
|
||||||
|
- [ ] Have it so that when an instance of 3dobs is open, any other file associated with 3dobs is opened in the same instance
|
||||||
|
- [ ] Show how many memory each loaded model is using
|
||||||
|
|
||||||
- [ ] Bring back the regular camera movement (can maybe be a switch between them) (for later)
|
- [ ] Bring back the regular camera movement (can maybe be a switch between them) (for later)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ pub enum TextureType {
|
|||||||
Emissive
|
Emissive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: take the textures out of the material and put it on the model/object
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Material {
|
pub struct Material {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|||||||
+2
-22
@@ -232,29 +232,9 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
|||||||
let y = iter.next().unwrap();
|
let y = iter.next().unwrap();
|
||||||
let z = iter.next().unwrap();
|
let z = iter.next().unwrap();
|
||||||
temp_vertices.push(glm::vec3(x, y, z));
|
temp_vertices.push(glm::vec3(x, y, z));
|
||||||
if x < min_aabb.x {
|
|
||||||
min_aabb.x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if y < min_aabb.y {
|
min_aabb = glm::vec3(min_aabb.x.min(x), min_aabb.y.min(y), min_aabb.z.min(z));
|
||||||
min_aabb.y = y;
|
max_aabb = glm::vec3(max_aabb.x.max(x), max_aabb.y.max(y), max_aabb.z.max(z));
|
||||||
}
|
|
||||||
|
|
||||||
if z < min_aabb.z {
|
|
||||||
min_aabb.z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
if x > max_aabb.x {
|
|
||||||
max_aabb.x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if y > max_aabb.y {
|
|
||||||
max_aabb.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if z > max_aabb.z {
|
|
||||||
max_aabb.z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Some(ObjToken::Normal) => {
|
Some(ObjToken::Normal) => {
|
||||||
|
|||||||
+115
-36
@@ -1,18 +1,113 @@
|
|||||||
use std::io::{Read, Seek, BufReader};
|
use std::io::{Read, Seek, BufReader, BufRead};
|
||||||
|
|
||||||
use crate::{importer::ObjMesh, importer::Object, importer::Material, aabb::AABB, mesh::Vertex};
|
use crate::{importer::ObjMesh, importer::Object, importer::Material, aabb::AABB, mesh::Vertex};
|
||||||
|
|
||||||
const STL_HEADER_SIZE: u64 = 80;
|
const STL_HEADER_SIZE: u64 = 80;
|
||||||
// might not be enough since it's not guaranteed. we'll see
|
const STL_TRIANGLE_SIZE: usize = 50;
|
||||||
const ASCII_STL_MAGIC: [u8; 11] = [0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x61, 0x73, 0x63, 0x69, 0x69]; // "solid ascii"
|
|
||||||
|
|
||||||
#[repr(packed(2))]
|
#[repr(packed(2))]
|
||||||
|
#[derive(Debug)]
|
||||||
struct STLTriangle {
|
struct STLTriangle {
|
||||||
normal: glm::Vec3,
|
normal: glm::Vec3,
|
||||||
verts: [glm::Vec3; 3],
|
verts: [glm::Vec3; 3],
|
||||||
attribute_byte_count: u16
|
attribute_byte_count: u16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FacetIterator<R: Read> {
|
||||||
|
reader: BufReader<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> FacetIterator<R> {
|
||||||
|
fn new(reader: R) -> Self {
|
||||||
|
FacetIterator {
|
||||||
|
reader: BufReader::new(reader),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> Iterator for FacetIterator<R> {
|
||||||
|
type Item = STLTriangle;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<STLTriangle> {
|
||||||
|
let mut line = String::with_capacity(0x50);
|
||||||
|
let mut normal = glm::vec3(0.0, 0.0, 0.0);
|
||||||
|
let mut vertices = Vec::with_capacity(3);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.reader.read_line(&mut line) {
|
||||||
|
Ok(0) => return None, // Reached EOF
|
||||||
|
Ok(_) => {
|
||||||
|
if line.contains("normal") {
|
||||||
|
let mut norm_iter = line.split_whitespace().skip(2).map(|s| s.parse::<f32>().unwrap());
|
||||||
|
normal.x = norm_iter.next().unwrap();
|
||||||
|
normal.y = norm_iter.next().unwrap();
|
||||||
|
normal.z = norm_iter.next().unwrap();
|
||||||
|
} else if line.contains("vertex") {
|
||||||
|
let mut vertex_iter = line.split_whitespace().skip(1).map(|s| s.parse::<f32>().unwrap());
|
||||||
|
vertices.push(glm::vec3(vertex_iter.next().unwrap(), vertex_iter.next().unwrap(), vertex_iter.next().unwrap()));
|
||||||
|
} else if line.contains("endfacet") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: might need to panic and log here in the future
|
||||||
|
Err(_) => return None, // Error reading
|
||||||
|
}
|
||||||
|
line.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut verts_iter = vertices.iter();
|
||||||
|
|
||||||
|
Some(STLTriangle{
|
||||||
|
normal,
|
||||||
|
verts: [*verts_iter.next().unwrap(), *verts_iter.next().unwrap(), *verts_iter.next().unwrap()],
|
||||||
|
attribute_byte_count: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ascii_stl(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> {
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
let mut vertices = Vec::new();
|
||||||
|
let mut indices = Vec::new();
|
||||||
|
let mut min_aabb = glm::vec3(f32::MAX, f32::MAX, f32::MAX);
|
||||||
|
let mut max_aabb = glm::vec3(f32::MIN, f32::MIN, f32::MIN);
|
||||||
|
|
||||||
|
reader.read_line(&mut String::new())?; // Skip the first line (solid name)
|
||||||
|
let facet_iter = FacetIterator::new(reader);
|
||||||
|
|
||||||
|
let tex_coords = glm::vec2(0.0, 0.0);
|
||||||
|
|
||||||
|
for (i, triangle) in facet_iter.enumerate() {
|
||||||
|
for vert in triangle.verts {
|
||||||
|
vertices.push(Vertex{
|
||||||
|
position: glm::vec3(vert.x, vert.y, vert.z),
|
||||||
|
normal: glm::vec3(triangle.normal.x, triangle.normal.y, triangle.normal.z),
|
||||||
|
tex_coords,
|
||||||
|
});
|
||||||
|
|
||||||
|
min_aabb = glm::min(min_aabb, vert);
|
||||||
|
max_aabb = glm::max(max_aabb, vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
indices.push(i as u32 * 3);
|
||||||
|
indices.push(i as u32 * 3 + 1);
|
||||||
|
indices.push(i as u32 * 3 + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let aabb = AABB::new(min_aabb, max_aabb);
|
||||||
|
|
||||||
|
Ok(Object{
|
||||||
|
name: "default_object".to_string(),
|
||||||
|
meshes: vec![ObjMesh{
|
||||||
|
name: "default_mesh".to_string(),
|
||||||
|
vertices,
|
||||||
|
indices,
|
||||||
|
material: Some(Material::default())
|
||||||
|
}],
|
||||||
|
aabb,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct TrianglesIter<R: Read> {
|
struct TrianglesIter<R: Read> {
|
||||||
reader: R,
|
reader: R,
|
||||||
@@ -25,7 +120,7 @@ impl<R: Read> TrianglesIter<R> {
|
|||||||
fn new(reader: R, triangles_to_read: usize) -> Self {
|
fn new(reader: R, triangles_to_read: usize) -> Self {
|
||||||
TrianglesIter {
|
TrianglesIter {
|
||||||
reader,
|
reader,
|
||||||
buf: vec![0u8; 50],
|
buf: vec![0u8; STL_TRIANGLE_SIZE],
|
||||||
triangles_to_read,
|
triangles_to_read,
|
||||||
triangles_read: 0,
|
triangles_read: 0,
|
||||||
}
|
}
|
||||||
@@ -62,10 +157,6 @@ impl<R: Read> Iterator for TrianglesIter<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ascii_stl(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> {
|
|
||||||
// TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_binary_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> {
|
fn parse_binary_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> {
|
||||||
// skip header for now
|
// skip header for now
|
||||||
file.seek(std::io::SeekFrom::Start(STL_HEADER_SIZE))?;
|
file.seek(std::io::SeekFrom::Start(STL_HEADER_SIZE))?;
|
||||||
@@ -92,29 +183,8 @@ fn parse_binary_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::erro
|
|||||||
tex_coords,
|
tex_coords,
|
||||||
});
|
});
|
||||||
|
|
||||||
if vert.x < min_aabb.x {
|
min_aabb = glm::min(min_aabb, vert);
|
||||||
min_aabb.x = vert.x;
|
max_aabb = glm::max(max_aabb, vert);
|
||||||
}
|
|
||||||
|
|
||||||
if vert.y < min_aabb.y {
|
|
||||||
min_aabb.y = vert.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if vert.z < min_aabb.z {
|
|
||||||
min_aabb.z = vert.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
if vert.x > max_aabb.x {
|
|
||||||
max_aabb.x = vert.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if vert.y > max_aabb.y {
|
|
||||||
max_aabb.y = vert.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if vert.z > max_aabb.z {
|
|
||||||
max_aabb.z = vert.z;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
indices.push(i as u32*3);
|
indices.push(i as u32*3);
|
||||||
@@ -136,21 +206,30 @@ fn parse_binary_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::erro
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_ascii(buf: &[u8]) -> bool {
|
||||||
|
for b in buf {
|
||||||
|
if *b > 127 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> {
|
pub fn load_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> {
|
||||||
let mut buf: [u8; 11] = [0; 11];
|
let mut buf: [u8; 512] = [0; 512];
|
||||||
let _ = file.read_exact(&mut buf);
|
let _ = file.read_exact(&mut buf);
|
||||||
_ = file.seek(std::io::SeekFrom::Start(0));
|
_ = file.seek(std::io::SeekFrom::Start(0));
|
||||||
let is_ascii = buf == ASCII_STL_MAGIC;
|
let is_ascii = is_ascii(&buf);
|
||||||
|
|
||||||
|
let now = std::time::Instant::now();
|
||||||
let obj = if is_ascii {
|
let obj = if is_ascii {
|
||||||
parse_ascii_stl(file)?
|
parse_ascii_stl(file)?
|
||||||
} else {
|
} else {
|
||||||
parse_binary_stl(file)?
|
parse_binary_stl(file)?
|
||||||
};
|
};
|
||||||
let now = std::time::Instant::now();
|
|
||||||
let obj = parse_binary_stl(file)?;
|
|
||||||
let elapsed = now.elapsed();
|
let elapsed = now.elapsed();
|
||||||
println!("STL file loaded in {} ms", elapsed.as_millis());
|
println!("STL file loaded in {} µs", elapsed.as_micros());
|
||||||
|
|
||||||
Ok(obj)
|
Ok(obj)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user