Create mandelwow_lib.
[mandelwow.git] / main.rs
1 extern crate mandelwow_lib;
2
3 extern crate cgmath;
4 #[macro_use(uniform)]
5 extern crate glium;
6 extern crate glutin;
7 extern crate image;
8
9 use cgmath::{Euler, Matrix4, Rad, Vector3};
10 use glium::{DisplayBuild, Surface};
11 use glutin::ElementState::Pressed;
12 use glutin::Event::KeyboardInput;
13 use glutin::VirtualKeyCode;
14 use mandelwow_lib::*;
15
16 #[cfg(target_os = "emscripten")]
17 use std::os::raw::{c_int, c_void};
18
19 fn screenshot(display : &glium::Display) {
20     let image: glium::texture::RawImage2d<u8> = display.read_front_buffer();
21     let image = image::ImageBuffer::from_raw(image.width, image.height, image.data.into_owned()).unwrap();
22     let image = image::DynamicImage::ImageRgba8(image).flipv();
23     let mut output = std::fs::File::create(&std::path::Path::new("screenshot.png")).unwrap();
24     image.save(&mut output, image::ImageFormat::PNG).unwrap();
25 }
26
27 fn gl_info(display : &glium::Display) {
28     let version = *display.get_opengl_version();
29     let api = match version {
30         glium::Version(glium::Api::Gl, _, _) => "OpenGL",
31         glium::Version(glium::Api::GlEs, _, _) => "OpenGL ES"
32     };
33     println!("{} context verson: {}", api, display.get_opengl_version_string());
34 }
35
36 #[cfg(target_os = "emscripten")]
37 #[allow(non_camel_case_types)]
38 type em_callback_func = unsafe extern fn();
39 #[cfg(target_os = "emscripten")]
40 extern {
41     fn emscripten_set_main_loop(func : em_callback_func, fps : c_int, simulate_infinite_loop : c_int);
42 }
43
44 #[cfg(target_os = "emscripten")]
45 thread_local!(static MAIN_LOOP_CALLBACK: std::cell::RefCell<*mut c_void> =
46               std::cell::RefCell::new(std::ptr::null_mut()));
47
48 #[cfg(target_os = "emscripten")]
49 pub fn set_main_loop_callback<F>(callback : F) where F : FnMut() -> support::Action {
50     MAIN_LOOP_CALLBACK.with(|log| {
51             *log.borrow_mut() = &callback as *const _ as *mut c_void;
52             });
53
54     unsafe { emscripten_set_main_loop(wrapper::<F>, 0, 1); }
55
56     unsafe extern "C" fn wrapper<F>() where F : FnMut() -> support::Action {
57         MAIN_LOOP_CALLBACK.with(|z| {
58             let closure = *z.borrow_mut() as *mut F;
59             (*closure)();
60         });
61     }
62 }
63
64 #[cfg(not(target_os = "emscripten"))]
65 pub fn set_main_loop_callback<F>(callback : F) where F : FnMut() -> support::Action {
66     support::start_loop(callback);
67 }
68
69 fn main() {
70     let _soundplayer = sound::start();
71
72     let display = glutin::WindowBuilder::new()
73         .with_dimensions(600, 600)
74         //.with_fullscreen(glutin::get_primary_monitor())
75         .with_depth_buffer(24)
76         .with_vsync()
77         .with_title(format!("MandelWow"))
78         .build_glium()
79         .unwrap();
80
81     gl_info(&display);
82
83     let mandelwow_program = mandelwow::program(&display);
84     let bounding_box_program = bounding_box::solid_fill_program(&display);
85
86     let mut camera = support::camera::CameraState::new();
87     let mut t: f32 = 0.0;
88     let mut pause = false;
89     let mut bounding_box_enabled = true;
90     let mut fullscreen = true;
91
92     // These are the bounds of the 3D Mandelwow section which we render in 3-space.
93     let bounds = Cube {
94         xmin: -2.0,
95         xmax:  0.7,
96         ymin: -1.0,
97         ymax:  1.0,
98         zmin: -1.1,
99         zmax:  1.1,
100     };
101     let mandelwow_bbox = bounding_box::BoundingBox::new(&display, &bounds, &bounding_box_program);
102
103     set_main_loop_callback(|| {
104         camera.update();
105
106         if !pause {
107             // Increment time
108             t += 0.01;
109         }
110
111         // Vary the wow factor to slice the Mandelwow along its 4th dimension.
112         let wmin = -0.8;
113         let wmax =  0.8;
114         let wsize = wmax - wmin;
115         let wow = (((t * 0.7).sin() + 1.0) / 2.0) * wsize + wmin;
116
117         //println!("t={} w={:?} camera={:?}", t, w, camera.get_pos());
118
119         let mut frame = display.draw();
120         frame.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
121
122         let rotation = Matrix4::from(
123             Euler { x: Rad(t.sin() / 3.), y: Rad(t.sin() / 2.), z: Rad(t / 1.5)});
124         let z_trans = -2.0;  // Send the model back a little bit so it fits the screen.
125         let model2 =
126             Matrix4::from_translation(Vector3::unit_z() * z_trans) * rotation;
127         let model = cgmath::conv::array4x4(model2);
128
129         // Draw the bounding box before the fractal, when the Z-buffer is still clear,
130         // so the lines behind the semi-translucent areas will be drawn.
131         if bounding_box_enabled {
132             let uniforms = uniform! {
133                 model: model,
134                 view:  camera.get_view(),
135                 perspective: camera.get_perspective(),
136             };
137             mandelwow_bbox.draw(&mut frame, &uniforms);
138         }
139
140         mandelwow::draw(&display, &mut frame, &mandelwow_program, model, &camera, &bounds, wow);
141         frame.finish().unwrap();
142
143         for ev in display.poll_events() {
144             match ev {
145                 glutin::Event::Closed |
146                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::Escape)) |
147                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::Q)) => {
148                     return support::Action::Stop
149                 },
150                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::B)) => {
151                     bounding_box_enabled ^= true;
152                 },
153                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::P)) => {
154                     pause ^= true;
155                 },
156                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::PageUp)) => {
157                     t += 0.01;
158                 },
159                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::PageDown)) => {
160                     t -= 0.01;
161                 },
162                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::F10)) => {
163                     screenshot(&display);
164                 },
165                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::F11)) => {
166                     fullscreen ^= true;
167                     if fullscreen {
168                         // Not implemented on Linux
169                         glutin::WindowBuilder::new()
170                             .with_fullscreen(glutin::get_primary_monitor())
171                             .with_depth_buffer(24)
172                             .rebuild_glium(&display).unwrap();
173                     } else {
174                         //glutin::WindowBuilder::new()
175                         //    .rebuild_glium(&display).unwrap();
176                     }
177                 },
178                 ev => camera.process_input(&ev),
179             }
180         }
181
182         support::Action::Continue
183     });
184 }