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 | } |