ruby2d 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1180 @@
1
+ // Simple2D.js — v0.1.0, built 03-01-2017
2
+
3
+ // start.js - Open the anonymous function defining the Simple 2D module
4
+
5
+ (function(undefined) {
6
+
7
+ // Check if Simple 2D is already loaded
8
+ if (typeof(this.S2D) !== 'undefined') {
9
+ console.warn("Simple 2D already loaded! Loading twice may cause problems.");
10
+ return this.S2D;
11
+ }
12
+
13
+ // Create the Simple 2D module
14
+ var S2D = this.S2D = {};
15
+
16
+ // ... Simple 2D library starts here ...
17
+
18
+
19
+ // simple2d.js
20
+
21
+ // Simple 2D OpenGL namespace
22
+ S2D.GL = {};
23
+
24
+ // Simple 2D definitions
25
+ Object.defineProperty(S2D, "KEYDOWN", { value: 1 });
26
+ Object.defineProperty(S2D, "KEY", { value: 2 });
27
+ Object.defineProperty(S2D, "KEYUP", { value: 3 });
28
+
29
+ // Viewport scaling modes
30
+ Object.defineProperty(S2D, "FIXED", { value: 1 });
31
+ Object.defineProperty(S2D, "SCALE", { value: 2 });
32
+ Object.defineProperty(S2D, "STRETCH", { value: 3 });
33
+
34
+ // Color
35
+ S2D.Color = {
36
+ r: 1.0,
37
+ g: 1.0,
38
+ b: 1.0,
39
+ a: 1.0
40
+ };
41
+
42
+ // Window
43
+ S2D.Window = {
44
+ title: null,
45
+ width: null,
46
+ height: null,
47
+ orig_width: null,
48
+ orig_height: null,
49
+ viewport: {
50
+ width: null,
51
+ height: null,
52
+ mode: null
53
+ },
54
+ pixel_ratio: null,
55
+ update: null,
56
+ render: null,
57
+ mouse: {
58
+ x: 0,
59
+ y: 0
60
+ },
61
+ on_key: null,
62
+ on_mouse: null,
63
+ element: null, // The HTML element to append the canvas
64
+ canvas: null,
65
+ background: null,
66
+ frames: 0,
67
+ close: false
68
+ };
69
+
70
+ // Image
71
+ S2D.Image = {
72
+ texture: null,
73
+ data: null,
74
+ color: null,
75
+ x: 0,
76
+ y: 0,
77
+ width: null,
78
+ height: null,
79
+ orig_width: null,
80
+ orig_height: null
81
+ };
82
+
83
+ // Sprite
84
+ S2D.Sprite = {
85
+ img: null,
86
+ x: 0,
87
+ y: 0,
88
+ width: null,
89
+ height: null,
90
+ tx1: null,
91
+ ty1: null,
92
+ tx2: null,
93
+ ty2: null,
94
+ tx3: null,
95
+ ty3: null,
96
+ tx4: null,
97
+ ty4: null
98
+ };
99
+
100
+ // Text
101
+ S2D.Text = {
102
+ texture: null,
103
+ data: null,
104
+ color: null,
105
+ x: 0,
106
+ y: 0,
107
+ width: null,
108
+ height: null,
109
+ font: null,
110
+ size: null,
111
+ msg: null
112
+ };
113
+
114
+ // Sound
115
+ S2D.Sound = {
116
+ data: null
117
+ };
118
+
119
+ // Music
120
+ S2D.Music = {
121
+ data: null
122
+ };
123
+
124
+ // Global current music playing
125
+ S2D.current_music = null;
126
+
127
+ // Collection of keys currently pressed
128
+ S2D.keys_down = [];
129
+
130
+ // On keyboard starting at top row, left to right
131
+ S2D.key_map = {
132
+ 27: "Escape",
133
+
134
+ 192: "`",
135
+ 189: "-",
136
+ 187: "=",
137
+ 8: "Backspace",
138
+
139
+ 9: "Tab",
140
+ 219: "[",
141
+ 221: "]",
142
+ 220: "\\",
143
+
144
+ 20: "CapsLock",
145
+ 186: ";",
146
+ 222: "'",
147
+ 13: "Return",
148
+
149
+ 16: "Shift",
150
+ 188: ",",
151
+ 190: ".",
152
+ 191: "/",
153
+
154
+ 17: "Ctrl",
155
+ 18: "Option",
156
+ 91: "Left Command",
157
+ 32: "Space",
158
+ 93: "Right Command",
159
+ 37: "Left",
160
+ 38: "Up",
161
+ 39: "Right",
162
+ 40: "Down"
163
+ };
164
+
165
+ // Web-specific helpers
166
+
167
+ // Looks up a key from a given keycode
168
+ S2D.GetKey = function(keycode) {
169
+ if (typeof(keycode) == "string") {
170
+ return keycode;
171
+ } else if (S2D.key_map[keycode]) {
172
+ return S2D.key_map[keycode];
173
+ } else {
174
+ return String.fromCharCode(keycode);
175
+ }
176
+ };
177
+
178
+ // Trim transparent pixels from canvas
179
+ // Adapted from: https://gist.github.com/remy/784508
180
+ S2D.TrimCanvas = function(c) {
181
+ var ctx = c.getContext('2d'),
182
+ copy = document.createElement('canvas').getContext('2d'),
183
+ pixels = ctx.getImageData(0, 0, c.width, c.height),
184
+ l = pixels.data.length,
185
+ i,
186
+ bound = {
187
+ top: null,
188
+ left: null,
189
+ right: null,
190
+ bottom: null
191
+ },
192
+ x, y;
193
+
194
+ for (i = 0; i < l; i += 4) {
195
+ if (pixels.data[i+3] !== 0) {
196
+ x = (i / 4) % c.width;
197
+ y = ~~((i / 4) / c.width);
198
+
199
+ if (bound.top === null) {
200
+ bound.top = y;
201
+ }
202
+
203
+ if (bound.left === null) {
204
+ bound.left = x;
205
+ } else if (x < bound.left) {
206
+ bound.left = x;
207
+ }
208
+
209
+ if (bound.right === null) {
210
+ bound.right = x;
211
+ } else if (bound.right < x) {
212
+ bound.right = x;
213
+ }
214
+
215
+ if (bound.bottom === null) {
216
+ bound.bottom = y;
217
+ } else if (bound.bottom < y) {
218
+ bound.bottom = y;
219
+ }
220
+ }
221
+ }
222
+
223
+ var trimHeight = bound.bottom - bound.top,
224
+ trimWidth = bound.right - bound.left,
225
+ trimmed = ctx.getImageData(bound.left, bound.top, trimWidth, trimHeight);
226
+
227
+ copy.canvas.width = trimWidth;
228
+ copy.canvas.height = trimHeight;
229
+ copy.putImageData(trimmed, 0, 0);
230
+
231
+ // open new window with trimmed image:
232
+ return copy.canvas;
233
+ };
234
+
235
+
236
+ // shapes.js
237
+
238
+ /*
239
+ * Draw a triangle
240
+ */
241
+ S2D.DrawTriangle = function(x1, y1, c1r, c1g, c1b, c1a,
242
+ x2, y2, c2r, c2g, c2b, c2a,
243
+ x3, y3, c3r, c3g, c3b, c3a) {
244
+
245
+ S2D.GL.DrawTriangle(x1, y1, c1r, c1g, c1b, c1a,
246
+ x2, y2, c2r, c2g, c2b, c2a,
247
+ x3, y3, c3r, c3g, c3b, c3a);
248
+ };
249
+
250
+
251
+ /*
252
+ * Draw a quad, using two triangles
253
+ */
254
+ S2D.DrawQuad = function(x1, y1,
255
+ c1r, c1g, c1b, c1a,
256
+ x2, y2,
257
+ c2r, c2g, c2b, c2a,
258
+ x3, y3,
259
+ c3r, c3g, c3b, c3a,
260
+ x4, y4,
261
+ c4r, c4g, c4b, c4a) {
262
+
263
+ S2D.GL.DrawTriangle(x1, y1, c1r, c1g, c1b, c1a,
264
+ x2, y2, c2r, c2g, c2b, c2a,
265
+ x3, y3, c3r, c3g, c3b, c3a);
266
+
267
+ S2D.GL.DrawTriangle(x3, y3, c3r, c3g, c3b, c3a,
268
+ x4, y4, c4r, c4g, c4b, c4a,
269
+ x1, y1, c1r, c1g, c1b, c1a);
270
+ };
271
+
272
+
273
+ // image.js
274
+
275
+ /*
276
+ * Create an image
277
+ * Params: path = image file path
278
+ */
279
+ S2D.CreateImage = function(path, loadedCallback) {
280
+
281
+ // TODO: Check if image file exists
282
+
283
+ // Create image object
284
+ var img = Object.create(S2D.Image);
285
+ img.data = new Image();
286
+ img.color = Object.create(S2D.Color);
287
+
288
+ img.data.onload = function() {
289
+ img.texture = S2D.GL.CreateTexture(this);
290
+ if (!img.width) img.width = this.width;
291
+ if (!img.height) img.height = this.height;
292
+ if (loadedCallback) loadedCallback();
293
+ };
294
+
295
+ // Causes image to be loaded
296
+ img.data.src = path;
297
+
298
+ return img;
299
+ };
300
+
301
+
302
+ /*
303
+ * Draw an image
304
+ */
305
+ S2D.DrawImage = function(img) {
306
+ if (!img) return;
307
+ S2D.GL.DrawImage(img);
308
+ };
309
+
310
+
311
+ // sprite.js
312
+
313
+ /*
314
+ * Create a sprite, given an image file path
315
+ */
316
+ S2D.CreateSprite = function(path) {
317
+
318
+ // TODO: Check if sprite image file exists
319
+
320
+ var spr = Object.create(S2D.Sprite);
321
+ spr.img = S2D.CreateImage(path, function() {
322
+ spr.width = spr.img.width;
323
+ spr.height = spr.img.height;
324
+ });
325
+
326
+ spr.tx1 = 0.0;
327
+ spr.ty1 = 0.0;
328
+ spr.tx2 = 1.0;
329
+ spr.ty2 = 0.0;
330
+ spr.tx3 = 1.0;
331
+ spr.ty3 = 1.0;
332
+ spr.tx4 = 0.0;
333
+ spr.ty4 = 1.0;
334
+
335
+ return spr;
336
+ };
337
+
338
+
339
+ /*
340
+ * Clip a sprite
341
+ */
342
+ S2D.ClipSprite = function(spr, x, y, w, h) {
343
+ if (!spr) return;
344
+
345
+ // Calculate ratios
346
+ // rw = ratio width; rh = ratio height
347
+ var rw = w / spr.img.width;
348
+ var rh = h / spr.img.height;
349
+
350
+ // Apply ratios to x, y coordinates
351
+ // cx = crop x coord; cy = crop y coord
352
+ var cx = x * rw;
353
+ var cy = y * rh;
354
+
355
+ // Convert given width, height to doubles
356
+ // cw = crop width; ch = crop height
357
+ var cw = w;
358
+ var ch = h;
359
+
360
+ // Apply ratio to texture width and height
361
+ // tw = texture width; th = texture height
362
+ var tw = rw * w;
363
+ var th = rh * h;
364
+
365
+ // Calculate and store sprite texture values
366
+
367
+ spr.tx1 = cx / cw;
368
+ spr.ty1 = cy / ch;
369
+
370
+ spr.tx2 = (cx + tw) / cw;
371
+ spr.ty2 = cy / ch;
372
+
373
+ spr.tx3 = (cx + tw) / cw;
374
+ spr.ty3 = (cy + th) / ch;
375
+
376
+ spr.tx4 = cx / cw;
377
+ spr.ty4 = (cy + th) / ch;
378
+
379
+ // Store the sprite width and height
380
+ spr.width = w;
381
+ spr.height = h;
382
+ };
383
+
384
+
385
+ /*
386
+ * Draw a sprite
387
+ */
388
+ S2D.DrawSprite = function(spr) {
389
+ if (!spr) return;
390
+ S2D.GL.DrawSprite(spr);
391
+ };
392
+
393
+
394
+ // text.js
395
+
396
+ /*
397
+ * Create text, given a font file path, the message, and size
398
+ */
399
+ S2D.CreateText = function(font, msg, size) {
400
+
401
+ // Create image object
402
+ var txt = Object.create(S2D.Text);
403
+ txt.color = Object.create(S2D.Color);
404
+ txt.font = font;
405
+ txt.msg = msg;
406
+ txt.size = size;
407
+
408
+ return txt;
409
+ };
410
+
411
+
412
+ /*
413
+ * Sets the text message
414
+ */
415
+ S2D.SetText = function(txt, msg) {
416
+ if (msg == "") return; // no need to create a texture
417
+
418
+ S2D.GL.FreeTexture(txt.texture);
419
+
420
+ // Create a canvas element to make a texture
421
+ var ctx = document.createElement("canvas").getContext("2d");
422
+
423
+ // TODO: Width and height should probably be variable, based on
424
+ // `ctx.measureText(msg).width` or something.
425
+ var w = 1000;
426
+ var h = 1000;
427
+
428
+ // Double size of font for high DPI
429
+ var size = txt.size * 2;
430
+
431
+ // Set context attributes and draw text
432
+ ctx.canvas.width = w;
433
+ ctx.canvas.height = h;
434
+ ctx.font = `${size}px ${txt.font}`;
435
+ ctx.textAlign = "right";
436
+ ctx.textBaseline = "bottom";
437
+ ctx.fillStyle = "white";
438
+ ctx.fillText(msg, w, h);
439
+
440
+ txt.data = S2D.TrimCanvas(ctx.canvas); // trim the transparent pixels
441
+ txt.texture = S2D.GL.CreateTexture(txt.data);
442
+ txt.width = txt.data.width / 2; // half size of texture for high DPI
443
+ txt.height = txt.data.height / 2;
444
+ };
445
+
446
+
447
+ /*
448
+ * Draw text
449
+ */
450
+ S2D.DrawText = function(txt) {
451
+ if (!txt) return;
452
+
453
+ if (!txt.texture) {
454
+ S2D.SetText(txt, txt.msg);
455
+ }
456
+
457
+ S2D.GL.DrawText(txt);
458
+ };
459
+
460
+
461
+ // sound.js
462
+
463
+ /*
464
+ * Create a sound, given an audio file path
465
+ */
466
+ S2D.CreateSound = function(path) {
467
+
468
+ // TODO: Check if audio file exists
469
+
470
+ var sound = Object.create(S2D.Sound);
471
+ sound.data = new Audio(path);
472
+
473
+ return sound;
474
+ };
475
+
476
+
477
+ /*
478
+ * Play the sound
479
+ */
480
+ S2D.PlaySound = function(sound) {
481
+ // Clone sound and play so audio can overlap
482
+ sound.data.cloneNode(true).play();
483
+ };
484
+
485
+
486
+ // music.js
487
+
488
+ /*
489
+ * Create the music, given an audio file path
490
+ */
491
+ S2D.CreateMusic = function(path) {
492
+
493
+ // TODO: Check if audio file exists
494
+
495
+ var music = Object.create(S2D.Music);
496
+ music.data = new Audio(path);
497
+
498
+ return music;
499
+ };
500
+
501
+
502
+ /*
503
+ * Play the music
504
+ */
505
+ S2D.PlayMusic = function(music, loop) {
506
+ S2D.StopMusic();
507
+ music.data.loop = loop;
508
+ S2D.current_music = music.data;
509
+ S2D.current_music.play();
510
+ };
511
+
512
+
513
+ /*
514
+ * Pause the playing music
515
+ */
516
+ S2D.PauseMusic = function() {
517
+ if (!S2D.current_music) return;
518
+ S2D.current_music.pause();
519
+ };
520
+
521
+
522
+ /*
523
+ * Resume the current music
524
+ */
525
+ S2D.ResumeMusic = function() {
526
+ if (!S2D.current_music) return;
527
+ S2D.current_music.play();
528
+ };
529
+
530
+
531
+ /*
532
+ * Stops the playing music; interrupts fader effects
533
+ */
534
+ S2D.StopMusic = function() {
535
+ if (!S2D.current_music) return;
536
+ S2D.current_music.pause();
537
+ S2D.current_music.currentTime = 0;
538
+ };
539
+
540
+
541
+ /*
542
+ * Fade out the playing music
543
+ */
544
+ S2D.FadeOutMusic = function(ms) {
545
+ if (!S2D.current_music) return;
546
+
547
+ if (S2D.current_music.paused) {
548
+ S2D.StopMusic();
549
+ return;
550
+ }
551
+
552
+ var fadeAudio = setInterval(function () {
553
+ if (S2D.current_music.volume >= 0.05) {
554
+ S2D.current_music.volume -= 0.05;
555
+ } else {
556
+ S2D.StopMusic();
557
+ S2D.current_music.volume = 1.0;
558
+ clearInterval(fadeAudio);
559
+ }
560
+
561
+ }, ms / 20);
562
+ };
563
+
564
+
565
+ // input.js
566
+
567
+ /*
568
+ * Get the mouse coordinates relative to the viewport
569
+ */
570
+ S2D.GetMouseOnViewport = function(win, wx, wy) {
571
+
572
+ var scale; // viewport scale factor
573
+ var w, h; // width and height of scaled viewport
574
+ var x, y; // mouse positions to be returned
575
+
576
+ switch (win.viewport.mode) {
577
+
578
+ case S2D.FIXED:
579
+ x = wx / (win.orig_width / win.viewport.width);
580
+ y = wy / (win.orig_height / win.viewport.height);
581
+ break;
582
+
583
+ case S2D.SCALE:
584
+ var o = S2D.GL.GetViewportScale(win);
585
+ x = wx * 1 / o.scale - (win.width - o.w) / (2.0 * o.scale);
586
+ y = wy * 1 / o.scale - (win.height - o.h) / (2.0 * o.scale);
587
+ break;
588
+
589
+ case S2D.STRETCH:
590
+ x = wx * win.viewport.width / win.width;
591
+ y = wy * win.viewport.height / win.height;
592
+ break;
593
+ }
594
+
595
+ return {
596
+ x: x,
597
+ y: y
598
+ };
599
+ };
600
+
601
+
602
+ // window.js
603
+
604
+ /*
605
+ * Create a window
606
+ */
607
+ S2D.CreateWindow = function(title, width, height, update, render, element, opts) {
608
+
609
+ var win = Object.create(S2D.Window);
610
+
611
+ win.title = title;
612
+ win.width = width;
613
+ win.height = height;
614
+ win.orig_width = width;
615
+ win.orig_height = height;
616
+ win.viewport.width = width;
617
+ win.viewport.height = height;
618
+ win.viewport.mode = S2D.SCALE;
619
+ win.update = update;
620
+ win.render = render;
621
+ win.background = Object.create(S2D.Color);
622
+ win.background.r = 0;
623
+ win.background.g = 0;
624
+ win.background.b = 0;
625
+ win.background.a = 1;
626
+
627
+ // `element` can be an ID string (e.g. "#game") or an actual DOM element
628
+ if (typeof(element) == 'string') {
629
+ win.element = document.getElementById(element);
630
+ } else {
631
+ win.element = element;
632
+ }
633
+
634
+ return win;
635
+ };
636
+
637
+
638
+ /*
639
+ * Show the window
640
+ */
641
+ S2D.Show = function(win) {
642
+
643
+ // Create the canvas element
644
+
645
+ var el = document.createElement('canvas');
646
+ win.element.appendChild(el);
647
+
648
+ el.setAttribute('width', win.width);
649
+ el.setAttribute('height', win.height);
650
+ el.innerHTML = "Your browser doesn't appear to support" +
651
+ "the <code>&lt;canvas&gt;</code> element.";
652
+
653
+ win.canvas = el;
654
+
655
+ // Detect and set up canvas for high DPI
656
+
657
+ win.canvas.style.width = win.width + "px";
658
+ win.canvas.style.height = win.height + "px";
659
+
660
+ var ratio = window.devicePixelRatio ||
661
+ window.webkitDevicePixelRatio ||
662
+ window.mozDevicePixelRatio ||
663
+ window.opDevicePixelRatio || 1;
664
+
665
+ win.canvas.width = win.width * devicePixelRatio;
666
+ win.canvas.height = win.height * devicePixelRatio;
667
+ win.pixel_ratio = ratio;
668
+
669
+ // Initialize WebGL
670
+ S2D.GL.Init(win);
671
+
672
+ S2D.onkeydown = function(e) {
673
+ var key = S2D.GetKey(e.keyCode);
674
+ if (!S2D.keys_down.includes(key)) {
675
+ S2D.keys_down.push(key);
676
+ if (win.on_key) win.on_key(S2D.KEYDOWN, key);
677
+ }
678
+ };
679
+ document.addEventListener("keydown", S2D.onkeydown);
680
+
681
+ S2D.onkeyup = function(e) {
682
+ var key = S2D.GetKey(e.keyCode);
683
+ var i = S2D.keys_down.indexOf(key);
684
+ if (i > -1) S2D.keys_down.splice(i, 1);
685
+ if (win.on_key) win.on_key(S2D.KEYUP, key);
686
+ };
687
+ document.addEventListener("keyup", S2D.onkeyup);
688
+
689
+ // Clear keys down list when focus is lost
690
+ window.addEventListener("blur", function functionName() {
691
+ var e = {};
692
+ S2D.keys_down.slice().forEach(function(key) {
693
+ e.keyCode = key;
694
+ S2D.onkeyup(e);
695
+ });
696
+ });
697
+
698
+ S2D.onmousedown = function(e) {
699
+ var x = e.pageX - win.canvas.offsetLeft;
700
+ var y = e.pageY - win.canvas.offsetTop;
701
+ var o = S2D.GetMouseOnViewport(win, x, y);
702
+ if (win.on_mouse) win.on_mouse(o.x, o.y);
703
+ };
704
+ document.addEventListener("mousedown", S2D.onmousedown);
705
+
706
+ // Get and store mouse position
707
+ S2D.onmousemove = function(e) {
708
+ var x = e.pageX - win.canvas.offsetLeft;
709
+ var y = e.pageY - win.canvas.offsetTop;
710
+ var o = S2D.GetMouseOnViewport(win, x, y);
711
+ win.mouse.x = o.x;
712
+ win.mouse.y = o.y;
713
+ };
714
+ document.addEventListener("mousemove", S2D.onmousemove);
715
+
716
+ // Main loop
717
+
718
+ var req; // the animation frame request
719
+ var start_ms = new Date();
720
+ var end_ms = new Date();
721
+ var elapsed_ms;
722
+
723
+ function mainLoop(win) {
724
+
725
+ if (win.close) {
726
+ cancelAnimationFrame(req);
727
+ return;
728
+ }
729
+
730
+ S2D.GL.Clear(win.background);
731
+
732
+ // Update frame counter
733
+ win.frames++;
734
+
735
+ // Calculate and store FPS
736
+ end_ms = new Date();
737
+ elapsed_ms = end_ms.getTime() - start_ms.getTime();
738
+ win.fps = win.frames / (elapsed_ms / 1000.0);
739
+
740
+ // Detect keys held down
741
+ S2D.keys_down.forEach(function(key) {
742
+ if (win.on_key) win.on_key(S2D.KEY, key);
743
+ });
744
+
745
+ if (win.update) win.update();
746
+ if (win.render) win.render();
747
+
748
+ requestAnimationFrame(function() { mainLoop(win); });
749
+ }
750
+
751
+ req = requestAnimationFrame(function() { mainLoop(win); });
752
+ };
753
+
754
+
755
+ /*
756
+ * Close the window
757
+ */
758
+ S2D.Close = function(win) {
759
+ win.close = true;
760
+ // win.canvas.remove();
761
+ };
762
+
763
+
764
+ // gl.js
765
+
766
+ var gl = null, // The WebGL context
767
+ canvas, // The HTML canvas element
768
+ indices = [0, 1, 2, 2, 3, 0],
769
+ // Triangle shader
770
+ shaderProgram,
771
+ positionLocation,
772
+ colorLocation,
773
+ // Texture shader
774
+ texShaderProgram,
775
+ texPositionLocation,
776
+ texColorLocation,
777
+ texCoordLocation,
778
+ samplerLocation;
779
+
780
+ var orthoMatrix = [
781
+ 0, 0, 0, 0,
782
+ 0, 0, 0, 0,
783
+ 0, 0, 0, 0,
784
+ -1.0, 1.0, -1.0, 1.0
785
+ ];
786
+
787
+
788
+ /*
789
+ * Initialize WebGL
790
+ */
791
+ S2D.GL.Init = function(win) {
792
+
793
+ // Initialize the GL context
794
+ try {
795
+ // Try to grab the standard context. If it fails, fallback to experimental.
796
+ gl = win.canvas.getContext("webgl") || win.canvas.getContext("experimental-webgl");
797
+ } catch(e) {
798
+ console.log("GL error caught");
799
+ }
800
+
801
+ // If we don't have a GL context, give up now
802
+ if (!gl) {
803
+ console.error("Unable to initialize WebGL. Your browser may not support it.");
804
+ return null;
805
+ }
806
+
807
+ S2D.GL.WebGLInit();
808
+ S2D.GL.SetViewport(win);
809
+ };
810
+
811
+
812
+ /*
813
+ * Initialize WebGL
814
+ */
815
+ S2D.GL.WebGLInit = function() {
816
+
817
+ // Enable transparency
818
+ gl.enable(gl.BLEND);
819
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
820
+
821
+ // Vertex shader source string
822
+ var vertexSource = `
823
+ uniform mat4 u_matrix;
824
+ attribute vec4 a_position;
825
+ attribute vec4 a_color;
826
+ attribute vec2 a_texcoord;
827
+ varying vec4 v_color;
828
+ varying vec2 v_texcoord;
829
+ void main(void) {
830
+ v_color = a_color;
831
+ v_texcoord = a_texcoord;
832
+ gl_Position = u_matrix * a_position;
833
+ }`;
834
+
835
+ // Fragment shader source string
836
+ var fragmentSource = `
837
+ precision mediump float;
838
+ varying vec4 v_color;
839
+ void main(void) {
840
+ gl_FragColor = v_color;
841
+ }`;
842
+
843
+ // Fragment shader source string for textures
844
+ var texFragmentSource = `
845
+ precision mediump float;
846
+ varying vec4 v_color;
847
+ varying vec2 v_texcoord;
848
+ uniform sampler2D s_texture;
849
+ void main(void) {
850
+ gl_FragColor = texture2D(s_texture, v_texcoord) * v_color;
851
+ }`;
852
+
853
+ // Load the vertex and fragment shaders
854
+ var vertexShader = S2D.GL.LoadShader( gl.VERTEX_SHADER, vertexSource, "Vertex");
855
+ var fragmentShader = S2D.GL.LoadShader(gl.FRAGMENT_SHADER, fragmentSource, "Fragment");
856
+ var texFragmentShader = S2D.GL.LoadShader(gl.FRAGMENT_SHADER, texFragmentSource, "Texture Fragment");
857
+
858
+ // Triangle Shader //
859
+
860
+ // Create the texture shader program object
861
+ shaderProgram = gl.createProgram();
862
+
863
+ // Attach the shader objects to the program object
864
+ gl.attachShader(shaderProgram, vertexShader);
865
+ gl.attachShader(shaderProgram, fragmentShader);
866
+
867
+ // Link the shader program
868
+ gl.linkProgram(shaderProgram);
869
+
870
+ // Check if linked
871
+ if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
872
+ console.error("Unable to initialize the shader program.");
873
+ }
874
+
875
+ // Get the attribute locations
876
+ positionLocation = gl.getAttribLocation(shaderProgram, "a_position");
877
+ colorLocation = gl.getAttribLocation(shaderProgram, "a_color");
878
+
879
+ // Texture Shader //
880
+
881
+ // Create the texture shader program object
882
+ texShaderProgram = gl.createProgram();
883
+
884
+ // Attach the shader objects to the program object
885
+ gl.attachShader(texShaderProgram, vertexShader);
886
+ gl.attachShader(texShaderProgram, texFragmentShader);
887
+
888
+ // Link the shader program
889
+ gl.linkProgram(texShaderProgram);
890
+
891
+ // Check if linked
892
+ if (!gl.getProgramParameter(texShaderProgram, gl.LINK_STATUS)) {
893
+ console.error("Unable to initialize the texture shader program.");
894
+ }
895
+
896
+ // Get the attribute locations
897
+ texPositionLocation = gl.getAttribLocation(texShaderProgram, "a_position");
898
+ texColorLocation = gl.getAttribLocation(texShaderProgram, "a_color");
899
+ texCoordLocation = gl.getAttribLocation(texShaderProgram, "a_texcoord");
900
+
901
+ // Get the sampler location
902
+ samplerLocation = gl.getUniformLocation(texShaderProgram, "s_texture");
903
+ };
904
+
905
+
906
+ /*
907
+ * Creates a shader object, loads shader string, and compiles.
908
+ */
909
+ S2D.GL.LoadShader = function(type, shaderSrc, shaderName) {
910
+
911
+ var shader = gl.createShader(type);
912
+
913
+ gl.shaderSource(shader, shaderSrc);
914
+ gl.compileShader(shader);
915
+
916
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
917
+ console.error("Error compiling shader \"" + shaderName + "\":\n" + gl.getShaderInfoLog(shader));
918
+ return null;
919
+ }
920
+
921
+ return shader;
922
+ };
923
+
924
+
925
+ /*
926
+ * Calculate the viewport's scaled width and height
927
+ */
928
+ S2D.GL.GetViewportScale = function(win) {
929
+
930
+ var s = Math.min(
931
+ win.width / win.viewport.width,
932
+ win.height / win.viewport.height
933
+ );
934
+
935
+ var w = win.viewport.width * s;
936
+ var h = win.viewport.height * s;
937
+
938
+ return {
939
+ w: w,
940
+ h: h,
941
+ scale: s
942
+ };
943
+ };
944
+
945
+
946
+ /*
947
+ * Sets the viewport and matrix projection
948
+ */
949
+ S2D.GL.SetViewport = function(win) {
950
+
951
+ var ortho_w = win.viewport.width;
952
+ var ortho_h = win.viewport.height;
953
+ var x, y, w, h; // calculated GL viewport values
954
+
955
+ x = 0; y = 0; w = win.width; h = win.height;
956
+
957
+ switch (win.viewport.mode) {
958
+
959
+ case S2D.FIXED:
960
+ w = win.orig_width;
961
+ h = win.orig_height;
962
+ y = win.height - h;
963
+ break;
964
+
965
+ case S2D.SCALE:
966
+ var o = S2D.GL.GetViewportScale(win);
967
+ // Center the viewport
968
+ x = win.width / 2.0 - o.w/2.0;
969
+ y = win.height / 2.0 - o.h/2.0;
970
+ break;
971
+
972
+ case S2D.STRETCH:
973
+ break;
974
+ }
975
+
976
+ gl.viewport(
977
+ x * win.pixel_ratio,
978
+ y * win.pixel_ratio,
979
+ w * win.pixel_ratio,
980
+ h * win.pixel_ratio
981
+ );
982
+
983
+ orthoMatrix[0] = 2.0 / ortho_w;
984
+ orthoMatrix[5] = -2.0 / ortho_h;
985
+
986
+ gl.useProgram(shaderProgram);
987
+
988
+ gl.uniformMatrix4fv(
989
+ gl.getUniformLocation(shaderProgram, "u_matrix"),
990
+ false, new Float32Array(orthoMatrix)
991
+ );
992
+
993
+ gl.useProgram(texShaderProgram);
994
+
995
+ gl.uniformMatrix4fv(
996
+ gl.getUniformLocation(texShaderProgram, "u_matrix"),
997
+ false, new Float32Array(orthoMatrix)
998
+ );
999
+ };
1000
+
1001
+
1002
+ /*
1003
+ * Clear buffers to given color values
1004
+ */
1005
+ S2D.GL.Clear = function(clr) {
1006
+ gl.clearColor(clr.r, clr.g, clr.b, clr.a);
1007
+ gl.clear(gl.COLOR_BUFFER_BIT);
1008
+ };
1009
+
1010
+
1011
+ /*
1012
+ * Creates a texture for rendering
1013
+ */
1014
+ S2D.GL.CreateTexture = function(data) {
1015
+
1016
+ var texture = gl.createTexture();
1017
+
1018
+ // Bind the named texture to a texturing target
1019
+ gl.bindTexture(gl.TEXTURE_2D, texture);
1020
+
1021
+ // Specifies the 2D texture image
1022
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data);
1023
+
1024
+ // Set the filtering mode
1025
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
1026
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
1027
+
1028
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1029
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1030
+
1031
+ gl.bindTexture(gl.TEXTURE_2D, null);
1032
+
1033
+ return texture;
1034
+ };
1035
+
1036
+
1037
+ /*
1038
+ * Free a texture
1039
+ */
1040
+ S2D.GL.FreeTexture = function(texture) {
1041
+ gl.deleteTexture(texture);
1042
+ };
1043
+
1044
+
1045
+ /*
1046
+ * Draw triangle
1047
+ */
1048
+ S2D.GL.DrawTriangle = function(x1, y1, c1r, c1g, c1b, c1a,
1049
+ x2, y2, c2r, c2g, c2b, c2a,
1050
+ x3, y3, c3r, c3g, c3b, c3a) {
1051
+
1052
+ var vertices = [
1053
+ x1, y1, 0.0,
1054
+ x2, y2, 0.0,
1055
+ x3, y3, 0.0
1056
+ ];
1057
+
1058
+ var colors = [
1059
+ c1r, c1g, c1b, c1a,
1060
+ c2r, c2g, c2b, c2a,
1061
+ c3r, c3g, c3b, c3a
1062
+ ];
1063
+
1064
+ gl.useProgram(shaderProgram);
1065
+
1066
+ // Vertex
1067
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
1068
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
1069
+
1070
+ gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
1071
+ gl.enableVertexAttribArray(positionLocation);
1072
+
1073
+ // Colors
1074
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
1075
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
1076
+
1077
+ gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
1078
+ gl.enableVertexAttribArray(colorLocation);
1079
+
1080
+ // Draw
1081
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
1082
+ };
1083
+
1084
+
1085
+ /*
1086
+ * Draw a texture
1087
+ */
1088
+ S2D.GL.DrawTexture = function(x, y, w, h,
1089
+ r, g, b, a,
1090
+ tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4,
1091
+ texture) {
1092
+
1093
+ var vertices =
1094
+ // x, y coords | x, y texture coords
1095
+ [ x, y, 0.0, tx1, ty1,
1096
+ x + w, y, 0.0, tx2, ty2,
1097
+ x + w, y + h, 0.0, tx3, ty3,
1098
+ x, y + h, 0.0, tx4, ty4 ];
1099
+
1100
+ var colors = [
1101
+ r, g, b, a,
1102
+ r, g, b, a,
1103
+ r, g, b, a,
1104
+ r, g, b, a
1105
+ ];
1106
+
1107
+ gl.useProgram(texShaderProgram);
1108
+
1109
+ // Vertex
1110
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
1111
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
1112
+
1113
+ gl.vertexAttribPointer(texPositionLocation, 3, gl.FLOAT, false, 5*4, 0);
1114
+ gl.enableVertexAttribArray(texPositionLocation);
1115
+
1116
+ gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 5*4, 3*4);
1117
+ gl.enableVertexAttribArray(texCoordLocation);
1118
+
1119
+ // Colors
1120
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
1121
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
1122
+
1123
+ gl.vertexAttribPointer(texColorLocation, 4, gl.FLOAT, false, 0, 0);
1124
+ gl.enableVertexAttribArray(texColorLocation);
1125
+
1126
+ gl.activeTexture(gl.TEXTURE0);
1127
+ gl.bindTexture(gl.TEXTURE_2D, texture);
1128
+
1129
+ gl.uniform1i(samplerLocation, 0);
1130
+
1131
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
1132
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
1133
+
1134
+ gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
1135
+ };
1136
+
1137
+
1138
+ /*
1139
+ * Draw image
1140
+ */
1141
+ S2D.GL.DrawImage = function(img) {
1142
+ S2D.GL.DrawTexture(
1143
+ img.x, img.y, img.width, img.height,
1144
+ img.color.r, img.color.g, img.color.b, img.color.a,
1145
+ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
1146
+ img.texture
1147
+ );
1148
+ };
1149
+
1150
+
1151
+ /*
1152
+ * Draw sprite
1153
+ */
1154
+ S2D.GL.DrawSprite = function(spr) {
1155
+ S2D.GL.DrawTexture(
1156
+ spr.x, spr.y, spr.width, spr.height,
1157
+ spr.img.color.r, spr.img.color.g, spr.img.color.b, spr.img.color.a,
1158
+ spr.tx1, spr.ty1, spr.tx2, spr.ty2, spr.tx3, spr.ty3, spr.tx4, spr.ty4,
1159
+ spr.img.texture
1160
+ );
1161
+ };
1162
+
1163
+
1164
+ /*
1165
+ * Draw text
1166
+ */
1167
+ S2D.GL.DrawText = function(txt) {
1168
+ S2D.GL.DrawTexture(
1169
+ txt.x, txt.y, txt.width, txt.height,
1170
+ txt.color.r, txt.color.g, txt.color.b, txt.color.a,
1171
+ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
1172
+ txt.texture
1173
+ );
1174
+ };
1175
+
1176
+
1177
+ // end.js - Close the anonymous function defining the Simple 2D module
1178
+
1179
+ // Call anonymous and the Simple 2D module to the global scope
1180
+ }).call(this);