littlejsengine 1.9.7 → 1.9.9

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.
Files changed (48) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +13 -8
  3. package/dist/littlejs.d.ts +75 -69
  4. package/dist/littlejs.esm.js +313 -354
  5. package/dist/littlejs.esm.min.js +1 -1
  6. package/dist/littlejs.js +307 -350
  7. package/dist/littlejs.min.js +1 -1
  8. package/dist/littlejs.release.js +244 -319
  9. package/examples/box2d/game.js +137 -0
  10. package/examples/box2d/index.html +12 -0
  11. package/examples/box2d/scenes.js +412 -0
  12. package/examples/box2d/tiles.png +0 -0
  13. package/examples/breakout/game.js +3 -3
  14. package/examples/breakout/index.html +4 -3
  15. package/examples/breakoutTutorial/index.html +2 -2
  16. package/examples/electron/build.js +1 -1
  17. package/examples/electron/index.html +2 -2
  18. package/examples/js13k/build.js +19 -1
  19. package/examples/js13k/index.html +13 -13
  20. package/examples/module/index.html +1 -1
  21. package/examples/particles/index.html +1 -1
  22. package/examples/platformer/gameEffects.js +2 -2
  23. package/examples/platformer/gameLevel.js +0 -1
  24. package/examples/platformer/index.html +8 -8
  25. package/examples/puzzle/index.html +2 -2
  26. package/examples/starter/build.js +1 -1
  27. package/examples/starter/index.html +13 -13
  28. package/examples/stress/index.html +1 -1
  29. package/examples/typescript/index.html +1 -1
  30. package/package.json +1 -1
  31. package/plugins/Box2D_v2.3.1_min.wasm.js +630 -0
  32. package/plugins/Box2D_v2.3.1_min.wasm.wasm +0 -0
  33. package/plugins/box2d.js +879 -0
  34. package/plugins/newgrounds.js +169 -0
  35. package/plugins/postProcess.js +102 -0
  36. package/src/engine.js +48 -29
  37. package/src/engineAudio.js +20 -12
  38. package/src/engineBuild.js +1 -1
  39. package/src/engineDebug.js +68 -33
  40. package/src/engineDraw.js +1 -1
  41. package/src/engineExport.js +6 -4
  42. package/src/engineInput.js +7 -4
  43. package/src/engineMedals.js +40 -171
  44. package/src/engineObject.js +33 -2
  45. package/src/engineRelease.js +5 -2
  46. package/src/engineSettings.js +14 -2
  47. package/src/engineUtilities.js +75 -2
  48. package/src/engineWebGL.js +1 -94
package/src/engineDraw.js CHANGED
@@ -320,7 +320,7 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, conte
320
320
  context.save();
321
321
  context.translate(pos.x+.5, pos.y+.5);
322
322
  context.rotate(angle);
323
- context.scale(mirror ? -size.x : size.x, size.y);
323
+ context.scale(mirror ? -size.x : size.x, -size.y);
324
324
  drawFunction(context);
325
325
  context.restore();
326
326
  }
@@ -20,6 +20,7 @@ export {
20
20
  engineObjectsUpdate,
21
21
  engineObjectsDestroy,
22
22
  engineObjectsCallback,
23
+ engineAddPlugin,
23
24
 
24
25
  // Globals
25
26
  debug,
@@ -29,13 +30,16 @@ export {
29
30
  // Debug
30
31
  ASSERT,
31
32
  debugRect,
33
+ debugPoly,
32
34
  debugCircle,
33
35
  debugPoint,
34
36
  debugLine,
35
- debugAABB,
37
+ debugOverlap,
36
38
  debugText,
37
39
  debugClear,
38
40
  debugSaveCanvas,
41
+ debugSaveText,
42
+ debugSaveDataURL,
39
43
 
40
44
  // Settings
41
45
  cameraPos,
@@ -98,6 +102,7 @@ export {
98
102
  setObjectMaxSpeed,
99
103
  setGravity,
100
104
  setParticleEmitRateScale,
105
+ setTouchInputEnable,
101
106
  setGamepadsEnable,
102
107
  setGamepadDirectionEmulateStick,
103
108
  setInputWASDEmulateDirection,
@@ -187,7 +192,6 @@ export {
187
192
  glCompileShader,
188
193
  glCreateProgram,
189
194
  glCreateTexture,
190
- glInitPostProcess,
191
195
 
192
196
  // Input
193
197
  keyIsDown,
@@ -246,7 +250,5 @@ export {
246
250
  medals,
247
251
  medalsPreventUnlock,
248
252
  medalsInit,
249
- newgroundsInit,
250
253
  Medal,
251
- Newgrounds,
252
254
  };
@@ -3,6 +3,7 @@
3
3
  * - Tracks keyboard down, pressed, and released
4
4
  * - Tracks mouse buttons, position, and wheel
5
5
  * - Tracks multiple analog gamepads
6
+ * - Touch input is handled as mouse input
6
7
  * - Virtual gamepad for touch devices
7
8
  * @namespace Input
8
9
  */
@@ -136,7 +137,8 @@ function inputUpdate()
136
137
  if (headlessMode) return;
137
138
 
138
139
  // clear input when lost focus (prevent stuck keys)
139
- isTouchDevice || document.hasFocus() || clearInput();
140
+ if(!(touchInputEnable && isTouchDevice) && !document.hasFocus())
141
+ clearInput();
140
142
 
141
143
  // update mouse world space position
142
144
  mousePos = screenToWorld(mousePosScreen);
@@ -208,7 +210,7 @@ function inputInit()
208
210
  oncontextmenu = (e)=> false; // prevent right click menu
209
211
 
210
212
  // init touch input
211
- if (isTouchDevice)
213
+ if (isTouchDevice && touchInputEnable && !headlessMode)
212
214
  touchInputInit();
213
215
  }
214
216
 
@@ -336,12 +338,12 @@ function vibrateStop() { vibrate(0); }
336
338
 
337
339
  /** True if a touch device has been detected
338
340
  * @memberof Input */
339
- const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
341
+ const isTouchDevice = window.ontouchstart !== undefined;
340
342
 
341
343
  // touch gamepad internal variables
342
344
  let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
343
345
 
344
- // try to enable touch mouse
346
+ // enable touch input mouse passthrough
345
347
  function touchInputInit()
346
348
  {
347
349
  // add non passive touch event listeners
@@ -450,6 +452,7 @@ function touchInputInit()
450
452
  // render the touch gamepad, called automatically by the engine
451
453
  function touchGamepadRender()
452
454
  {
455
+ if (!touchInputEnable || !isTouchDevice || headlessMode) return;
453
456
  if (!touchGamepadEnable || !touchGamepadTimer.isSet())
454
457
  return;
455
458
 
@@ -9,9 +9,9 @@
9
9
  'use strict';
10
10
 
11
11
  /** List of all medals
12
- * @type {Array}
12
+ * @type {Object}
13
13
  * @memberof Medals */
14
- const medals = [];
14
+ const medals = {};
15
15
 
16
16
  // Engine internal variables not exposed to documentation
17
17
  let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
@@ -27,9 +27,46 @@ function medalsInit(saveName)
27
27
  {
28
28
  // check if medals are unlocked
29
29
  medalsSaveName = saveName;
30
- debugMedals || medals.forEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
30
+ if (!debugMedals)
31
+ medalsForEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
32
+
33
+ // engine automatically renders medals
34
+ engineAddPlugin(undefined, medalsRender);
35
+ function medalsRender()
36
+ {
37
+ if (!medalsDisplayQueue.length)
38
+ return;
39
+
40
+ // update first medal in queue
41
+ const medal = medalsDisplayQueue[0];
42
+ const time = timeReal - medalsDisplayTimeLast;
43
+ if (!medalsDisplayTimeLast)
44
+ medalsDisplayTimeLast = timeReal;
45
+ else if (time > medalDisplayTime)
46
+ {
47
+ medalsDisplayTimeLast = 0;
48
+ medalsDisplayQueue.shift();
49
+ }
50
+ else
51
+ {
52
+ // slide on/off medals
53
+ const slideOffTime = medalDisplayTime - medalDisplaySlideTime;
54
+ const hidePercent =
55
+ time < medalDisplaySlideTime ? 1 - time / medalDisplaySlideTime :
56
+ time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
57
+ medal.render(hidePercent);
58
+ }
59
+ }
31
60
  }
32
61
 
62
+ /** Calls a function for each medal
63
+ * @param {Function} callback
64
+ * @memberof Medals */
65
+ function medalsForEach(callback)
66
+ { Object.values(medals).forEach(medal=>callback(medal)); }
67
+
68
+ ///////////////////////////////////////////////////////////////////////////////
69
+
33
70
  /**
34
71
  * Medal - Tracks an unlockable medal
35
72
  * @example
@@ -74,7 +111,6 @@ class Medal
74
111
  ASSERT(medalsSaveName, 'save name must be set');
75
112
  localStorage[this.storageKey()] = this.unlocked = 1;
76
113
  medalsDisplayQueue.push(this);
77
- newgrounds && newgrounds.unlockMedal(this.id);
78
114
  }
79
115
 
80
116
  /** Render a medal
@@ -122,171 +158,4 @@ class Medal
122
158
 
123
159
  // Get local storage key used by the medal
124
160
  storageKey() { return medalsSaveName + '_' + this.id; }
125
- }
126
-
127
- // engine automatically renders medals
128
- function medalsRender()
129
- {
130
- if (!medalsDisplayQueue.length)
131
- return;
132
-
133
- // update first medal in queue
134
- const medal = medalsDisplayQueue[0];
135
- const time = timeReal - medalsDisplayTimeLast;
136
- if (!medalsDisplayTimeLast)
137
- medalsDisplayTimeLast = timeReal;
138
- else if (time > medalDisplayTime)
139
- {
140
- medalsDisplayTimeLast = 0;
141
- medalsDisplayQueue.shift();
142
- }
143
- else
144
- {
145
- // slide on/off medals
146
- const slideOffTime = medalDisplayTime - medalDisplaySlideTime;
147
- const hidePercent =
148
- time < medalDisplaySlideTime ? 1 - time / medalDisplaySlideTime :
149
- time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
150
- medal.render(hidePercent);
151
- }
152
- }
153
-
154
- ///////////////////////////////////////////////////////////////////////////////
155
-
156
- // global Newgrounds object
157
- let newgrounds;
158
-
159
- /** This can used to enable Newgrounds functionality
160
- * @param {Number} app_id - The newgrounds App ID
161
- * @param {String} [cipher] - The encryption Key (AES-128/Base64)
162
- * @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher
163
- * @memberof Medals */
164
- function newgroundsInit(app_id, cipher, cryptoJS)
165
- { newgrounds = new Newgrounds(app_id, cipher, cryptoJS); }
166
-
167
- /**
168
- * Newgrounds API wrapper object
169
- * @example
170
- * // create a newgrounds object, replace the app id with your own
171
- * const app_id = '53123:1ZuSTQ9l';
172
- * newgrounds = new Newgrounds(app_id);
173
- */
174
- class Newgrounds
175
- {
176
- /** Create a newgrounds object
177
- * @param {Number} app_id - The newgrounds App ID
178
- * @param {String} [cipher] - The encryption Key (AES-128/Base64)
179
- * @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
180
- constructor(app_id, cipher, cryptoJS)
181
- {
182
- ASSERT(!newgrounds && app_id>0, 'there can only be one newgrounds object');
183
- ASSERT(!cipher || cryptoJS, 'must provide cryptojs if there is a cipher');
184
-
185
- this.app_id = app_id;
186
- this.cipher = cipher;
187
- this.cryptoJS = cryptoJS;
188
- this.host = location ? location.hostname : '';
189
-
190
- // get session id from url search params
191
- const url = new URL(location.href);
192
- this.session_id = url.searchParams.get('ngio_session_id');
193
-
194
- if (!this.session_id)
195
- return; // only use newgrounds when logged in
196
-
197
- // get medals
198
- const medalsResult = this.call('Medal.getList');
199
- this.medals = medalsResult ? medalsResult.result.data['medals'] : [];
200
- debugMedals && console.log(this.medals);
201
- for (const newgroundsMedal of this.medals)
202
- {
203
- const medal = medals[newgroundsMedal['id']];
204
- if (medal)
205
- {
206
- // copy newgrounds medal data
207
- medal.image = new Image;
208
- medal.image.src = newgroundsMedal['icon'];
209
- medal.name = newgroundsMedal['name'];
210
- medal.description = newgroundsMedal['description'];
211
- medal.unlocked = newgroundsMedal['unlocked'];
212
- medal.difficulty = newgroundsMedal['difficulty'];
213
- medal.value = newgroundsMedal['value'];
214
-
215
- if (medal.value)
216
- medal.description = medal.description + ' (' + medal.value + ')';
217
- }
218
- }
219
-
220
- // get scoreboards
221
- const scoreboardResult = this.call('ScoreBoard.getBoards');
222
- this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
223
- debugMedals && console.log(this.scoreboards);
224
-
225
- const keepAliveMS = 5 * 60 * 1e3;
226
- setInterval(()=>this.call('Gateway.ping', 0, true), keepAliveMS);
227
- }
228
-
229
- /** Send message to unlock a medal by id
230
- * @param {Number} id - The medal id */
231
- unlockMedal(id) { return this.call('Medal.unlock', {'id':id}, true); }
232
-
233
- /** Send message to post score
234
- * @param {Number} id - The scoreboard id
235
- * @param {Number} value - The score value */
236
- postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, true); }
237
-
238
- /** Get scores from a scoreboard
239
- * @param {Number} id - The scoreboard id
240
- * @param {String} [user] - A user's id or name
241
- * @param {Number} [social] - If true, only social scores will be loaded
242
- * @param {Number} [skip] - Number of scores to skip before start
243
- * @param {Number} [limit] - Number of scores to include in the list
244
- * @return {Object} - The response JSON object
245
- */
246
- getScores(id, user, social=0, skip=0, limit=10)
247
- { return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
248
-
249
- /** Send message to log a view */
250
- logView() { return this.call('App.logView', {'host':this.host}, true); }
251
-
252
- /** Send a message to call a component of the Newgrounds API
253
- * @param {String} component - Name of the component
254
- * @param {Object} [parameters] - Parameters to use for call
255
- * @param {Boolean} [async] - If true, don't wait for response before continuing
256
- * @return {Object} - The response JSON object
257
- */
258
- call(component, parameters, async=false)
259
- {
260
- const call = {'component':component, 'parameters':parameters};
261
- if (this.cipher)
262
- {
263
- // encrypt using AES-128 Base64 with cryptoJS
264
- const cryptoJS = this.cryptoJS;
265
- const aesKey = cryptoJS['enc']['Base64']['parse'](this.cipher);
266
- const iv = cryptoJS['lib']['WordArray']['random'](16);
267
- const encrypted = cryptoJS['AES']['encrypt'](JSON.stringify(call), aesKey, {'iv':iv});
268
- call['secure'] = cryptoJS['enc']['Base64']['stringify'](iv.concat(encrypted['ciphertext']));
269
- call['parameters'] = 0;
270
- }
271
-
272
- // build the input object
273
- const input =
274
- {
275
- 'app_id': this.app_id,
276
- 'session_id': this.session_id,
277
- 'call': call
278
- };
279
-
280
- // build post data
281
- const formData = new FormData();
282
- formData.append('input', JSON.stringify(input));
283
-
284
- // send post data
285
- const xmlHttp = new XMLHttpRequest();
286
- const url = 'https://newgrounds.io/gateway_v3.php';
287
- xmlHttp.open('POST', url, !debugMedals && async);
288
- xmlHttp.send(formData);
289
- debugMedals && console.log(xmlHttp.responseText);
290
- return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
291
- }
292
161
  }
@@ -205,7 +205,7 @@ class EngineObject
205
205
  if (o.mass) // push away if not fixed
206
206
  o.velocity = o.velocity.subtract(velocity);
207
207
 
208
- debugOverlay && debugPhysics && debugAABB(this.pos, this.size, o.pos, o.size, '#f00');
208
+ debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
209
209
  continue;
210
210
  }
211
211
 
@@ -267,7 +267,7 @@ class EngineObject
267
267
  else // bounce if other object is fixed
268
268
  this.velocity.x *= -elasticity;
269
269
  }
270
- debugOverlay && debugPhysics && debugAABB(this.pos, this.size, o.pos, o.size, '#f0f');
270
+ debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f0f');
271
271
  }
272
272
  }
273
273
  if (this.collideTiles)
@@ -328,6 +328,22 @@ class EngineObject
328
328
  for (const child of this.children)
329
329
  child.destroy(child.parent = 0);
330
330
  }
331
+
332
+ /** Convert from local space to world space
333
+ * @param {Vector2} pos - local space point */
334
+ localToWorld(pos) { return this.pos.add(pos.rotate(-this.angle)); }
335
+
336
+ /** Convert from world space to local space
337
+ * @param {Vector2} pos - world space point */
338
+ worldToLocal(pos) { return pos.subtract(this.pos).rotate(this.angle); }
339
+
340
+ /** Convert from local space to world space for a vector (rotation only)
341
+ * @param {Vector2} vec - local space vector */
342
+ localToWorldVector(vec) { return vec.rotate(this.angle); }
343
+
344
+ /** Convert from world space to local space for a vector (rotation only)
345
+ * @param {Vector2} vec - world space vector */
346
+ worldToLocalVector(vec) { return vec.rotate(-this.angle); }
331
347
 
332
348
  /** Called to check if a tile collision should be resolved
333
349
  * @param {Number} tileData - the value of the tile at the position
@@ -414,4 +430,19 @@ class EngineObject
414
430
  return text;
415
431
  }
416
432
  }
433
+
434
+ /** Render debug info for this object */
435
+ renderDebugInfo()
436
+ {
437
+ if (debug)
438
+ {
439
+ // show object info for debugging
440
+ const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
441
+ const color1 = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, this.parent?.2:.5);
442
+ const color2 = this.parent ? rgb(1,1,1,.5) : rgb(0,0,0,.8);
443
+ drawRect(this.pos, size, color1, this.angle, false);
444
+ drawRect(this.pos, size.scale(.8), color2, this.angle, false);
445
+ this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(0,0,1,.5), false);
446
+ }
447
+ }
417
448
  }
@@ -22,10 +22,13 @@ function debugInit (){}
22
22
  function debugUpdate (){}
23
23
  function debugRender (){}
24
24
  function debugRect (){}
25
+ function debugPoly (){}
25
26
  function debugCircle (){}
26
27
  function debugPoint (){}
27
28
  function debugLine (){}
28
- function debugAABB (){}
29
+ function debugOverlap (){}
29
30
  function debugText (){}
30
31
  function debugClear (){}
31
- function debugSaveCanvas (){}
32
+ function debugSaveCanvas (){}
33
+ function debugSaveText (){}
34
+ function debugSaveDataURL(){}
@@ -26,9 +26,9 @@ let cameraScale = 32;
26
26
 
27
27
  /** The max size of the canvas, centered if window is larger
28
28
  * @type {Vector2}
29
- * @default Vector2(1920,1200)
29
+ * @default Vector2(1920,1080)
30
30
  * @memberof Settings */
31
- let canvasMaxSize = vec2(1920, 1200);
31
+ let canvasMaxSize = vec2(1920, 1080);
32
32
 
33
33
  /** Fixed size of the canvas, if enabled canvas size never changes
34
34
  * - you may also need to set mainCanvasSize if using screen space coords in startup
@@ -169,6 +169,13 @@ let gamepadDirectionEmulateStick = true;
169
169
  * @memberof Settings */
170
170
  let inputWASDEmulateDirection = true;
171
171
 
172
+ /** True if touch input is enabled for mobile devices
173
+ * - Touch events will be routed to mouse events
174
+ * @type {Boolean}
175
+ * @default
176
+ * @memberof Settings */
177
+ let touchInputEnable = true;
178
+
172
179
  /** True if touch gamepad should appear on mobile devices
173
180
  * - Supports left analog stick, 4 face buttons and start button (button 9)
174
181
  * - Must be set by end of gameInit to be activated
@@ -384,6 +391,11 @@ function setGamepadDirectionEmulateStick(enable) { gamepadDirectionEmulateStick
384
391
  * @memberof Settings */
385
392
  function setInputWASDEmulateDirection(enable) { inputWASDEmulateDirection = enable; }
386
393
 
394
+ /** Set if touch input is allowed
395
+ * @param {Boolean} enable
396
+ * @memberof Settings */
397
+ function setTouchInputEnable(enable) { touchInputEnable = enable; }
398
+
387
399
  /** Set if touch gamepad should appear on mobile devices
388
400
  * @param {Boolean} enable
389
401
  * @memberof Settings */
@@ -301,7 +301,7 @@ class RandomGenerator
301
301
  */
302
302
  function vec2(x=0, y)
303
303
  {
304
- return typeof x === 'number' ?
304
+ return typeof x == 'number' ?
305
305
  new Vector2(x, y == undefined? x : y) :
306
306
  new Vector2(x.x, x.y);
307
307
  }
@@ -330,13 +330,19 @@ class Vector2
330
330
  * @param {Number} [y] - Y axis location */
331
331
  constructor(x=0, y=0)
332
332
  {
333
- ASSERT(typeof x === 'number' && typeof y === 'number');
333
+ ASSERT(typeof x == 'number' && typeof y == 'number');
334
334
  /** @property {Number} - X axis location */
335
335
  this.x = x;
336
336
  /** @property {Number} - Y axis location */
337
337
  this.y = y;
338
338
  }
339
339
 
340
+ /** Sets values of this vector and returns self
341
+ * @param {Number} [x] - X axis location
342
+ * @param {Number} [y] - Y axis location
343
+ * @return {Vector2} */
344
+ set(x=0, y=0) { this.x=x; this.y=y; return this; }
345
+
340
346
  /** Returns a new vector that is a copy of this
341
347
  * @return {Vector2} */
342
348
  copy() { return new Vector2(this.x, this.y); }
@@ -588,6 +594,15 @@ class Color
588
594
  this.a = a;
589
595
  }
590
596
 
597
+ /** Sets values of this color and returns self
598
+ * @param {Number} [r] - red
599
+ * @param {Number} [g] - green
600
+ * @param {Number} [b] - blue
601
+ * @param {Number} [a] - alpha
602
+ * @return {Color} */
603
+ set(r=1, g=1, b=1, a=1)
604
+ { this.r=r; this.g=g; this.b=b; this.a=a; return this; }
605
+
591
606
  /** Returns a new color that is a copy of this
592
607
  * @return {Color} */
593
608
  copy() { return new Color(this.r, this.g, this.b, this.a); }
@@ -749,6 +764,64 @@ class Color
749
764
  }
750
765
  }
751
766
 
767
+ ///////////////////////////////////////////////////////////////////////////////
768
+ // default colors
769
+
770
+ /** Color - White
771
+ * @type {Color}
772
+ * @memberof Utilities */
773
+ const WHITE = rgb();
774
+
775
+ /** Color - Black
776
+ * @type {Color}
777
+ * @memberof Utilities */
778
+ const BLACK = rgb(0,0,0);
779
+
780
+ /** Color - Gray
781
+ * @type {Color}
782
+ * @memberof Utilities */
783
+ const GRAY = rgb(.5,.5,.5);
784
+
785
+ /** Color - Red
786
+ * @type {Color}
787
+ * @memberof Utilities */
788
+ const RED = rgb(1,0,0);
789
+
790
+ /** Color - Orange
791
+ * @type {Color}
792
+ * @memberof Utilities */
793
+ const ORANGE = rgb(1,.5,0);
794
+
795
+ /** Color - Yellow
796
+ * @type {Color}
797
+ * @memberof Utilities */
798
+ const YELLOW = rgb(1,1,0);
799
+
800
+ /** Color - Green
801
+ * @type {Color}
802
+ * @memberof Utilities */
803
+ const GREEN = rgb(0,1,0);
804
+
805
+ /** Color - Cyan
806
+ * @type {Color}
807
+ * @memberof Utilities */
808
+ const CYAN = rgb(0,1,1);
809
+
810
+ /** Color - Blue
811
+ * @type {Color}
812
+ * @memberof Utilities */
813
+ const BLUE = rgb(0,0,1);
814
+
815
+ /** Color - Purple
816
+ * @type {Color}
817
+ * @memberof Utilities */
818
+ const PURPLE = rgb(.5,0,1);
819
+
820
+ /** Color - Magenta
821
+ * @type {Color}
822
+ * @memberof Utilities */
823
+ const MAGENTA = rgb(1,0,1);
824
+
752
825
  ///////////////////////////////////////////////////////////////////////////////
753
826
 
754
827
  /**
@@ -87,7 +87,7 @@ function glPreRender()
87
87
 
88
88
  // clear and set to same size as main canvas
89
89
  glContext.viewport(0, 0, glCanvas.width=mainCanvas.width, glCanvas.height=mainCanvas.height);
90
- //glContext.clear(gl_COLOR_BUFFER_BIT); // auto cleared when size is set
90
+ glContext.clear(gl_COLOR_BUFFER_BIT);
91
91
 
92
92
  // set up the shader
93
93
  glContext.useProgram(glShader);
@@ -270,99 +270,6 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
270
270
  glInstanceCount++;
271
271
  }
272
272
 
273
- ///////////////////////////////////////////////////////////////////////////////
274
- // post processing - can be enabled to pass other canvases through a final shader
275
-
276
- let glPostShader, glPostTexture, glPostIncludeOverlay;
277
-
278
- /** Set up a post processing shader
279
- * @param {String} shaderCode
280
- * @param {Boolean} includeOverlay
281
- * @memberof WebGL */
282
- function glInitPostProcess(shaderCode, includeOverlay=false)
283
- {
284
- ASSERT(!glPostShader, 'can only have 1 post effects shader');
285
- if (headlessMode) return;
286
- if (!shaderCode) // default shader pass through
287
- shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
288
-
289
- // create the shader
290
- glPostShader = glCreateProgram(
291
- '#version 300 es\n' + // specify GLSL ES version
292
- 'precision highp float;'+ // use highp for better accuracy
293
- 'in vec2 p;'+ // position
294
- 'void main(){'+ // shader entry point
295
- 'gl_Position=vec4(p+p-1.,1,1);'+ // set position
296
- '}' // end of shader
297
- ,
298
- '#version 300 es\n' + // specify GLSL ES version
299
- 'precision highp float;'+ // use highp for better accuracy
300
- 'uniform sampler2D iChannel0;'+ // input texture
301
- 'uniform vec3 iResolution;'+ // size of output texture
302
- 'uniform float iTime;'+ // time
303
- 'out vec4 c;'+ // out color
304
- '\n' + shaderCode + '\n'+ // insert custom shader code
305
- 'void main(){'+ // shader entry point
306
- 'mainImage(c,gl_FragCoord.xy);'+ // call post process function
307
- 'c.a=1.;'+ // always use full alpha
308
- '}' // end of shader
309
- );
310
-
311
- // create buffer and texture
312
- glPostTexture = glCreateTexture(undefined);
313
- glPostIncludeOverlay = includeOverlay;
314
-
315
- // hide the original 2d canvas
316
- mainCanvas.style.visibility = 'hidden';
317
- if (glPostIncludeOverlay)
318
- overlayCanvas.style.visibility = 'hidden';
319
- }
320
-
321
- // Render the post processing shader, called automatically by the engine
322
- function glRenderPostProcess()
323
- {
324
- if (!glPostShader || headlessMode) return;
325
-
326
- // prepare to render post process shader
327
- if (glEnable)
328
- {
329
- glFlush(); // clear out the buffer
330
- mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
331
- }
332
- else
333
- {
334
- // set the viewport
335
- glContext.viewport(0, 0, glCanvas.width = mainCanvas.width, glCanvas.height = mainCanvas.height);
336
- }
337
-
338
- // copy overlay canvas so it will be included in post processing
339
- glPostIncludeOverlay && mainContext.drawImage(overlayCanvas, 0, 0);
340
-
341
- // setup shader program to draw one triangle
342
- glContext.useProgram(glPostShader);
343
- glContext.bindBuffer(gl_ARRAY_BUFFER, glGeometryBuffer);
344
- glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, 1);
345
- glContext.disable(gl_BLEND);
346
-
347
- // set textures, pass in the 2d canvas and gl canvas in separate texture channels
348
- glContext.activeTexture(gl_TEXTURE0);
349
- glContext.bindTexture(gl_TEXTURE_2D, glPostTexture);
350
- glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, mainCanvas);
351
-
352
- // set vertex position attribute
353
- const vertexByteStride = 8;
354
- const pLocation = glContext.getAttribLocation(glPostShader, 'p');
355
- glContext.enableVertexAttribArray(pLocation);
356
- glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, false, vertexByteStride, 0);
357
-
358
- // set uniforms and draw
359
- const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
360
- glContext.uniform1i(uniformLocation('iChannel0'), 0);
361
- glContext.uniform1f(uniformLocation('iTime'), time);
362
- glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
363
- glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 4);
364
- }
365
-
366
273
  ///////////////////////////////////////////////////////////////////////////////
367
274
  // store gl constants as integers so their name doesn't use space in minifed
368
275
  const