Some more abstraction + basic OBJ file
Getty Ritter
7 years ago
| 1 | *~ |
| 1 | pub mod obj; |
| 1 | #[derive(Debug,PartialEq)] | |
| 2 | pub struct OBJError(String); | |
| 3 | ||
| 4 | #[derive(Debug,PartialEq)] | |
| 5 | pub struct Vertex { | |
| 6 | pub x: f32, | |
| 7 | pub y: f32, | |
| 8 | pub z: f32, | |
| 9 | pub w: f32, | |
| 10 | } | |
| 11 | ||
| 12 | #[derive(Debug,PartialEq)] | |
| 13 | pub struct UV { | |
| 14 | pub u: f32, | |
| 15 | pub v: f32, | |
| 16 | } | |
| 17 | ||
| 18 | #[derive(Debug,PartialEq)] | |
| 19 | pub struct Normal { | |
| 20 | pub x: f32, | |
| 21 | pub y: f32, | |
| 22 | pub z: f32, | |
| 23 | } | |
| 24 | ||
| 25 | #[derive(Debug,PartialEq)] | |
| 26 | pub struct Point { | |
| 27 | vertex: usize, | |
| 28 | uv: Option<usize>, | |
| 29 | normal: Option<usize>, | |
| 30 | } | |
| 31 | ||
| 32 | impl Point { | |
| 33 | fn parse(s: &str) -> Result<Point, OBJError> { | |
| 34 | let err = || OBJError(format!("Invalid point: {}", s)); | |
| 35 | let mut chunks = s.split("/"); | |
| 36 | ||
| 37 | let vertex = chunks.next().ok_or(err())?.parse().map_err(|_| err())?; | |
| 38 | let uv = match chunks.next() { | |
| 39 | Some("") => None, | |
| 40 | Some(n) => Some(n.parse().map_err(|_| err())?), | |
| 41 | None => None, | |
| 42 | }; | |
| 43 | ||
| 44 | let normal = match chunks.next() { | |
| 45 | Some("") => None, | |
| 46 | Some(n) => Some(n.parse().map_err(|_| err())?), | |
| 47 | None => None, | |
| 48 | }; | |
| 49 | ||
| 50 | Ok(Point { vertex, uv, normal, }) | |
| 51 | } | |
| 52 | } | |
| 53 | ||
| 54 | #[derive(Debug,PartialEq)] | |
| 55 | pub struct Face(Vec<Point>); | |
| 56 | ||
| 57 | #[derive(Debug,PartialEq)] | |
| 58 | pub struct OBJFile { | |
| 59 | pub vertices: Vec<Vertex>, | |
| 60 | pub texcoords: Vec<UV>, | |
| 61 | pub normals: Vec<Normal>, | |
| 62 | pub faces: Vec<Face>, | |
| 63 | } | |
| 64 | ||
| 65 | impl OBJFile { | |
| 66 | pub fn parse(s: &str) -> Result<OBJFile, OBJError> { | |
| 67 | let mut vertices = Vec::new(); | |
| 68 | let mut texcoords = Vec::new(); | |
| 69 | let mut normals = Vec::new(); | |
| 70 | let mut faces = Vec::new(); | |
| 71 | ||
| 72 | for line in s.lines() { | |
| 73 | let err = || OBJError(format!("Invalid line: {}", line)); | |
| 74 | let mut chunks = line.split_whitespace(); | |
| 75 | match chunks.next() { | |
| 76 | Some("v") => vertices.push(Vertex { | |
| 77 | x: chunks.next().ok_or(err())?.parse().map_err(|_| err())?, | |
| 78 | y: chunks.next().ok_or(err())?.parse().map_err(|_| err())?, | |
| 79 | z: chunks.next().ok_or(err())?.parse().map_err(|_| err())?, | |
| 80 | w: 1.0, | |
| 81 | }), | |
| 82 | ||
| 83 | Some("vt") => texcoords.push(UV { | |
| 84 | u: chunks.next().ok_or(err())?.parse().map_err(|_| err())?, | |
| 85 | v: chunks.next().ok_or(err())?.parse().map_err(|_| err())?, | |
| 86 | }), | |
| 87 | ||
| 88 | Some("vn") => normals.push(Normal { | |
| 89 | x: chunks.next().ok_or(err())?.parse().map_err(|_| err())?, | |
| 90 | y: chunks.next().ok_or(err())?.parse().map_err(|_| err())?, | |
| 91 | z: chunks.next().ok_or(err())?.parse().map_err(|_| err())?, | |
| 92 | }), | |
| 93 | ||
| 94 | Some("f") => | |
| 95 | match chunks.map(Point::parse).collect() { | |
| 96 | Ok(ps) => faces.push(Face(ps)), | |
| 97 | Err(e) => return Err(e), | |
| 98 | } | |
| 99 | ||
| 100 | Some("") => (), | |
| 101 | _ => return Err(err()), | |
| 102 | } | |
| 103 | } | |
| 104 | ||
| 105 | Ok(OBJFile { vertices, texcoords, normals, faces }) | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | #[cfg(test)] | |
| 110 | mod test { | |
| 111 | use super::*; | |
| 112 | ||
| 113 | #[test] | |
| 114 | fn just_a_vertex() { | |
| 115 | assert_eq!( | |
| 116 | OBJFile::parse("v 1.0 2.0 3.0\n").unwrap(), | |
| 117 | OBJFile { | |
| 118 | vertices: vec![ Vertex { x: 1.0, y: 2.0, z: 3.0, w: 1.0 } ], | |
| 119 | texcoords: vec![], | |
| 120 | normals: vec![], | |
| 121 | faces: vec![], | |
| 122 | }, | |
| 123 | ); | |
| 124 | } | |
| 125 | ||
| 126 | #[test] | |
| 127 | fn just_a_uv() { | |
| 128 | assert_eq!( | |
| 129 | OBJFile::parse("vt 1.0 0.5\n").unwrap(), | |
| 130 | OBJFile { | |
| 131 | vertices: vec![], | |
| 132 | texcoords: vec![ UV { u: 1.0, v: 0.5 } ], | |
| 133 | normals: vec![], | |
| 134 | faces: vec![], | |
| 135 | }, | |
| 136 | ); | |
| 137 | } | |
| 138 | ||
| 139 | #[test] | |
| 140 | fn simple_tri() { | |
| 141 | assert_eq!( | |
| 142 | OBJFile::parse("v 0 0 0 | |
| 143 | v 0 1 0 | |
| 144 | v 1 0 0 | |
| 145 | f 1 2 3").unwrap(), | |
| 146 | OBJFile { | |
| 147 | vertices: vec![ | |
| 148 | Vertex { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }, | |
| 149 | Vertex { x: 0.0, y: 1.0, z: 0.0, w: 1.0 }, | |
| 150 | Vertex { x: 1.0, y: 0.0, z: 0.0, w: 1.0 }, | |
| 151 | ], | |
| 152 | texcoords: vec![], | |
| 153 | normals: vec![], | |
| 154 | faces: vec![ Face(vec![ | |
| 155 | Point { vertex: 1, uv: None, normal: None }, | |
| 156 | Point { vertex: 2, uv: None, normal: None }, | |
| 157 | Point { vertex: 3, uv: None, normal: None }, | |
| 158 | ]) ], | |
| 159 | }, | |
| 160 | ); | |
| 161 | } | |
| 162 | ||
| 163 | #[test] | |
| 164 | fn simple_tri_with_uvs() { | |
| 165 | assert_eq!( | |
| 166 | OBJFile::parse("v 0 0 0 | |
| 167 | v 0 1 0 | |
| 168 | v 1 0 0 | |
| 169 | vt 0 0 | |
| 170 | vt 1 1 | |
| 171 | vt 1 0 | |
| 172 | f 1/1 2/2 3/3\n").unwrap(), | |
| 173 | ||
| 174 | OBJFile { | |
| 175 | vertices: vec![ | |
| 176 | Vertex { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }, | |
| 177 | Vertex { x: 0.0, y: 1.0, z: 0.0, w: 1.0 }, | |
| 178 | Vertex { x: 1.0, y: 0.0, z: 0.0, w: 1.0 }, | |
| 179 | ], | |
| 180 | texcoords: vec![ | |
| 181 | UV { u: 0.0, v: 0.0 }, | |
| 182 | UV { u: 1.0, v: 1.0 }, | |
| 183 | UV { u: 1.0, v: 0.0 }, | |
| 184 | ], | |
| 185 | normals: vec![], | |
| 186 | faces: vec![ Face(vec![ | |
| 187 | Point { vertex: 1, uv: Some(1), normal: None }, | |
| 188 | Point { vertex: 2, uv: Some(2), normal: None }, | |
| 189 | Point { vertex: 3, uv: Some(3), normal: None }, | |
| 190 | ]) ], | |
| 191 | }, | |
| 192 | ); | |
| 193 | } | |
| 194 | ||
| 195 | } |
| 1 | use glutin; | |
| 2 | use std::{convert, ffi, string}; | |
| 3 | ||
| 4 | #[derive(Debug)] | |
| 5 | pub enum Error { | |
| 6 | BadCString, | |
| 7 | InvalidShaderLog, | |
| 8 | CompileError(String), | |
| 9 | LinkError(String), | |
| 10 | GlutinCreation(glutin::CreationError), | |
| 11 | GlutinContext(glutin::ContextError), | |
| 12 | } | |
| 13 | ||
| 14 | impl convert::From<glutin::CreationError> for Error { | |
| 15 | fn from(err: glutin::CreationError) -> Error { | |
| 16 | Error::GlutinCreation(err) | |
| 17 | } | |
| 18 | } | |
| 19 | ||
| 20 | impl convert::From<glutin::ContextError> for Error { | |
| 21 | fn from(err: glutin::ContextError) -> Error { | |
| 22 | Error::GlutinContext(err) | |
| 23 | } | |
| 24 | } | |
| 25 | ||
| 26 | impl convert::From<ffi::NulError> for Error { | |
| 27 | fn from(_: ffi::NulError) -> Error { | |
| 28 | Error::BadCString | |
| 29 | } | |
| 30 | } | |
| 31 | ||
| 32 | impl convert::From<string::FromUtf8Error> for Error { | |
| 33 | fn from(_: string::FromUtf8Error) -> Error { | |
| 34 | Error::InvalidShaderLog | |
| 35 | } | |
| 36 | } |
| 2 | 2 | pub use gl::types::*; |
| 3 | 3 | pub use gl::*; |
| 4 | 4 | } |
| 5 |
use std:: |
|
| 5 | use std::ptr; | |
| 6 | ||
| 7 | use errors::Error; | |
| 6 | 8 | use graphics; |
| 7 | 9 | |
| 8 | 10 | // Vertex data |
| 9 | 11 | static VERTEX_DATA: [gl::GLfloat; 6] = [0.0, 0.5, 0.5, -0.5, -0.5, -0.5]; |
| 12 | static ALT_VERTEX_DATA: [gl::GLfloat; 6] = [0.0, -0.5, -0.5, 0.5, 0.5, 0.5]; | |
| 10 | 13 | |
| 11 | 14 | // Shader sources |
| 12 | 15 | static VS_SRC: &'static str = " |
| 23 | 26 | out_color = vec4(1.0, 1.0, 1.0, 1.0); |
| 24 | 27 | }"; |
| 25 | 28 | |
| 26 | #[derive(Debug)] | |
| 27 | pub enum Error { | |
| 28 | Graphics(graphics::GraphicsError), | |
| 29 | } | |
| 30 | ||
| 31 | impl convert::From<graphics::GraphicsError> for Error { | |
| 32 | fn from(err: graphics::GraphicsError) -> Error { | |
| 33 | Error::Graphics(err) | |
| 34 | } | |
| 35 | } | |
| 36 | ||
| 37 | 29 | pub fn main_loop() -> Result<(), Error> { |
| 38 | 30 | let window = graphics::Window::create()?; |
| 39 | 31 | |
| 41 | 33 | let fs = graphics::Shader::compile(graphics::ShaderType::Fragment, FS_SRC)?; |
| 42 | 34 | let program = graphics::Program::link(vec![vs, fs])?; |
| 43 | 35 | |
| 44 | let _vao = graphics::VertexArray::new(); | |
| 45 | let _vbo = graphics::VertexBuffer::new_array_buffer(&VERTEX_DATA); | |
| 36 | let vbo = graphics::VertexBuffer::new_array_buffer( | |
| 37 | graphics::VertexArray::new(), | |
| 38 | &VERTEX_DATA, | |
| 39 | ); | |
| 46 | 40 | |
| 47 | 41 | program.use_program(); |
| 48 | 42 | program.bind_frag_data_location(0, "out_color")?; |
| 60 | 54 | ); |
| 61 | 55 | } |
| 62 | 56 | |
| 57 | vbo.unbind(); | |
| 58 | ||
| 63 | 59 | window.run(|event| { |
| 64 | 60 | use glutin::{ControlFlow, Event, WindowEvent, VirtualKeyCode}; |
| 65 | 61 | |
| 66 | 62 | match event { |
| 67 | 63 | Event::WindowEvent { event: WindowEvent::Closed, .. } => |
| 68 | 64 | return ControlFlow::Break, |
| 69 |
Event::WindowEvent { |
|
| 65 | Event::WindowEvent { | |
| 66 | event: WindowEvent::KeyboardInput { | |
| 67 | input: k, | |
| 68 | .. | |
| 69 | }, | |
| 70 | .. | |
| 71 | } => { | |
| 70 | 72 | match k.virtual_keycode { |
| 71 | 73 | Some(VirtualKeyCode::Q) => |
| 72 | 74 | return ControlFlow::Break, |
| 80 | 82 | gl::ClearColor(0.3, 0.3, 0.3, 1.0); |
| 81 | 83 | gl::Clear(gl::COLOR_BUFFER_BIT); |
| 82 | 84 | |
| 85 | vbo.bind(); | |
| 86 | ||
| 83 | 87 | gl::DrawArrays(gl::TRIANGLES, 0, 3); |
| 84 | 88 | } |
| 85 | 89 | |
| 3 | 3 | pub use gl::*; |
| 4 | 4 | } |
| 5 | 5 | use glutin; |
| 6 | use std::{convert,ffi,mem,ptr,string}; | |
| 7 | ||
| 8 | #[derive(Debug)] | |
| 9 | pub enum GraphicsError { | |
| 10 | BadCString, | |
| 11 | InvalidShaderLog, | |
| 12 | CompileError(String), | |
| 13 | LinkError(String), | |
| 14 | GlutinCreation(glutin::CreationError), | |
| 15 | GlutinContext(glutin::ContextError), | |
| 16 | } | |
| 17 | ||
| 18 | impl convert::From<glutin::CreationError> for GraphicsError { | |
| 19 | fn from(err: glutin::CreationError) -> GraphicsError { | |
| 20 | GraphicsError::GlutinCreation(err) | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | impl convert::From<glutin::ContextError> for GraphicsError { | |
| 25 | fn from(err: glutin::ContextError) -> GraphicsError { | |
| 26 | GraphicsError::GlutinContext(err) | |
| 27 | } | |
| 28 | } | |
| 29 | ||
| 30 | impl convert::From<ffi::NulError> for GraphicsError { | |
| 31 | fn from(_: ffi::NulError) -> GraphicsError { | |
| 32 | GraphicsError::BadCString | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | impl convert::From<string::FromUtf8Error> for GraphicsError { | |
| 37 | fn from(_: string::FromUtf8Error) -> GraphicsError { | |
| 38 | GraphicsError::InvalidShaderLog | |
| 39 | } | |
| 40 | } | |
| 6 | use errors::Error; | |
| 7 | use std::{ffi,mem,ptr}; | |
| 41 | 8 | |
| 42 | 9 | // |
| 43 | 10 | |
| 47 | 14 | } |
| 48 | 15 | |
| 49 | 16 | impl Window { |
| 50 |
pub fn create() -> Result<Window, |
|
| 17 | pub fn create() -> Result<Window, Error> { | |
| 51 | 18 | use glutin::GlContext; |
| 52 | 19 | let events = glutin::EventsLoop::new(); |
| 53 | 20 | let window = glutin::WindowBuilder::new(); |
| 61 | 28 | Ok(Window { events, gl_window }) |
| 62 | 29 | } |
| 63 | 30 | |
| 64 |
pub fn run<F>(self, mut cb: F) -> Result<(), |
|
| 31 | pub fn run<F>(self, mut cb: F) -> Result<(), Error> | |
| 65 | 32 | where F: FnMut(glutin::Event) -> glutin::ControlFlow |
| 66 | 33 | { |
| 67 | 34 | use glutin::GlContext; |
| 74 | 41 | match gl_window.swap_buffers() { |
| 75 | 42 | Ok(()) => r, |
| 76 | 43 | Err(ctx) => { |
| 77 |
result = Err( |
|
| 44 | result = Err(Error::GlutinContext(ctx)); | |
| 78 | 45 | glutin::ControlFlow::Break |
| 79 | 46 | } |
| 80 | 47 | } |
| 110 | 77 | } |
| 111 | 78 | |
| 112 | 79 | impl Shader { |
| 113 |
pub fn compile(ty: ShaderType, src: &str) -> Result<Shader, |
|
| 80 | pub fn compile(ty: ShaderType, src: &str) -> Result<Shader, Error> { | |
| 114 | 81 | let shader; |
| 115 | 82 | unsafe { |
| 116 | 83 | shader = gl::CreateShader(ty.to_glenum()); |
| 132 | 99 | ptr::null_mut(), |
| 133 | 100 | buf.as_mut_ptr() as *mut gl::GLchar |
| 134 | 101 | ); |
| 135 |
return Err( |
|
| 102 | return Err(Error::CompileError(String::from_utf8(buf)?)); | |
| 136 | 103 | } |
| 137 | 104 | } |
| 138 | 105 | Ok(Shader { n: shader }) |
| 157 | 124 | } |
| 158 | 125 | |
| 159 | 126 | impl Program { |
| 160 |
pub fn link(shaders: Vec<Shader>) -> Result<Program, |
|
| 127 | pub fn link(shaders: Vec<Shader>) -> Result<Program, Error> { | |
| 161 | 128 | unsafe { |
| 162 | 129 | let program = gl::CreateProgram(); |
| 163 | 130 | for s in shaders.iter() { |
| 180 | 147 | ptr::null_mut(), |
| 181 | 148 | buf.as_mut_ptr() as *mut gl::GLchar, |
| 182 | 149 | ); |
| 183 |
return Err( |
|
| 150 | return Err(Error::LinkError(String::from_utf8(buf)?)); | |
| 184 | 151 | } |
| 185 | 152 | |
| 186 | 153 | Ok(Program { |
| 194 | 161 | unsafe { gl::UseProgram(self.p); } |
| 195 | 162 | } |
| 196 | 163 | |
| 197 |
pub fn bind_frag_data_location(&self, idx: u32, s: &str) -> Result<(), |
|
| 164 | pub fn bind_frag_data_location(&self, idx: u32, s: &str) -> Result<(), Error> { | |
| 198 | 165 | unsafe { |
| 199 | 166 | gl::BindFragDataLocation( |
| 200 | 167 | self.p, |
| 201 | 168 | idx, |
| 202 |
ffi::CString::new(s)?.as_ptr() |
|
| 169 | ffi::CString::new(s)?.as_ptr(), | |
| 203 | 170 | ); |
| 204 | 171 | } |
| 205 | 172 | Ok(()) |
| 206 | 173 | } |
| 207 | 174 | |
| 208 |
pub fn get_attrib_location(&self, s: &str) -> Result<i32, |
|
| 175 | pub fn get_attrib_location(&self, s: &str) -> Result<i32, Error> { | |
| 209 | 176 | Ok(unsafe { |
| 210 | 177 | gl::GetAttribLocation( |
| 211 | 178 | self.p, |
| 212 |
ffi::CString::new(s)?.as_ptr() |
|
| 179 | ffi::CString::new(s)?.as_ptr(), | |
| 213 | 180 | ) |
| 214 | 181 | }) |
| 215 | 182 | } |
| 229 | 196 | |
| 230 | 197 | pub struct VertexBuffer { |
| 231 | 198 | idx: u32, |
| 199 | data: VertexArray, | |
| 232 | 200 | } |
| 233 | 201 | |
| 234 | 202 | impl Drop for VertexBuffer { |
| 247 | 215 | |
| 248 | 216 | VertexArray { idx } |
| 249 | 217 | } |
| 218 | ||
| 219 | pub fn bind(&self) { | |
| 220 | unsafe { | |
| 221 | gl::BindVertexArray(self.idx); | |
| 222 | } | |
| 223 | } | |
| 224 | ||
| 225 | pub fn unbind(&self) { | |
| 226 | unsafe { | |
| 227 | gl::BindVertexArray(0); | |
| 228 | } | |
| 229 | } | |
| 250 | 230 | } |
| 251 | 231 | |
| 252 | 232 | impl VertexBuffer { |
| 253 |
pub fn new_array_buffer( |
|
| 233 | pub fn new_array_buffer( | |
| 234 | data: VertexArray, | |
| 235 | vertex_data: &[gl::GLfloat], | |
| 236 | ) -> VertexBuffer { | |
| 254 | 237 | let mut idx = 0; |
| 255 | 238 | unsafe { |
| 256 | 239 | gl::GenBuffers(1, &mut idx); |
| 263 | 246 | ); |
| 264 | 247 | } |
| 265 | 248 | |
| 266 | VertexBuffer { idx } | |
| 267 | } | |
| 268 |
|
|
| 249 | VertexBuffer { idx, data } | |
| 250 | } | |
| 251 | ||
| 252 | pub fn bind(&self) { | |
| 253 | unsafe { | |
| 254 | self.data.bind(); | |
| 255 | gl::BindBuffer(gl::ARRAY_BUFFER, self.idx); | |
| 256 | } | |
| 257 | } | |
| 258 | ||
| 259 | pub fn unbind(&self) { | |
| 260 | unsafe { | |
| 261 | gl::BindBuffer(gl::ARRAY_BUFFER, 0); | |
| 262 | self.data.unbind(); | |
| 263 | } | |
| 264 | } | |
| 265 | } | |