ruby2d 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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);