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