1 /* -*- mode: javascript; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 /** 3 * @file Main executable script; this should be appended to the very 4 * end of the executable code (preferrably using automatic tools). 5 * 6 * This uses variables defined in {@link library.js} which should be 7 * automatically added <strong>before</strong> the place where your 8 * own creative code goes. 9 */ 10 11 /** 12 * This is the event loop that happens on every frame. 13 * 14 * Assumes prior initialization of C (canvas), audio. 15 * 16 * Assumes prior initialization of gl and prg. 17 * 18 * Debug mode needs initialization of dbg_xxx 19 * 20 */ 21 var startTimeInMillis = null; 22 var loopfunc = function(curTimeInMillis) 23 { 24 try //DEBUG 25 { //DEBUG 26 // Time of this frame; fix beginning upon first entry. 27 if (!startTimeInMillis) startTimeInMillis = curTimeInMillis; 28 var t = (curTimeInMillis - startTimeInMillis) * songBeatsPerMinute / 60000; 29 // Update canvas size (window object is implicit; need no "window.X") 30 var w = innerWidth, h = innerHeight; 31 if (w != Cw || h != Ch) { 32 gl.viewport(0, 0, Cw=C.width=w, Ch=C.height=h); 33 } 34 35 /* In debug mode, show window size, aspect ratio, and time. */ 36 /* Omit info if the URL ends in '#'. Use for tidy screenshots... */ 37 dbg_url=window.location.href; //DEBUG 38 if (dbg_url.substring(dbg_url.length -1)!='#'){ //DEBUG 39 dbg_show_aspect.nodeValue="Size: "+w+"x"+h+" "+w/h; //DEBUG 40 dbg_show_time.nodeValue=" time=" + "?" /*(audio.currentTime|0)*/ //DEBUG 41 + "(beat " +(t|0)+ ")"; //DEBUG 42 } //DEBUG 43 44 // Update the HTML and CSS parts, if you wanna have something like text.. 45 updateDocument(t); 46 47 // Re-build the scenegraph for every frame (can animate): 48 var sceneroot = buildSceneAtTime(t); 49 50 // Scene is built. Then we actually draw it. 51 // Clear buffer 52 gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); 53 gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT); 54 55 // Transfer perspective matrix to shader: 56 var persmat = perspectiveFhc(5,w/h); 57 gl.uniformMatrix4fv(gl.getUniformLocation(prg,"p"), false, persmat); 58 59 // TODO: Could switch the shader based on time / object properties: 60 gl.useProgram(prg); 61 62 // Then we display the scenegraph 63 findcam_wi(sceneroot,rotX_wi(0)); 64 65 // Set light position (very naive as of yet): 66 // TODO: Multiple lights, included in the scenegraph as "Light objects". 67 // As of now, we have to hardcode light position separately from the scene transforms: 68 /* FIXME: 69 gl.uniform4fv(gl.getUniformLocation(prg, "l"), 70 matmul4(cameraTransformation,[1,.1,1.5,1])); 71 */ 72 gl.uniform4fv(gl.getUniformLocation(prg, "l"), 73 defaultLightDirection); 74 75 traverse_wi(sceneroot,cameraTransformation); 76 requestAnimationFrame(loopfunc); 77 78 } //DEBUG 79 catch (err) //DEBUG 80 { //DEBUG 81 alert("Error: " + err.message); //DEBUG 82 } //DEBUG 83 }; 84 85 86 87 /*----------------------------------------------------------------- 88 This is what actually gets executed on loading the page! 89 -------------------------------------------------------------------*/ 90 91 try //DEBUG 92 { //DEBUG 93 // NOTE: In debug mode, should probably adhere to 94 // https://www.khronos.org/webgl/wiki/FAQ ... 95 _document.body.appendChild(C = _document.createElement("canvas")); 96 s = C.style; s.position = "fixed"; s.left = s.top = 0; 97 //s.cursor='none'; 98 gl = C.getContext('experimental-webgl'); 99 if (!gl){ //DEBUG 100 alert("This demo requires WebGL"); //DEBUG 101 return; //DEBUG 102 } //DEBUG 103 //DEBUG 104 // Debug print of location and aspect //DEBUG 105 var dbg_show_aspect=document.createTextNode(""); //DEBUG 106 var dbgInfoDiv=document.createElement("div"); //DEBUG 107 document.body.appendChild(dbgInfoDiv); //DEBUG 108 dbgInfoDiv.style.position = "fixed"; //DEBUG 109 dbgInfoDiv.style.right = 10; //DEBUG 110 dbgInfoDiv.style.bottom = 10; //DEBUG 111 dbgInfoDiv.style.color = "#cde"; //DEBUG 112 dbgInfoDiv.appendChild(dbg_show_aspect); //DEBUG 113 var dbg_show_time=document.createTextNode(""); //DEBUG 114 dbgInfoDiv.appendChild(dbg_show_time); //DEBUG 115 //DEBUG 116 // Debug version seek, play and pause //DEBUG 117 C.addEventListener("click", function(e){ //DEBUG 118 audio.currentTime = //DEBUG 119 e.pageX/C.width*audio.duration; //DEBUG 120 if (e.pageY<(C.height/2))audio.pause(); //DEBUG 121 else audio.play(); //DEBUG 122 for(var t=(audio.currentTime*( //DEBUG 123 songBeatsPerMinute/60))|0;t>=0;t--){ //DEBUG 124 } //DEBUG 125 }); //DEBUG 126 127 128 // Apply p01's trick for grabbing short names from GL obj 129 // (http://slides.com/pdesch/js-demoscene-techniques#/5/6) 130 // This didn't help me earlier when trying to make a 1k.. 131 // is OK now for 4k. saves something like 30 bytes / 4kb. 132 // Not much.. The trick itself costs some 45 bytes compressed. 133 134 for(s in gl){ 135 //For creating a sed script to change original names: 136 //var shortname = k.match(/^..|[A-Z]|\d\D+$/g).join(''); 137 //console.log("s/gl\\."+k+"(/gl."+shortname+"(/g"); 138 gl[s.match(/^..|[A-Z]|\d\D+$/g).join('')]=gl[s]; 139 140 //The following is bloaty, but works with "use strict": 141 //gl[k.match(/^[a-z].|[A-Z](?=[a-z])|\d\D+$/g).join('')]=gl[k]; 142 } 143 144 gl.enable(gl.DEPTH_TEST); 145 gl.enable(gl.CULL_FACE); // Performance opt., costs 6 bytes. 146 147 // Initializations seem to pack a bit better inlined. 148 // prg = gl.createProgram(); 149 // TODO: Multiple, switchable shaders! 150 151 // Reuse the variable name "s" 152 s = gl.createShader(gl.VERTEX_SHADER); 153 gl.shaderSource(s, vertex_shader_minimal_with_normalmatrix); 154 //gl.shaderSource(s, test_vert); 155 gl.compileShader(s); 156 if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) //DEBUG 157 alert("Vertex shader: "+ gl.getShaderInfoLog(s)); //DEBUG 158 gl.attachShader(prg = gl.createProgram(), s); 159 160 s = gl.createShader(gl.FRAGMENT_SHADER); 161 gl.shaderSource(s, fragment_shader_camspace_directed_with_white_fog); 162 //gl.shaderSource(s, fragment_shader_camspace_directed); 163 //gl.shaderSource(s, fragment_shader_pointlight_cameraspace); 164 //gl.shaderSource(s, noisz_frag); 165 gl.compileShader(s); 166 if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) //DEBUG 167 alert("Fragment shader: "+ gl.getShaderInfoLog(s)); //DEBUG 168 gl.attachShader(prg, s); 169 170 gl.linkProgram(prg); 171 if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) //DEBUG 172 alert("Link program: "+ gl.getProgramInfoLog(prg)); //DEBUG 173 174 // Initialization code is now separated production-wise. Easier to find for editing. 175 initAssets(); 176 // Things like scolltext could initialized in this call, also on product side: 177 initDocument(); 178 179 180 /* Initialize song. */ 181 var audio,player = new CPlayer(); 182 player.init(song); 183 while (player.generate() < 1){}; 184 185 audio_time=0; 186 187 /** The onaudioprocess handler. This gets called, technically, for audio output. */ 188 function audioHandler(event){ 189 player.cpy(audio_time, 4096, event.outputBuffer.getChannelData(0)); 190 audio_time += 4096; 191 }; 192 193 194 /* Nowadays, browsers allow media only upon user event, so click pls: */ 195 _document.onclick=()=> 196 { 197 /* In debug mode I want to control the fullscreen myself, so iffalse..*/ 198 if (false) //DEBUG 199 C.style.cursor='none'; 200 if (false) //DEBUG 201 C.requestFullscreen(); 202 audioctx = new AudioContext; 203 sp = audioctx.createScriptProcessor(4096, 0, 1); 204 sp.connect(audioctx.destination); 205 sp.onaudioprocess = audioHandler; 206 requestAnimationFrame(loopfunc); 207 208 /* In debug mode I want to be able to seek, so reset callback : */ 209 /* FIXME: Will be fixed during Instanssi, if there is a time window for repair 210 C.onclick=function(e){ //DEBUG 211 audio.currentTime = //DEBUG 212 e.pageX/C.width*audio.duration; //DEBUG 213 if (e.pageY<(C.height/2))audio.pause(); //DEBUG 214 else audio.play(); //DEBUG 215 for(var t=(audio.currentTime*( //DEBUG 216 songBeatsPerMinute/60))|0;t>=0;t--){ //DEBUG 217 } //DEBUG 218 }; //DEBUG 219 */ 220 }; 221 //C.fillText("Please click", w/2, h/2); 222 //document.getElementById("body").innerHTML="<a>Please click to start</a>"; 223 224 } //DEBUG 225 catch (err) //DEBUG 226 { //DEBUG 227 alert("Error initializing: " + err.message); //DEBUG 228 } //DEBUG 229