A pile of gross hacks, but works in a browser so it's all justified.
authorBernie Innocenti <bernie@codewiz.org>
Mon, 17 Apr 2017 02:18:05 +0000 (22:18 -0400)
committerBernie Innocenti <bernie@codewiz.org>
Mon, 17 Apr 2017 02:18:05 +0000 (22:18 -0400)
Cargo.toml
debug.sh [new file with mode: 0755]
index.html [new file with mode: 0644]
main.rs
mandelwow.frag
mandelwow.rs
mandelwow.vert
release.sh [new file with mode: 0755]
solid.frag
solid.vert
sound_emscripten.rs [new file with mode: 0644]

index 82a174ccf21bb4701ed22f75977f570d0f258bbc..37d847a9826943d31464e03090f991e7b75a9d77 100644 (file)
@@ -5,18 +5,29 @@ authors = ["Bernie Innocenti <bernie@codewiz.org>"]
 license = "GPL-3.0+"
 
 [profile.release]
-lto = true
-panic = 'abort'
+#lto = true
+#panic = 'abort'
 
 [dependencies]
 cgmath = "*"
-glium = "*"
+glium = "0.15.0"
 glutin = "*"
 genmesh = "0.4.1"
 image = "0.12.0"
 obj = { version = "0.5", features = ["usegenmesh"] }
+
+[target.'cfg(not(target_os = "emscripten"))'.dependencies]
 libxm = "1.0.0"
 sdl2 = "*"
+backtrace = "0.2.2"
+backtrace-sys = "0.1.10"
+
+[target.'cfg(target_os = "emscripten")'.dependencies]
+glutin = "0.6.1"
+backtrace = "0.2.3"
+
+[replace]
+"backtrace:0.2.3" = { git = "https://github.com/badboy/backtrace-rs", branch = "emscripten-fix" }
 
 [[bin]]
 name = "mandelwow"
diff --git a/debug.sh b/debug.sh
new file mode 100755 (executable)
index 0000000..27121aa
--- /dev/null
+++ b/debug.sh
@@ -0,0 +1,4 @@
+set -e
+cargo build --target asmjs-unknown-emscripten
+cp target/asmjs-unknown-emscripten/debug/mandelwow.js .
+emrun .
diff --git a/index.html b/index.html
new file mode 100644 (file)
index 0000000..00444fc
--- /dev/null
@@ -0,0 +1,190 @@
+<!doctype html>
+<html lang="en-us">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title>Emscripten-Generated Code</title>
+    <style>
+      body {
+        font-family: arial;
+        margin: 0;
+        padding: none;
+      }
+      .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
+      div.emscripten { text-align: center; }      
+      div.emscripten_border { border: 1px solid black; }
+      canvas.emscripten { width: 600px; height: 600px; }
+      /* the canvas *must not* have any border or padding, or mouse coords will be wrong */
+      canvas.emscripten { border: 0px none; }
+      #emscripten_logo {
+        display: inline-block;
+        margin: 0;
+      }
+      .spinner {
+        height: 30px;
+        width: 30px;
+        margin: 0;
+        margin-top: 20px;
+        margin-left: 20px;
+        display: inline-block;
+        vertical-align: top;
+        -webkit-animation: rotation .8s linear infinite;
+        -moz-animation: rotation .8s linear infinite;
+        -o-animation: rotation .8s linear infinite;
+        animation: rotation 0.8s linear infinite;
+        border-left: 5px solid rgb(235, 235, 235);
+        border-right: 5px solid rgb(235, 235, 235);
+        border-bottom: 5px solid rgb(235, 235, 235);
+        border-top: 5px solid rgb(120, 120, 120);
+        
+        border-radius: 100%;
+        background-color: rgb(189, 215, 46);
+      }
+      @-webkit-keyframes rotation {
+        from {-webkit-transform: rotate(0deg);}
+        to {-webkit-transform: rotate(360deg);}
+      }
+      @-moz-keyframes rotation {
+        from {-moz-transform: rotate(0deg);}
+        to {-moz-transform: rotate(360deg);}
+      }
+      @-o-keyframes rotation {
+        from {-o-transform: rotate(0deg);}
+        to {-o-transform: rotate(360deg);}
+      }
+      @keyframes rotation {
+        from {transform: rotate(0deg);}
+        to {transform: rotate(360deg);}
+      }
+      #status {
+        display: inline-block;
+        vertical-align: top;
+        margin-top: 30px;
+        margin-left: 20px;
+        font-weight: bold;
+        color: rgb(120, 120, 120);
+      }
+      #progress {
+        height: 20px;
+        width: 30px;
+      }
+      #controls {
+        display: inline-block;
+        float: right;
+        vertical-align: top;
+        margin-top: 30px;
+        margin-right: 20px;
+      }
+      #output {
+        width: 100%;
+        height: 200px;
+        margin: 0 auto;
+        margin-top: 10px;
+        display: block;
+        background-color: black;
+        color: white;
+        font-family: 'Lucida Console', Monaco, monospace;
+        outline: none;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="spinner" id='spinner'></div>
+    <div class="emscripten" id="status">Downloading...</div>
+
+<span id='controls'>
+  <span><input type="checkbox" id="resize">Resize canvas</span>
+  <span><input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer &nbsp;&nbsp;&nbsp;</span>
+  <span><input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked, 
+                                                                            document.getElementById('resize').checked)">
+  </span>
+</span>
+
+    <div class="emscripten">
+      <progress value="0" max="100" id="progress" hidden=1></progress>
+    </div>
+
+    
+    <div class="emscripten_border">
+      <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
+    </div>
+    <textarea id="output" rows="8"></textarea>
+
+    <script type='text/javascript'>
+      var statusElement = document.getElementById('status');
+      var progressElement = document.getElementById('progress');
+      var spinnerElement = document.getElementById('spinner');
+      var Module = {
+        preRun: [],
+        postRun: [],
+        print: (function() {
+          var element = document.getElementById('output');
+          if (element) element.value = ''; // clear browser cache
+          return function(text) {
+            if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+            // These replacements are necessary if you render to raw HTML
+            //text = text.replace(/&/g, "&amp;");
+            //text = text.replace(/</g, "&lt;");
+            //text = text.replace(/>/g, "&gt;");
+            //text = text.replace('\n', '<br>', 'g');
+            console.log(text);
+            if (element) {
+              element.value += text + "\n";
+              element.scrollTop = element.scrollHeight; // focus on bottom
+            }
+          };
+        })(),
+        printErr: function(text) {
+          if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+          if (0) { // XXX disabled for safety typeof dump == 'function') {
+            dump(text + '\n'); // fast, straight to the real console
+          } else {
+            console.error(text);
+          }
+        },
+        canvas: (function() {
+          var canvas = document.getElementById('canvas');
+          // As a default initial behavior, pop up an alert when webgl context is lost. To make your
+          // application robust, you may want to override this behavior before shipping!
+          // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+          canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
+          return canvas;
+        })(),
+        setStatus: function(text) {
+          if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
+          if (text === Module.setStatus.text) return;
+          var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
+          var now = Date.now();
+          if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon
+          if (m) {
+            text = m[1];
+            progressElement.value = parseInt(m[2])*100;
+            progressElement.max = parseInt(m[4])*100;
+            progressElement.hidden = false;
+            spinnerElement.hidden = false;
+          } else {
+            progressElement.value = null;
+            progressElement.max = null;
+            progressElement.hidden = true;
+            if (!text) spinnerElement.style.display = 'none';
+          }
+          statusElement.innerHTML = text;
+        },
+        totalDependencies: 0,
+        monitorRunDependencies: function(left) {
+          this.totalDependencies = Math.max(this.totalDependencies, left);
+          Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
+        }
+      };
+      Module.setStatus('Downloading...');
+      window.onerror = function(event) {
+        // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
+        Module.setStatus('Exception thrown, see JavaScript console');
+        spinnerElement.style.display = 'none';
+        Module.setStatus = function(text) {
+          if (text) Module.printErr('[post-exception status] ' + text);
+        };
+      };
+    </script>
+<script async type="text/javascript"  src="mandelwow.js">
+</script>
diff --git a/main.rs b/main.rs
index 12a127b9df1472c665730d69af6c1e9b322c2406..2d68c82622f6fb98b7caf70d89e0553e566b645f 100644 (file)
--- a/main.rs
+++ b/main.rs
@@ -1,11 +1,11 @@
-// Wow. Such fractal.
-
 extern crate cgmath;
 #[macro_use(uniform,program,implement_vertex)]
 extern crate glium;
 extern crate glutin;
 extern crate image;
+#[cfg(not(target_os = "emscripten"))]
 extern crate libxm;
+#[cfg(not(target_os = "emscripten"))]
 extern crate sdl2;
 
 //use cgmath::prelude::*;
@@ -15,10 +15,15 @@ use glium::{DisplayBuild, Surface};
 use glutin::ElementState::Pressed;
 use glutin::Event::KeyboardInput;
 use glutin::VirtualKeyCode;
+use std::os::raw::{c_int, c_void};
 
 mod bounding_box;
 mod cube;
 mod mandelwow;
+#[cfg(not(target_os = "emscripten"))]
+mod sound;
+#[cfg(target_os = "emscripten")]
+#[path = "sound_emscripten.rs"]
 mod sound;
 mod support;
 
@@ -30,12 +35,36 @@ fn screenshot(display : &glium::Display) {
     image.save(&mut output, image::ImageFormat::PNG).unwrap();
 }
 
+#[allow(non_camel_case_types)]
+type em_callback_func = unsafe extern fn();
+extern {
+    fn emscripten_set_main_loop(func : em_callback_func, fps : c_int, simulate_infinite_loop : c_int);
+}
+
+thread_local!(static MAIN_LOOP_CALLBACK: std::cell::RefCell<*mut c_void> =
+              std::cell::RefCell::new(std::ptr::null_mut()));
+
+pub fn set_main_loop_callback<F>(callback : F) where F : FnMut() {
+    MAIN_LOOP_CALLBACK.with(|log| {
+            *log.borrow_mut() = &callback as *const _ as *mut c_void;
+            });
+
+    unsafe { emscripten_set_main_loop(wrapper::<F>, 0, 1); }
+
+    unsafe extern "C" fn wrapper<F>() where F : FnMut() {
+        MAIN_LOOP_CALLBACK.with(|z| {
+            let closure = *z.borrow_mut() as *mut F;
+            (*closure)();
+        });
+    }
+}
+
 fn main() {
     let _soundplayer = sound::start();
 
     let display = glutin::WindowBuilder::new()
-        //.with_dimensions(1024, 768)
-        .with_fullscreen(glutin::get_primary_monitor())
+        .with_dimensions(1024, 768)
+        //.with_fullscreen(glutin::get_primary_monitor())
         .with_depth_buffer(24)
         .with_vsync()
         .with_title(format!("MandelWow"))
@@ -61,7 +90,8 @@ fn main() {
         zmax:  1.1,
     };
 
-    support::start_loop(|| {
+    //support::start_loop(|| {
+    set_main_loop_callback(|| {
         camera.update();
 
         if !pause {
@@ -106,7 +136,7 @@ fn main() {
                 glutin::Event::Closed |
                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::Escape)) |
                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::Q)) => {
-                    return support::Action::Stop
+                    //return support::Action::Stop
                 },
                 KeyboardInput(Pressed, _, Some(VirtualKeyCode::B)) => {
                     bounding_box_enabled ^= true;
@@ -140,7 +170,6 @@ fn main() {
             }
         }
 
-        support::Action::Continue
+        //support::Action::Continue
     });
-
 }
index 260e1bf784e984b2b4502003c0ad919f9a51e738..c2e4e2d728c2fb45b36b49c26bd1e56fc3583389 100644 (file)
@@ -1,28 +1,25 @@
-#version 140
+#version 100
 precision highp float;
-in vec2 c;
-in vec2 z;
-out vec4 f_color;
+varying vec2 c;
+varying vec2 z;
 
 void main() {
     float zx = z.x;
     float zy = z.y;
-    int maxiter = 64;
-    int iter = maxiter;
-    while (iter > 0) {
+    const int maxiter = 64;
+    for (int iter = maxiter; iter > 0; iter--) {
         float zx2 = zx * zx;
         float zy2 = zy * zy;
         if (zx2 * zy2 > 4.0) {
           float index = float(iter) / float(maxiter);
-          f_color = vec4(index, 0.1, 1.0 - index / 2, 0.8 - index);
+          gl_FragColor = vec4(index, 0.1, 1.0 - index / 2.0, 0.8 - index);
           return;
         }
         zy = zx * zy * 2.0 + c.y;
         zx = zx2 - zy2 + c.x;
-        iter -= 1;
     }
-    f_color = vec4((sin(z.y) + 1.0) / 4,
-                   (sin(z.x) + 1.0) / 4,
-                   (sin(c.x) + 1.0) / 4,
-                   1.0);
+    gl_FragColor = vec4((sin(z.y) + 1.0) / 4.0,
+                        (sin(z.x) + 1.0) / 4.0,
+                        (sin(c.x) + 1.0) / 4.0,
+                        1.0);
 }
index d3b86e6ee1aa0e49779386826dfee6797fc32bc9..8e759ef8013a1efc8943e0e78853063dca79a5fe 100644 (file)
@@ -30,7 +30,7 @@ fn mand(cx: f32, cy: f32) -> [f32; 3] {
 
 pub fn program(display: &glium::Display) -> glium::Program {
     return program!(display,
-        140 => {
+        100 => {
             vertex: include_str!("mandelwow.vert"),
             fragment: include_str!("mandelwow.frag"),
         }).unwrap();
index 9c3e5f83aad5318a368decb521a8a77fc017e3e4..0e67eda0dbe0978125073f164f24143c9409d779 100644 (file)
@@ -1,11 +1,11 @@
-#version 140
+#version 100
 uniform mat4 perspective;
 uniform mat4 view;
 uniform mat4 model;
 uniform vec2 z0;
-in vec3 position;
-out vec2 c;
-out vec2 z;
+attribute mediump vec3 position;
+varying mediump vec2 c;
+varying mediump vec2 z;
 
 void main() {
     mat4 modelview = view * model;
diff --git a/release.sh b/release.sh
new file mode 100755 (executable)
index 0000000..171d4f9
--- /dev/null
@@ -0,0 +1,4 @@
+set -e
+cargo build --target asmjs-unknown-emscripten --release
+cp target/asmjs-unknown-emscripten/release/mandelwow.js .
+emrun .
index 7d1a7c771d1dcc250138f098f82faa206833288f..c7e5a5156d218c0460332fe2d1b2e2def9ee8c21 100644 (file)
@@ -1,7 +1,5 @@
-#version 140
-
-out vec4 color;
+#version 100
 
 void main() {
-    color = vec4(1.0, 1.0, 1.0, 1.0);
+    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
 }
index aaacaa93b0691b8ab3bf4d93ee7fcd820b71bf4f..e66723f66ab3934681ccae6224602e1dd5ec3b05 100644 (file)
@@ -1,5 +1,5 @@
-#version 140
-in vec3 position;
+#version 100
+attribute vec3 position;
 uniform mat4 perspective;
 uniform mat4 view;
 uniform mat4 model;
diff --git a/sound_emscripten.rs b/sound_emscripten.rs
new file mode 100644 (file)
index 0000000..2b3d2a3
--- /dev/null
@@ -0,0 +1,3 @@
+pub struct SoundPlayer {}
+
+pub fn start() -> SoundPlayer { SoundPlayer {} }