littlejsengine 1.3.8 → 1.4.5
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.
- package/README.md +30 -9
- package/build.bat +1 -1
- package/buildSetup.bat +2 -2
- package/engine/engine.all.js +276 -167
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.module.js +4812 -0
- package/engine/engine.all.release.js +259 -143
- package/engine/engine.all.release.module.js +4457 -0
- package/engine/engine.js +12 -8
- package/engine/engineBuild.bat +1 -1
- package/engine/engineBuildModule.mjs +14 -0
- package/engine/engineDebug.js +17 -24
- package/engine/engineDraw.js +16 -16
- package/engine/engineExportModule.js +333 -0
- package/engine/engineInput.js +9 -8
- package/engine/engineMedals.js +10 -6
- package/engine/engineObject.js +4 -4
- package/engine/engineParticles.js +45 -21
- package/engine/engineTileLayer.js +2 -2
- package/engine/engineUtilities.js +47 -25
- package/engine/engineWebGL.js +114 -54
- package/engine/index.d.ts +64 -30
- package/examples/breakout/game.js +63 -11
- package/examples/breakout/gameObjects.js +14 -12
- package/examples/breakout/index.html +3 -3
- package/examples/empty/game.js +35 -0
- package/examples/empty/index.html +9 -0
- package/examples/empty/tiles.png +0 -0
- package/examples/module/game.js +122 -0
- package/examples/module/index.html +9 -0
- package/examples/module/tiles.png +0 -0
- package/examples/particles/index.html +14 -14
- package/examples/particles/tiles.png +0 -0
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameObjects.js +7 -7
- package/examples/platformer/gamePlayer.js +7 -5
- package/examples/platformer/index.html +6 -6
- package/examples/puzzle/index.html +2 -2
- package/examples/stress/index.html +2 -2
- package/game.js +10 -12
- package/index.html +13 -13
- package/package.json +36 -32
package/engine/engineMedals.js
CHANGED
|
@@ -16,11 +16,6 @@ const medals = [];
|
|
|
16
16
|
* @memberof Medals */
|
|
17
17
|
let medalsPreventUnlock;
|
|
18
18
|
|
|
19
|
-
/** This can used to enable Newgrounds functionality
|
|
20
|
-
* @type {Newgrounds}
|
|
21
|
-
* @memberof Medals */
|
|
22
|
-
let newgrounds;
|
|
23
|
-
|
|
24
19
|
// Engine internal variables not exposed to documentation
|
|
25
20
|
let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
|
|
26
21
|
|
|
@@ -35,7 +30,7 @@ function medalsInit(saveName)
|
|
|
35
30
|
{
|
|
36
31
|
// check if medals are unlocked
|
|
37
32
|
medalsSaveName = saveName;
|
|
38
|
-
debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
|
|
33
|
+
debugMedals || medals.forEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
|
|
39
34
|
}
|
|
40
35
|
|
|
41
36
|
/**
|
|
@@ -168,6 +163,15 @@ function medalsRender()
|
|
|
168
163
|
|
|
169
164
|
///////////////////////////////////////////////////////////////////////////////
|
|
170
165
|
|
|
166
|
+
// global Newgrounds object
|
|
167
|
+
let newgrounds;
|
|
168
|
+
|
|
169
|
+
/** This can used to enable Newgrounds functionality
|
|
170
|
+
* @param {Number} app_id - The newgrounds App ID
|
|
171
|
+
* @param {String} [cipher] - The encryption Key (AES-128/Base64)
|
|
172
|
+
* @memberof Medals */
|
|
173
|
+
function newgroundsInit(app_id, cipher) { newgrounds = new Newgrounds(app_id, cipher); }
|
|
174
|
+
|
|
171
175
|
/**
|
|
172
176
|
* Newgrounds API wrapper object
|
|
173
177
|
* @example
|
package/engine/engineObject.js
CHANGED
|
@@ -296,7 +296,7 @@ class EngineObject
|
|
|
296
296
|
* @return {Boolean} - true if the raycast should hit */
|
|
297
297
|
collideWithTileRaycast(tileData, pos) { return tileData > 0; }
|
|
298
298
|
|
|
299
|
-
/** Called to check if a
|
|
299
|
+
/** Called to check if a object collision should be resolved
|
|
300
300
|
* @param {EngineObject} object - the object to test against
|
|
301
301
|
* @return {Boolean} - true if the collision should be resolved
|
|
302
302
|
*/
|
|
@@ -341,10 +341,10 @@ class EngineObject
|
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
/** Set how this object collides
|
|
344
|
-
* @param {boolean} [collideSolidObjects=
|
|
345
|
-
* @param {boolean} [isSolid=
|
|
344
|
+
* @param {boolean} [collideSolidObjects=1] - Does it collide with solid objects
|
|
345
|
+
* @param {boolean} [isSolid=1] - Does it collide with and block other objects (expensive in large numbers)
|
|
346
346
|
* @param {boolean} [collideTiles=1] - Does it collide with the tile collision */
|
|
347
|
-
setCollision(collideSolidObjects=
|
|
347
|
+
setCollision(collideSolidObjects=1, isSolid=1, collideTiles=1)
|
|
348
348
|
{
|
|
349
349
|
ASSERT(collideSolidObjects || !isSolid); // solid objects must be set to collide
|
|
350
350
|
|
|
@@ -54,6 +54,7 @@ class ParticleEmitter extends EngineObject
|
|
|
54
54
|
* @param {Boolean} [additive=0] - Should particles use addtive blend
|
|
55
55
|
* @param {Boolean} [randomColorLinear=1] - Should color be randomized linearly or across each component
|
|
56
56
|
* @param {Number} [renderOrder=0] - Render order for particles (additive is above other stuff by default)
|
|
57
|
+
* @param {Boolean} [localSpace=0] - Should it be in local space of emitter (world space is default)
|
|
57
58
|
*/
|
|
58
59
|
constructor
|
|
59
60
|
(
|
|
@@ -83,7 +84,8 @@ class ParticleEmitter extends EngineObject
|
|
|
83
84
|
collideTiles,
|
|
84
85
|
additive,
|
|
85
86
|
randomColorLinear = 1,
|
|
86
|
-
renderOrder = additive ? 1e9 : 0
|
|
87
|
+
renderOrder = additive ? 1e9 : 0,
|
|
88
|
+
localSpace
|
|
87
89
|
)
|
|
88
90
|
{
|
|
89
91
|
super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
|
|
@@ -137,6 +139,8 @@ class ParticleEmitter extends EngineObject
|
|
|
137
139
|
this.collideTiles = collideTiles;
|
|
138
140
|
/** @property {Number} - Should particles use addtive blend */
|
|
139
141
|
this.additive = additive;
|
|
142
|
+
/** @property {Boolean} - Should it be in local space of emitter */
|
|
143
|
+
this.localSpace = localSpace;
|
|
140
144
|
/** @property {Number} - If set the partile is drawn as a trail, stretched in the drection of velocity */
|
|
141
145
|
this.trailScale = 0;
|
|
142
146
|
|
|
@@ -172,30 +176,38 @@ class ParticleEmitter extends EngineObject
|
|
|
172
176
|
emitParticle()
|
|
173
177
|
{
|
|
174
178
|
// spawn a particle
|
|
175
|
-
|
|
176
|
-
(new Vector2(rand(-.5,.5), rand(-.5,.5)))
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
179
|
+
let pos = this.emitSize.x != undefined ? // check if vec2 was used for size
|
|
180
|
+
(new Vector2(rand(-.5,.5), rand(-.5,.5)))
|
|
181
|
+
.multiply(this.emitSize).rotate(this.angle) // box emitter
|
|
182
|
+
: randInCircle(this.emitSize * .5); // circle emitter
|
|
183
|
+
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
184
|
+
if (!this.localSpace)
|
|
185
|
+
{
|
|
186
|
+
pos = this.pos.add(pos);
|
|
187
|
+
angle += this.angle;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const particle = new Particle(pos, this.tileIndex, this.tileSize, angle);
|
|
180
191
|
|
|
181
192
|
// randomness scales each paremeter by a percentage
|
|
182
193
|
const randomness = this.randomness;
|
|
183
194
|
const randomizeScale = (v)=> v + v*rand(randomness, -randomness);
|
|
184
195
|
|
|
185
196
|
// randomize particle settings
|
|
186
|
-
const particleTime
|
|
187
|
-
const sizeStart
|
|
188
|
-
const sizeEnd
|
|
189
|
-
const speed
|
|
190
|
-
const angleSpeed
|
|
191
|
-
const coneAngle
|
|
192
|
-
const colorStart
|
|
193
|
-
const colorEnd
|
|
197
|
+
const particleTime = randomizeScale(this.particleTime);
|
|
198
|
+
const sizeStart = randomizeScale(this.sizeStart);
|
|
199
|
+
const sizeEnd = randomizeScale(this.sizeEnd);
|
|
200
|
+
const speed = randomizeScale(this.speed);
|
|
201
|
+
const angleSpeed = randomizeScale(this.angleSpeed) * randSign();
|
|
202
|
+
const coneAngle = rand(this.emitConeAngle, -this.emitConeAngle);
|
|
203
|
+
const colorStart = randColor(this.colorStartA, this.colorStartB, this.randomColorLinear);
|
|
204
|
+
const colorEnd = randColor(this.colorEndA, this.colorEndB, this.randomColorLinear);
|
|
205
|
+
const velocityAngle = this.localSpace ? coneAngle : this.angle + coneAngle;
|
|
194
206
|
|
|
195
207
|
// build particle settings
|
|
196
208
|
particle.colorStart = colorStart;
|
|
197
209
|
particle.colorEndDelta = colorEnd.subtract(colorStart);
|
|
198
|
-
particle.velocity = (new Vector2).setAngle(
|
|
210
|
+
particle.velocity = (new Vector2).setAngle(velocityAngle, speed);
|
|
199
211
|
particle.angleVelocity = angleSpeed;
|
|
200
212
|
particle.lifeTime = particleTime;
|
|
201
213
|
particle.sizeStart = sizeStart;
|
|
@@ -211,6 +223,7 @@ class ParticleEmitter extends EngineObject
|
|
|
211
223
|
particle.renderOrder = this.renderOrder;
|
|
212
224
|
particle.trailScale = this.trailScale;
|
|
213
225
|
particle.mirror = rand()<.5;
|
|
226
|
+
particle.localSpaceEmitter = this.localSpace && this;
|
|
214
227
|
|
|
215
228
|
// setup callbacks for particles
|
|
216
229
|
particle.destroyCallback = this.particleDestroyCallback;
|
|
@@ -257,20 +270,31 @@ class Particle extends EngineObject
|
|
|
257
270
|
|
|
258
271
|
// draw the particle
|
|
259
272
|
this.additive && setBlendMode(1);
|
|
273
|
+
|
|
274
|
+
let pos = this.pos, angle = this.angle;
|
|
275
|
+
if (this.localSpaceEmitter)
|
|
276
|
+
{
|
|
277
|
+
// in local space of emitter
|
|
278
|
+
pos = this.localSpaceEmitter.pos.add(pos.rotate(-this.localSpaceEmitter.angle));
|
|
279
|
+
angle += this.localSpaceEmitter.angle;
|
|
280
|
+
}
|
|
260
281
|
if (this.trailScale)
|
|
261
282
|
{
|
|
262
283
|
// trail style particles
|
|
263
|
-
|
|
264
|
-
|
|
284
|
+
let velocity = this.velocity;
|
|
285
|
+
if (this.localSpaceEmitter)
|
|
286
|
+
velocity = velocity.rotate(-this.localSpaceEmitter.angle);
|
|
287
|
+
const speed = velocity.length();
|
|
288
|
+
const direction = velocity.scale(1/speed);
|
|
265
289
|
const trailLength = speed * this.trailScale;
|
|
266
290
|
size.y = max(size.x, trailLength);
|
|
267
|
-
|
|
268
|
-
drawTile(
|
|
291
|
+
angle = direction.angle();
|
|
292
|
+
drawTile(pos.add(direction.multiply(vec2(0,-trailLength/2))), size, this.tileIndex, this.tileSize, color, angle, this.mirror);
|
|
269
293
|
}
|
|
270
294
|
else
|
|
271
|
-
drawTile(
|
|
295
|
+
drawTile(pos, size, this.tileIndex, this.tileSize, color, angle, this.mirror);
|
|
272
296
|
this.additive && setBlendMode();
|
|
273
|
-
debugParticles && debugRect(
|
|
297
|
+
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
274
298
|
|
|
275
299
|
if (p == 1)
|
|
276
300
|
{
|
|
@@ -254,7 +254,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
254
254
|
redrawEnd()
|
|
255
255
|
{
|
|
256
256
|
ASSERT(mainContext == this.context); // must call redrawStart() before drawing tiles
|
|
257
|
-
glCopyToContext(mainContext, 1);
|
|
257
|
+
glEnable && glCopyToContext(mainContext, 1);
|
|
258
258
|
//debugSaveCanvas(this.canvas);
|
|
259
259
|
|
|
260
260
|
// set stuff back to normal
|
|
@@ -328,7 +328,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
|
|
|
328
328
|
const cols = tileImage.width/tileSize.x;
|
|
329
329
|
context.globalAlpha = color.a; // only alpha, no color, is supported in this mode
|
|
330
330
|
context.drawImage(tileImage,
|
|
331
|
-
(tileIndex%cols)*tileSize.x, (tileIndex/cols|0)*tileSize.
|
|
331
|
+
(tileIndex%cols)*tileSize.x, (tileIndex/cols|0)*tileSize.y,
|
|
332
332
|
tileSize.x, tileSize.y, -.5, -.5, 1, 1);
|
|
333
333
|
}
|
|
334
334
|
});
|
|
@@ -322,6 +322,28 @@ class Vector2
|
|
|
322
322
|
|
|
323
323
|
///////////////////////////////////////////////////////////////////////////////
|
|
324
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Create a color object with RGBA values
|
|
327
|
+
* @param {Number} [r=1]
|
|
328
|
+
* @param {Number} [g=1]
|
|
329
|
+
* @param {Number} [b=1]
|
|
330
|
+
* @param {Number} [a=1]
|
|
331
|
+
* @return {Color}
|
|
332
|
+
* @memberof Utilities
|
|
333
|
+
*/
|
|
334
|
+
const colorRGBA = (r, g, b, a)=> new Color(r, g, b, a);
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Create a color object with HSLA values
|
|
338
|
+
* @param {Number} [h=0]
|
|
339
|
+
* @param {Number} [s=0]
|
|
340
|
+
* @param {Number} [l=1]
|
|
341
|
+
* @param {Number} [a=1]
|
|
342
|
+
* @return {Color}
|
|
343
|
+
* @memberof Utilities
|
|
344
|
+
*/
|
|
345
|
+
const colorHSLA = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
|
|
346
|
+
|
|
325
347
|
/**
|
|
326
348
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
327
349
|
* @example
|
|
@@ -413,10 +435,10 @@ class Color
|
|
|
413
435
|
* @return {Array} */
|
|
414
436
|
getHSLA()
|
|
415
437
|
{
|
|
416
|
-
const r = this.r;
|
|
417
|
-
const g = this.g;
|
|
418
|
-
const b = this.b;
|
|
419
|
-
const a = this.a;
|
|
438
|
+
const r = clamp(this.r);
|
|
439
|
+
const g = clamp(this.g);
|
|
440
|
+
const b = clamp(this.b);
|
|
441
|
+
const a = clamp(this.a);
|
|
420
442
|
const max = Math.max(r, g, b);
|
|
421
443
|
const min = Math.min(r, g, b);
|
|
422
444
|
const l = (max + min) / 2;
|
|
@@ -452,20 +474,13 @@ class Color
|
|
|
452
474
|
).clamp();
|
|
453
475
|
}
|
|
454
476
|
|
|
455
|
-
/** Returns this color expressed as
|
|
477
|
+
/** Returns this color expressed as a hex color code
|
|
478
|
+
* @param {Boolean} [useAlpha=1] - if alpha should be included in result
|
|
456
479
|
* @return {String} */
|
|
457
|
-
toString()
|
|
480
|
+
toString(useAlpha = 1)
|
|
458
481
|
{
|
|
459
|
-
|
|
460
|
-
return
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/** Returns this color expressed as 32 bit integer RGBA value
|
|
464
|
-
* @return {Number} */
|
|
465
|
-
rgbaInt()
|
|
466
|
-
{
|
|
467
|
-
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
468
|
-
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
482
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
483
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
469
484
|
}
|
|
470
485
|
|
|
471
486
|
/** Set this color from a hex code
|
|
@@ -473,21 +488,24 @@ class Color
|
|
|
473
488
|
* @return {Color} */
|
|
474
489
|
setHex(hex)
|
|
475
490
|
{
|
|
476
|
-
const fromHex = (
|
|
491
|
+
const fromHex = (c)=> clamp(parseInt(hex.slice(c,c+2),16)/255);
|
|
477
492
|
this.r = fromHex(1);
|
|
478
493
|
this.g = fromHex(3),
|
|
479
494
|
this.b = fromHex(5);
|
|
480
|
-
this.a = 1;
|
|
481
|
-
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
495
|
+
this.a = hex.length > 7 ? fromHex(7) : 1;
|
|
482
496
|
return this;
|
|
483
497
|
}
|
|
484
|
-
|
|
485
|
-
/** Returns this color expressed as
|
|
486
|
-
* @return {
|
|
487
|
-
|
|
498
|
+
|
|
499
|
+
/** Returns this color expressed as 32 bit RGBA value
|
|
500
|
+
* @return {Number} */
|
|
501
|
+
rgbaInt()
|
|
488
502
|
{
|
|
489
|
-
const
|
|
490
|
-
|
|
503
|
+
const toByte = (c)=> clamp(c)*255|0;
|
|
504
|
+
const r = toByte(this.r);
|
|
505
|
+
const g = toByte(this.g)<<8;
|
|
506
|
+
const b = toByte(this.b)<<16;
|
|
507
|
+
const a = toByte(this.a)<<24;
|
|
508
|
+
return r + g + b + a;
|
|
491
509
|
}
|
|
492
510
|
}
|
|
493
511
|
|
|
@@ -538,4 +556,8 @@ class Timer
|
|
|
538
556
|
/** Returns this timer expressed as a string
|
|
539
557
|
* @return {String} */
|
|
540
558
|
toString() { if (debug) { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); } }
|
|
559
|
+
|
|
560
|
+
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
561
|
+
* @return {Number} */
|
|
562
|
+
valueOf() { return this.get(); }
|
|
541
563
|
}
|
package/engine/engineWebGL.js
CHANGED
|
@@ -27,15 +27,13 @@ let glContext;
|
|
|
27
27
|
let glTileTexture;
|
|
28
28
|
|
|
29
29
|
// WebGL internal variables not exposed to documentation
|
|
30
|
-
let glActiveTexture, glShader, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
|
|
30
|
+
let glActiveTexture, glShader, glArrayBuffer, glVertexData, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
|
|
31
31
|
|
|
32
32
|
///////////////////////////////////////////////////////////////////////////////
|
|
33
33
|
|
|
34
34
|
// Init WebGL, called automatically by the engine
|
|
35
35
|
function glInit()
|
|
36
36
|
{
|
|
37
|
-
if (!glEnable) return;
|
|
38
|
-
|
|
39
37
|
// create the canvas and tile texture
|
|
40
38
|
glCanvas = document.createElement('canvas');
|
|
41
39
|
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
@@ -68,24 +66,11 @@ function glInit()
|
|
|
68
66
|
);
|
|
69
67
|
|
|
70
68
|
// init buffers
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
glVertexData = new ArrayBuffer(gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STRIDE);
|
|
70
|
+
glArrayBuffer = glContext.createBuffer();
|
|
73
71
|
glPositionData = new Float32Array(glVertexData);
|
|
74
72
|
glColorData = new Uint32Array(glVertexData);
|
|
75
|
-
|
|
76
|
-
// setup the vertex data array
|
|
77
|
-
let offset = glBatchCount = 0;
|
|
78
|
-
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
79
|
-
{
|
|
80
|
-
const location = glContext.getAttribLocation(glShader, name);
|
|
81
|
-
glContext.enableVertexAttribArray(location);
|
|
82
|
-
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
83
|
-
offset += size*typeSize;
|
|
84
|
-
}
|
|
85
|
-
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
86
|
-
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
87
|
-
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
88
|
-
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
73
|
+
glBatchCount = 0;
|
|
89
74
|
}
|
|
90
75
|
|
|
91
76
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
@@ -93,8 +78,6 @@ function glInit()
|
|
|
93
78
|
* @memberof WebGL */
|
|
94
79
|
function glSetBlendMode(additive)
|
|
95
80
|
{
|
|
96
|
-
if (!glEnable) return;
|
|
97
|
-
|
|
98
81
|
// setup blending
|
|
99
82
|
glAdditive = additive;
|
|
100
83
|
}
|
|
@@ -105,8 +88,6 @@ function glSetBlendMode(additive)
|
|
|
105
88
|
* @memberof WebGL */
|
|
106
89
|
function glSetTexture(texture=glTileTexture)
|
|
107
90
|
{
|
|
108
|
-
if (!glEnable) return;
|
|
109
|
-
|
|
110
91
|
// must flush cache with the old texture to set a new one
|
|
111
92
|
if (texture != glActiveTexture)
|
|
112
93
|
glFlush();
|
|
@@ -121,8 +102,6 @@ function glSetTexture(texture=glTileTexture)
|
|
|
121
102
|
* @memberof WebGL */
|
|
122
103
|
function glCompileShader(source, type)
|
|
123
104
|
{
|
|
124
|
-
if (!glEnable) return;
|
|
125
|
-
|
|
126
105
|
// build the shader
|
|
127
106
|
const shader = glContext.createShader(type);
|
|
128
107
|
glContext.shaderSource(shader, source);
|
|
@@ -141,8 +120,6 @@ function glCompileShader(source, type)
|
|
|
141
120
|
* @memberof WebGL */
|
|
142
121
|
function glCreateProgram(vsSource, fsSource)
|
|
143
122
|
{
|
|
144
|
-
if (!glEnable) return;
|
|
145
|
-
|
|
146
123
|
// build the program
|
|
147
124
|
const program = glContext.createProgram();
|
|
148
125
|
glContext.attachShader(program, glCompileShader(vsSource, gl_VERTEX_SHADER));
|
|
@@ -155,35 +132,16 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
155
132
|
return program;
|
|
156
133
|
}
|
|
157
134
|
|
|
158
|
-
/** Create WebGL buffer
|
|
159
|
-
* @param bufferType
|
|
160
|
-
* @param size
|
|
161
|
-
* @param usage
|
|
162
|
-
* @return {WebGLBuffer}
|
|
163
|
-
* @memberof WebGL */
|
|
164
|
-
function glCreateBuffer(bufferType, size, usage)
|
|
165
|
-
{
|
|
166
|
-
if (!glEnable) return;
|
|
167
|
-
|
|
168
|
-
// build the buffer
|
|
169
|
-
const buffer = glContext.createBuffer();
|
|
170
|
-
glContext.bindBuffer(bufferType, buffer);
|
|
171
|
-
glContext.bufferData(bufferType, size, usage);
|
|
172
|
-
return buffer;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
135
|
/** Create WebGL texture from an image and set the texture settings
|
|
176
136
|
* @param {Image} image
|
|
177
137
|
* @return {WebGLTexture}
|
|
178
138
|
* @memberof WebGL */
|
|
179
139
|
function glCreateTexture(image)
|
|
180
140
|
{
|
|
181
|
-
if (!glEnable || !image || !image.width) return;
|
|
182
|
-
|
|
183
141
|
// build the texture
|
|
184
142
|
const texture = glContext.createTexture();
|
|
185
143
|
glContext.bindTexture(gl_TEXTURE_2D, texture);
|
|
186
|
-
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
144
|
+
image && image.width && glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
187
145
|
|
|
188
146
|
// use point filtering for pixelated rendering
|
|
189
147
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, cavasPixelated ? gl_NEAREST : gl_LINEAR);
|
|
@@ -196,16 +154,31 @@ function glCreateTexture(image)
|
|
|
196
154
|
// called automatically by engine before render
|
|
197
155
|
function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
198
156
|
{
|
|
199
|
-
if (!glEnable) return;
|
|
200
|
-
|
|
201
157
|
// clear and set to same size as main canvas
|
|
202
158
|
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height);
|
|
203
159
|
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
204
160
|
|
|
205
161
|
// set up the shader
|
|
206
|
-
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
207
162
|
glContext.useProgram(glShader);
|
|
163
|
+
glContext.activeTexture(gl_TEXTURE0);
|
|
164
|
+
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
165
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
|
|
166
|
+
glContext.bufferData(gl_ARRAY_BUFFER, glVertexData.byteLength, gl_DYNAMIC_DRAW);
|
|
208
167
|
glSetBlendMode();
|
|
168
|
+
|
|
169
|
+
// set vertex attributes
|
|
170
|
+
let offset = 0;
|
|
171
|
+
const initVertexAttribArray = (name, type, typeSize, size, normalize=0)=>
|
|
172
|
+
{
|
|
173
|
+
const location = glContext.getAttribLocation(glShader, name);
|
|
174
|
+
glContext.enableVertexAttribArray(location);
|
|
175
|
+
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
176
|
+
offset += size*typeSize;
|
|
177
|
+
}
|
|
178
|
+
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
179
|
+
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
180
|
+
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
181
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
209
182
|
|
|
210
183
|
// build the transform matrix
|
|
211
184
|
const sx = 2 * cameraScale / width;
|
|
@@ -224,7 +197,7 @@ function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
|
224
197
|
* @memberof WebGL */
|
|
225
198
|
function glFlush()
|
|
226
199
|
{
|
|
227
|
-
if (!
|
|
200
|
+
if (!glBatchCount) return;
|
|
228
201
|
|
|
229
202
|
const destBlend = glBatchAdditive ? gl_ONE : gl_ONE_MINUS_SRC_ALPHA;
|
|
230
203
|
glContext.blendFuncSeparate(gl_SRC_ALPHA, destBlend, gl_ONE, destBlend);
|
|
@@ -244,7 +217,7 @@ function glFlush()
|
|
|
244
217
|
* @memberof WebGL */
|
|
245
218
|
function glCopyToContext(context, forceDraw)
|
|
246
219
|
{
|
|
247
|
-
if (
|
|
220
|
+
if (!glBatchCount && !forceDraw) return;
|
|
248
221
|
|
|
249
222
|
glFlush();
|
|
250
223
|
|
|
@@ -268,8 +241,6 @@ function glCopyToContext(context, forceDraw)
|
|
|
268
241
|
* @memberof WebGL */
|
|
269
242
|
function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffffff, rgbaAdditive=0)
|
|
270
243
|
{
|
|
271
|
-
if (!glEnable) return;
|
|
272
|
-
|
|
273
244
|
// flush if there is no room for more verts or if different blend mode
|
|
274
245
|
if (glBatchCount == gl_MAX_BATCH || glBatchAdditive != glAdditive)
|
|
275
246
|
glFlush();
|
|
@@ -318,6 +289,90 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
318
289
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
319
290
|
}
|
|
320
291
|
|
|
292
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
293
|
+
// post processing - can be enabled to pass other canvases through a final shader
|
|
294
|
+
|
|
295
|
+
let glPostShader, glPostArrayBuffer, glPostTexture;
|
|
296
|
+
|
|
297
|
+
/** Set up a post processing shader
|
|
298
|
+
* @param {String} shaderCode
|
|
299
|
+
* @memberof WebGL */
|
|
300
|
+
function glInitPostProcess(shaderCode)
|
|
301
|
+
{
|
|
302
|
+
ASSERT(!glPostShader); // can only have 1 post effects shader
|
|
303
|
+
|
|
304
|
+
if (!shaderCode) // default shader
|
|
305
|
+
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture2D(iChannel0,p/iResolution.xy);}';
|
|
306
|
+
|
|
307
|
+
// create the shader
|
|
308
|
+
glPostShader = glCreateProgram(
|
|
309
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
310
|
+
'attribute vec2 p;'+ // position
|
|
311
|
+
'void main(){'+ // shader entry point
|
|
312
|
+
'gl_Position=vec4(p,1,1);'+ // set position
|
|
313
|
+
'}' // end of shader
|
|
314
|
+
,
|
|
315
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
316
|
+
'uniform sampler2D iChannel0;'+ // input texture
|
|
317
|
+
'uniform vec3 iResolution;'+ // size of output texture
|
|
318
|
+
'uniform float iTime;'+ // time passed
|
|
319
|
+
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
320
|
+
'void main(){'+ // shader entry point
|
|
321
|
+
'mainImage(gl_FragColor,gl_FragCoord.xy);'+ // call post process function
|
|
322
|
+
'gl_FragColor.a=1.;'+ // always use full alpha
|
|
323
|
+
'}' // end of shader
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
// create buffer and texture
|
|
327
|
+
glPostArrayBuffer = glContext.createBuffer();
|
|
328
|
+
glPostTexture = glCreateTexture();
|
|
329
|
+
|
|
330
|
+
// hide the original 2d canvas
|
|
331
|
+
mainCanvas.style.visibility = 'hidden';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Render the post processing shader, called automatically by the engine
|
|
335
|
+
function glRenderPostProcess()
|
|
336
|
+
{
|
|
337
|
+
if (!glPostShader)
|
|
338
|
+
return;
|
|
339
|
+
|
|
340
|
+
// prepare to render post process shader
|
|
341
|
+
const width = mainCanvas.width, height = mainCanvas.height;
|
|
342
|
+
if (glEnable)
|
|
343
|
+
{
|
|
344
|
+
glFlush(); // clear out the buffer
|
|
345
|
+
mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
|
|
346
|
+
}
|
|
347
|
+
else
|
|
348
|
+
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height); // set viewport
|
|
349
|
+
|
|
350
|
+
// setup shader program to draw one triangle
|
|
351
|
+
glContext.useProgram(glPostShader);
|
|
352
|
+
glContext.disable(gl_BLEND);
|
|
353
|
+
glContext.bindBuffer(gl_ARRAY_BUFFER, glPostArrayBuffer);
|
|
354
|
+
glContext.bufferData(gl_ARRAY_BUFFER, new Float32Array([-3,1,1,-3,1,1]), gl_STATIC_DRAW);
|
|
355
|
+
glContext.pixelStorei(gl_UNPACK_FLIP_Y_WEBGL, true);
|
|
356
|
+
|
|
357
|
+
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
358
|
+
glContext.activeTexture(gl_TEXTURE0);
|
|
359
|
+
glContext.bindTexture(gl_TEXTURE_2D, glPostTexture);
|
|
360
|
+
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, mainCanvas);
|
|
361
|
+
|
|
362
|
+
// set vertex position attribute
|
|
363
|
+
const vertexByteStride = 8;
|
|
364
|
+
const pLocation = glContext.getAttribLocation(glPostShader, 'p');
|
|
365
|
+
glContext.enableVertexAttribArray(pLocation);
|
|
366
|
+
glContext.vertexAttribPointer(pLocation, 2, gl_FLOAT, 0, vertexByteStride, 0);
|
|
367
|
+
|
|
368
|
+
// set uniforms and draw
|
|
369
|
+
const uniformLocation = (name)=>glContext.getUniformLocation(glPostShader, name);
|
|
370
|
+
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
371
|
+
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
372
|
+
glContext.uniform3f(uniformLocation('iResolution'), width, height, 1);
|
|
373
|
+
glContext.drawArrays(gl_TRIANGLES, 0, 3);
|
|
374
|
+
}
|
|
375
|
+
|
|
321
376
|
///////////////////////////////////////////////////////////////////////////////
|
|
322
377
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
323
378
|
const
|
|
@@ -328,6 +383,7 @@ gl_ONE_MINUS_SRC_ALPHA = 771,
|
|
|
328
383
|
gl_BLEND = 3042,
|
|
329
384
|
gl_TEXTURE_2D = 3553,
|
|
330
385
|
gl_UNSIGNED_BYTE = 5121,
|
|
386
|
+
gl_BYTE = 5120,
|
|
331
387
|
gl_FLOAT = 5126,
|
|
332
388
|
gl_RGBA = 6408,
|
|
333
389
|
gl_NEAREST = 9728,
|
|
@@ -338,12 +394,16 @@ gl_TEXTURE_WRAP_S = 10242,
|
|
|
338
394
|
gl_TEXTURE_WRAP_T = 10243,
|
|
339
395
|
gl_COLOR_BUFFER_BIT = 16384,
|
|
340
396
|
gl_CLAMP_TO_EDGE = 33071,
|
|
397
|
+
gl_TEXTURE0 = 33984,
|
|
398
|
+
gl_TEXTURE1 = 33985,
|
|
341
399
|
gl_ARRAY_BUFFER = 34962,
|
|
400
|
+
gl_STATIC_DRAW = 35044,
|
|
342
401
|
gl_DYNAMIC_DRAW = 35048,
|
|
343
402
|
gl_FRAGMENT_SHADER = 35632,
|
|
344
403
|
gl_VERTEX_SHADER = 35633,
|
|
345
404
|
gl_COMPILE_STATUS = 35713,
|
|
346
405
|
gl_LINK_STATUS = 35714,
|
|
406
|
+
gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
347
407
|
|
|
348
408
|
// constants for batch rendering
|
|
349
409
|
gl_VERTICES_PER_QUAD = 6,
|