Some basic shading; textures now supported in the Utmyen model format
Getty Ritter
8 years ago
4 | 4 | authors = ["Getty Ritter <gdritter@galois.com>"] |
5 | 5 | |
6 | 6 | [dependencies] |
7 | image = "*" | |
7 | 8 | glium = "*" |
163 | 163 | } |
164 | 164 | } |
165 | 165 | |
166 | pub fn as_bytes(&self) -> Result<Vec<u8>, String> { | |
167 | match self { | |
168 | &Bencode::Str(ref bs) => Ok(bs.clone()), | |
169 | _ => fail("not a string"), | |
170 | } | |
171 | } | |
172 | ||
166 | 173 | pub fn map<A, F>(&self, func: F) -> Result<Vec<A>, String> |
167 | 174 | where F: 'static + Fn(&Bencode) -> Result<A, String> |
168 | 175 | { |
1 | 1 | extern crate core; |
2 | 2 | #[macro_use] |
3 | 3 | extern crate glium; |
4 | extern crate image; | |
4 | 5 | |
5 | 6 | // mod adnot; |
6 | 7 | mod bencode; |
9 | 10 | use glium::{DisplayBuild, Program, Surface}; |
10 | 11 | use glium::glutin::Event; |
11 | 12 | use glium::glutin::WindowBuilder; |
12 | ||
13 | // use bencode::Bencode; | |
13 | use glium::texture::Texture2d; | |
14 | 14 | |
15 | 15 | fn sample() -> model::Model { |
16 | 16 | let file = std::env::args().nth(1) |
18 | 18 | model::Model::load_from_file(&file).unwrap() |
19 | 19 | } |
20 | 20 | |
21 | fn sample_texture(display: &glium::Display) -> Texture2d { | |
22 | use std::io::Cursor; | |
23 | let img = image::load(Cursor::new(&include_bytes!("/home/gdritter/pictures/cube-uv.png")[..]), | |
24 | image::PNG).unwrap().to_rgba(); | |
25 | let dims = img.dimensions(); | |
26 | let img = glium::texture::RawImage2d::from_raw_rgba_reversed(img.into_raw(), dims); | |
27 | Texture2d::new(display, img).unwrap() | |
28 | } | |
29 | ||
21 | 30 | const VERTEX_SHADER: &'static str = r#" |
22 | 31 | #version 140 |
32 | ||
23 | 33 | in vec3 pos; |
34 | in vec3 nrm; | |
35 | in vec2 tex; | |
24 | 36 | |
25 | 37 | uniform mat4 perspective; |
26 | 38 | uniform mat4 matrix; |
27 | 39 | |
28 |
out vec3 o_ |
|
40 | out vec3 o_nrm; | |
41 | out vec2 o_tex; | |
29 | 42 | |
30 | 43 | void main() { |
31 |
o_ |
|
44 | o_nrm = transpose(inverse(mat3(matrix))) * nrm; | |
45 | o_tex = tex; | |
32 | 46 | gl_Position = perspective * matrix * vec4(pos, 1.0); |
33 | 47 | } |
34 | 48 | "#; |
35 | 49 | |
36 | 50 | const FRAGMENT_SHADER: &'static str = r#" |
37 | 51 | #version 140 |
38 | in vec3 o_pos; | |
52 | ||
53 | in vec3 o_nrm; | |
54 | in vec2 o_tex; | |
55 | ||
56 | uniform vec3 u_light; | |
57 | uniform sampler2D tex; | |
58 | ||
39 | 59 | out vec4 color; |
60 | ||
40 | 61 | void main() { |
41 |
|
|
62 | float brightness = dot(normalize(o_nrm), normalize(u_light)); | |
63 | float factor = 0.5 + (brightness * 0.5); | |
64 | color = texture(tex, o_tex) * factor; | |
42 | 65 | } |
43 | 66 | "#; |
44 | 67 | |
49 | 72 | .with_depth_buffer(24) |
50 | 73 | .build_glium() |
51 | 74 | .unwrap(); |
75 | ||
52 | 76 | let vbuf = md.get_vbuf(&display); |
53 | 77 | let idxs = md.get_ibuf(&display); |
78 | let tx = md.get_tex(&display).unwrap_or(sample_texture(&display)); | |
79 | ||
54 | 80 | let program = Program::from_source( |
55 | 81 | &display, |
56 | 82 | VERTEX_SHADER, |
57 | 83 | FRAGMENT_SHADER, |
58 | 84 | None).unwrap(); |
59 | 85 | |
86 | let light = [-1.0, 0.4, 0.9f32]; | |
60 | 87 | let mut px = 0f32; |
61 | 88 | let mut py = 0.02f32; |
89 | let mut pz = 0.0f32; | |
62 | 90 | let mut rt = 0f32; |
63 | 91 | let dx = 0.001; |
64 | 92 | let sx = 0.01f32; |
86 | 114 | |
87 | 115 | let uniforms = uniform! { |
88 | 116 | perspective: perspective, |
117 | u_light: light, | |
118 | tex: &tx, | |
89 | 119 | matrix: [ [ rt.cos() * sx, 0.0, -rt.sin() * sx, 0.0 ], |
90 | 120 | [ 0.0, sx, 0.0, 0.0 ], |
91 | 121 | [ rt.sin() * sx, 0.0, rt.cos() * sx, 0.0 ], |
92 |
[ px, |
|
122 | [ px, pz, py, sx ] | |
93 | 123 | ] |
94 | 124 | }; |
95 | 125 | |
113 | 143 | for ev in display.poll_events() { |
114 | 144 | match ev { |
115 | 145 | Event::Closed => return, |
116 | Event::KeyboardInput(_, 9, _) => return, | |
117 | Event::KeyboardInput(_, 24, _) => { | |
118 | rt -= 0.1; | |
119 | }, | |
120 | Event::KeyboardInput(_, 25, _) => { | |
121 | py -= dx; | |
122 | }, | |
123 | Event::KeyboardInput(_, 26, _) => { | |
124 | rt += 0.1; | |
125 | }, | |
126 | Event::KeyboardInput(_, 38, _) => { | |
127 | px -= dx; | |
128 | }, | |
129 | Event::KeyboardInput(_, 39, _) => { | |
130 | py += dx; | |
131 | }, | |
132 | Event::KeyboardInput(_, 40, _) => { | |
133 | px += dx; | |
134 | }, | |
135 | evt => { | |
136 |
|
|
146 | Event::KeyboardInput(_, n, _) => | |
147 | match n { | |
148 | 9 => return, | |
149 | 24 => rt -= 0.1, | |
150 | 25 => py -= dx, | |
151 | 26 => rt += 0.1, | |
152 | 38 => px -= dx, | |
153 | 39 => py += dx, | |
154 | 40 => px += dx, | |
155 | 44 => pz -= dx, | |
156 | 45 => pz += dx, | |
157 | _ => {}, | |
158 | }, | |
159 | _ => {}, | |
137 | 160 | } |
138 | 161 | } |
139 | 162 | } |
1 | 1 | extern crate glium; |
2 | extern crate image; | |
2 | 3 | |
3 | 4 | use bencode::Bencode; |
5 | use glium::{VertexBuffer, IndexBuffer}; | |
6 | use glium::texture::Texture2d; | |
4 | 7 | |
5 | 8 | #[derive(Debug, Copy, Clone)] |
6 | 9 | pub struct V3D { |
7 | 10 | pub pos: [f32; 3], |
11 | pub nrm: [f32; 3], | |
8 | 12 | pub tex: [f32; 2], |
9 | 13 | } |
10 | 14 | |
11 |
implement_vertex!(V3D, pos, |
|
15 | implement_vertex!(V3D, pos, nrm, tex); | |
12 | 16 | |
13 | 17 | #[derive(Debug, Clone)] |
14 | 18 | pub struct Model { |
15 | pub points: Vec<V3D>, | |
16 | pub tris: Vec<u16>, | |
19 | pub points: Vec<V3D>, | |
20 | pub tris: Vec<u16>, | |
21 | pub texture: Option<Vec<u8>>, | |
17 | 22 | } |
18 | 23 | |
19 | 24 | impl Model { |
31 | 36 | |
32 | 37 | pub fn load(is: &[u8]) -> Result<Model, String> { |
33 | 38 | let bs = try!(Bencode::parse(is)); |
34 |
|
|
39 | let tex = match bs.get_field("tex") { | |
40 | Ok(t) => Some(try!(t.as_bytes())), | |
41 | Err(_) => None, | |
42 | }; | |
35 | 43 | Ok(Model { |
36 | 44 | points: try!(try!(bs.get_field("pts")).map(load_v3d)), |
37 | 45 | tris: try!(try!(bs.get_field("tris")).map(|v| v.as_u16())), |
46 | texture: tex, | |
38 | 47 | }) |
39 | 48 | } |
40 | 49 | |
41 | pub fn get_vbuf(&self, display: &glium::Display) -> glium::VertexBuffer<V3D> { | |
42 | glium::VertexBuffer::new(display, self.points.as_slice()).unwrap() | |
50 | pub fn get_vbuf(&self, display: &glium::Display) -> VertexBuffer<V3D> { | |
51 | VertexBuffer::new(display, self.points.as_slice()).unwrap() | |
43 | 52 | } |
44 | 53 | |
45 | pub fn get_ibuf(&self, display: &glium::Display) -> glium::IndexBuffer<u16> { | |
46 | glium::IndexBuffer::new( | |
54 | pub fn get_ibuf(&self, display: &glium::Display) -> IndexBuffer<u16> { | |
55 | IndexBuffer::new( | |
47 | 56 | display, |
48 | 57 | glium::index::PrimitiveType::TrianglesList, |
49 | 58 | &self.tris).unwrap() |
59 | } | |
60 | ||
61 | pub fn get_tex(&self, display: &glium::Display) -> Option<Texture2d> { | |
62 | use std::io::Cursor; | |
63 | use glium::texture::RawImage2d; | |
64 | if let Some(ref tex) = self.texture { | |
65 | let img = image::load(Cursor::new(tex.clone()), | |
66 | image::PNG).unwrap().to_rgba(); | |
67 | let dims = img.dimensions(); | |
68 | let img = RawImage2d::from_raw_rgba_reversed(img.into_raw(), | |
69 | dims); | |
70 | Some(Texture2d::new(display, img).unwrap()) | |
71 | } else { | |
72 | None | |
73 | } | |
50 | 74 | } |
51 | 75 | } |
52 | 76 | |
53 | 77 | fn load_v3d(bs: &Bencode) -> Result<V3D, String> { |
54 | 78 | Ok(V3D { |
55 |
pos: [ try!(try!(bs.get_idx( |
|
79 | pos: [ try!(try!(bs.get_idx(0)).as_float()) * 0.5, | |
80 | try!(try!(bs.get_idx(1)).as_float()) * 0.5, | |
56 | 81 | try!(try!(bs.get_idx(2)).as_float()) * 0.5, |
57 |
|
|
82 | ], | |
83 | nrm: [ try!(try!(bs.get_idx(5)).as_float()), | |
84 | try!(try!(bs.get_idx(6)).as_float()), | |
85 | try!(try!(bs.get_idx(7)).as_float()), | |
58 | 86 | ], |
59 | 87 | tex: [ try!(try!(bs.get_idx(3)).as_float()), |
60 | 88 | try!(try!(bs.get_idx(4)).as_float()), |