pre-refactor
[NgWiz.git] / game / src / DstEng.js
1 /* globals $ */
2
3 var timeScale = 1;
4
5 function DstEng(config) {
6     this.accTs = 0;
7     this.res = {};
8
9     this.state = {
10         timeDelta: 0,
11         time: 0.0,
12         lastTime: 0,
13         frame: 0,
14         virtRes: [320, 240],
15         pointer: {
16             pos: [0, 0],
17             down: false
18         }
19     };
20
21     this.drawFrameRequestId = null;
22     this.pauseState = false;
23
24     var ok = true;
25     this.cfg = config;
26
27     this.log("DstEng Startup:", this.cfg);
28
29     this.cfg.screen.width = this.cfg.width;
30     this.cfg.screen.height = this.cfg.height;
31
32     if (this.initGl()) {
33         this.log("Configured gl rendering");
34     } else {
35         this.log("Fail, no rendering available");
36         ok = false;
37     }
38
39     if (ok) {
40         this.preLoad();
41     } else {
42         this.cfg.readyCallback(false);
43     }
44
45 }
46
47 DstEng.prototype.log = function() {
48     console.log.apply(console, arguments);
49 };
50
51 DstEng.prototype.initGl = function() {
52     try {
53         this.gl = this.cfg.screen.getContext("webgl");
54         if(! this.gl ) { 
55             this.gl = this.cfg.screen.getContext("experimental-webgl");
56         }
57         this.gl.viewportWidth = this.cfg.screen.width;
58         this.gl.viewportHeight = this.cfg.screen.height;
59   
60   
61   //http://mrdoob.github.io/webgl-blendfunctions/blendfunc.html      
62 /*        var gl = this.gl;
63  
64 gl.enable( gl.BLEND );
65 gl.blendEquation( gl.FUNC_ADD );
66 gl.blendFunc( gl.SRC_ALPHA, gl.DST_ALPHA ); */
67     } catch (e) {}
68
69     return !!this.gl;
70 };
71
72
73
74 DstEng.prototype.isPowerOf2 = function(value) {
75     return (value & (value - 1)) === 0;
76 };
77
78 DstEng.prototype.setupTextureFiltering = function(width, height) {
79     if (this.isPowerOf2(width) && this.isPowerOf2(height)) {
80         // the dimensions are power of 2 so generate mips and turn on 
81         // tri-linear filtering.
82         this.gl.generateMipmap(this.gl.TEXTURE_2D);
83         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR_MIPMAP_LINEAR);
84         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST_MIPMAP_NEAREST);
85     } else {
86         // at least one of the dimensions is not a power of 2 so set the filtering
87         // so WebGL will render it.
88         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
89         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
90         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
91         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
92
93
94     }
95 };
96
97
98 DstEng.prototype.imgLoader = function(obj, cb) {
99
100     var image = new Image();
101     image.src = obj.url;
102     image.addEventListener('load', function() {
103
104         var texture = this.gl.createTexture();
105         this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
106         // Fill the texture with a 1x1 blue pixel.
107         this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, 1, 1, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE,
108             new Uint8Array([0, 0, 255, 255]));
109         // Asynchronously load an image
110
111         this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
112         this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image);
113
114         this.setupTextureFiltering(image.width, image.height);
115
116         cb({
117             valid: true,
118             resType: 'img',
119             image: image,
120             texture: texture
121         });
122     }.bind(this));
123     image.addEventListener('error', function(e) {
124         cb({
125             valid: false,
126             error: 'Could not load the image'
127         });
128     });
129 };
130
131 DstEng.prototype.compileShader = function(gl, shaderSource, shaderType) {
132     // Create the shader object
133     var obj = {
134         valid: true
135     };
136     var shader = gl.createShader(shaderType);
137
138
139     // Set the shader source code.
140     gl.shaderSource(shader, shaderSource);
141
142     // Compile the shader
143     gl.compileShader(shader);
144
145     // Check if it compiled
146     var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
147     if (!success) {
148         // Something went wrong during compilation; get the error
149         obj.valid = false;
150         obj.error = 'Compilation failed: ' + gl.getShaderInfoLog(shader);
151
152     } else {
153         obj.shaderType = shaderType;
154         obj.resType = (shaderType === gl.FRAGMENT_SHADER) ? 'fragment_shader' : 'vertex_shader';
155         obj.shader = shader;
156     }
157
158     return obj;
159 };
160
161 DstEng.prototype.createProgram = function(gl, vertexShader, fragmentShader) {
162     // create a program.
163     var program = gl.createProgram();
164
165     // attach the shaders.
166     gl.attachShader(program, vertexShader);
167     gl.attachShader(program, fragmentShader);
168
169     // link the program.
170     gl.linkProgram(program);
171
172     // Check if it linked.
173     var success = gl.getProgramParameter(program, gl.LINK_STATUS);
174     if (!success) {
175         // something went wrong with the link
176         throw ("program failed to link:" + gl.getProgramInfoLog(program));
177     }
178
179     var resObj = {
180         resType: 'shader_program',
181         program: program,
182         a_texCoord: gl.getAttribLocation(program, "a_texCoord"),
183         a_pos: gl.getAttribLocation(program, "a_position"),
184         u_frameSize: gl.getUniformLocation(program, "u_frameSize"),
185         u_frameGridPos: gl.getUniformLocation(program, "u_frameGridPos"),
186         u_res: gl.getUniformLocation(program, "u_resolution"),
187         u_translation: gl.getUniformLocation(program, "u_translation")
188     };
189
190     //Enable textures and verticies for out programs
191     gl.enableVertexAttribArray(resObj.a_pos);
192     gl.enableVertexAttribArray(resObj.a_texCoord);
193
194     //Set default resolution
195     gl.useProgram(program);
196     gl.uniform2fv(resObj.u_res, this.state.virtRes);
197
198     window.onfocus = this.unpause.bind(this);
199     window.onblur = this.pause.bind(this);
200
201     return resObj;
202 };
203
204
205 DstEng.prototype.shaderLoader = function(obj, cb) {
206     $.ajax({
207         url: obj.url,
208         dataType: 'text',
209         success: function(data) {
210             cb(this.compileShader(this.gl, data, obj.shaderType));
211         }.bind(this),
212         error: function(e) {
213             cb({
214                 valid: false,
215                 error: e.status + ' ' + e.statusText
216             });
217         }
218     });
219 };
220
221
222 DstEng.prototype.resLoader = function(loadList, done) {
223     var left = loadList.length;
224     var loaded = 0;
225
226     loadList.forEach(function(r) {
227         r.loader(r, function(data) {
228             if (data) {
229                 this.addRes(r.name, data);
230                 loaded++;
231             }
232             left--;
233
234             if (!data.valid) {
235                 this.log('Resource loading failed for "' + r.name + '" from "' + r.url + '" error:', data.error);
236             }
237             if (left === 0) {
238                 done((loaded != loadList.length));
239             }
240         }.bind(this));
241     }, this);
242 };
243
244 DstEng.prototype.sndLoader = function(obj, cb) {
245
246     var request = new XMLHttpRequest();
247     request.open('GET', obj.url, true);
248     request.responseType = 'arraybuffer';
249
250     // Decode asynchronously
251     request.onload = function() {
252
253         this.audio.ctx.decodeAudioData(request.response, function(buffer) {
254             var snd = {
255                 valid: true,
256                 resType: 'snd',
257                 play: this.audio.play.bind(this.audio, buffer)
258             };
259
260             console.log(snd);
261             cb(snd);
262
263         }.bind(this));
264
265     }.bind(this);
266
267     request.send();
268
269 };
270
271 DstEng.prototype.jsonLoader = function(obj, cb) {
272     $.ajax({
273         url: obj.url,
274         dataType: 'text',
275         success: function(data) {
276
277             var ret = false;
278
279             try {
280                 ret = JSON.parse(data);
281             } catch (e) {
282                 ret = false;
283             }
284             cb({
285                 valid: !!(ret),
286                 value: ret
287             });
288         }.bind(this),
289         error: function(e) {
290             cb({
291                 valid: false,
292                 error: e.status + ' ' + e.statusText
293             });
294         }
295     });
296 };
297
298 DstEng.prototype.parseIni = function(txt) {
299
300     var ret = {};
301
302     var lines = txt.split('\n');
303
304     var trimmed = [];
305
306     var curSegment = '_root';
307
308     lines.forEach(function(l) {
309         //Strip comments
310         var idx = l.indexOf('#');
311         if (idx !== -1) {
312             l = l.substring(0, idx);
313         }
314
315         //Trim trailing whitespace
316         l = l.trim();
317
318         if (l.length > 0) {
319             trimmed.push(l);
320         }
321     });
322
323
324     trimmed.forEach(function(l) {
325         //Is this line an assignment?
326         var eqIdx = l.indexOf('=');
327         if (eqIdx !== -1) {
328             var spl = l.split('=');
329             var keyName = spl[0];
330             var val = spl.slice(1).join('=');
331
332             if (!ret[curSegment]) {
333                 ret[curSegment] = {};
334             }
335
336             if (!ret[curSegment][keyName]) {
337                 ret[curSegment][keyName] = val;
338             } else {
339                 if (!Array.isArray(ret[curSegment][keyName])) {
340                     ret[curSegment][keyName] = [ret[curSegment][keyName]];
341                 }
342                 ret[curSegment][keyName].push(val);
343             }
344         } else if (l.charAt(0) === '[' && l.charAt(l.length - 1) === ']') {
345             curSegment = l.substring(1, l.length - 1);
346         } else {
347             if (!ret[curSegment]) {
348                 ret[curSegment] = {
349                     _block: []
350                 };
351             }
352
353             ret[curSegment]._block.push(l);
354         }
355     });
356
357     return (ret);
358 };
359
360 DstEng.prototype.iniLoader = function(obj, cb) {
361     $.ajax({
362         url: obj.url,
363         dataType: 'text',
364         success: function(data) {
365
366             var ret = this.parseIni(data);
367
368             cb({
369                 valid: !!(ret),
370                 value: ret
371             });
372         }.bind(this),
373         error: function(e) {
374             cb({
375                 valid: false,
376                 error: e.status + ' ' + e.statusText
377             });
378         }
379     });
380 };
381
382 DstEng.prototype.audio = (function() {
383     function Aud() {
384
385         window.AudioContext = window.AudioContext || window.webkitAudioContext;
386         this.ctx = new AudioContext();
387     }
388
389     Aud.prototype.play = function(buffer) {
390         var source = this.ctx.createBufferSource();
391         source.buffer = buffer;
392         source.connect(this.ctx.destination);
393         source.start(0);
394     };
395
396     return new Aud();
397 }());
398
399
400
401 DstEng.prototype.preLoad = function() {
402     var res = [{
403             name: 'img_DstEng',
404             url: '/data/img/DstEng.jpg',
405             loader: this.imgLoader.bind(this)
406         }, {
407             name: 'img_tiles',
408             url: '/data/packs/000_wizznic/themes/thor/tiles/thor.png',
409             loader: this.imgLoader.bind(this)
410         }, {
411             name: 'snd_click',
412             url: '/data/packs/000_wizznic/themes/oldskool/snd/click.ogg',
413             loader: this.sndLoader.bind(this)
414         }, {
415             name: 'structure_test',
416             url: '/data/test.json',
417             loader: this.jsonLoader.bind(this)
418         }, {
419             name: 'ini_test',
420             url: '/data/packs/000_wizznic/info.ini',
421             loader: this.iniLoader.bind(this)
422         }, {
423             name: 'shader_vertex2d',
424             url: '/data/gl/2d_vertex.js',
425             shaderType: this.gl.VERTEX_SHADER,
426             loader: this.shaderLoader.bind(this)
427         }, {
428             name: 'shader_fragment2d',
429             url: '/data/gl/2d_fragment.js',
430             shaderType: this.gl.FRAGMENT_SHADER,
431             loader: this.shaderLoader.bind(this)
432         }, {
433             name: 'shader_fragment2d_sat',
434             url: '/data/gl/2d_fragment_sat.js',
435             shaderType: this.gl.FRAGMENT_SHADER,
436             loader: this.shaderLoader.bind(this)
437         }
438
439     ];
440
441     this.resLoader(res, function(err) {
442         if (!err) {
443             this.startLoader();
444         } else {
445             this.cfg.readyCallback(false);
446         }
447     }.bind(this));
448
449
450 };
451
452 DstEng.prototype.realCoordsToGl = function(evt) {
453     var r = this.cfg.screen.getBoundingClientRect();
454     var sx = this.state.virtRes[0] / this.cfg.screen.width;
455     var sy = this.state.virtRes[1] / this.cfg.screen.height;
456     return ([(evt.clientX - r.left) * sx, (evt.clientY - r.top) * sy]);
457 };
458
459 DstEng.prototype.setPointerState = function(e) {
460     this.state.pointer.pos = this.realCoordsToGl(e);
461 };
462
463
464 DstEng.prototype.onMouseMove = function(cb) {
465     this.cfg.screen.addEventListener('mousemove', function(e) {
466         this.setPointerState(e);
467         cb(this.state.pointer);
468     }.bind(this));
469 };
470
471 DstEng.prototype.pause = function() {
472     this.pauseState = true;
473
474     if (this.drawFrameRequestId) {
475         window.cancelAnimationFrame(this.drawFrameRequestId);
476     }
477 };
478
479 DstEng.prototype.unpause = function() {
480     if (this.pauseState) {
481         this.drawFrame();
482         this.pauseState = false;
483     }
484 };
485
486 DstEng.prototype.onMouseClick = function(cb) {
487     this.cfg.screen.addEventListener('mousedown', function(e) {
488         this.setPointerState(e);
489         this.state.pointer.down = true;
490         cb(this.state.pointer);
491     }.bind(this));
492
493     this.cfg.screen.addEventListener('mouseup', function(e) {
494         this.setPointerState(e);
495         this.state.pointer.down = false;
496         cb(this.state.pointer);
497     }.bind(this));
498 };
499
500
501 DstEng.prototype.addRes = function(name, resource) {
502
503     if (this.res.hasOwnProperty(name)) {
504         this.log('Note: Overriding existing resource ' + name);
505         this.log('Ow: ', this.res[name]);
506         this.log('By: ', resource);
507
508     }
509     if (resource.type) {
510         this.log('No type defined for ' + name);
511     }
512
513     this.res[name] = resource;
514
515     return (resource);
516 };
517
518 DstEng.prototype.startLoader = function() {
519
520     this.addRes('glPrg', this.createProgram(this.gl, this.res.shader_vertex2d.shader, this.res.shader_fragment2d.shader));
521     this.addRes('glPrgSat', this.createProgram(this.gl, this.res.shader_vertex2d.shader, this.res.shader_fragment2d_sat.shader));
522
523     var texture = new this.Texture(this.gl, {
524         img: this.res.img_DstEng,
525         cut: [0, 0, 160, 120],
526         numFrames: 1,
527         animate: false,
528         frameTime: 0
529     });
530
531
532     var drawGroup = new this.DrawGroup(this.gl, this.res.glPrg);
533
534     console.log(this.res);
535
536
537     var w = 320,
538         h = 240;
539
540     var polyBuffer = this.gl.createBuffer();
541     this.gl.bindBuffer(this.gl.ARRAY_BUFFER, polyBuffer);
542     this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
543         0, 0,
544         0.0, 0.0,
545         w, 0,
546         1.0, 0.0,
547         0, h,
548         0.0, 1.0,
549         0, h,
550         0.0, 1.0,
551         w, 0,
552         1.0, 0.0,
553         w, h,
554         1.0, 1.0
555     ]), this.gl.STATIC_DRAW);
556
557     var brick = this.gl.createBuffer();
558     this.gl.bindBuffer(this.gl.ARRAY_BUFFER, brick);
559     w = h = 20;
560     var glw = 20 / 480;
561     this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
562         0, 0,
563         0.0, 0.0,
564         w, 0,
565         glw, 0.0,
566         0, h,
567         0.0, 1.0,
568         0, h,
569         0.0, 1.0,
570         w, 0,
571         glw, 0.0,
572         w, h,
573         glw, 1.0
574     ]), this.gl.STATIC_DRAW);
575
576     var bricks = new this.PolyGroup(brick);
577     var brickTex = new this.Texture(this.gl, {
578         img: this.res.img_tiles,
579         cut: [0, 0, 20, 20],
580         numFrames: 10,
581         frameTime: 100,
582         animate: true
583     });
584
585
586
587     var brickTexGroup = new this.TexGroup(brickTex);
588
589     var sprite;
590
591     var snd = this.res.snd_click;
592
593     this.Sprite.prototype.animate = function() {
594         this.pos[0] += this.vx * timeScale;
595         this.pos[1] += this.vy * timeScale;
596
597         if (this.pos[0] < 0) {
598             this.pos[0] = 0;
599             this.vx *= -1;
600             //   snd.play();
601             this.frameGridPos[0]++;
602             if (this.frameGridPos[0] > 9) {
603                 this.frameGridPos[0] = 0;
604             }
605         } else if (this.pos[0] > 320 - 20) {
606             this.pos[0] = 320 - 20;
607             this.vx *= -1;
608             //            this.frameGridPos = [Math.floor(Math.random() * 10), 0];
609             this.frameGridPos[0]++;
610             if (this.frameGridPos[0] > 9) {
611                 this.frameGridPos[0] = 0;
612             }
613         }
614
615         if (this.pos[1] < 0) {
616             this.frameGridPos[0]++;
617             if (this.frameGridPos[0] > 9) {
618                 this.frameGridPos[0] = 0;
619             }
620             //          this.frameGridPos = [Math.floor(Math.random() * 10), 0];
621             this.pos[1] = 0;
622             this.vy *= -1;
623         } else if (this.pos[1] > 240 - 20) {
624             //        this.frameGridPos = [Math.floor(Math.random() * 10), 0];
625             this.frameGridPos[0]++;
626             if (this.frameGridPos[0] > 9) {
627                 this.frameGridPos[0] = 0;
628             }
629             this.pos[1] = 240 - 20;
630             this.vy *= -1;
631         }
632
633     };
634
635     for (var i = 0; i < 10; ++i) {
636         sprite = new this.Sprite([300, 220]);
637         sprite.frameGridPos = [Math.floor(Math.random() * 15), 0];
638         sprite.vx = Math.random() * 0.5 - 0.25;
639         sprite.vy = Math.random() * 0.5 - 0.25;
640
641         // sprite.animate = animate.bind(sprite);
642         brickTexGroup.addSprite(sprite);
643     }
644
645     bricks.addTexGroup(brickTexGroup);
646
647     var backgrounds = new this.PolyGroup(polyBuffer);
648
649     drawGroup.addPolyGroup(backgrounds);
650
651     drawGroup.addPolyGroup(bricks);
652
653     var bgTexSingle = new this.TexGroup(texture);
654
655
656     //Add an empty s
657     bgTexSingle.addSprite(new this.Sprite());
658
659
660     backgrounds.addTexGroup(bgTexSingle);
661
662
663     this.dg = drawGroup;
664
665
666     this.drawFrame();
667     //Say we are ready
668     this.cfg.readyCallback(true);
669 };
670
671
672 //TODO: Rewrite to use vertex arrays for texture coords instead.
673 DstEng.prototype.Texture = (function() {
674     function Tex(gl, setup) {
675
676         if (!gl) {
677             throw Error('Need gl');
678         }
679
680         if (!setup.img.resType) {
681             throw Error('Required: img.');
682         }
683
684         if (!setup.img.image || !setup.img.image.height) {
685             throw Error('Invalid or no image in setup.img');
686         }
687
688         this.w = setup.img.image.width;
689         this.h = setup.img.image.height;
690
691         this.numFrames = setup.numFrames || 1;
692         this.animate = setup.animate || false;
693         this.frameTime = setup.frameTime || false;
694
695         this.pxCut = setup.cut || [0, 0, this.w, this.h];
696
697         this.glTex = setup.img.texture;
698
699         this.glFrameSize = new Float32Array([(this.pxCut[2] / this.w), (this.pxCut[3] / this.h)]);
700
701     }
702
703     return Tex;
704 }());
705
706 DstEng.prototype.DrawGroup = (function() {
707     function Dg(gl, prgInfo) {
708         this.prgInfo = prgInfo;
709         this.polyGroups = [];
710     }
711
712     Dg.prototype.addPolyGroup = function(pg) {
713         this.polyGroups.push(pg);
714     };
715
716     Dg.prototype.draw = function(gl) {
717
718         //Program is same for all drawGroups
719         var prgInfo = this.prgInfo;
720         gl.useProgram(prgInfo.program);
721
722         this.polyGroups.forEach(function drawPolyGroup(pg) {
723
724             //VertexBuffer is the same for a polygroup
725             gl.bindBuffer(gl.ARRAY_BUFFER, pg.buffer);
726             gl.vertexAttribPointer(prgInfo.a_pos, 2, gl.FLOAT, false, pg.stride, pg.vertexOffset);
727             gl.vertexAttribPointer(prgInfo.a_texCoord, 2, gl.FLOAT, false, pg.stride, pg.texCoordOffset);
728
729             pg.texGroups.forEach(function drawTexGroup(tg) {
730                 //Texture is the same for each texGroup
731                 gl.bindTexture(gl.TEXTURE_2D, tg.texture.glTex);
732
733                 gl.uniform2fv(prgInfo.u_frameSize, tg.texture.glFrameSize);
734                 tg.sprites.forEach(function drawSprite(spr) {
735                     //Set geometry location and texture location
736                     if (spr.vx) spr.animate();
737
738                     gl.uniform2fv(prgInfo.u_translation, spr.pos);
739                     gl.uniform2fv(prgInfo.u_frameGridPos, spr.frameGridPos);
740
741
742
743                     gl.drawArrays(gl.TRIANGLES, 0, 6);
744                 });
745             });
746         });
747     };
748
749     return Dg;
750 }());
751
752 DstEng.prototype.PolyGroup = (function() {
753     function Pg(buffer) {
754         this.buffer = buffer;
755         this.vertexOffset = 0;
756         this.texCoordOffset = Float32Array.BYTES_PER_ELEMENT * 2;
757         this.stride = Float32Array.BYTES_PER_ELEMENT * 4;
758         this.texGroups = [];
759     }
760
761     Pg.prototype.addTexGroup = function(tg) {
762         this.texGroups.push(tg);
763     };
764
765     return Pg;
766 }());
767
768 DstEng.prototype.TexGroup = (function() {
769     function Tg(texture) {
770         this.texture = texture;
771         this.sprites = [];
772     }
773
774     Tg.prototype.addSprite = function(spr) {
775         this.sprites.push(spr);
776     };
777
778     return Tg;
779 }());
780
781
782 DstEng.prototype.Sprite = (function() {
783     function Spr() {
784         this.pos = new Float32Array([0, 0]);
785         this.frameGridPos = [0, 0];
786     }
787
788     Spr.prototype.setPos = function(pos) {
789         this.pos = pos;
790     };
791
792     return Spr;
793 }());
794
795
796
797 DstEng.prototype.drawFrame = function(timeStamp) {
798
799     if (timeStamp) {
800         this.state.timeDelta = timeStamp - this.state.lastTime;
801         this.state.lastTime = timeStamp;
802     } else {
803         this.state.timeDelta = 16.0;
804         this.state.lastTime = performance.now();
805     }
806
807
808     timeScale = this.state.timeDelta / 16.00;
809     this.accTs += timeScale;
810
811
812     this.state.frame++;
813     this.state.time += this.state.timeDelta;
814
815
816     if (timeScale > 10) {
817         console.log('warning, timescale is large (' + timeScale + ') clipping to 10 frames');
818         timeScale = 10;
819     } else if (timeScale < 0.25) {
820         console.log('warning, timescale is very small (' + timeScale + ') clipping to 0.25 frames');
821         timeScale = 0.25;
822     }
823
824
825     /*    if (this.state.frame % 60 === 0) {
826             this.state.fps = 1000 / this.state.timeDelta;
827             console.log(this.state.fps, (1000.0 / (this.state.time / this.state.frame)), this.state.time - timeStamp, this.state.frame, this.accTs / this.state.frame);
828         }*/
829
830
831     //  this.spr.pos = this.state.pointer.pos;
832     //    this.spr.frameGridPos = [ Math.floor(this.state.pointer.pos[0]) % 10, 0 ];
833     this.dg.draw(this.gl);
834
835     this.drawFrameRequestId = window.requestAnimationFrame(this.drawFrame.bind(this));
836
837 };