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