Update gitignore.
[NgWiz.git] / game / src / DstEng-first.js
1 /* globals $ */
2
3 var timeScale=1;
4
5 function DstEng(config) {
6 this.accTs =0;
7     this.res = {};
8
9
10     this.state = {
11         timeDelta: 0,
12         tdAvg: 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
23     var ok = true;
24     this.cfg = config;
25
26     this.log("DstEng Startup:", this.cfg);
27
28     this.cfg.screen.width = this.cfg.width;
29     this.cfg.screen.height = this.cfg.height;
30     console.log(this.cfg.screen.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 }
48
49 DstEng.prototype.log = function() {
50     console.log.apply(console, arguments);
51 };
52
53 DstEng.prototype.initGl = function() {
54     try {
55         this.gl = this.cfg.screen.getContext("experimental-webgl");
56         this.gl.viewportWidth = this.cfg.screen.width;
57         this.gl.viewportHeight = this.cfg.screen.height;
58     } catch (e) {}
59
60     return !!this.gl;
61 };
62
63
64
65 DstEng.prototype.isPowerOf2 = function(value) {
66     return (value & (value - 1)) === 0;
67 };
68
69 DstEng.prototype.setupTextureFiltering = function(width, height) {
70     if (this.isPowerOf2(width) && this.isPowerOf2(height)) {
71         // the dimensions are power of 2 so generate mips and turn on 
72         // tri-linear filtering.
73         this.gl.generateMipmap(this.gl.TEXTURE_2D);
74         //              this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR);
75         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST_MIPMAP_NEAREST);
76     } else {
77         // at least one of the dimensions is not a power of 2 so set the filtering
78         // so WebGL will render it.
79         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
80         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
81         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
82         this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
83     }
84 };
85
86
87 DstEng.prototype.imgLoader = function(obj, cb) {
88
89     var image = new Image();
90     image.src = obj.url;
91     image.addEventListener('load', function() {
92
93         var texture = this.gl.createTexture();
94         this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
95         // Fill the texture with a 1x1 blue pixel.
96         this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, 1, 1, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE,
97             new Uint8Array([0, 0, 255, 255]));
98         // Asynchronously load an image
99
100         this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
101         this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image);
102
103         this.setupTextureFiltering(image.width, image.height);
104
105         cb({
106             valid: true,
107             resType: 'img',
108             image: image,
109             texture: texture
110         });
111     }.bind(this));
112     image.addEventListener('error', function(e) {
113         cb({
114             valid: false,
115             error: 'Could not load the image'
116         });
117     });
118 };
119
120 DstEng.prototype.compileShader = function(gl, shaderSource, shaderType) {
121     // Create the shader object
122     var obj = {
123         valid: true
124     };
125     var shader = gl.createShader(shaderType);
126
127
128     // Set the shader source code.
129     gl.shaderSource(shader, shaderSource);
130
131     // Compile the shader
132     gl.compileShader(shader);
133
134     // Check if it compiled
135     var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
136     if (!success) {
137         // Something went wrong during compilation; get the error
138         obj.valid = false;
139         obj.error = 'Compilation failed: ' + gl.getShaderInfoLog(shader);
140
141     } else {
142         obj.shaderType = shaderType;
143         obj.resType = (shaderType === gl.FRAGMENT_SHADER) ? 'fragment_shader' : 'vertex_shader';
144         obj.shader = shader;
145     }
146
147     return obj;
148 };
149
150 DstEng.prototype.createProgram = function(gl, vertexShader, fragmentShader) {
151     // create a program.
152     var program = gl.createProgram();
153
154     // attach the shaders.
155     gl.attachShader(program, vertexShader);
156     gl.attachShader(program, fragmentShader);
157
158     // link the program.
159     gl.linkProgram(program);
160
161     // Check if it linked.
162     var success = gl.getProgramParameter(program, gl.LINK_STATUS);
163     if (!success) {
164         // something went wrong with the link
165         throw ("program failed to link:" + gl.getProgramInfoLog(program));
166     }
167
168     var resObj = {
169         resType: 'shader_program',
170         program: program,
171         a_texCoord: gl.getAttribLocation(program, "a_texCoord"),
172         a_pos: gl.getAttribLocation(program, "a_position"),
173         u_frameSize: gl.getUniformLocation(program, "u_frameSize"),
174         u_frameGridPos: gl.getUniformLocation(program, "u_frameGridPos"),
175         u_res: gl.getUniformLocation(program, "u_resolution"),
176         u_translation: gl.getUniformLocation(program, "u_translation")
177     };
178
179     //Enable textures and verticies for out programs
180     gl.enableVertexAttribArray(resObj.a_pos);
181     gl.enableVertexAttribArray(resObj.a_texCoord);
182
183     //Set default resolution
184     gl.useProgram(program);
185     gl.uniform2fv(resObj.u_res, this.state.virtRes);
186
187     return resObj;
188 };
189
190
191 DstEng.prototype.shaderLoader = function(obj, cb) {
192     $.ajax({
193         url: obj.url,
194         dataType: 'text',
195         success: function(data) {
196             cb(this.compileShader(this.gl, data, obj.shaderType));
197         }.bind(this),
198         error: function(e) {
199             cb({
200                 valid: false,
201                 error: e.status + ' ' + e.statusText
202             });
203         }
204     });
205 };
206
207
208 DstEng.prototype.resLoader = function(loadList, done) {
209     var left = loadList.length;
210     var loaded = 0;
211
212     loadList.forEach(function(r) {
213         r.loader(r, function(data) {
214             if (data) {
215                 this.addRes(r.name, data);
216                 loaded++;
217             }
218             left--;
219
220             if (!data.valid) {
221                 this.log('Resource loading failed for "' + r.name + '" from "' + r.url + '" error:', data.error);
222             }
223             if (left === 0) {
224                 done((loaded != loadList.length));
225             }
226         }.bind(this));
227     }, this);
228 };
229
230 DstEng.prototype.preLoad = function() {
231     var res = [{
232             name: 'img_DstEng',
233             url: '/data/img/DstEng.jpg',
234             loader: this.imgLoader.bind(this)
235         }, {
236             name: 'img_tiles',
237             url: '/data/packs/000_wizznic/themes/thor/tiles/thor.png',
238             loader: this.imgLoader.bind(this)
239         }, {
240             name: 'shader_vertex2d',
241             url: '/data/gl/2d_vertex.js',
242             shaderType: this.gl.VERTEX_SHADER,
243             loader: this.shaderLoader.bind(this)
244         }, {
245             name: 'shader_fragment2d',
246             url: '/data/gl/2d_fragment.js',
247             shaderType: this.gl.FRAGMENT_SHADER,
248             loader: this.shaderLoader.bind(this)
249         }, {
250             name: 'shader_fragment2d_sat',
251             url: '/data/gl/2d_fragment_sat.js',
252             shaderType: this.gl.FRAGMENT_SHADER,
253             loader: this.shaderLoader.bind(this)
254         }
255
256     ];
257
258     this.resLoader(res, function(err) {
259         if (!err) {
260             this.startLoader();
261         } else {
262             this.cfg.readyCallback(true);
263         }
264     }.bind(this));
265
266
267 };
268
269 DstEng.prototype.realCoordsToGl = function(evt) {
270     var r = this.cfg.screen.getBoundingClientRect();
271     var sx = this.state.virtRes[0] / this.cfg.screen.width;
272     var sy = this.state.virtRes[1] / this.cfg.screen.height;
273     return ([(evt.clientX - r.left) * sx, (evt.clientY - r.top) * sy]);
274 };
275
276 DstEng.prototype.setPointerState = function(e) {
277     this.state.pointer.pos = this.realCoordsToGl(e);
278 };
279
280
281 DstEng.prototype.onMouseMove = function(cb) {
282     this.cfg.screen.addEventListener('mousemove', function(e) {
283         this.setPointerState(e);
284         cb(this.state.pointer);
285     }.bind(this));
286 };
287
288 DstEng.prototype.onMouseClick = function(cb) {
289     this.cfg.screen.addEventListener('mousedown', function(e) {
290         this.setPointerState(e);
291         this.state.pointer.down = true;
292         cb(this.state.pointer);
293     }.bind(this));
294
295     this.cfg.screen.addEventListener('mouseup', function(e) {
296         this.setPointerState(e);
297         this.state.pointer.down = false;
298         cb(this.state.pointer);
299     }.bind(this));
300 };
301
302
303 DstEng.prototype.randomInt = function(range) {
304     return Math.floor(Math.random() * range);
305 };
306
307 DstEng.prototype.addRes = function(name, resource) {
308
309     if (this.res.hasOwnProperty(name)) {
310         this.log('Note: Overriding existing resource ' + name);
311         this.log('Ow: ', this.res[name]);
312         this.log('By: ', resource);
313
314     }
315     if (resource.type) {
316         this.log('No type defined for ' + name);
317     }
318
319     this.res[name] = resource;
320
321     return (resource);
322 };
323
324
325 DstEng.prototype.startLoader = function() {
326
327     this.addRes('glPrg', this.createProgram(this.gl, this.res.shader_vertex2d.shader, this.res.shader_fragment2d.shader));
328     this.addRes('glPrgSat', this.createProgram(this.gl, this.res.shader_vertex2d.shader, this.res.shader_fragment2d_sat.shader));
329
330     //this.gl.useProgram( this.res.glPrg.program );
331
332     // The idea is to have:
333     // DrawGroups, each member of this group will be drawn using the same program
334     //  - PolyGroups, each member will be drawn using the same program and vertex geometry
335     //     - TexGroups, each member will be drawn using the same program, vertex, geometry and texture, it will contain an array of texCoord arrays to be used
336     //        - Sprite, each sprite will be drawn at a desired position, using the desired texCoord array, optional array of program params may be supplied
337     // Usage:
338     // Create a vertexbuffers
339     // Create a dg
340     // Create a pg and add to dg
341     // Create a tg and add to pg
342     // Create a sp and add to tg
343     // draw dgs
344
345     // "Add object: thisProgram, thatimage, " = { dg, pg, tg, sp } ?
346
347
348     /*...*/
349
350     var texture = new this.Texture(this.gl, {
351         img: this.res.img_DstEng,
352         cut: [0, 0, 160, 120],
353         numFrames: 1,
354         animate: false,
355         frameTime: 0
356     });
357
358
359
360
361     var drawGroup = new this.DrawGroup(this.gl, this.res.glPrg);
362
363
364
365     var w = 320,
366         h = 240;
367
368     var polyBuffer = this.gl.createBuffer();
369     this.gl.bindBuffer(this.gl.ARRAY_BUFFER, polyBuffer);
370     this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
371         0, 0,
372         0.0, 0.0,
373         w, 0,
374         1.0, 0.0,
375         0, h,
376         0.0, 1.0,
377         0, h,
378         0.0, 1.0,
379         w, 0,
380         1.0, 0.0,
381         w, h,
382         1.0, 1.0
383     ]), this.gl.STATIC_DRAW);
384
385     var brick = this.gl.createBuffer();
386     this.gl.bindBuffer(this.gl.ARRAY_BUFFER, brick);
387     w = h = 20;
388     var glw = 20 / 480;
389     this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
390         0, 0,
391         0.0, 0.0,
392         w, 0,
393         glw, 0.0,
394         0, h,
395         0.0, 1.0,
396         0, h,
397         0.0, 1.0,
398         w, 0,
399         glw, 0.0,
400         w, h,
401         glw, 1.0
402     ]), this.gl.STATIC_DRAW);
403
404     var bricks = new this.PolyGroup(brick);
405     var brickTex = new this.Texture(this.gl, {
406         img: this.res.img_tiles,
407         cut: [0, 0, 20, 20],
408         numFrames: 10,
409         frameTime: 100,
410         animate: true
411     });
412
413
414
415     var brickTexGroup = new this.TexGroup(brickTex);
416
417     var sprite;
418  
419     this.Sprite.prototype.animate = function() {
420         this.pos[0] += this.vx*timeScale;
421         this.pos[1] += this.vy*timeScale;
422
423         if (this.pos[0] < 0) {
424             this.pos[0] = 0;
425             this.vx *= -1;
426             this.frameGridPos[0]++;
427             if(this.frameGridPos[0] > 9) {
428                 this.frameGridPos[0]=0;
429             }
430         } else if (this.pos[0] > 320 - 20) {
431             this.pos[0] = 320 - 20;
432             this.vx *= -1;
433 //            this.frameGridPos = [Math.floor(Math.random() * 10), 0];
434             this.frameGridPos[0]++;
435             if(this.frameGridPos[0] > 9) {
436                 this.frameGridPos[0]=0;
437             }
438         }
439
440         if (this.pos[1] < 0) {
441             this.frameGridPos[0]++;
442             if(this.frameGridPos[0] > 9) {
443                 this.frameGridPos[0]=0;
444             }
445   //          this.frameGridPos = [Math.floor(Math.random() * 10), 0];
446             this.pos[1] = 0;
447             this.vy *= -1;
448         } else if (this.pos[1] > 240 - 20) {
449     //        this.frameGridPos = [Math.floor(Math.random() * 10), 0];
450             this.frameGridPos[0]++;
451             if(this.frameGridPos[0] > 9) {
452                 this.frameGridPos[0]=0;
453             }
454             this.pos[1] = 240 - 20;
455             this.vy *= -1;
456         }
457
458     };
459
460     for (var i = 0; i < 100000; ++i) {
461         sprite = new this.Sprite();
462         sprite.frameGridPos = [Math.floor(Math.random() * 10), 0];
463         sprite.vx = Math.random()*4-2;
464         sprite.vy = Math.random()*4-2;
465         
466        // sprite.animate = animate.bind(sprite);
467         brickTexGroup.addSprite(sprite);
468     }
469
470
471     bricks.addTexGroup(brickTexGroup);
472
473     var backgrounds = new this.PolyGroup(polyBuffer);
474
475     drawGroup.addPolyGroup(backgrounds);
476
477     drawGroup.addPolyGroup(bricks);
478
479     var bgTexSingle = new this.TexGroup(texture);
480
481
482     //Add an empty s
483     bgTexSingle.addSprite(new this.Sprite());
484
485
486     backgrounds.addTexGroup(bgTexSingle);
487
488
489
490
491     this.dg = drawGroup;
492
493
494
495     this.drawFrame();
496     //Say we are ready
497     this.cfg.readyCallback(true);
498 };
499
500 DstEng.prototype.Texture = (function() {
501     function Tex(gl, setup) {
502
503         if (!gl) {
504             throw Error('Need gl');
505         }
506
507         if (!setup.img.resType) {
508             throw Error('Required: img.');
509         }
510
511         if (!setup.img.image || !setup.img.image.height) {
512             throw Error('Invalid or no image in setup.img');
513         }
514
515         this.w = setup.img.image.width;
516         this.h = setup.img.image.height;
517
518         this.numFrames = setup.numFrames || 1;
519         this.animate = setup.animate || false;
520         this.frameTime = setup.frameTime || false;
521
522         this.pxCut = setup.cut || [0, 0, this.w, this.h];
523
524         this.glTex = setup.img.texture;
525
526         this.glFrameSize = new Float32Array([(this.pxCut[2] / this.w), (this.pxCut[3] / this.h)]);
527
528     }
529
530     return Tex;
531 }());
532
533 DstEng.prototype.DrawGroup = (function() {
534     function Dg(gl, prgInfo) {
535         this.prgInfo = prgInfo;
536         this.polyGroups = [];
537     }
538
539     Dg.prototype.addPolyGroup = function(pg) {
540         this.polyGroups.push(pg);
541     };
542
543     Dg.prototype.draw = function(gl) {
544
545         //Program is same for all drawGroups
546         var prgInfo = this.prgInfo;
547         gl.useProgram(prgInfo.program);
548
549         this.polyGroups.forEach(function drawPolyGroup(pg) {
550
551             //VertexBuffer is the same for a polygroup
552             gl.bindBuffer(gl.ARRAY_BUFFER, pg.buffer);
553             gl.vertexAttribPointer(prgInfo.a_pos, 2, gl.FLOAT, false, pg.stride, pg.vertexOffset);
554             gl.vertexAttribPointer(prgInfo.a_texCoord, 2, gl.FLOAT, false, pg.stride, pg.texCoordOffset);
555
556             pg.texGroups.forEach(function drawTexGroup(tg) {
557                 //Texture is the same for each texGroup
558                 gl.bindTexture(gl.TEXTURE_2D, tg.texture.glTex);
559
560                 gl.uniform2fv(prgInfo.u_frameSize, tg.texture.glFrameSize);
561                 tg.sprites.forEach(function drawSprite(spr) {
562                     //Set geometry location and texture location
563                     if (spr.vx) spr.animate();
564
565                     gl.uniform2fv(prgInfo.u_translation, spr.pos);
566                     gl.uniform2fv(prgInfo.u_frameGridPos, spr.frameGridPos);
567
568
569
570                     gl.drawArrays(gl.TRIANGLES, 0, 6);
571                 });
572             });
573         });
574     };
575
576     return Dg;
577 }());
578
579 DstEng.prototype.PolyGroup = (function() {
580     function Pg(buffer) {
581         this.buffer = buffer;
582         this.vertexOffset = 0;
583         this.texCoordOffset = Float32Array.BYTES_PER_ELEMENT * 2;
584         this.stride = Float32Array.BYTES_PER_ELEMENT * 4;
585         this.texGroups = [];
586     }
587
588     Pg.prototype.addTexGroup = function(tg) {
589         this.texGroups.push(tg);
590     };
591
592     return Pg;
593 }());
594
595 DstEng.prototype.TexGroup = (function() {
596     function Tg(texture) {
597         this.texture = texture;
598         this.sprites = [];
599     }
600
601     Tg.prototype.addSprite = function(spr) {
602         this.sprites.push(spr);
603     };
604
605     return Tg;
606 }());
607
608
609 DstEng.prototype.Sprite = (function() {
610     function Spr() {
611         this.pos = new Float32Array([0, 0]);
612         this.frameGridPos = [0, 0];
613     }
614
615     Spr.prototype.setPos = function(pos) {
616         this.pos = pos;
617     };
618
619
620     return Spr;
621 }());
622
623
624
625 DstEng.prototype.drawFrame = function(timeStamp) {
626
627     timeStamp = timeStamp || 16.0;
628
629     this.state.timeDelta = timeStamp - this.state.lastTime;
630     
631     timeScale = this.state.timeDelta/16.00;
632     this.accTs +=timeScale;
633   //  console.log(timeScale);
634     
635     this.state.lastTime = timeStamp;
636
637     this.state.frame++;
638     this.state.tdAvg += this.state.timeDelta;
639
640
641
642
643     if (this.state.frame % 120 === 0) {
644         this.state.fps = 1000 / this.state.timeDelta;
645         console.log(this.state.fps, (1000.0 / (this.state.tdAvg / this.state.frame)), this.state.tdAvg - timeStamp, this.state.frame, this.accTs/this.state.frame);
646     }
647
648
649     //  this.spr.pos = this.state.pointer.pos;
650     //    this.spr.frameGridPos = [ Math.floor(this.state.pointer.pos[0]) % 10, 0 ];
651     this.dg.draw(this.gl);
652
653
654     window.requestAnimationFrame(this.drawFrame.bind(this));
655
656 };