dac8b37cb9f0f0dfb350d9293b762c44dd08a484
[mandelwow.git] / main.rs
1 // Wow. Such fractal.
2
3 #[macro_use]
4
5 extern crate glium;
6 extern crate glutin;
7 extern crate libxm;
8 extern crate sdl2;
9
10 use cube::Cube;
11 use glium::{DisplayBuild, Surface};
12 use glium::index::{IndexBuffer, PrimitiveType};
13 use glutin::ElementState::Pressed;
14 use glutin::Event::KeyboardInput;
15 use glutin::VirtualKeyCode;
16
17 mod cube;
18 mod mandelwow;
19 mod sound;
20 mod support;
21
22 fn solid_fill_program(display: &glium::Display) -> glium::Program {
23     let vertex_shader_src = r#"
24         #version 140
25         in vec3 position;
26         uniform mat4 perspective;
27         uniform mat4 view;
28         uniform mat4 model;
29
30         void main() {
31             mat4 modelview = view * model;
32             gl_Position = perspective * modelview * vec4(position, 1.0);
33         }
34     "#;
35
36     let fragment_shader_src = r#"
37         #version 140
38
39         out vec4 color;
40
41         void main() {
42             color = vec4(1.0, 1.0, 1.0, 1.0);
43         }
44     "#;
45
46     return glium::Program::from_source(display,
47                                        vertex_shader_src,
48                                        fragment_shader_src,
49                                        None).unwrap();
50 }
51
52 fn bounding_box<U>(display: &glium::Display,
53                    frame: &mut glium::Frame,
54                    program: &glium::Program,
55                    uniforms: &U,
56                    cube: &Cube) where U: glium::uniforms::Uniforms {
57
58     #[derive(Copy, Clone)]
59     struct Vertex { position: [f32; 3] }
60     implement_vertex!(Vertex, position);
61
62     let cube = [
63         Vertex { position: [cube.xmin, cube.ymin, cube.zmin] },
64         Vertex { position: [cube.xmax, cube.ymin, cube.zmin] },
65         Vertex { position: [cube.xmax, cube.ymax, cube.zmin] },
66         Vertex { position: [cube.xmin, cube.ymax, cube.zmin] },
67         Vertex { position: [cube.xmin, cube.ymin, cube.zmax] },
68         Vertex { position: [cube.xmax, cube.ymin, cube.zmax] },
69         Vertex { position: [cube.xmax, cube.ymax, cube.zmax] },
70         Vertex { position: [cube.xmin, cube.ymax, cube.zmax] },
71     ];
72     let vb = glium::VertexBuffer::new(display, &cube).unwrap();
73
74     let params = glium::DrawParameters {
75         depth: glium::Depth {
76             test: glium::draw_parameters::DepthTest::IfLess,
77             write: true,
78             ..Default::default()
79         },
80         blend: glium::Blend::alpha_blending(),
81         ..Default::default()
82     };
83
84     let front_indices = IndexBuffer::new(display, PrimitiveType::LineLoop,
85                                          &[0, 1, 2, 3u16]).unwrap();
86     frame.draw(&vb, &front_indices, program, uniforms, &params).unwrap();
87
88     let back_indices = IndexBuffer::new(display, PrimitiveType::LineLoop,
89                                         &[4, 5, 6, 7u16]).unwrap();
90     frame.draw(&vb, &back_indices, program, uniforms, &params).unwrap();
91
92     let sides_indices = IndexBuffer::new(display, PrimitiveType::LinesList,
93                                          &[0, 4, 1, 5, 2, 6, 3, 7u16]).unwrap();
94     frame.draw(&vb, &sides_indices, program, uniforms, &params).unwrap();
95 }
96
97 fn main() {
98     sound::start();
99
100     let display = glium::glutin::WindowBuilder::new()
101         //.with_dimensions(1024, 768)
102         .with_fullscreen(glutin::get_primary_monitor())
103         .with_depth_buffer(24)
104         .with_vsync()
105         .with_title(format!("MandelWow"))
106         .build_glium()
107         .unwrap();
108
109     let mandelwow_program = mandelwow::program(&display);
110     let bounding_box_program = solid_fill_program(&display);
111
112     let mut camera = support::camera::CameraState::new();
113     let mut t: f32 = 0.0;
114     let mut pause = false;
115     let mut bounding_box_enabled = true;
116     let mut fullscreen = true;
117
118     support::start_loop(|| {
119         camera.update();
120
121         if !pause {
122             // Increment time
123             t += 0.01;
124         }
125
126         // These are the bounds of the 3D Mandelwow section which we render in 3-space.
127         let bounds = Cube {
128             xmin: -2.0,
129             xmax:  0.7,
130             ymin: -1.0,
131             ymax:  1.0,
132             zmin: -1.1,
133             zmax:  1.1,
134         };
135
136         // Vary the wow factor to slice the Mandelwow along its 4th dimension.
137         let wmin = -0.8;
138         let wmax =  0.8;
139         let wsize = wmax - wmin;
140         let wow = (((t * 0.7).sin() + 1.0) / 2.0) * wsize + wmin;
141
142         //println!("t={} w={:?} camera={:?}", t, w, camera.get_pos());
143
144         let mut frame = display.draw();
145         frame.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
146
147         let z_trans = -2.0;  // Send the model back a little bit so it fits the screen.
148         let model = [
149             [ t.cos(),  t.sin(),  0.0,     0.0],
150             [-t.sin(),  t.cos(),  0.0,     0.0],
151             [     0.0,  0.0,      1.0,     0.0],
152             [     0.0,  0.0,      z_trans, 1.0f32]
153         ];
154
155         // Draw the bounding box before the fractal, when the Z-buffer is still clear,
156         // so the lines behind the semi-translucent areas will be drawn.
157         if bounding_box_enabled {
158             let uniforms = uniform! {
159                 model: model,
160                 view:  camera.get_view(),
161                 perspective: camera.get_perspective(),
162             };
163             bounding_box(&display, &mut frame, &bounding_box_program, &uniforms, &bounds);
164         }
165
166         mandelwow::draw(&display, &mut frame, &mandelwow_program, model, &camera, &bounds, wow);
167         frame.finish().unwrap();
168
169         for ev in display.poll_events() {
170             match ev {
171                 glium::glutin::Event::Closed |
172                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::Escape)) |
173                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::Q)) => {
174                     return support::Action::Stop
175                 },
176                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::B)) => {
177                     bounding_box_enabled ^= true;
178                 },
179                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::F)) => {
180                     fullscreen ^= true;
181                     if fullscreen {
182                         glutin::WindowBuilder::new()
183                             .with_fullscreen(glutin::get_primary_monitor())
184                             .rebuild_glium(&display).unwrap();
185                     } else {
186                         glutin::WindowBuilder::new()
187                             .rebuild_glium(&display).unwrap();
188                     }
189                 },
190                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::P)) => {
191                     pause ^= true;
192                 },
193                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::PageUp)) => {
194                     t += 0.01;
195                 },
196                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::PageDown)) => {
197                     t -= 0.01;
198                 },
199                 ev => camera.process_input(&ev),
200             }
201         }
202
203         support::Action::Continue
204     });
205
206 }