Cleanups.
[mandelwow.git] / mandelwow.rs
1 // Wow. Such fractal.
2
3 #[macro_use]
4
5 extern crate glium;
6 extern crate glutin;
7
8 use glium::DisplayBuild;
9 use glium::Surface;
10 use glium::index::PrimitiveType;
11 use glium::index::IndexBuffer;
12
13 mod support;
14
15 #[derive(Copy, Clone)]
16 struct Cube {
17     xmin: f32,
18     ymin: f32,
19     zmin: f32,
20     xmax: f32,
21     ymax: f32,
22     zmax: f32,
23 }
24
25 /*
26 fn mand(cx: f32, cy: f32) -> [f32; 3] {
27     let maxiter = 64;
28     let mut iter = maxiter;
29     let mut zx = cx;
30     let mut zy = cy;
31     while iter > 0 {
32         let zx2 = zx * zx;
33         let zy2 = zy * zy;
34         if zx2 + zy2 > 4.0 {
35             return [iter as f32 / maxiter as f32, 1.0, 1.0];
36         }
37         zy = zx * zy * 2.0 + cy;
38         zx = zx2 - zy2 + cx;
39         iter -= 1;
40     }
41
42     [0.0, 0.0, 0.0]
43 }
44 */
45
46 fn mandelwow_program(display: &glium::Display) -> glium::Program {
47     return program!(display,
48         140 => {
49             vertex: r#"
50                 #version 140
51                 uniform mat4 perspective;
52                 uniform mat4 view;
53                 uniform mat4 model;
54                 uniform vec2 z0;
55                 in vec3 position;
56                 out vec2 c;
57                 out vec2 z;
58
59                 void main() {
60                     mat4 modelview = view * model;
61                     gl_Position = perspective * modelview * vec4(position, 1.0);
62                     c = vec2(position.x, position.y);
63                     z = vec2(z0.x, z0.y);
64                 }
65             "#,
66
67             fragment: r#"
68                 #version 140
69                 precision highp float;
70                 in vec2 c;
71                 in vec2 z;
72                 out vec4 f_color;
73
74                 void main() {
75                     float zx = z.x;
76                     float zy = z.y;
77                     int maxiter = 64;
78                     int iter = maxiter;
79                     while (iter > 0) {
80                         float zx2 = zx * zx;
81                         float zy2 = zy * zy;
82                         if (zx2 * zy2 > 4.0) {
83                           float index = 1.0 - float(iter) / float(maxiter);
84                           f_color = vec4(index, index * 0.5, index, index * 0.5);
85                           return;
86                         }
87                         zy = zx * zy * 2.0 + c.y;
88                         zx = zx2 - zy2 + c.x;
89                         iter -= 1;
90                     }
91                     f_color = vec4((sin(z.y) + 1.0) / 2,
92                                    (sin(c.y) + 1.0) / 2,
93                                    (sin(c.x) + 1.0) / 2,
94                                    1.0);
95                 }
96             "#
97         }).unwrap();
98 }
99
100 fn solid_fill_program(display: &glium::Display) -> glium::Program {
101     let vertex_shader_src = r#"
102         #version 140
103         in vec3 position;
104         uniform mat4 perspective;
105         uniform mat4 view;
106         uniform mat4 model;
107
108         void main() {
109             mat4 modelview = view * model;
110             gl_Position = perspective * modelview * vec4(position, 1.0);
111         }
112     "#;
113
114     let fragment_shader_src = r#"
115         #version 140
116
117         out vec4 color;
118
119         void main() {
120             color = vec4(1.0, 1.0, 1.0, 1.0);
121         }
122     "#;
123
124     return glium::Program::from_source(display,
125                                        vertex_shader_src,
126                                        fragment_shader_src,
127                                        None).unwrap();
128 }
129
130 fn bounding_box<U>(display: &glium::Display,
131                    frame: &mut glium::Frame,
132                    program: &glium::Program,
133                    uniforms: &U,
134                    cube: &Cube) where U: glium::uniforms::Uniforms {
135
136     #[derive(Copy, Clone)]
137     struct Vertex { position: [f32; 3] }
138     implement_vertex!(Vertex, position);
139
140     let cube = [
141         Vertex { position: [cube.xmin, cube.ymin, cube.zmin] },
142         Vertex { position: [cube.xmax, cube.ymin, cube.zmin] },
143         Vertex { position: [cube.xmax, cube.ymax, cube.zmin] },
144         Vertex { position: [cube.xmin, cube.ymax, cube.zmin] },
145         Vertex { position: [cube.xmin, cube.ymin, cube.zmax] },
146         Vertex { position: [cube.xmax, cube.ymin, cube.zmax] },
147         Vertex { position: [cube.xmax, cube.ymax, cube.zmax] },
148         Vertex { position: [cube.xmin, cube.ymax, cube.zmax] },
149     ];
150     let vb = glium::VertexBuffer::new(display, &cube).unwrap();
151
152     let params = Default::default();
153
154     let front_indices = IndexBuffer::new(display, PrimitiveType::LineLoop,
155                                          &[0, 1, 2, 3u16]).unwrap();
156     frame.draw(&vb, &front_indices, program, uniforms, &params).unwrap();
157
158     let back_indices = IndexBuffer::new(display, PrimitiveType::LineLoop,
159                                         &[4, 5, 6, 7u16]).unwrap();
160     frame.draw(&vb, &back_indices, program, uniforms, &params).unwrap();
161
162     let sides_indices = IndexBuffer::new(display, PrimitiveType::LinesList,
163                                          &[0, 4, 1, 5, 2, 6, 3, 7u16]).unwrap();
164     frame.draw(&vb, &sides_indices, program, uniforms, &params).unwrap();
165 }
166
167 fn mandel<U>(display: &glium::Display,
168           frame: &mut glium::Frame,
169           program: &glium::Program,
170           uniforms: &U,
171           bounds: &Cube,
172           z: [f32; 2]) where U: glium::uniforms::Uniforms {
173
174     #[derive(Copy, Clone)]
175     struct Vertex {
176         position: [f32; 3],
177         color: [f32; 3],
178     }
179     implement_vertex!(Vertex, position, color);
180
181     let xmin = bounds.xmin;
182     let xmax = bounds.xmax;
183     let ymin = bounds.ymin;
184     let ymax = bounds.ymax;
185
186     let width = xmax - xmin;
187     let height = ymax - ymin;
188     let xres: usize = 1;
189     let yres: usize = 1;
190     let xstep = width / (xres as f32);
191     let ystep = height / (yres as f32);
192     let vb_size = (xres * 2 + 4) * yres;
193     let mut v : Vec<Vertex> = Vec::with_capacity(vb_size);
194     v.resize(vb_size, Vertex { position: [0.0, 0.0, -1.0], color: [0.0, 0.0, 0.0] });
195     let mut i: usize = 0;
196     let mut vy = ymin;
197     let vz = z[1];
198     for _ in 0..yres {
199         let mut vx = xmin;
200         let c = [0.0, 0.0, 1.0];
201         v[i] = Vertex { position: [vx, vy+ystep, vz], color: c }; i += 1;
202         v[i] = Vertex { position: [vx, vy,       vz], color: c }; i += 1;
203         for _ in 0..xres {
204             //let c = mand(vx, vy);
205             v[i] = Vertex { position: [vx+xstep, vy+ystep, vz], color: c }; i += 1;
206             v[i] = Vertex { position: [vx+xstep, vy,       vz], color: c }; i += 1;
207             vx += xstep;
208         }
209         v[i] = Vertex { position: [vx,   vy, vz], color: c }; i += 1;
210         v[i] = Vertex { position: [xmin, vy, vz], color: c }; i += 1;
211         vy += ystep;
212     }
213
214     //let vb = glium::VertexBuffer::empty_persistent(display, width*height*3).unwrap();
215     let vb = glium::VertexBuffer::new(display, &v).unwrap();
216
217     let indices = glium::index::NoIndices(glium::index::PrimitiveType::TriangleStrip);
218     //let indices = glium::index::NoIndices(glium::index::PrimitiveType::LineStrip);
219     //let indices = glium::IndexBuffer::new(display, PrimitiveType::TrianglesList,
220     //                                      &[0u16, 1, 2]).unwrap();
221
222     let params = glium::DrawParameters {
223         depth: glium::Depth {
224             test: glium::draw_parameters::DepthTest::IfLess,
225             write: true,
226             .. Default::default()
227         },
228         blend: glium::Blend::alpha_blending(),
229         .. Default::default()
230     };
231
232     frame.draw(&vb, &indices, program, uniforms, &params).unwrap();
233 }
234
235 fn mandelwow(display: &glium::Display,
236              mut frame: &mut glium::Frame,
237              program: &glium::Program,
238              model: [[f32; 4]; 4],
239              camera: &support::camera::CameraState,
240              bounds: &Cube,
241              mandel_w: f32) {
242     let mut z0 = [mandel_w, 0f32];
243     let zres = 50;
244     let zmin = bounds.zmin;
245     let zmax = bounds.zmax;
246     let zstep = (zmax - zmin) / zres as f32;
247     let mut zy = zmin;
248     for _ in 0..zres {
249         z0[1] = zy;
250         zy += zstep;
251
252         let uniforms = uniform! {
253             z0: z0,
254             model: model,
255             view:  camera.get_view(),
256             perspective: camera.get_perspective(),
257         };
258
259         mandel(&display, &mut frame, &program, &uniforms, bounds, z0);
260     }
261 }
262
263 fn main() {
264     let display = glium::glutin::WindowBuilder::new()
265         .with_dimensions(1024, 768)
266         .with_depth_buffer(24)
267         .with_title(format!("MandelWow"))
268         .build_glium()
269         .unwrap();
270
271     let mut camera = support::camera::CameraState::new();
272     let mut t: f32 = 0.0;
273     let mut pause = false;
274
275     support::start_loop(|| {
276         camera.update();
277
278         if !pause {
279             // Increment time
280             t += 0.01;
281         }
282
283         // These are the bounds of the 3D Mandelwow section which we render in 3-space.
284         let bounds = Cube {
285             xmin: -2.0,
286             xmax:  0.7,
287             ymin: -1.0,
288             ymax:  1.0,
289             zmin: -1.2,
290             zmax:  1.2,
291         };
292
293         // Vary the wow factor to slice the Mandelwow along its 4th dimension.
294         let wmin = -0.8;
295         let wmax =  0.8;
296         let wsize = wmax - wmin;
297         let wow = (((t * 0.7).sin() + 1.0) / 2.0) * wsize + wmin;
298
299         //println!("t={} w={:?} camera={:?}", t, w, camera.get_pos());
300
301         let mut frame = display.draw();
302         frame.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
303
304         let z_trans = -2.0;  // Send the model back a little bit so it fits the screen.
305         let model = [
306             [ t.cos(),  t.sin(),  0.0,     0.0],
307             [-t.sin(),  t.cos(),  0.0,     0.0],
308             [     0.0,  0.0,      1.0,     0.0],
309             [     0.0,  0.0,      z_trans, 1.0f32]
310         ];
311
312         let program = mandelwow_program(&display);
313         let bounding_box_program = solid_fill_program(&display);
314
315         mandelwow(&display, &mut frame, &program, model, &camera, &bounds, wow);
316
317         let uniforms = uniform! {
318             model: model,
319             view:  camera.get_view(),
320             perspective: camera.get_perspective(),
321         };
322         bounding_box(&display, &mut frame, &bounding_box_program, &uniforms, &bounds);
323
324         for ev in display.poll_events() {
325             match ev {
326                 glium::glutin::Event::Closed => {
327                     frame.finish().unwrap();
328                     return support::Action::Stop
329                 },
330                 glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(glutin::VirtualKeyCode::PageUp)) => {
331                     t += 0.01;
332                 },
333                 glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(glutin::VirtualKeyCode::PageDown)) => {
334                     t -= 0.01;
335                 },
336                 glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(glutin::VirtualKeyCode::P)) => {
337                     pause ^= true;
338                 },
339                 ev => camera.process_input(&ev),
340             }
341         }
342
343         frame.finish().unwrap();
344         support::Action::Continue
345     });
346 }