littlejsengine 1.16.2 → 1.17.1
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/dist/littlejs.d.ts +200 -166
- package/dist/littlejs.esm.js +1231 -1086
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +888 -735
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +884 -731
- package/examples/box2d/gameObjects.js +1 -1
- package/examples/breakout/game.js +5 -6
- package/examples/electron/index.html +2 -2
- package/examples/electron/tiles.png +0 -0
- package/examples/htmlMenu/tiles.png +0 -0
- package/examples/index.html +1 -1
- package/examples/logo.png +0 -0
- package/examples/module/tiles.png +0 -0
- package/examples/platformer/gameEffects.js +23 -22
- package/examples/platformer/gameLevel.js +24 -25
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/clock.js +3 -3
- package/examples/shorts/fontImage.js +4 -3
- package/examples/shorts/parallax.js +1 -1
- package/examples/shorts/sequencer.js +1 -1
- package/examples/shorts/shapes.js +1 -1
- package/examples/shorts/texture.js +10 -6
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +2 -0
- package/examples/starter/index.html +2 -2
- package/examples/starter/tiles.png +0 -0
- package/examples/typescript/tiles.png +0 -0
- package/examples/uiSystem/game.js +1 -1
- package/examples/uiSystem/tiles.png +0 -0
- package/package.json +1 -1
- package/plugins/postProcess.js +47 -28
- package/plugins/uiSystem.js +15 -15
- package/reference.md +1 -1
- package/src/engine.js +174 -174
- package/src/engineAudio.js +4 -6
- package/src/engineDebug.js +4 -4
- package/src/engineDraw.js +179 -122
- package/src/engineExport.js +326 -334
- package/src/engineFont.png +0 -0
- package/src/engineInput.js +14 -20
- package/src/engineMath.js +15 -104
- package/src/engineObject.js +26 -25
- package/src/engineParticles.js +2 -2
- package/src/engineTileLayer.js +196 -170
- package/src/engineUtilities.js +100 -4
- package/src/engineWebGL.js +123 -72
package/dist/littlejs.esm.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.
|
|
36
|
+
const engineVersion = '1.17.1';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -320,7 +320,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
320
320
|
|
|
321
321
|
// responsive aspect ratio with native resolution
|
|
322
322
|
const innerAspect = innerWidth / innerHeight;
|
|
323
|
-
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
323
|
+
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
324
324
|
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
325
325
|
{
|
|
326
326
|
// full height
|
|
@@ -377,7 +377,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
377
377
|
debugInit();
|
|
378
378
|
|
|
379
379
|
// setup canvases
|
|
380
|
-
// transform way is still more reliable
|
|
380
|
+
// transform way is still more reliable than flexbox or grid
|
|
381
381
|
const styleCanvas = 'position:absolute;'+ // allow canvases to overlap
|
|
382
382
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center on screen
|
|
383
383
|
mainCanvas.style.cssText = styleCanvas;
|
|
@@ -422,6 +422,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
422
422
|
}));
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
// load engine font image
|
|
426
|
+
promises.push(fontImageInit());
|
|
427
|
+
|
|
425
428
|
if (showSplashScreen)
|
|
426
429
|
{
|
|
427
430
|
// draw splash screen
|
|
@@ -433,7 +436,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
433
436
|
function updateSplash()
|
|
434
437
|
{
|
|
435
438
|
inputClear();
|
|
436
|
-
|
|
439
|
+
drawEngineLogo(t+=.01);
|
|
437
440
|
t>1 ? resolve() : setTimeout(updateSplash, 16);
|
|
438
441
|
}
|
|
439
442
|
}));
|
|
@@ -449,172 +452,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
449
452
|
await gameInit();
|
|
450
453
|
engineUpdate();
|
|
451
454
|
}
|
|
452
|
-
|
|
453
|
-
///////////////////////////////////////////////////////////////////////////
|
|
454
|
-
// LittleJS Splash Screen
|
|
455
|
-
function drawEngineSplashScreen(t)
|
|
456
|
-
{
|
|
457
|
-
const x = mainContext;
|
|
458
|
-
const w = mainCanvas.width = innerWidth;
|
|
459
|
-
const h = mainCanvas.height = innerHeight;
|
|
460
|
-
|
|
461
|
-
{
|
|
462
|
-
// background
|
|
463
|
-
const p3 = percent(t, 1, .8);
|
|
464
|
-
const p4 = percent(t, 0, .5);
|
|
465
|
-
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
466
|
-
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
467
|
-
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
468
|
-
x.save();
|
|
469
|
-
x.fillStyle = g;
|
|
470
|
-
x.fillRect(0,0,w,h);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// draw LittleJS logo...
|
|
474
|
-
const rect = (X, Y, W, H, C)=>
|
|
475
|
-
{
|
|
476
|
-
x.beginPath();
|
|
477
|
-
x.rect(X,Y,W,C?H*p:H);
|
|
478
|
-
x.fillStyle = C;
|
|
479
|
-
C ? x.fill() : x.stroke();
|
|
480
|
-
};
|
|
481
|
-
const line = (X, Y, Z, W)=>
|
|
482
|
-
{
|
|
483
|
-
x.beginPath();
|
|
484
|
-
x.lineTo(X,Y);
|
|
485
|
-
x.lineTo(Z,W);
|
|
486
|
-
x.stroke();
|
|
487
|
-
};
|
|
488
|
-
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
489
|
-
{
|
|
490
|
-
const D = (A+B)/2, E = p*(B-A)/2;
|
|
491
|
-
x.beginPath();
|
|
492
|
-
F && x.lineTo(X,Y);
|
|
493
|
-
x.arc(X,Y,R,D-E,D+E);
|
|
494
|
-
x.fillStyle = C;
|
|
495
|
-
C ? x.fill() : x.stroke();
|
|
496
|
-
};
|
|
497
|
-
const color = (c=0, l=0) =>
|
|
498
|
-
hsl([.98,.3,.57,.14][c%4],.8,[0,.3,.5,.8,.9][l]).toString();
|
|
499
|
-
const alpha = wave(1,1,t);
|
|
500
|
-
const p = percent(alpha, .1, .5);
|
|
501
|
-
|
|
502
|
-
// setup
|
|
503
|
-
x.translate(w/2,h/2);
|
|
504
|
-
const size = min(6, min(w,h)/99); // fit to screen
|
|
505
|
-
x.scale(size,size);
|
|
506
|
-
x.translate(-40,-35);
|
|
507
|
-
x.lineJoin = x.lineCap = 'round';
|
|
508
|
-
x.lineWidth = .1 + p*1.9;
|
|
509
|
-
|
|
510
|
-
// drawing effect
|
|
511
|
-
const p2 = percent(alpha,.1,1);
|
|
512
|
-
x.setLineDash([99*p2,99]);
|
|
513
|
-
|
|
514
|
-
// cab top
|
|
515
|
-
rect(7,16,18,-8,color(2,2));
|
|
516
|
-
rect(7,8,18,4,color(2,3));
|
|
517
|
-
rect(25,8,8,8,color(2,1));
|
|
518
|
-
rect(25,8,-18,8);
|
|
519
|
-
rect(25,8,8,8);
|
|
520
|
-
|
|
521
|
-
// cab
|
|
522
|
-
rect(25,16,7,23,color());
|
|
523
|
-
rect(11,39,14,-23,color(1,1));
|
|
524
|
-
rect(11,16,14,18,color(1,2));
|
|
525
|
-
rect(11,16,14,8,color(1,3));
|
|
526
|
-
rect(25,16,-14,24);
|
|
527
|
-
|
|
528
|
-
// cab window
|
|
529
|
-
rect(15,29,6,-9,color(2,2));
|
|
530
|
-
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
531
|
-
rect(21,21,-6,9);
|
|
532
|
-
|
|
533
|
-
// little stack
|
|
534
|
-
rect(37,14,9,6,color(3,2));
|
|
535
|
-
rect(37,14,4.5,6,color(3,3));
|
|
536
|
-
rect(37,14,9,6);
|
|
537
|
-
|
|
538
|
-
// big stack
|
|
539
|
-
rect(50,20,10,-8,color(0,1));
|
|
540
|
-
rect(50,20,6.5,-8,color(0,2));
|
|
541
|
-
rect(50,20,3.5,-8,color(0,3));
|
|
542
|
-
rect(50,20,10,-8);
|
|
543
|
-
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
544
|
-
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
545
|
-
circle(55,2,11.4,.5,PI-.5);
|
|
546
|
-
rect(45,7,20,-7,color(0,2));
|
|
547
|
-
rect(45,-1,20,4,color(0,3));
|
|
548
|
-
rect(45,-1,20,8);
|
|
549
|
-
|
|
550
|
-
// engine
|
|
551
|
-
for (let i=5; i--;)
|
|
552
|
-
{
|
|
553
|
-
// stagger radius to fix slight seam
|
|
554
|
-
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
555
|
-
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
556
|
-
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// engine outline
|
|
560
|
-
circle(36,30,10,PI/2,PI*3/2);
|
|
561
|
-
circle(48,30,10,PI/2,PI*3/2);
|
|
562
|
-
circle(60,30,10);
|
|
563
|
-
line(36,20,60,20);
|
|
564
|
-
|
|
565
|
-
// engine front light
|
|
566
|
-
circle(60,30,4,PI,3*PI,color(3,2));
|
|
567
|
-
circle(60,30,4,PI,2*PI,color(3,3));
|
|
568
|
-
circle(60,30,4,PI,3*PI);
|
|
569
|
-
|
|
570
|
-
// front brush
|
|
571
|
-
for (let i=6; i--;)
|
|
572
|
-
{
|
|
573
|
-
x.beginPath();
|
|
574
|
-
x.lineTo(53,54);
|
|
575
|
-
x.lineTo(53,40);
|
|
576
|
-
x.lineTo(53+(1+i*2.9)*p,40);
|
|
577
|
-
x.lineTo(53+(4+i*3.5)*p,54);
|
|
578
|
-
x.fillStyle = color(0,i%2+2);
|
|
579
|
-
x.fill();
|
|
580
|
-
i%2 && x.stroke();
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// wheels
|
|
584
|
-
rect(6,40,5,5);
|
|
585
|
-
rect(6,40,5,5,color());
|
|
586
|
-
rect(15,54,38,-14,color());
|
|
587
|
-
for (let i=3; i--;)
|
|
588
|
-
for (let j=2; j--;)
|
|
589
|
-
{
|
|
590
|
-
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
591
|
-
x.stroke();
|
|
592
|
-
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
593
|
-
x.stroke();
|
|
594
|
-
}
|
|
595
|
-
line(6,40,68,40); // center
|
|
596
|
-
line(77,54,4,54); // bottom
|
|
597
|
-
|
|
598
|
-
// draw engine name
|
|
599
|
-
const s = engineName;
|
|
600
|
-
x.font = '900 16px arial';
|
|
601
|
-
x.textAlign = 'center';
|
|
602
|
-
x.textBaseline = 'top';
|
|
603
|
-
x.lineWidth = .1+p*3.9;
|
|
604
|
-
let w2 = 0;
|
|
605
|
-
for (let i=0; i<s.length; ++i)
|
|
606
|
-
w2 += x.measureText(s[i]).width;
|
|
607
|
-
for (let j=2; j--;)
|
|
608
|
-
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
609
|
-
{
|
|
610
|
-
x.fillStyle = color(i,2);
|
|
611
|
-
const w = x.measureText(s[i]).width;
|
|
612
|
-
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
613
|
-
X += w;
|
|
614
|
-
}
|
|
615
|
-
x.restore();
|
|
616
|
-
}
|
|
617
|
-
///////////////////////////////////////////////////////////////////////////
|
|
618
455
|
}
|
|
619
456
|
|
|
620
457
|
/** Update each engine object, remove destroyed objects, and update time
|
|
@@ -628,8 +465,7 @@ function engineObjectsUpdate()
|
|
|
628
465
|
// recursive object update
|
|
629
466
|
function updateObject(o)
|
|
630
467
|
{
|
|
631
|
-
if (o.destroyed)
|
|
632
|
-
return;
|
|
468
|
+
if (o.destroyed) return;
|
|
633
469
|
|
|
634
470
|
o.update();
|
|
635
471
|
for (const child of o.children)
|
|
@@ -637,8 +473,7 @@ function engineObjectsUpdate()
|
|
|
637
473
|
}
|
|
638
474
|
for (const o of engineObjects)
|
|
639
475
|
{
|
|
640
|
-
if (o.parent)
|
|
641
|
-
continue;
|
|
476
|
+
if (o.parent || o.destroyed) continue;
|
|
642
477
|
|
|
643
478
|
// update top level objects
|
|
644
479
|
o.update();
|
|
@@ -727,6 +562,171 @@ function engineObjectsRaycast(start, end, objects=engineObjects)
|
|
|
727
562
|
|
|
728
563
|
debugRaycast && debugLine(start, end, hitObjects.length ? '#f00' : '#00f', .02);
|
|
729
564
|
return hitObjects;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
568
|
+
function drawEngineLogo(t)
|
|
569
|
+
{
|
|
570
|
+
// LittleJS Logo and Splash Screen
|
|
571
|
+
const x = mainContext;
|
|
572
|
+
const w = mainCanvas.width = innerWidth;
|
|
573
|
+
const h = mainCanvas.height = innerHeight;
|
|
574
|
+
|
|
575
|
+
{
|
|
576
|
+
// background
|
|
577
|
+
const p3 = percent(t, 1, .8);
|
|
578
|
+
const p4 = percent(t, 0, .5);
|
|
579
|
+
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
580
|
+
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
581
|
+
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
582
|
+
x.save();
|
|
583
|
+
x.fillStyle = g;
|
|
584
|
+
x.fillRect(0,0,w,h);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// draw LittleJS logo...
|
|
588
|
+
const rect = (X, Y, W, H, C)=>
|
|
589
|
+
{
|
|
590
|
+
x.beginPath();
|
|
591
|
+
x.rect(X,Y,W,C?H*p:H);
|
|
592
|
+
x.fillStyle = C;
|
|
593
|
+
C ? x.fill() : x.stroke();
|
|
594
|
+
};
|
|
595
|
+
const line = (X, Y, Z, W)=>
|
|
596
|
+
{
|
|
597
|
+
x.beginPath();
|
|
598
|
+
x.lineTo(X,Y);
|
|
599
|
+
x.lineTo(Z,W);
|
|
600
|
+
x.stroke();
|
|
601
|
+
};
|
|
602
|
+
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
603
|
+
{
|
|
604
|
+
const D = (A+B)/2, E = p*(B-A)/2;
|
|
605
|
+
x.beginPath();
|
|
606
|
+
F && x.lineTo(X,Y);
|
|
607
|
+
x.arc(X,Y,R,D-E,D+E);
|
|
608
|
+
x.fillStyle = C;
|
|
609
|
+
C ? x.fill() : x.stroke();
|
|
610
|
+
};
|
|
611
|
+
const color = (c=0, l=0) =>
|
|
612
|
+
hsl([.98,.3,.57,.14][c%4],.9,[0,.3,.5,.8,.9][l]).toString();
|
|
613
|
+
const alpha = wave(1,1,t);
|
|
614
|
+
const p = percent(alpha, .1, .5);
|
|
615
|
+
|
|
616
|
+
// setup
|
|
617
|
+
x.translate(w/2,h/2);
|
|
618
|
+
const size = min(6, min(w,h)/99); // fit to screen
|
|
619
|
+
x.scale(size,size);
|
|
620
|
+
x.translate(-40,-35);
|
|
621
|
+
x.lineJoin = x.lineCap = 'round';
|
|
622
|
+
x.lineWidth = .1 + p*1.9;
|
|
623
|
+
|
|
624
|
+
// drawing effect
|
|
625
|
+
const p2 = percent(alpha,.1,1);
|
|
626
|
+
x.setLineDash([99*p2,99]);
|
|
627
|
+
|
|
628
|
+
// cab top
|
|
629
|
+
rect(7,16,18,-8,color(2,2));
|
|
630
|
+
rect(7,8,18,4,color(2,3));
|
|
631
|
+
rect(25,8,8,8,color(2,1));
|
|
632
|
+
rect(25,8,-18,8);
|
|
633
|
+
rect(25,8,8,8);
|
|
634
|
+
|
|
635
|
+
// cab
|
|
636
|
+
rect(25,16,7,24,color());
|
|
637
|
+
rect(11,39,14,-23,color(1,1));
|
|
638
|
+
rect(11,16,14,18,color(1,2));
|
|
639
|
+
rect(11,16,14,8,color(1,3));
|
|
640
|
+
rect(25,16,-14,24);
|
|
641
|
+
|
|
642
|
+
// cab window
|
|
643
|
+
rect(15,29,6,-9,color(2,2));
|
|
644
|
+
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
645
|
+
rect(21,21,-6,9);
|
|
646
|
+
|
|
647
|
+
// little stack
|
|
648
|
+
rect(37,14,9,6,color(3,2));
|
|
649
|
+
rect(37,14,4.5,6,color(3,3));
|
|
650
|
+
rect(37,14,9,6);
|
|
651
|
+
|
|
652
|
+
// big stack
|
|
653
|
+
rect(50,20,10,-8,color(0,1));
|
|
654
|
+
rect(50,20,6.5,-8,color(0,2));
|
|
655
|
+
rect(50,20,3.5,-8,color(0,3));
|
|
656
|
+
rect(50,20,10,-8);
|
|
657
|
+
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
658
|
+
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
659
|
+
circle(55,2,11.4,.5,PI-.5);
|
|
660
|
+
rect(45,7,20,-7,color(0,2));
|
|
661
|
+
rect(45,-1,20,4,color(0,3));
|
|
662
|
+
rect(45,-1,20,8);
|
|
663
|
+
|
|
664
|
+
// engine
|
|
665
|
+
for (let i=5; i--;)
|
|
666
|
+
{
|
|
667
|
+
// stagger radius to fix slight seam
|
|
668
|
+
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
669
|
+
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
670
|
+
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// engine outline
|
|
674
|
+
circle(36,30,10,PI/2,PI*3/2);
|
|
675
|
+
circle(48,30,10,PI/2,PI*3/2);
|
|
676
|
+
circle(60,30,10);
|
|
677
|
+
line(36,20,60,20);
|
|
678
|
+
|
|
679
|
+
// engine front light
|
|
680
|
+
circle(60,30,4,PI,3*PI,color(3,2));
|
|
681
|
+
circle(60,30,4,PI,2*PI,color(3,3));
|
|
682
|
+
circle(60,30,4,PI,3*PI);
|
|
683
|
+
|
|
684
|
+
// front brush
|
|
685
|
+
for (let i=6; i--;)
|
|
686
|
+
{
|
|
687
|
+
x.beginPath();
|
|
688
|
+
x.lineTo(53,54);
|
|
689
|
+
x.lineTo(53,40);
|
|
690
|
+
x.lineTo(53+(1+i*2.9)*p,40);
|
|
691
|
+
x.lineTo(53+(4+i*3.5)*p,54);
|
|
692
|
+
x.fillStyle = color(0,i%2+2);
|
|
693
|
+
x.fill();
|
|
694
|
+
i%2 && x.stroke();
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// wheels
|
|
698
|
+
rect(6,40,5,5);
|
|
699
|
+
rect(6,40,5,5,color());
|
|
700
|
+
rect(15,54,38,-14,color());
|
|
701
|
+
for (let i=3; i--;)
|
|
702
|
+
for (let j=2; j--;)
|
|
703
|
+
{
|
|
704
|
+
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
705
|
+
x.stroke();
|
|
706
|
+
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
707
|
+
x.stroke();
|
|
708
|
+
}
|
|
709
|
+
line(6,40,68,40); // center
|
|
710
|
+
line(77,54,4,54); // bottom
|
|
711
|
+
|
|
712
|
+
// draw engine name
|
|
713
|
+
const s = engineName;
|
|
714
|
+
x.font = '900 16px arial';
|
|
715
|
+
x.textAlign = 'center';
|
|
716
|
+
x.textBaseline = 'top';
|
|
717
|
+
x.lineWidth = .1+p*3.9;
|
|
718
|
+
let w2 = 0;
|
|
719
|
+
for (let i=0; i<s.length; ++i)
|
|
720
|
+
w2 += x.measureText(s[i]).width;
|
|
721
|
+
for (let j=2; j--;)
|
|
722
|
+
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
723
|
+
{
|
|
724
|
+
x.fillStyle = color(i,2);
|
|
725
|
+
const w = x.measureText(s[i]).width;
|
|
726
|
+
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
727
|
+
X += w;
|
|
728
|
+
}
|
|
729
|
+
x.restore();
|
|
730
730
|
}
|
|
731
731
|
/**
|
|
732
732
|
* LittleJS Debug System
|
|
@@ -793,7 +793,7 @@ function LOG(...output) { console.log(...output); }
|
|
|
793
793
|
|
|
794
794
|
/** Draw a debug rectangle in world space
|
|
795
795
|
* @param {Vector2} pos
|
|
796
|
-
* @param {Vector2} [size=
|
|
796
|
+
* @param {Vector2} [size=vec2(0)]
|
|
797
797
|
* @param {Color|string} [color]
|
|
798
798
|
* @param {number} [time]
|
|
799
799
|
* @param {number} [angle]
|
|
@@ -1117,7 +1117,7 @@ function debugRender()
|
|
|
1117
1117
|
if (tileCollisionTest(mousePos))
|
|
1118
1118
|
{
|
|
1119
1119
|
// show floored tile pick for tile collision
|
|
1120
|
-
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), rgb(1,1,0,.5));
|
|
1120
|
+
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), rgb(1,1,0,.5), 0, false);
|
|
1121
1121
|
}
|
|
1122
1122
|
}
|
|
1123
1123
|
|
|
@@ -1196,8 +1196,8 @@ function debugRender()
|
|
|
1196
1196
|
if (debugObject)
|
|
1197
1197
|
{
|
|
1198
1198
|
const raycastHitPos = tileCollisionRaycast(debugObject.pos, mousePos);
|
|
1199
|
-
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), rgb(0,1,1,.3));
|
|
1200
|
-
drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5));
|
|
1199
|
+
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), rgb(0,1,1,.3), 0, false);
|
|
1200
|
+
drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5), undefined, undefined, false);
|
|
1201
1201
|
|
|
1202
1202
|
let debugText = 'mouse pos = ' + mousePos;
|
|
1203
1203
|
if (tileCollisionLayers.length)
|
|
@@ -1448,12 +1448,11 @@ function debugProtectConstant(obj)
|
|
|
1448
1448
|
return Object.freeze(obj);
|
|
1449
1449
|
}
|
|
1450
1450
|
/**
|
|
1451
|
-
* LittleJS
|
|
1451
|
+
* LittleJS Math Classes and Functions
|
|
1452
1452
|
* - General purpose math library
|
|
1453
|
-
* - Vector2 - fast, simple, easy 2D vector class
|
|
1454
|
-
* - Color - holds a rgba color with some math functions
|
|
1455
|
-
* - Timer - tracks time automatically
|
|
1456
1453
|
* - RandomGenerator - seeded random number generator
|
|
1454
|
+
* - Vector2 - fast, simple, easy 2D vector class
|
|
1455
|
+
* - Color - holds a rgba color with math functions
|
|
1457
1456
|
* @namespace Math
|
|
1458
1457
|
*/
|
|
1459
1458
|
|
|
@@ -1641,11 +1640,11 @@ function nearestPowerOfTwo(value) { return 2**ceil(log2(value)); }
|
|
|
1641
1640
|
|
|
1642
1641
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
1643
1642
|
* this can be used for simple collision detection between objects
|
|
1644
|
-
* @param {Vector2} posA
|
|
1645
|
-
* @param {Vector2} sizeA
|
|
1646
|
-
* @param {Vector2} posB
|
|
1647
|
-
* @param {Vector2} [sizeB=(
|
|
1648
|
-
* @return {boolean}
|
|
1643
|
+
* @param {Vector2} posA - Center of box A
|
|
1644
|
+
* @param {Vector2} sizeA - Size of box A
|
|
1645
|
+
* @param {Vector2} posB - Center of box B
|
|
1646
|
+
* @param {Vector2} [sizeB=vec2()] - Size of box B, uses a point if undefined
|
|
1647
|
+
* @return {boolean} - True if overlapping
|
|
1649
1648
|
* @memberof Math */
|
|
1650
1649
|
function isOverlapping(posA, sizeA, posB, sizeB=vec2())
|
|
1651
1650
|
{
|
|
@@ -1719,7 +1718,7 @@ function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
|
1719
1718
|
* @param {any} s
|
|
1720
1719
|
* @return {boolean}
|
|
1721
1720
|
* @memberof Math */
|
|
1722
|
-
function isString(s) { return s
|
|
1721
|
+
function isString(s) { return s != null && typeof s?.toString() === 'string'; }
|
|
1723
1722
|
|
|
1724
1723
|
/**
|
|
1725
1724
|
* Check if object is an array
|
|
@@ -1863,8 +1862,8 @@ function randInCircle(radius=1, minRadius=0)
|
|
|
1863
1862
|
{ return radius > 0 ? randVec2(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
|
|
1864
1863
|
|
|
1865
1864
|
/** Returns a random color between the two passed in colors, combine components if linear
|
|
1866
|
-
* @param {Color} [colorA=
|
|
1867
|
-
* @param {Color} [colorB=
|
|
1865
|
+
* @param {Color} [colorA=WHITE]
|
|
1866
|
+
* @param {Color} [colorB=BLACK]
|
|
1868
1867
|
* @param {boolean} [linear]
|
|
1869
1868
|
* @return {Color}
|
|
1870
1869
|
* @memberof Random */
|
|
@@ -1943,8 +1942,8 @@ class RandomGenerator
|
|
|
1943
1942
|
{ return vec2(this.float(valueA, valueB), this.float(valueA, valueB)); }
|
|
1944
1943
|
|
|
1945
1944
|
/** Returns a random color between the two passed in colors, combine components if linear
|
|
1946
|
-
* @param {Color} [colorA=
|
|
1947
|
-
* @param {Color} [colorB=
|
|
1945
|
+
* @param {Color} [colorA=WHITE]
|
|
1946
|
+
* @param {Color} [colorB=BLACK]
|
|
1948
1947
|
* @param {boolean} [linear]
|
|
1949
1948
|
* @return {Color} */
|
|
1950
1949
|
randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
@@ -1988,7 +1987,7 @@ class RandomGenerator
|
|
|
1988
1987
|
* a = vec2(5); // set a to (5, 5)
|
|
1989
1988
|
* b = vec2(); // set b to (0, 0)
|
|
1990
1989
|
* @memberof Math */
|
|
1991
|
-
function vec2(x=0, y) { return new Vector2(x, y
|
|
1990
|
+
function vec2(x=0, y) { return new Vector2(x, y ?? x); }
|
|
1992
1991
|
|
|
1993
1992
|
/**
|
|
1994
1993
|
* Check if object is a valid Vector2
|
|
@@ -2196,10 +2195,6 @@ class Vector2
|
|
|
2196
2195
|
* @return {number} */
|
|
2197
2196
|
area() { return abs(this.x * this.y); }
|
|
2198
2197
|
|
|
2199
|
-
/** Returns true if this vector is (0,0)
|
|
2200
|
-
* @return {boolean} */
|
|
2201
|
-
isZero() { return !this.x && !this.y; }
|
|
2202
|
-
|
|
2203
2198
|
/** Returns a new vector that is p percent between this and the vector passed in
|
|
2204
2199
|
* @param {Vector2} v - other vector
|
|
2205
2200
|
* @param {number} percent
|
|
@@ -2561,7 +2556,97 @@ const PURPLE = debugProtectConstant(rgb(.5,0,1));
|
|
|
2561
2556
|
/** Color - Magenta #ff00ff
|
|
2562
2557
|
* @type {Color}
|
|
2563
2558
|
* @memberof Math */
|
|
2564
|
-
const MAGENTA = debugProtectConstant(rgb(1,0,1));
|
|
2559
|
+
const MAGENTA = debugProtectConstant(rgb(1,0,1));
|
|
2560
|
+
/**
|
|
2561
|
+
* LittleJS Utility Classes and Functions
|
|
2562
|
+
* - General purpose utilities
|
|
2563
|
+
* - Timer - tracks time automatically
|
|
2564
|
+
* @namespace Utilities
|
|
2565
|
+
*/
|
|
2566
|
+
|
|
2567
|
+
/** Formats seconds to mm:ss style for display purposes
|
|
2568
|
+
* @param {number} t - time in seconds
|
|
2569
|
+
* @return {string}
|
|
2570
|
+
* @memberof Utilities */
|
|
2571
|
+
function formatTime(t)
|
|
2572
|
+
{
|
|
2573
|
+
const sign = t < 0 ? '-' : '';
|
|
2574
|
+
t = abs(t)|0;
|
|
2575
|
+
return sign + (t/60|0) + ':' + (t%60<10?'0':'') + t%60;
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
/** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
|
|
2579
|
+
* @param {string} url - URL of JSON file
|
|
2580
|
+
* @return {Promise<object>}
|
|
2581
|
+
* @memberof Utilities */
|
|
2582
|
+
async function fetchJSON(url)
|
|
2583
|
+
{
|
|
2584
|
+
const response = await fetch(url);
|
|
2585
|
+
if (!response.ok)
|
|
2586
|
+
throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
|
|
2587
|
+
return response.json();
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2591
|
+
|
|
2592
|
+
/** Save a text file to disk
|
|
2593
|
+
* @param {string} text
|
|
2594
|
+
* @param {string} [filename]
|
|
2595
|
+
* @param {string} [type]
|
|
2596
|
+
* @memberof Utilities */
|
|
2597
|
+
function saveText(text, filename='text', type='text/plain')
|
|
2598
|
+
{ saveDataURL(URL.createObjectURL(new Blob([text], {'type':type})), filename); }
|
|
2599
|
+
|
|
2600
|
+
/** Save a canvas to disk
|
|
2601
|
+
* @param {HTMLCanvasElement|OffscreenCanvas} canvas
|
|
2602
|
+
* @param {string} [filename]
|
|
2603
|
+
* @param {string} [type]
|
|
2604
|
+
* @memberof Utilities */
|
|
2605
|
+
function saveCanvas(canvas, filename='screenshot', type='image/png')
|
|
2606
|
+
{
|
|
2607
|
+
if (canvas instanceof OffscreenCanvas)
|
|
2608
|
+
{
|
|
2609
|
+
// copy to temporary canvas and save
|
|
2610
|
+
const saveCanvas = document.createElement('canvas');
|
|
2611
|
+
saveCanvas.width = canvas.width;
|
|
2612
|
+
saveCanvas.height = canvas.height;
|
|
2613
|
+
saveCanvas.getContext('2d').drawImage(canvas, 0, 0);
|
|
2614
|
+
saveDataURL(saveCanvas.toDataURL(type), filename);
|
|
2615
|
+
}
|
|
2616
|
+
else
|
|
2617
|
+
saveDataURL(canvas.toDataURL(type), filename);
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
/** Save a data url to disk
|
|
2621
|
+
* @param {string} url
|
|
2622
|
+
* @param {string} [filename]
|
|
2623
|
+
* @param {number} [revokeTime] - how long before revoking the url
|
|
2624
|
+
* @memberof Utilities */
|
|
2625
|
+
function saveDataURL(url, filename='download', revokeTime)
|
|
2626
|
+
{
|
|
2627
|
+
ASSERT(isString(url), 'saveDataURL requires url string');
|
|
2628
|
+
ASSERT(isString(filename), 'saveDataURL requires filename string');
|
|
2629
|
+
|
|
2630
|
+
// create link for saving screenshots
|
|
2631
|
+
const link = document.createElement('a');
|
|
2632
|
+
link.download = filename;
|
|
2633
|
+
link.href = url;
|
|
2634
|
+
link.click();
|
|
2635
|
+
if (revokeTime !== undefined)
|
|
2636
|
+
setTimeout(()=> URL.revokeObjectURL(url), revokeTime);
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
/** Share content using the native share dialog if available
|
|
2640
|
+
* @param {string} title - title of the share
|
|
2641
|
+
* @param {string} url - url to share
|
|
2642
|
+
* @param {Function} [callback] - Called when share is complete
|
|
2643
|
+
* @memberof Utilities */
|
|
2644
|
+
function shareURL(title, url, callback)
|
|
2645
|
+
{
|
|
2646
|
+
ASSERT(isString(title), 'shareURL requires title string');
|
|
2647
|
+
ASSERT(isString(url), 'shareURL requires url string');
|
|
2648
|
+
navigator.share?.({title, url}).then(()=>callback?.());
|
|
2649
|
+
}
|
|
2565
2650
|
|
|
2566
2651
|
///////////////////////////////////////////////////////////////////////////////
|
|
2567
2652
|
|
|
@@ -2646,84 +2731,6 @@ class Timer
|
|
|
2646
2731
|
* @return {number} */
|
|
2647
2732
|
valueOf() { return this.get(); }
|
|
2648
2733
|
}
|
|
2649
|
-
/**
|
|
2650
|
-
* LittleJS Utility Classes and Functions
|
|
2651
|
-
* - General purpose math library
|
|
2652
|
-
* - Vector2 - fast, simple, easy 2D vector class
|
|
2653
|
-
* - Color - holds a rgba color with some math functions
|
|
2654
|
-
* - Timer - tracks time automatically
|
|
2655
|
-
* - RandomGenerator - seeded random number generator
|
|
2656
|
-
* @namespace Utilities
|
|
2657
|
-
*/
|
|
2658
|
-
|
|
2659
|
-
/** Formats seconds to mm:ss style for display purposes
|
|
2660
|
-
* @param {number} t - time in seconds
|
|
2661
|
-
* @return {string}
|
|
2662
|
-
* @memberof Utilities */
|
|
2663
|
-
function formatTime(t)
|
|
2664
|
-
{
|
|
2665
|
-
const sign = t < 0 ? '-' : '';
|
|
2666
|
-
t = abs(t)|0;
|
|
2667
|
-
return sign + (t/60|0) + ':' + (t%60<10?'0':'') + t%60;
|
|
2668
|
-
}
|
|
2669
|
-
|
|
2670
|
-
/** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
|
|
2671
|
-
* @param {string} url - URL of JSON file
|
|
2672
|
-
* @return {Promise<object>}
|
|
2673
|
-
* @memberof Utilities */
|
|
2674
|
-
async function fetchJSON(url)
|
|
2675
|
-
{
|
|
2676
|
-
const response = await fetch(url);
|
|
2677
|
-
if (!response.ok)
|
|
2678
|
-
throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
|
|
2679
|
-
return response.json();
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
2683
|
-
|
|
2684
|
-
/** Save a text file to disk
|
|
2685
|
-
* @param {string} text
|
|
2686
|
-
* @param {string} [filename]
|
|
2687
|
-
* @param {string} [type]
|
|
2688
|
-
* @memberof Utilities */
|
|
2689
|
-
function saveText(text, filename='text', type='text/plain')
|
|
2690
|
-
{ saveDataURL(URL.createObjectURL(new Blob([text], {'type':type})), filename); }
|
|
2691
|
-
|
|
2692
|
-
/** Save a canvas to disk
|
|
2693
|
-
* @param {HTMLCanvasElement|OffscreenCanvas} canvas
|
|
2694
|
-
* @param {string} [filename]
|
|
2695
|
-
* @param {string} [type]
|
|
2696
|
-
* @memberof Utilities */
|
|
2697
|
-
function saveCanvas(canvas, filename='screenshot', type='image/png')
|
|
2698
|
-
{
|
|
2699
|
-
if (canvas instanceof OffscreenCanvas)
|
|
2700
|
-
{
|
|
2701
|
-
// copy to temporary canvas and save
|
|
2702
|
-
const saveCanvas = document.createElement('canvas');
|
|
2703
|
-
saveCanvas.width = canvas.width;
|
|
2704
|
-
saveCanvas.height = canvas.height;
|
|
2705
|
-
saveCanvas.getContext('2d').drawImage(canvas, 0, 0);
|
|
2706
|
-
saveDataURL(saveCanvas.toDataURL(type), filename);
|
|
2707
|
-
}
|
|
2708
|
-
else
|
|
2709
|
-
saveDataURL(canvas.toDataURL(type), filename);
|
|
2710
|
-
}
|
|
2711
|
-
|
|
2712
|
-
/** Save a data url to disk
|
|
2713
|
-
* @param {string} url
|
|
2714
|
-
* @param {string} [filename]
|
|
2715
|
-
* @param {number} [revokeTime] - how long before revoking the url
|
|
2716
|
-
* @memberof Utilities */
|
|
2717
|
-
function saveDataURL(url, filename='download', revokeTime)
|
|
2718
|
-
{
|
|
2719
|
-
// create link for saving screenshots
|
|
2720
|
-
const link = document.createElement('a');
|
|
2721
|
-
link.download = filename;
|
|
2722
|
-
link.href = url;
|
|
2723
|
-
link.click();
|
|
2724
|
-
if (revokeTime !== undefined)
|
|
2725
|
-
setTimeout(()=> URL.revokeObjectURL(url), revokeTime);
|
|
2726
|
-
}
|
|
2727
2734
|
/**
|
|
2728
2735
|
* LittleJS Engine Settings
|
|
2729
2736
|
* - All settings for the engine are here
|
|
@@ -3350,10 +3357,10 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
3350
3357
|
class EngineObject
|
|
3351
3358
|
{
|
|
3352
3359
|
/** Create an engine object and adds it to the list of objects
|
|
3353
|
-
* @param {Vector2} [pos=(
|
|
3354
|
-
* @param {Vector2} [size=(1
|
|
3355
|
-
* @param {TileInfo} [tileInfo]
|
|
3356
|
-
* @param {number} [angle]
|
|
3360
|
+
* @param {Vector2} [pos=vec2()] - World space position of the object
|
|
3361
|
+
* @param {Vector2} [size=vec2(1)] - World space size of the object
|
|
3362
|
+
* @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
|
|
3363
|
+
* @param {number} [angle] - Angle the object is rotated by
|
|
3357
3364
|
* @param {Color} [color=WHITE] - Color to apply to tile when rendered
|
|
3358
3365
|
* @param {number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
3359
3366
|
*/
|
|
@@ -3481,15 +3488,16 @@ class EngineObject
|
|
|
3481
3488
|
// physics sanity checks
|
|
3482
3489
|
ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
|
|
3483
3490
|
ASSERT(this.damping >= 0 && this.damping <= 1);
|
|
3484
|
-
|
|
3485
|
-
|
|
3491
|
+
|
|
3492
|
+
// don't do collision for static objects or if solver disabled
|
|
3493
|
+
if (!enablePhysicsSolver || !this.mass) return;
|
|
3486
3494
|
|
|
3487
3495
|
const wasFalling = this.velocity.y < 0 && gravity.y < 0 || this.velocity.y > 0 && gravity.y > 0;
|
|
3488
3496
|
if (this.groundObject)
|
|
3489
3497
|
{
|
|
3490
3498
|
// apply friction in local space of ground object
|
|
3491
3499
|
const friction = max(this.friction, this.groundObject.friction);
|
|
3492
|
-
const groundSpeed = this.groundObject.velocity
|
|
3500
|
+
const groundSpeed = this.groundObject.velocity.x;
|
|
3493
3501
|
this.velocity.x = groundSpeed + (this.velocity.x - groundSpeed) * friction;
|
|
3494
3502
|
this.groundObject = undefined;
|
|
3495
3503
|
}
|
|
@@ -3500,19 +3508,19 @@ class EngineObject
|
|
|
3500
3508
|
const epsilon = .001; // necessary to push slightly outside of the collision
|
|
3501
3509
|
for (const o of engineObjectsCollide)
|
|
3502
3510
|
{
|
|
3511
|
+
// skip destroyed, child objects, or self collision
|
|
3512
|
+
if (o.destroyed || o.parent || o === this) continue;
|
|
3513
|
+
|
|
3503
3514
|
// non solid objects don't collide with each other
|
|
3504
|
-
if (
|
|
3505
|
-
continue;
|
|
3515
|
+
if (!this.isSolid && !o.isSolid) continue;
|
|
3506
3516
|
|
|
3507
3517
|
// check collision
|
|
3508
|
-
if (!this.isOverlappingObject(o))
|
|
3509
|
-
continue;
|
|
3518
|
+
if (!this.isOverlappingObject(o)) continue;
|
|
3510
3519
|
|
|
3511
3520
|
// notify objects of collision and check if should be resolved
|
|
3512
3521
|
const collide1 = this.collideWithObject(o);
|
|
3513
3522
|
const collide2 = o.collideWithObject(this);
|
|
3514
|
-
if (!collide1 || !collide2)
|
|
3515
|
-
continue;
|
|
3523
|
+
if (!collide1 || !collide2) continue;
|
|
3516
3524
|
|
|
3517
3525
|
if (isOverlapping(oldPos, this.size, o.pos, o.size))
|
|
3518
3526
|
{
|
|
@@ -3614,7 +3622,7 @@ class EngineObject
|
|
|
3614
3622
|
const delta = y - this.pos.y;
|
|
3615
3623
|
if (delta < maxMoveUp)
|
|
3616
3624
|
if (!tileCollisionTest(vec2(this.pos.x, y), this.size, this))
|
|
3617
|
-
{
|
|
3625
|
+
{
|
|
3618
3626
|
this.pos.y = y;
|
|
3619
3627
|
debugPhysics && debugRect(this.pos, this.size, '#ff0');
|
|
3620
3628
|
return;
|
|
@@ -3674,10 +3682,10 @@ class EngineObject
|
|
|
3674
3682
|
|
|
3675
3683
|
// disconnect from parent and destroy children
|
|
3676
3684
|
this.destroyed = 1;
|
|
3677
|
-
this.parent
|
|
3685
|
+
this.parent?.removeChild(this);
|
|
3678
3686
|
for (const child of this.children)
|
|
3679
3687
|
{
|
|
3680
|
-
child.parent =
|
|
3688
|
+
child.parent = undefined;
|
|
3681
3689
|
child.destroy();
|
|
3682
3690
|
}
|
|
3683
3691
|
}
|
|
@@ -3698,15 +3706,15 @@ class EngineObject
|
|
|
3698
3706
|
* @param {Vector2} vec - world space vector */
|
|
3699
3707
|
worldToLocalVector(vec) { return vec.rotate(-this.angle); }
|
|
3700
3708
|
|
|
3701
|
-
/** Called to check if a tile collision should be resolved
|
|
3709
|
+
/** Called to check if a tile collision should be resolved. Return true for physics to resolve the collision or false to ignore and resolve it manually.
|
|
3702
3710
|
* @param {number} tileData - the value of the tile at the position
|
|
3703
|
-
* @param {Vector2} pos
|
|
3704
|
-
* @return {boolean}
|
|
3711
|
+
* @param {Vector2} pos - tile where the collision occurred
|
|
3712
|
+
* @return {boolean} - true if the collision should be resolved by modifying it's position and velocity */
|
|
3705
3713
|
collideWithTile(tileData, pos) { return tileData > 0; }
|
|
3706
3714
|
|
|
3707
|
-
/** Called to check if
|
|
3715
|
+
/** Called by the engine to check if an object collision should be resolved. Return true for physics to resolve the collision or false to ignore and resolve it manually.
|
|
3708
3716
|
* @param {EngineObject} object - the object to test against
|
|
3709
|
-
* @return {boolean}
|
|
3717
|
+
* @return {boolean} - true if the collision should be resolved by modifying it's position and velocity
|
|
3710
3718
|
*/
|
|
3711
3719
|
collideWithObject(object) { return true; }
|
|
3712
3720
|
|
|
@@ -3747,9 +3755,9 @@ class EngineObject
|
|
|
3747
3755
|
* @return {number} -1 if this.mirror is true, or 1 if not mirrored */
|
|
3748
3756
|
getMirrorSign() { return this.mirror ? -1 : 1; }
|
|
3749
3757
|
|
|
3750
|
-
/** Attaches a child to this with a local transform, returns child for chaining
|
|
3758
|
+
/** Attaches a child to this with a local transform, returns child for chaining
|
|
3751
3759
|
* @param {EngineObject} child
|
|
3752
|
-
* @param {Vector2} [localPos=(
|
|
3760
|
+
* @param {Vector2} [localPos=vec2()]
|
|
3753
3761
|
* @param {number} [localAngle]
|
|
3754
3762
|
* @return {EngineObject} The child object added */
|
|
3755
3763
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
@@ -3773,7 +3781,7 @@ class EngineObject
|
|
|
3773
3781
|
const index = this.children.indexOf(child);
|
|
3774
3782
|
ASSERT(index >= 0, 'child not found in children array');
|
|
3775
3783
|
index >= 0 && this.children.splice(index, 1);
|
|
3776
|
-
child.parent =
|
|
3784
|
+
child.parent = undefined;
|
|
3777
3785
|
}
|
|
3778
3786
|
|
|
3779
3787
|
/** Check if overlapping another engine object
|
|
@@ -3785,7 +3793,7 @@ class EngineObject
|
|
|
3785
3793
|
|
|
3786
3794
|
/** Check if overlapping a point or aligned bounding box
|
|
3787
3795
|
* @param {Vector2} pos - Center of box
|
|
3788
|
-
* @param {Vector2} [size=(
|
|
3796
|
+
* @param {Vector2} [size=vec2()] - Size of box, uses a point if undefined
|
|
3789
3797
|
* @return {boolean} */
|
|
3790
3798
|
isOverlapping(pos, size=vec2())
|
|
3791
3799
|
{ return isOverlapping(this.pos, this.size, pos, size); }
|
|
@@ -3916,10 +3924,11 @@ let drawCount;
|
|
|
3916
3924
|
* Create a tile info object using a grid based system
|
|
3917
3925
|
* - This can take vecs or floats for easier use and conversion
|
|
3918
3926
|
* - If an index is passed in, the tile size and index will determine the position
|
|
3919
|
-
* @param {Vector2|number} [
|
|
3927
|
+
* @param {Vector2|number} [index=0] - Index of the tile in 1d or 2d form
|
|
3920
3928
|
* @param {Vector2|number} [size] - Size of tile in pixels
|
|
3921
|
-
* @param {number} [
|
|
3929
|
+
* @param {TextureInfo|number} [texture] - Texture index or info to use
|
|
3922
3930
|
* @param {number} [padding] - How many pixels padding around tiles
|
|
3931
|
+
* @param {number} [bleed] - How many pixels smaller to draw tiles
|
|
3923
3932
|
* @return {TileInfo}
|
|
3924
3933
|
* @example
|
|
3925
3934
|
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
@@ -3927,36 +3936,43 @@ let drawCount;
|
|
|
3927
3936
|
* tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
|
|
3928
3937
|
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
3929
3938
|
* @memberof Draw */
|
|
3930
|
-
function tile(
|
|
3939
|
+
function tile(index=new Vector2, size=tileDefaultSize, texture=0, padding=tileDefaultPadding, bleed=tileDefaultBleed)
|
|
3931
3940
|
{
|
|
3932
|
-
|
|
3933
|
-
|
|
3941
|
+
ASSERT(isVector2(index) || typeof index === 'number', 'index must be a vec2 or number');
|
|
3942
|
+
ASSERT(isVector2(size) || typeof size === 'number', 'size must be a vec2 or number');
|
|
3943
|
+
ASSERT(isNumber(texture) || texture instanceof TextureInfo, 'texture must be a number or TextureInfo');
|
|
3944
|
+
ASSERT(isNumber(padding), 'padding must be a number');
|
|
3945
|
+
|
|
3946
|
+
if (headlessMode) return new TileInfo;
|
|
3934
3947
|
|
|
3935
|
-
// if size is a number, make it a vector
|
|
3936
3948
|
if (typeof size === 'number')
|
|
3937
3949
|
{
|
|
3950
|
+
// if size is a number, make it a vector
|
|
3938
3951
|
ASSERT(size > 0);
|
|
3939
3952
|
size = new Vector2(size, size);
|
|
3940
3953
|
}
|
|
3941
3954
|
|
|
3942
3955
|
// create tile info object
|
|
3943
|
-
const
|
|
3956
|
+
const textureInfo = typeof texture === 'number' ?
|
|
3957
|
+
textureInfos[texture] : texture;
|
|
3944
3958
|
|
|
3945
3959
|
// get the position of the tile
|
|
3946
|
-
const textureInfo = textureInfos[textureIndex];
|
|
3947
|
-
ASSERT(!!textureInfo, 'Texture not loaded');
|
|
3948
3960
|
const sizePaddedX = size.x + padding*2;
|
|
3949
3961
|
const sizePaddedY = size.y + padding*2;
|
|
3950
|
-
|
|
3962
|
+
let x, y;
|
|
3963
|
+
if (typeof index === 'number')
|
|
3951
3964
|
{
|
|
3952
3965
|
const cols = textureInfo.size.x / sizePaddedX |0;
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
tileInfo.pos.set(posX*sizePaddedX+padding, posY*sizePaddedY+padding);
|
|
3966
|
+
x = index % cols;
|
|
3967
|
+
y = index / cols |0;
|
|
3956
3968
|
}
|
|
3957
3969
|
else
|
|
3958
|
-
|
|
3959
|
-
|
|
3970
|
+
{
|
|
3971
|
+
x = index.x;
|
|
3972
|
+
y = index.y;
|
|
3973
|
+
}
|
|
3974
|
+
const pos = new Vector2(x*sizePaddedX + padding, y*sizePaddedY + padding);
|
|
3975
|
+
return new TileInfo(pos, size, textureInfo, padding, bleed);
|
|
3960
3976
|
}
|
|
3961
3977
|
|
|
3962
3978
|
/**
|
|
@@ -3966,24 +3982,22 @@ function tile(pos=new Vector2, size=tileDefaultSize, textureIndex=0, padding=til
|
|
|
3966
3982
|
class TileInfo
|
|
3967
3983
|
{
|
|
3968
3984
|
/** Create a tile info object
|
|
3969
|
-
* @param {Vector2} [pos=(
|
|
3985
|
+
* @param {Vector2} [pos=vec2()] - Top left corner of tile in pixels
|
|
3970
3986
|
* @param {Vector2} [size] - Size of tile in pixels
|
|
3971
|
-
* @param {
|
|
3972
|
-
* @param {number}
|
|
3973
|
-
* @param {number}
|
|
3987
|
+
* @param {TextureInfo} [textureInfo] - Texture info to use
|
|
3988
|
+
* @param {number} [padding] - How many pixels padding around tiles
|
|
3989
|
+
* @param {number} [bleed] - How many pixels smaller to draw tiles
|
|
3974
3990
|
*/
|
|
3975
|
-
constructor(pos=vec2(), size=tileDefaultSize,
|
|
3991
|
+
constructor(pos=vec2(), size=tileDefaultSize, textureInfo=textureInfos[0], padding=tileDefaultPadding, bleed=tileDefaultBleed)
|
|
3976
3992
|
{
|
|
3977
3993
|
/** @property {Vector2} - Top left corner of tile in pixels */
|
|
3978
3994
|
this.pos = pos.copy();
|
|
3979
3995
|
/** @property {Vector2} - Size of tile in pixels */
|
|
3980
3996
|
this.size = size.copy();
|
|
3981
|
-
/** @property {number} - Texture index to use */
|
|
3982
|
-
this.textureIndex = textureIndex;
|
|
3983
3997
|
/** @property {number} - How many pixels padding around tiles */
|
|
3984
3998
|
this.padding = padding;
|
|
3985
3999
|
/** @property {TextureInfo} - The texture info for this tile */
|
|
3986
|
-
this.textureInfo =
|
|
4000
|
+
this.textureInfo = textureInfo;
|
|
3987
4001
|
/** @property {number} - Shrinks tile by this many pixels to prevent neighbors bleeding */
|
|
3988
4002
|
this.bleed = bleed;
|
|
3989
4003
|
}
|
|
@@ -3993,7 +4007,7 @@ class TileInfo
|
|
|
3993
4007
|
* @return {TileInfo}
|
|
3994
4008
|
*/
|
|
3995
4009
|
offset(offset)
|
|
3996
|
-
{ return new TileInfo(this.pos.add(offset), this.size, this.
|
|
4010
|
+
{ return new TileInfo(this.pos.add(offset), this.size, this.textureInfo, this.padding, this.bleed); }
|
|
3997
4011
|
|
|
3998
4012
|
/** Returns a copy of this tile offset by a number of animation frames
|
|
3999
4013
|
* @param {number} frame - Offset to apply in animation frames
|
|
@@ -4002,23 +4016,33 @@ class TileInfo
|
|
|
4002
4016
|
frame(frame)
|
|
4003
4017
|
{
|
|
4004
4018
|
ASSERT(typeof frame === 'number');
|
|
4005
|
-
|
|
4019
|
+
const w = this.size.x + this.padding*2;
|
|
4020
|
+
const x = frame*w;
|
|
4021
|
+
ASSERT(x < this.textureInfo.size.x, 'frame extends beyond texture width!');
|
|
4022
|
+
return this.offset(new Vector2(x));
|
|
4006
4023
|
}
|
|
4007
4024
|
|
|
4008
4025
|
/**
|
|
4009
4026
|
* Set this tile to use a full image in a texture info
|
|
4010
|
-
* @param {TextureInfo} textureInfo
|
|
4027
|
+
* @param {TextureInfo} [textureInfo]
|
|
4011
4028
|
* @return {TileInfo}
|
|
4012
4029
|
*/
|
|
4013
|
-
setFullImage(textureInfo)
|
|
4030
|
+
setFullImage(textureInfo=this.textureInfo)
|
|
4014
4031
|
{
|
|
4032
|
+
this.textureInfo = textureInfo;
|
|
4015
4033
|
this.pos = new Vector2;
|
|
4016
4034
|
this.size = textureInfo.size.copy();
|
|
4017
|
-
this.textureInfo = textureInfo;
|
|
4018
|
-
// do not use padding or bleed
|
|
4019
4035
|
this.bleed = this.padding = 0;
|
|
4020
4036
|
return this;
|
|
4021
4037
|
}
|
|
4038
|
+
|
|
4039
|
+
/**
|
|
4040
|
+
* Returns a tile info for an index using this tile as refrence
|
|
4041
|
+
* @param {Vector2|number} [index=0]
|
|
4042
|
+
* @return {TileInfo}
|
|
4043
|
+
*/
|
|
4044
|
+
tile(index)
|
|
4045
|
+
{ return tile(index, this.size, this.textureInfo, this.padding, this.bleed); }
|
|
4022
4046
|
}
|
|
4023
4047
|
|
|
4024
4048
|
/**
|
|
@@ -4059,19 +4083,19 @@ class TextureInfo
|
|
|
4059
4083
|
///////////////////////////////////////////////////////////////////////////////
|
|
4060
4084
|
// Drawing functions
|
|
4061
4085
|
|
|
4062
|
-
/** Draw textured tile centered in world space
|
|
4063
|
-
* @param {Vector2} pos
|
|
4064
|
-
* @param {Vector2} [size=(1
|
|
4065
|
-
* @param {TileInfo} [tileInfo]
|
|
4066
|
-
* @param {Color} [color=
|
|
4067
|
-
* @param {number} [angle]
|
|
4068
|
-
* @param {boolean} [mirror]
|
|
4069
|
-
* @param {Color} [additiveColor]
|
|
4086
|
+
/** Draw textured tile centered in world space
|
|
4087
|
+
* @param {Vector2} pos - Center of the tile in world space
|
|
4088
|
+
* @param {Vector2} [size=vec2(1)] - Size of the tile in world space
|
|
4089
|
+
* @param {TileInfo} [tileInfo] - Tile info to use, untextured if undefined
|
|
4090
|
+
* @param {Color} [color=WHITE] - Color to modulate with
|
|
4091
|
+
* @param {number} [angle] - Angle to rotate by
|
|
4092
|
+
* @param {boolean} [mirror] - Is image flipped along the Y axis?
|
|
4093
|
+
* @param {Color} [additiveColor] - Additive color to be applied if any
|
|
4070
4094
|
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering?
|
|
4071
4095
|
* @param {boolean} [screenSpace=false] - Are the pos and size are in screen space?
|
|
4072
4096
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
4073
4097
|
* @memberof Draw */
|
|
4074
|
-
function drawTile(pos, size=
|
|
4098
|
+
function drawTile(pos, size=vec2(1), tileInfo, color=WHITE,
|
|
4075
4099
|
angle=0, mirror, additiveColor, useWebGL=glEnable, screenSpace, context)
|
|
4076
4100
|
{
|
|
4077
4101
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -4146,8 +4170,8 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4146
4170
|
|
|
4147
4171
|
/** Draw colored rect centered on pos
|
|
4148
4172
|
* @param {Vector2} pos
|
|
4149
|
-
* @param {Vector2} [size=(1
|
|
4150
|
-
* @param {Color} [color=
|
|
4173
|
+
* @param {Vector2} [size=vec2(1)]
|
|
4174
|
+
* @param {Color} [color=WHITE]
|
|
4151
4175
|
* @param {number} [angle]
|
|
4152
4176
|
* @param {boolean} [useWebGL=glEnable]
|
|
4153
4177
|
* @param {boolean} [screenSpace]
|
|
@@ -4160,9 +4184,9 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
4160
4184
|
|
|
4161
4185
|
/** Draw a rect centered on pos with a gradient from top to bottom
|
|
4162
4186
|
* @param {Vector2} pos
|
|
4163
|
-
* @param {Vector2} [size=(1
|
|
4164
|
-
* @param {Color} [colorTop=
|
|
4165
|
-
* @param {Color} [colorBottom=
|
|
4187
|
+
* @param {Vector2} [size=vec2(1)]
|
|
4188
|
+
* @param {Color} [colorTop=WHITE]
|
|
4189
|
+
* @param {Color} [colorBottom=BLACK]
|
|
4166
4190
|
* @param {number} [angle]
|
|
4167
4191
|
* @param {boolean} [useWebGL=glEnable]
|
|
4168
4192
|
* @param {boolean} [screenSpace]
|
|
@@ -4224,9 +4248,9 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
4224
4248
|
/** Draw connected lines between a series of points
|
|
4225
4249
|
* @param {Array<Vector2>} points
|
|
4226
4250
|
* @param {number} [width]
|
|
4227
|
-
* @param {Color} [color=
|
|
4251
|
+
* @param {Color} [color=WHITE]
|
|
4228
4252
|
* @param {boolean} [wrap] - Should the last point connect to the first?
|
|
4229
|
-
* @param {Vector2} [pos=(
|
|
4253
|
+
* @param {Vector2} [pos=vec2()] - Offset to apply
|
|
4230
4254
|
* @param {number} [angle] - Angle to rotate by
|
|
4231
4255
|
* @param {boolean} [useWebGL=glEnable]
|
|
4232
4256
|
* @param {boolean} [screenSpace]
|
|
@@ -4261,13 +4285,9 @@ function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0,
|
|
|
4261
4285
|
for (let i=0; i<points.length; ++i)
|
|
4262
4286
|
{
|
|
4263
4287
|
const point = points[i];
|
|
4264
|
-
|
|
4265
|
-
context.lineTo(point.x, point.y);
|
|
4266
|
-
else
|
|
4267
|
-
context.moveTo(point.x, point.y);
|
|
4288
|
+
context.lineTo(point.x, point.y);
|
|
4268
4289
|
}
|
|
4269
|
-
|
|
4270
|
-
context.closePath();
|
|
4290
|
+
wrap && context.closePath();
|
|
4271
4291
|
context.stroke();
|
|
4272
4292
|
}, screenSpace, context);
|
|
4273
4293
|
}
|
|
@@ -4277,8 +4297,8 @@ function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0,
|
|
|
4277
4297
|
* @param {Vector2} posA
|
|
4278
4298
|
* @param {Vector2} posB
|
|
4279
4299
|
* @param {number} [width]
|
|
4280
|
-
* @param {Color} [color=
|
|
4281
|
-
* @param {Vector2} [pos=(
|
|
4300
|
+
* @param {Color} [color=WHITE]
|
|
4301
|
+
* @param {Vector2} [pos=vec2()] - Offset to apply
|
|
4282
4302
|
* @param {number} [angle] - Angle to rotate by
|
|
4283
4303
|
* @param {boolean} [useWebGL=glEnable]
|
|
4284
4304
|
* @param {boolean} [screenSpace]
|
|
@@ -4297,12 +4317,12 @@ function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, sc
|
|
|
4297
4317
|
|
|
4298
4318
|
/** Draw colored regular polygon using passed in number of sides
|
|
4299
4319
|
* @param {Vector2} pos
|
|
4300
|
-
* @param {Vector2} [size=(1
|
|
4320
|
+
* @param {Vector2} [size=vec2(1)]
|
|
4301
4321
|
* @param {number} [sides]
|
|
4302
|
-
* @param {Color} [color=
|
|
4322
|
+
* @param {Color} [color=WHITE]
|
|
4303
4323
|
* @param {number} [angle]
|
|
4304
4324
|
* @param {number} [lineWidth]
|
|
4305
|
-
* @param {Color} [lineColor=
|
|
4325
|
+
* @param {Color} [lineColor=BLACK]
|
|
4306
4326
|
* @param {boolean} [useWebGL=glEnable]
|
|
4307
4327
|
* @param {boolean} [screenSpace]
|
|
4308
4328
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
@@ -4325,10 +4345,10 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
|
|
|
4325
4345
|
|
|
4326
4346
|
/** Draw colored polygon using passed in points
|
|
4327
4347
|
* @param {Array<Vector2>} points - Array of Vector2 points
|
|
4328
|
-
* @param {Color} [color=
|
|
4348
|
+
* @param {Color} [color=WHITE]
|
|
4329
4349
|
* @param {number} [lineWidth]
|
|
4330
|
-
* @param {Color} [lineColor=
|
|
4331
|
-
* @param {Vector2} [pos=(
|
|
4350
|
+
* @param {Color} [lineColor=BLACK]
|
|
4351
|
+
* @param {Vector2} [pos=vec2()] - Offset to apply
|
|
4332
4352
|
* @param {number} [angle] - Angle to rotate by
|
|
4333
4353
|
* @param {boolean} [useWebGL=glEnable]
|
|
4334
4354
|
* @param {boolean} [screenSpace]
|
|
@@ -4375,11 +4395,11 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
|
|
|
4375
4395
|
|
|
4376
4396
|
/** Draw colored ellipse using passed in point
|
|
4377
4397
|
* @param {Vector2} pos
|
|
4378
|
-
* @param {Vector2} [size=(1
|
|
4379
|
-
* @param {Color} [color=
|
|
4398
|
+
* @param {Vector2} [size=vec2(1)] - Width and height diameter
|
|
4399
|
+
* @param {Color} [color=WHITE]
|
|
4380
4400
|
* @param {number} [angle]
|
|
4381
4401
|
* @param {number} [lineWidth]
|
|
4382
|
-
* @param {Color} [lineColor=
|
|
4402
|
+
* @param {Color} [lineColor=BLACK]
|
|
4383
4403
|
* @param {boolean} [useWebGL=glEnable]
|
|
4384
4404
|
* @param {boolean} [screenSpace]
|
|
4385
4405
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
@@ -4421,9 +4441,9 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
|
|
|
4421
4441
|
/** Draw colored circle using passed in point
|
|
4422
4442
|
* @param {Vector2} pos
|
|
4423
4443
|
* @param {number} [size=1] - Diameter
|
|
4424
|
-
* @param {Color} [color=
|
|
4444
|
+
* @param {Color} [color=WHITE]
|
|
4425
4445
|
* @param {number} [lineWidth=0]
|
|
4426
|
-
* @param {Color} [lineColor=
|
|
4446
|
+
* @param {Color} [lineColor=BLACK]
|
|
4427
4447
|
* @param {boolean} [useWebGL=glEnable]
|
|
4428
4448
|
* @param {boolean} [screenSpace]
|
|
4429
4449
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
@@ -4478,9 +4498,9 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
|
|
|
4478
4498
|
* @param {string|number} text
|
|
4479
4499
|
* @param {Vector2} pos
|
|
4480
4500
|
* @param {number} [size]
|
|
4481
|
-
* @param {Color} [color=
|
|
4501
|
+
* @param {Color} [color=WHITE]
|
|
4482
4502
|
* @param {number} [lineWidth]
|
|
4483
|
-
* @param {Color} [lineColor=
|
|
4503
|
+
* @param {Color} [lineColor=BLACK]
|
|
4484
4504
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
4485
4505
|
* @param {string} [font=fontDefault]
|
|
4486
4506
|
* @param {string} [fontStyle]
|
|
@@ -4504,10 +4524,10 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
4504
4524
|
* Automatically splits new lines into rows
|
|
4505
4525
|
* @param {string|number} text
|
|
4506
4526
|
* @param {Vector2} pos
|
|
4507
|
-
* @param {number}
|
|
4508
|
-
* @param {Color} [color=
|
|
4527
|
+
* @param {number} size
|
|
4528
|
+
* @param {Color} [color=WHITE]
|
|
4509
4529
|
* @param {number} [lineWidth]
|
|
4510
|
-
* @param {Color} [lineColor=
|
|
4530
|
+
* @param {Color} [lineColor=BLACK]
|
|
4511
4531
|
* @param {CanvasTextAlign} [textAlign]
|
|
4512
4532
|
* @param {string} [font=fontDefault]
|
|
4513
4533
|
* @param {string} [fontStyle]
|
|
@@ -4515,7 +4535,7 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
4515
4535
|
* @param {number} [angle]
|
|
4516
4536
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4517
4537
|
* @memberof Draw */
|
|
4518
|
-
function drawTextScreen(text, pos, size
|
|
4538
|
+
function drawTextScreen(text, pos, size, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=drawContext)
|
|
4519
4539
|
{
|
|
4520
4540
|
ASSERT(isString(text), 'text must be a string');
|
|
4521
4541
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -4639,7 +4659,7 @@ function worldToScreenDelta(worldDelta)
|
|
|
4639
4659
|
|
|
4640
4660
|
/** Convert screen space transform to world space
|
|
4641
4661
|
* @param {Vector2} screenPos
|
|
4642
|
-
* @param {Vector2} screenSize
|
|
4662
|
+
* @param {Vector2} screenSize
|
|
4643
4663
|
* @param {number} [screenAngle]
|
|
4644
4664
|
* @return {[Vector2, Vector2, number]} - [pos, size, angle]
|
|
4645
4665
|
* @memberof Draw */
|
|
@@ -4700,10 +4720,9 @@ function isOnScreen(pos, size=0)
|
|
|
4700
4720
|
* @param {boolean} [additive]
|
|
4701
4721
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
4702
4722
|
* @memberof Draw */
|
|
4703
|
-
function setBlendMode(additive=false, context)
|
|
4723
|
+
function setBlendMode(additive=false, context=drawContext)
|
|
4704
4724
|
{
|
|
4705
4725
|
glAdditive = additive;
|
|
4706
|
-
context ||= drawContext;
|
|
4707
4726
|
context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
4708
4727
|
}
|
|
4709
4728
|
|
|
@@ -4718,7 +4737,6 @@ function combineCanvases()
|
|
|
4718
4737
|
workContext.fillRect(0,0,w,h); // remove background alpha
|
|
4719
4738
|
glCopyToContext(workContext);
|
|
4720
4739
|
workContext.drawImage(mainCanvas, 0, 0);
|
|
4721
|
-
mainCanvas.width |= 0;
|
|
4722
4740
|
mainContext.drawImage(workCanvas, 0, 0);
|
|
4723
4741
|
}
|
|
4724
4742
|
|
|
@@ -4823,17 +4841,22 @@ function setCursor(cursorStyle = 'auto')
|
|
|
4823
4841
|
|
|
4824
4842
|
///////////////////////////////////////////////////////////////////////////////
|
|
4825
4843
|
|
|
4844
|
+
/** Engine font image, 8x8 font provided by the engine
|
|
4845
|
+
* @type {FontImage}
|
|
4846
|
+
* @memberof Draw */
|
|
4826
4847
|
let engineFontImage;
|
|
4827
4848
|
|
|
4828
4849
|
/**
|
|
4829
|
-
* Font Image Object - Draw text
|
|
4850
|
+
* Font Image Object - Draw text by using tiles in an image
|
|
4830
4851
|
* - 96 characters (from space to tilde) are stored in an image
|
|
4831
|
-
* -
|
|
4832
|
-
* -
|
|
4852
|
+
* - A 8x8 default engine font is supplied for general use
|
|
4853
|
+
* - This system is WebGL enabled for fast text rendering
|
|
4854
|
+
* - Fonts can also be colored and scaled along each axis
|
|
4855
|
+
*
|
|
4833
4856
|
* @memberof Draw
|
|
4834
4857
|
* @example
|
|
4835
4858
|
* // use built in font
|
|
4836
|
-
* const font =
|
|
4859
|
+
* const font = engineFontImage;
|
|
4837
4860
|
*
|
|
4838
4861
|
* // draw text
|
|
4839
4862
|
* font.drawTextScreen('LittleJS\nHello World!', vec2(200, 50));
|
|
@@ -4841,70 +4864,112 @@ let engineFontImage;
|
|
|
4841
4864
|
class FontImage
|
|
4842
4865
|
{
|
|
4843
4866
|
/** Create an image font
|
|
4844
|
-
* @param {
|
|
4845
|
-
* @param {Vector2} [tileSize=(8,8)] - Size of the font source tiles
|
|
4846
|
-
* @param {Vector2} [paddingSize=(0,1)] - How much space between characters
|
|
4867
|
+
* @param {TileInfo} tileInfo - Tile info of first characeter in font
|
|
4847
4868
|
*/
|
|
4848
|
-
constructor(
|
|
4869
|
+
constructor(tileInfo)
|
|
4849
4870
|
{
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
{
|
|
4853
|
-
|
|
4854
|
-
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
4855
|
-
}
|
|
4856
|
-
|
|
4857
|
-
this.image = image || engineFontImage;
|
|
4858
|
-
this.tileSize = tileSize;
|
|
4859
|
-
this.paddingSize = paddingSize;
|
|
4871
|
+
ASSERT(!!tileInfo, 'tileInfo is required for FontImage');
|
|
4872
|
+
|
|
4873
|
+
/** @property {TileInfo} - Tile info for the font */
|
|
4874
|
+
this.tileInfo = tileInfo.frame(0);
|
|
4860
4875
|
}
|
|
4861
4876
|
|
|
4862
4877
|
/** Draw text in world space using the image font
|
|
4863
|
-
* @param {string|number}
|
|
4878
|
+
* @param {string|number} text
|
|
4864
4879
|
* @param {Vector2} pos
|
|
4865
|
-
* @param {number}
|
|
4866
|
-
* @param {boolean} [center]
|
|
4867
|
-
* @param {
|
|
4880
|
+
* @param {Vector2|number} [size]
|
|
4881
|
+
* @param {boolean} [center=true]
|
|
4882
|
+
* @param {Color} [color=WHITE]
|
|
4883
|
+
* @param {boolean} [useWebGL=glEnable]
|
|
4884
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
4868
4885
|
*/
|
|
4869
|
-
drawText(text, pos,
|
|
4886
|
+
drawText(text, pos, size=1, center, color, useWebGL, context)
|
|
4870
4887
|
{
|
|
4871
|
-
|
|
4888
|
+
ASSERT(isVector2(size) || typeof size === 'number', 'size must be a vec2 or number');
|
|
4889
|
+
|
|
4890
|
+
if (typeof size === 'number')
|
|
4891
|
+
{
|
|
4892
|
+
// if size is a number, make it a vector
|
|
4893
|
+
ASSERT(size > 0);
|
|
4894
|
+
size *= cameraScale;
|
|
4895
|
+
size = new Vector2(size, size);
|
|
4896
|
+
}
|
|
4897
|
+
else
|
|
4898
|
+
size = size.scale(cameraScale);
|
|
4899
|
+
this.drawTextScreen(text, worldToScreen(pos), size, center, color, useWebGL, context);
|
|
4872
4900
|
}
|
|
4873
4901
|
|
|
4874
4902
|
/** Draw text in screen space using the image font
|
|
4875
|
-
* @param {string|number}
|
|
4903
|
+
* @param {string|number} text
|
|
4876
4904
|
* @param {Vector2} pos
|
|
4877
|
-
* @param {number}
|
|
4905
|
+
* @param {Vector2|number} size
|
|
4878
4906
|
* @param {boolean} [center]
|
|
4879
|
-
* @param {
|
|
4907
|
+
* @param {Color} [color=WHITE]
|
|
4908
|
+
* @param {boolean} [useWebGL=glEnable]
|
|
4909
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
4880
4910
|
*/
|
|
4881
|
-
drawTextScreen(text, pos,
|
|
4911
|
+
drawTextScreen(text, pos, size, center=true, color=WHITE, useWebGL=glEnable, context)
|
|
4882
4912
|
{
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4913
|
+
ASSERT(isString(text), 'text must be a string');
|
|
4914
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
4915
|
+
ASSERT(isVector2(size) || typeof size === 'number', 'size must be a vec2 or number');
|
|
4916
|
+
ASSERT(isColor(color), 'color must be a color');
|
|
4917
|
+
|
|
4918
|
+
// if size is a number, make it a vector
|
|
4919
|
+
size = typeof size === 'number' ? new Vector2(size, size) : size;
|
|
4920
|
+
|
|
4921
|
+
// precache objects for drawing
|
|
4922
|
+
const drawPos = new Vector2;
|
|
4923
|
+
const tileInfo = this.tileInfo;
|
|
4924
|
+
const padding = tileInfo.padding;
|
|
4925
|
+
const sizePaddedX = tileInfo.size.x + padding*2;
|
|
4926
|
+
const sizePaddedY = tileInfo.size.y + padding*2;
|
|
4927
|
+
const cols = tileInfo.textureInfo.size.x / sizePaddedX |0;
|
|
4928
|
+
|
|
4929
|
+
// draw each line of text
|
|
4930
|
+
(text+'').split('\n').forEach((line, j)=>
|
|
4888
4931
|
{
|
|
4889
|
-
const centerOffset = center ? line.length * size.x
|
|
4890
|
-
for (let
|
|
4932
|
+
const centerOffset = center ? (line.length-1) * size.x / 2 : 0;
|
|
4933
|
+
for (let i=line.length; i--;)
|
|
4891
4934
|
{
|
|
4892
|
-
//
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
charCode
|
|
4896
|
-
|
|
4897
|
-
// get the
|
|
4898
|
-
const
|
|
4899
|
-
const
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4935
|
+
// get the character index
|
|
4936
|
+
const charCode = line.charCodeAt(i);
|
|
4937
|
+
const index = charCode < 32 || charCode > 127 ?
|
|
4938
|
+
95 : charCode - 32; // handle out of range characters
|
|
4939
|
+
|
|
4940
|
+
// get the position of the tile
|
|
4941
|
+
const x = index % cols;
|
|
4942
|
+
const y = index / cols |0;
|
|
4943
|
+
tileInfo.pos.x = x*sizePaddedX + padding;
|
|
4944
|
+
tileInfo.pos.y = y*sizePaddedY + padding;
|
|
4945
|
+
|
|
4946
|
+
// draw the tile
|
|
4947
|
+
drawPos.x = pos.x + i * size.x - centerOffset |0;
|
|
4948
|
+
drawPos.y = pos.y + j * size.y |0;
|
|
4949
|
+
drawTile(drawPos, size, tileInfo, color, 0, false, undefined, useWebGL, true, context);
|
|
4904
4950
|
}
|
|
4905
4951
|
});
|
|
4906
|
-
context.restore();
|
|
4907
4952
|
}
|
|
4953
|
+
}
|
|
4954
|
+
|
|
4955
|
+
// load engine font, called automatically on startup
|
|
4956
|
+
function fontImageInit()
|
|
4957
|
+
{
|
|
4958
|
+
return new Promise(resolve =>
|
|
4959
|
+
{
|
|
4960
|
+
// create the engine font
|
|
4961
|
+
const image = new Image;
|
|
4962
|
+
image.onerror = image.onload = ()=>
|
|
4963
|
+
{
|
|
4964
|
+
const tilePos=vec2(), tileSize=vec2(8), padding=1, bleed=0;
|
|
4965
|
+
const textureInfo = new TextureInfo(image);
|
|
4966
|
+
const tileInfo = new TileInfo(tilePos, tileSize, textureInfo, padding, bleed);
|
|
4967
|
+
engineFontImage = new FontImage(tileInfo);
|
|
4968
|
+
resolve();
|
|
4969
|
+
}
|
|
4970
|
+
image.crossOrigin = 'anonymous';
|
|
4971
|
+
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAeAQMAAABnrVXaAAAABlBMVEUAAAD///+l2Z/dAAAAAXRSTlMAQObYZgAAAjpJREFUOMu9kzFu2zAUhn+CAROgqrk+B2l0BWYxMjlXeYaAtFtbdA1sGgHqRQfI0CNkSG5AwYB0BQ8d5Bsomwah6CPVeGg6tEPzAxLwyI+P78cP4u9lNO9OoMKnLMOobG5020/yaj/MrRcCGh1gBbyiLTPJEYaIiom5KM9Jq7KgynMGtb6L4GL4MF2H4LQKCXTvDVw2I4MsgZT7QLExdiutH+D08VOP3INXRrWX1/mmpbkNgAPYRVANb4xpcegYvhiNbIXauQICEjBuYLfMakaakWQeXxiZ0VDtuJCKs3ztMV59QtsHJNcRxDzfdL21ty3PrfIcXTN+E+GFAv6T5nbT9jd50/WFxb5ksdAv49qS6ouymG66ji08UMT6moykYLAo+V0j23GN4m829ZySAD5K7QsBfQTvOG8eE+gTeGYRAmnNAubN3hf5Zv9tJWDHp/VTuaSm7SN4fyINQqaNO3RMVxvpSPXnOChnRNvFcGY0gnwiPswYwTKVPE0zVtX3mTEIOoFzaqLrGuJaV+Uqumb71fVk/VoOH3cdLNQP/FHi8hV0CQNoqBZsUPlLPMsdCJro9QAaQQ0woDy9BJm0eTxCFnO9srcYlhNVlfR2EyTrph1uUtbUtAJifwRgrKuYdXVHeb0YI3QpawohQHkloI3J5FuVwI5ORxC9k2Tuz9Ir1IjgeIPGMHYkAZe2RuYkmWFmt3gGbTPOmBUWVTmRmHtGrfpzG/yuQNOKa6gBB/WA9khitPgl6/GP+gl2Af6tCbvaygAAAABJRU5ErkJggg==';
|
|
4972
|
+
});
|
|
4908
4973
|
}
|
|
4909
4974
|
/**
|
|
4910
4975
|
* LittleJS Input System
|
|
@@ -4961,6 +5026,10 @@ let inputPreventDefault = true;
|
|
|
4961
5026
|
* @memberof Input */
|
|
4962
5027
|
let gamepadPrimary = 0;
|
|
4963
5028
|
|
|
5029
|
+
/** True if a touch device has been detected
|
|
5030
|
+
* @memberof Input */
|
|
5031
|
+
const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
5032
|
+
|
|
4964
5033
|
/** Prevents input continuing to the default browser handling
|
|
4965
5034
|
* This is useful to disable for html menus so the browser can handle input normally
|
|
4966
5035
|
* @param {boolean} preventDefault
|
|
@@ -5159,10 +5228,6 @@ function gamepadStickCount(gamepad=gamepadPrimary)
|
|
|
5159
5228
|
return gamepadStickData[gamepad]?.length ?? 0;
|
|
5160
5229
|
}
|
|
5161
5230
|
|
|
5162
|
-
/** True if a touch device has been detected
|
|
5163
|
-
* @memberof Input */
|
|
5164
|
-
const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
5165
|
-
|
|
5166
5231
|
///////////////////////////////////////////////////////////////////////////////
|
|
5167
5232
|
|
|
5168
5233
|
/** Pulse the vibration hardware if it exists
|
|
@@ -5171,7 +5236,7 @@ const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
|
5171
5236
|
function vibrate(pattern=100)
|
|
5172
5237
|
{
|
|
5173
5238
|
ASSERT(isNumber(pattern) || isArray(pattern), 'pattern must be a number or array');
|
|
5174
|
-
vibrateEnable && !headlessMode && navigator
|
|
5239
|
+
vibrateEnable && !headlessMode && navigator?.vibrate?.(pattern);
|
|
5175
5240
|
}
|
|
5176
5241
|
|
|
5177
5242
|
/** Cancel any ongoing vibration
|
|
@@ -5289,8 +5354,8 @@ function inputInit()
|
|
|
5289
5354
|
}
|
|
5290
5355
|
function onMouseUp(e)
|
|
5291
5356
|
{
|
|
5292
|
-
if (isTouchDevice && touchInputEnable)
|
|
5293
|
-
|
|
5357
|
+
if (isTouchDevice && touchInputEnable) return;
|
|
5358
|
+
|
|
5294
5359
|
inputData[0][e.button] = (inputData[0][e.button]&2) | 4;
|
|
5295
5360
|
}
|
|
5296
5361
|
function onMouseMove(e)
|
|
@@ -5476,8 +5541,7 @@ function inputUpdate()
|
|
|
5476
5541
|
// update touch gamepad if enabled
|
|
5477
5542
|
if (touchGamepadEnable && isTouchDevice)
|
|
5478
5543
|
{
|
|
5479
|
-
if (!touchGamepadTimer.isSet())
|
|
5480
|
-
return;
|
|
5544
|
+
if (!touchGamepadTimer.isSet()) return;
|
|
5481
5545
|
|
|
5482
5546
|
// read virtual analog stick
|
|
5483
5547
|
gamepadPrimary = 0; // touch gamepad uses index 0
|
|
@@ -5515,12 +5579,10 @@ function inputUpdate()
|
|
|
5515
5579
|
}
|
|
5516
5580
|
|
|
5517
5581
|
// return if gamepads are disabled or not supported
|
|
5518
|
-
if (!gamepadsEnable || !navigator || !navigator.getGamepads)
|
|
5519
|
-
return;
|
|
5582
|
+
if (!gamepadsEnable || !navigator || !navigator.getGamepads) return;
|
|
5520
5583
|
|
|
5521
5584
|
// only poll gamepads when focused or in debug mode
|
|
5522
|
-
if (!debug && !document.hasFocus())
|
|
5523
|
-
return;
|
|
5585
|
+
if (!debug && !document.hasFocus()) return;
|
|
5524
5586
|
|
|
5525
5587
|
// poll gamepads
|
|
5526
5588
|
const maxGamepads = 8;
|
|
@@ -5557,8 +5619,7 @@ function inputUpdate()
|
|
|
5557
5619
|
data[j] = button.pressed ? wasDown ? 1 : 3 : wasDown ? 4 : 0;
|
|
5558
5620
|
|
|
5559
5621
|
// check for any input on this gamepad, analog must be full press
|
|
5560
|
-
if (button.pressed)
|
|
5561
|
-
if (!button.value || button.value > .9)
|
|
5622
|
+
if (button.pressed && (!button.value || button.value > .9))
|
|
5562
5623
|
hadInput = true;
|
|
5563
5624
|
}
|
|
5564
5625
|
|
|
@@ -5587,7 +5648,7 @@ function inputUpdate()
|
|
|
5587
5648
|
}
|
|
5588
5649
|
|
|
5589
5650
|
// copy dpad to left analog stick when pressed
|
|
5590
|
-
if (gamepadDirectionEmulateStick &&
|
|
5651
|
+
if (gamepadDirectionEmulateStick && (dpad.x || dpad.y))
|
|
5591
5652
|
sticks[0] = dpad.clampLength();
|
|
5592
5653
|
}
|
|
5593
5654
|
|
|
@@ -5616,13 +5677,11 @@ function inputRender()
|
|
|
5616
5677
|
function touchGamepadRender()
|
|
5617
5678
|
{
|
|
5618
5679
|
if (!touchInputEnable || !isTouchDevice || headlessMode) return;
|
|
5619
|
-
if (!touchGamepadEnable || !touchGamepadTimer.isSet())
|
|
5620
|
-
return;
|
|
5680
|
+
if (!touchGamepadEnable || !touchGamepadTimer.isSet()) return;
|
|
5621
5681
|
|
|
5622
5682
|
// fade off when not touching or paused
|
|
5623
5683
|
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
5624
|
-
if (!alpha || paused)
|
|
5625
|
-
return;
|
|
5684
|
+
if (!alpha || paused) return;
|
|
5626
5685
|
|
|
5627
5686
|
// setup the canvas
|
|
5628
5687
|
const context = mainContext;
|
|
@@ -5775,8 +5834,7 @@ class Sound
|
|
|
5775
5834
|
{
|
|
5776
5835
|
// remove randomness so it can be applied on playback
|
|
5777
5836
|
const randomnessIndex = 1, defaultRandomness = .05;
|
|
5778
|
-
this.randomness = zzfxSound[randomnessIndex]
|
|
5779
|
-
zzfxSound[randomnessIndex] : defaultRandomness;
|
|
5837
|
+
this.randomness = zzfxSound[randomnessIndex] ?? defaultRandomness;
|
|
5780
5838
|
zzfxSound[randomnessIndex] = 0;
|
|
5781
5839
|
|
|
5782
5840
|
// generate the zzfx samples
|
|
@@ -5856,7 +5914,7 @@ class Sound
|
|
|
5856
5914
|
* @return {number} - How long the sound is in seconds (undefined if loading)
|
|
5857
5915
|
*/
|
|
5858
5916
|
getDuration()
|
|
5859
|
-
{ return this.sampleChannels
|
|
5917
|
+
{ return this.sampleChannels?.[0].length / this.sampleRate || 0; }
|
|
5860
5918
|
|
|
5861
5919
|
/** Check if sound is loaded, for sounds fetched from a url
|
|
5862
5920
|
* @return {boolean} - True if sound is loaded and ready to play
|
|
@@ -5948,8 +6006,7 @@ class SoundWave extends Sound
|
|
|
5948
6006
|
this.sampleRate = audioBuffer.sampleRate;
|
|
5949
6007
|
this.sampleChannels = sampleChannels;
|
|
5950
6008
|
this.loadedPercent = 1;
|
|
5951
|
-
|
|
5952
|
-
this.onloadCallback(this);
|
|
6009
|
+
this.onloadCallback?.(this);
|
|
5953
6010
|
}
|
|
5954
6011
|
}
|
|
5955
6012
|
|
|
@@ -6152,7 +6209,7 @@ function speak(text, language='', volume=1, rate=1, pitch=1)
|
|
|
6152
6209
|
|
|
6153
6210
|
/** Stop all queued speech
|
|
6154
6211
|
* @memberof Audio */
|
|
6155
|
-
function speakStop() {speechSynthesis
|
|
6212
|
+
function speakStop() {speechSynthesis?.cancel();}
|
|
6156
6213
|
|
|
6157
6214
|
/** Get frequency of a note on a musical scale
|
|
6158
6215
|
* @param {number} semitoneOffset - How many semitones away from the root note
|
|
@@ -6411,7 +6468,7 @@ function tileCollisionGetData(pos)
|
|
|
6411
6468
|
|
|
6412
6469
|
/** Check if a tile layer collides with another object
|
|
6413
6470
|
* @param {Vector2} pos
|
|
6414
|
-
* @param {Vector2} [size=(
|
|
6471
|
+
* @param {Vector2} [size=vec2()]
|
|
6415
6472
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
6416
6473
|
* @param {boolean} [solidOnly] - Only check solid layers if true
|
|
6417
6474
|
* @return {TileCollisionLayer}
|
|
@@ -6529,20 +6586,20 @@ function tileLayersLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisionLa
|
|
|
6529
6586
|
class TileLayerData
|
|
6530
6587
|
{
|
|
6531
6588
|
/** Create a tile layer data object, one for each tile in a TileLayer
|
|
6532
|
-
* @param {number} [tile]
|
|
6589
|
+
* @param {number} [tile] - The tile to use, untextured if undefined
|
|
6533
6590
|
* @param {number} [direction] - Integer direction of tile, in 90 degree increments
|
|
6534
|
-
* @param {boolean} [mirror]
|
|
6535
|
-
* @param {Color} [color]
|
|
6591
|
+
* @param {boolean} [mirror] - If the tile should be mirrored along the x axis
|
|
6592
|
+
* @param {Color} [color] - Color of the tile */
|
|
6536
6593
|
constructor(tile, direction=0, mirror=false, color=new Color)
|
|
6537
6594
|
{
|
|
6538
|
-
/** @property {number}
|
|
6539
|
-
this.tile
|
|
6540
|
-
/** @property {number}
|
|
6595
|
+
/** @property {number} - The tile to use, untextured if undefined */
|
|
6596
|
+
this.tile = tile;
|
|
6597
|
+
/** @property {number} - Integer direction of tile, in 90 degree increments */
|
|
6541
6598
|
this.direction = direction;
|
|
6542
6599
|
/** @property {boolean} - If the tile should be mirrored along the x axis */
|
|
6543
|
-
this.mirror
|
|
6544
|
-
/** @property {Color}
|
|
6545
|
-
this.color
|
|
6600
|
+
this.mirror = mirror;
|
|
6601
|
+
/** @property {Color} - Color of the tile */
|
|
6602
|
+
this.color = color.copy();
|
|
6546
6603
|
}
|
|
6547
6604
|
|
|
6548
6605
|
/** Set this tile to clear, it will not be rendered */
|
|
@@ -6553,7 +6610,7 @@ class TileLayerData
|
|
|
6553
6610
|
/**
|
|
6554
6611
|
* Canvas Layer - cached off screen rendering system
|
|
6555
6612
|
* - Contains an offscreen canvas that can be rendered to
|
|
6556
|
-
* - WebGL rendering is optional, call
|
|
6613
|
+
* - WebGL rendering is optional, call updateWebGL to enable/update
|
|
6557
6614
|
* @extends EngineObject
|
|
6558
6615
|
* @memberof TileLayers
|
|
6559
6616
|
* @example
|
|
@@ -6567,8 +6624,9 @@ class CanvasLayer extends EngineObject
|
|
|
6567
6624
|
* @param {number} [angle] - Angle the layer is rotated by
|
|
6568
6625
|
* @param {number} [renderOrder] - Objects sorted by renderOrder
|
|
6569
6626
|
* @param {Vector2} [canvasSize] - Default size of canvas, can be changed later
|
|
6627
|
+
* @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
|
|
6570
6628
|
*/
|
|
6571
|
-
constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512))
|
|
6629
|
+
constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512), useWebGL=glEnable)
|
|
6572
6630
|
{
|
|
6573
6631
|
ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
|
|
6574
6632
|
super(position, size, undefined, angle, WHITE, renderOrder);
|
|
@@ -6578,13 +6636,10 @@ class CanvasLayer extends EngineObject
|
|
|
6578
6636
|
/** @property {OffscreenCanvasRenderingContext2D} - The 2D canvas context used by this layer */
|
|
6579
6637
|
this.context = this.canvas?.getContext('2d');
|
|
6580
6638
|
/** @property {TextureInfo} - Texture info to use for this object rendering */
|
|
6581
|
-
const useWebGL = false; // do not use webgl by default
|
|
6582
6639
|
this.textureInfo = new TextureInfo(this.canvas, useWebGL);
|
|
6583
|
-
/** @property {boolean} - True if WebGL texture needs to be refreshed */
|
|
6584
|
-
this.refreshWebGL = false;
|
|
6585
6640
|
|
|
6586
6641
|
// disable physics by default
|
|
6587
|
-
this.mass =
|
|
6642
|
+
this.mass = 0;
|
|
6588
6643
|
}
|
|
6589
6644
|
|
|
6590
6645
|
/** Destroy this canvas layer */
|
|
@@ -6599,7 +6654,7 @@ class CanvasLayer extends EngineObject
|
|
|
6599
6654
|
// Render the layer, called automatically by the engine
|
|
6600
6655
|
render()
|
|
6601
6656
|
{
|
|
6602
|
-
this.draw(this.pos, this.size, this.
|
|
6657
|
+
this.draw(this.pos, this.size, this.color, this.angle, this.mirror, this.additiveColor);
|
|
6603
6658
|
}
|
|
6604
6659
|
|
|
6605
6660
|
/** Draw this canvas layer centered in world space, with color applied if using WebGL
|
|
@@ -6612,103 +6667,54 @@ class CanvasLayer extends EngineObject
|
|
|
6612
6667
|
* @param {boolean} [screenSpace] - If true the pos and size are in screen space
|
|
6613
6668
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
6614
6669
|
* @memberof Draw */
|
|
6615
|
-
draw(pos, size,
|
|
6670
|
+
draw(pos, size, color=WHITE, angle=0, mirror=false, additiveColor, screenSpace=false, context)
|
|
6616
6671
|
{
|
|
6617
|
-
const useWebGL = glEnable && this.textureInfo.hasWebGL();
|
|
6618
|
-
if (useWebGL && this.refreshWebGL)
|
|
6619
|
-
{
|
|
6620
|
-
// update the WebGL texture
|
|
6621
|
-
this.textureInfo.createWebGLTexture();
|
|
6622
|
-
this.refreshWebGL = false;
|
|
6623
|
-
}
|
|
6624
|
-
|
|
6625
6672
|
// draw the canvas layer as a single tile that uses the whole texture
|
|
6626
6673
|
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
6674
|
+
const useWebGL = this.hasWebGL();
|
|
6627
6675
|
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
|
|
6628
6676
|
}
|
|
6629
6677
|
|
|
6630
|
-
/**
|
|
6631
|
-
* @callback Canvas2DDrawCallback - Function that draws to a canvas 2D context
|
|
6632
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
6633
|
-
* @memberof TileLayers
|
|
6634
|
-
*/
|
|
6635
|
-
|
|
6636
|
-
/** Draw onto the layer canvas in world space (bypass WebGL)
|
|
6678
|
+
/** Draw a tile onto the layer canvas in world space
|
|
6637
6679
|
* @param {Vector2} pos
|
|
6638
|
-
* @param {Vector2} size
|
|
6639
|
-
* @param {
|
|
6640
|
-
* @param {
|
|
6641
|
-
* @param {
|
|
6642
|
-
|
|
6680
|
+
* @param {Vector2} [size=vec2(1)]
|
|
6681
|
+
* @param {TileInfo} [tileInfo]
|
|
6682
|
+
* @param {Color} [color=WHITE]
|
|
6683
|
+
* @param {number} [angle]
|
|
6684
|
+
* @param {boolean} [mirror] */
|
|
6685
|
+
drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle=0, mirror=false)
|
|
6643
6686
|
{
|
|
6644
|
-
if (!this.context) return;
|
|
6645
|
-
|
|
6646
|
-
const context = this.context;
|
|
6647
|
-
context.save();
|
|
6648
6687
|
pos = pos.subtract(this.pos).multiply(this.tileInfo.size);
|
|
6649
6688
|
size = size.multiply(this.tileInfo.size);
|
|
6650
|
-
|
|
6651
|
-
context.rotate(angle);
|
|
6652
|
-
context.scale(mirror ? -size.x : size.x, size.y);
|
|
6653
|
-
drawFunction(context);
|
|
6654
|
-
context.restore();
|
|
6655
|
-
}
|
|
6689
|
+
pos.y = this.canvas.height - pos.y;
|
|
6656
6690
|
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
this.drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
6667
|
-
{
|
|
6668
|
-
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
6669
|
-
if (textureInfo)
|
|
6670
|
-
{
|
|
6671
|
-
context.globalAlpha = color.a; // only alpha is supported
|
|
6672
|
-
context.drawImage(textureInfo.image,
|
|
6673
|
-
tileInfo.pos.x, tileInfo.pos.y,
|
|
6674
|
-
tileInfo.size.x, tileInfo.size.y, -.5, -.5, 1, 1);
|
|
6675
|
-
context.globalAlpha = 1;
|
|
6676
|
-
}
|
|
6677
|
-
else
|
|
6678
|
-
{
|
|
6679
|
-
// untextured
|
|
6680
|
-
context.fillStyle = color.toString();
|
|
6681
|
-
context.fillRect(-.5, -.5, 1, 1);
|
|
6682
|
-
}
|
|
6683
|
-
});
|
|
6691
|
+
// draw the tile onto the layer canvas
|
|
6692
|
+
const oldMainCanvasSize = mainCanvasSize;
|
|
6693
|
+
mainCanvasSize = vec2(this.canvas.width, this.canvas.height);
|
|
6694
|
+
const useWebGL = this.hasWebGL();
|
|
6695
|
+
useWebGL && glSetRenderTarget(this.textureInfo.glTexture);
|
|
6696
|
+
const drawContext = useWebGL ? undefined : this.context;
|
|
6697
|
+
drawTile(pos, size, tileInfo, color, angle, mirror, undefined, useWebGL, true, drawContext);
|
|
6698
|
+
useWebGL && glSetRenderTarget();
|
|
6699
|
+
mainCanvasSize = oldMainCanvasSize;
|
|
6684
6700
|
}
|
|
6685
6701
|
|
|
6686
6702
|
/** Draw a rectangle onto the layer canvas in world space
|
|
6687
6703
|
* @param {Vector2} pos
|
|
6688
|
-
* @param {Vector2} [size=(1
|
|
6689
|
-
* @param {Color} [color=
|
|
6690
|
-
* @param {number} [angle
|
|
6704
|
+
* @param {Vector2} [size=vec2(1)]
|
|
6705
|
+
* @param {Color} [color=WHITE]
|
|
6706
|
+
* @param {number} [angle] */
|
|
6691
6707
|
drawRect(pos, size, color, angle)
|
|
6692
6708
|
{ this.drawTile(pos, size, undefined, color, angle); }
|
|
6693
6709
|
|
|
6694
|
-
/** Create
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
*/
|
|
6698
|
-
useWebGL(enable=true, immediate=false)
|
|
6699
|
-
{
|
|
6700
|
-
if (!immediate && enable && this.textureInfo.hasWebGL())
|
|
6701
|
-
{
|
|
6702
|
-
// refresh the texture when needed
|
|
6703
|
-
this.refreshWebGL = true;
|
|
6704
|
-
return;
|
|
6705
|
-
}
|
|
6710
|
+
/** Create WebGL texture if necessary and copy layer canvas to it */
|
|
6711
|
+
updateWebGL()
|
|
6712
|
+
{ this.textureInfo.createWebGLTexture(); }
|
|
6706
6713
|
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
}
|
|
6714
|
+
/** Check if this layer is using WebGL
|
|
6715
|
+
* @return {boolean} */
|
|
6716
|
+
hasWebGL()
|
|
6717
|
+
{ return glEnable && this.textureInfo.hasWebGL(); }
|
|
6712
6718
|
}
|
|
6713
6719
|
|
|
6714
6720
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -6727,50 +6733,67 @@ class CanvasLayer extends EngineObject
|
|
|
6727
6733
|
class TileLayer extends CanvasLayer
|
|
6728
6734
|
{
|
|
6729
6735
|
/** Create a tile layer object
|
|
6730
|
-
* @param {Vector2} position
|
|
6731
|
-
* @param {Vector2} size
|
|
6732
|
-
* @param {TileInfo} [tileInfo]
|
|
6736
|
+
* @param {Vector2} position - World space position
|
|
6737
|
+
* @param {Vector2} size - World space size
|
|
6738
|
+
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
6733
6739
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
6734
6740
|
*/
|
|
6735
6741
|
constructor(position, size, tileInfo=tile(), renderOrder=0)
|
|
6736
6742
|
{
|
|
6737
6743
|
const canvasSize = tileInfo ? size.multiply(tileInfo.size) : size;
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
+
const useWebGL = true;
|
|
6745
|
+
super(position, size, 0, renderOrder, canvasSize, useWebGL);
|
|
6746
|
+
|
|
6747
|
+
/** @property {TileInfo} - Default tile info for layer */
|
|
6748
|
+
this.tileInfo = undefined;
|
|
6749
|
+
/** @property {Array<TileLayerData>} - Default tile info for layer */
|
|
6744
6750
|
this.data = [];
|
|
6745
|
-
|
|
6746
|
-
|
|
6751
|
+
/** @property {boolean} - Is this layer using a webgl texture? */
|
|
6752
|
+
this.isUsingWebGL = false;
|
|
6747
6753
|
|
|
6748
6754
|
if (headlessMode)
|
|
6749
6755
|
{
|
|
6750
|
-
// disable rendering
|
|
6751
|
-
this.
|
|
6752
|
-
this.
|
|
6753
|
-
this.redrawStart
|
|
6754
|
-
this.redrawEnd
|
|
6755
|
-
this.drawTileData
|
|
6756
|
-
this.
|
|
6757
|
-
this.
|
|
6756
|
+
// disable rendering in headless mode
|
|
6757
|
+
this.render = () => {};
|
|
6758
|
+
this.redraw = () => {};
|
|
6759
|
+
this.redrawStart = () => {};
|
|
6760
|
+
this.redrawEnd = () => {};
|
|
6761
|
+
this.drawTileData = () => {};
|
|
6762
|
+
this.redrawTileData = () => {};
|
|
6763
|
+
this.drawLayerTile = () => {};
|
|
6764
|
+
this.drawLayerRect = () => {};
|
|
6765
|
+
this.clearLayerRect = () => {};
|
|
6766
|
+
return;
|
|
6758
6767
|
}
|
|
6768
|
+
|
|
6769
|
+
if (tileInfo)
|
|
6770
|
+
{
|
|
6771
|
+
// set tile info
|
|
6772
|
+
this.tileInfo = tileInfo.frame(0);
|
|
6773
|
+
this.tileInfo.bleed = 0; // disable bleed for tile layers
|
|
6774
|
+
}
|
|
6775
|
+
|
|
6776
|
+
// init tile data
|
|
6777
|
+
for (let j = this.size.area(); j--;)
|
|
6778
|
+
this.data.push(new TileLayerData);
|
|
6759
6779
|
}
|
|
6760
6780
|
|
|
6761
6781
|
/** Set data at a given position in the array
|
|
6762
6782
|
* @param {Vector2} layerPos - Local position in array
|
|
6763
|
-
* @param {TileLayerData} data
|
|
6783
|
+
* @param {TileLayerData} data - Data to set
|
|
6764
6784
|
* @param {boolean} [redraw] - Force the tile to redraw if true */
|
|
6765
6785
|
setData(layerPos, data, redraw=false)
|
|
6766
6786
|
{
|
|
6787
|
+
layerPos = layerPos.floor();
|
|
6767
6788
|
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
6768
6789
|
ASSERT(data instanceof TileLayerData, 'data must be a TileLayerData');
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6790
|
+
|
|
6791
|
+
if (!layerPos.arrayCheck(this.size)) return;
|
|
6792
|
+
this.data[(layerPos.y|0)*this.size.x+layerPos.x|0] = data;
|
|
6793
|
+
|
|
6794
|
+
if (!redraw) return;
|
|
6795
|
+
const isRedraw = drawContext === this.context;
|
|
6796
|
+
isRedraw ? this.drawTileData(layerPos) : this.redrawTileData(layerPos);
|
|
6774
6797
|
}
|
|
6775
6798
|
|
|
6776
6799
|
/** Get data at a given position in the array
|
|
@@ -6779,7 +6802,18 @@ class TileLayer extends CanvasLayer
|
|
|
6779
6802
|
getData(layerPos)
|
|
6780
6803
|
{
|
|
6781
6804
|
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
6782
|
-
return layerPos.arrayCheck(this.size) && this.data[(layerPos.y|0)*this.size.x+layerPos.x|0];
|
|
6805
|
+
return layerPos.arrayCheck(this.size) && this.data[(layerPos.y|0)*this.size.x+layerPos.x|0];
|
|
6806
|
+
}
|
|
6807
|
+
|
|
6808
|
+
// Update the tile layer, refresh texture if needed
|
|
6809
|
+
update()
|
|
6810
|
+
{
|
|
6811
|
+
if (!glEnable && this.isUsingWebGL)
|
|
6812
|
+
{
|
|
6813
|
+
// redraw the layer if webgl was disabled or context lost
|
|
6814
|
+
this.isUsingWebGL = false;
|
|
6815
|
+
this.redraw();
|
|
6816
|
+
}
|
|
6783
6817
|
}
|
|
6784
6818
|
|
|
6785
6819
|
// Render the tile layer, called automatically by the engine
|
|
@@ -6787,40 +6821,35 @@ class TileLayer extends CanvasLayer
|
|
|
6787
6821
|
{
|
|
6788
6822
|
ASSERT(drawContext !== this.context, 'must call redrawEnd() after drawing tiles!');
|
|
6789
6823
|
|
|
6790
|
-
if (this.refreshWebGL)
|
|
6791
|
-
{
|
|
6792
|
-
// update the WebGL texture
|
|
6793
|
-
this.textureInfo.createWebGLTexture();
|
|
6794
|
-
this.refreshWebGL = false;
|
|
6795
|
-
}
|
|
6796
|
-
|
|
6797
|
-
// draw the tile layer as a single tile
|
|
6798
|
-
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
6799
6824
|
const size = this.drawSize || this.size;
|
|
6800
6825
|
const pos = this.pos.add(size.scale(.5));
|
|
6801
|
-
|
|
6802
|
-
drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
|
|
6826
|
+
this.draw(pos, this.size, this.color, this.angle, this.mirror, this.additiveColor);
|
|
6803
6827
|
}
|
|
6804
6828
|
|
|
6829
|
+
/** Called after this layer is redrawn, does nothing by default */
|
|
6830
|
+
onRedraw() {}
|
|
6831
|
+
|
|
6805
6832
|
/** Draw all the tile data to an offscreen canvas
|
|
6806
|
-
* - This may be slow
|
|
6833
|
+
* - This may be slow if not using webgl but only needs to be done once */
|
|
6807
6834
|
redraw()
|
|
6808
6835
|
{
|
|
6809
6836
|
this.redrawStart(true);
|
|
6810
6837
|
for (let x = this.size.x; x--;)
|
|
6811
6838
|
for (let y = this.size.y; y--;)
|
|
6812
6839
|
this.drawTileData(vec2(x,y), false);
|
|
6840
|
+
this.hasWebGL() && glFlush();
|
|
6841
|
+
this.onRedraw();
|
|
6813
6842
|
this.redrawEnd();
|
|
6814
|
-
this.useWebGL();
|
|
6815
6843
|
}
|
|
6816
6844
|
|
|
6817
6845
|
/** Call to start the redraw process
|
|
6818
|
-
* - This can be used to manually update
|
|
6846
|
+
* - This can be used to manually update parts of the level
|
|
6819
6847
|
* @param {boolean} [clear] - Should it clear the canvas before drawing */
|
|
6820
6848
|
redrawStart(clear=false)
|
|
6821
6849
|
{
|
|
6822
6850
|
if (!this.context) return;
|
|
6823
|
-
|
|
6851
|
+
ASSERT(drawContext !== this.context);
|
|
6852
|
+
|
|
6824
6853
|
// save current render settings
|
|
6825
6854
|
/** @type {[CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number, Color]} */
|
|
6826
6855
|
this.savedRenderSettings = [drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor];
|
|
@@ -6828,33 +6857,37 @@ class TileLayer extends CanvasLayer
|
|
|
6828
6857
|
// set the draw canvas and context to this layer
|
|
6829
6858
|
// use camera settings to match this layer's canvas
|
|
6830
6859
|
drawContext = this.context;
|
|
6831
|
-
|
|
6832
|
-
const tileSize = this.tileInfo ? this.tileInfo.size : vec2(1);
|
|
6833
|
-
cameraScale = tileSize.x;
|
|
6834
|
-
canvasClearColor = CLEAR_BLACK;
|
|
6860
|
+
const tileSize = this.tileInfo?.size ?? vec2(1);
|
|
6835
6861
|
mainCanvasSize = this.size.multiply(tileSize);
|
|
6836
|
-
|
|
6862
|
+
canvasClearColor = CLEAR_BLACK;
|
|
6863
|
+
cameraPos = this.size.multiply(tileSize).scale(.5);
|
|
6864
|
+
cameraScale = 1;
|
|
6865
|
+
|
|
6866
|
+
// set render target to this layer
|
|
6867
|
+
this.isUsingWebGL = this.hasWebGL();
|
|
6868
|
+
if (this.isUsingWebGL)
|
|
6869
|
+
glSetRenderTarget(this.textureInfo.glTexture, clear);
|
|
6870
|
+
else
|
|
6837
6871
|
{
|
|
6838
|
-
//
|
|
6839
|
-
this.
|
|
6840
|
-
|
|
6872
|
+
// disable smoothing for pixel art
|
|
6873
|
+
this.context.imageSmoothingEnabled = !tilesPixelated;
|
|
6874
|
+
if (clear)
|
|
6875
|
+
{
|
|
6876
|
+
// clear and set size
|
|
6877
|
+
this.canvas.width = mainCanvasSize.x;
|
|
6878
|
+
this.canvas.height = mainCanvasSize.y;
|
|
6879
|
+
}
|
|
6841
6880
|
}
|
|
6842
|
-
|
|
6843
|
-
// disable smoothing for pixel art
|
|
6844
|
-
drawContext.imageSmoothingEnabled = !tilesPixelated;
|
|
6845
|
-
|
|
6846
|
-
// setup gl rendering if enabled
|
|
6847
|
-
glPreRender();
|
|
6848
6881
|
}
|
|
6849
6882
|
|
|
6850
6883
|
/** Call to end the redraw process */
|
|
6851
6884
|
redrawEnd()
|
|
6852
6885
|
{
|
|
6853
6886
|
if (!this.context) return;
|
|
6887
|
+
ASSERT(drawContext === this.context);
|
|
6854
6888
|
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
//saveCanvas(this.canvas);
|
|
6889
|
+
if (glEnable && this.textureInfo.glTexture)
|
|
6890
|
+
glSetRenderTarget();
|
|
6858
6891
|
|
|
6859
6892
|
// set stuff back to normal
|
|
6860
6893
|
[drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor] = this.savedRenderSettings;
|
|
@@ -6869,24 +6902,75 @@ class TileLayer extends CanvasLayer
|
|
|
6869
6902
|
drawTileData(layerPos, clear=true)
|
|
6870
6903
|
{
|
|
6871
6904
|
if (!this.context) return;
|
|
6905
|
+
ASSERT(drawContext === this.context, 'must call redrawStart() before drawing tiles');
|
|
6872
6906
|
|
|
6873
|
-
// clear out where the tile was, for
|
|
6874
|
-
const
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
const pos = layerPos.multiply(s);
|
|
6878
|
-
this.context.clearRect(pos.x, this.canvas.height-pos.y, s.x, -s.y);
|
|
6879
|
-
}
|
|
6907
|
+
// clear out where the tile was, can be skipped for fully opaque tiles
|
|
6908
|
+
const drawSize = this.tileInfo?.size ?? vec2(1);
|
|
6909
|
+
const drawPos = layerPos.multiply(drawSize);
|
|
6910
|
+
clear && this.clearLayerRect(drawPos, drawSize);
|
|
6880
6911
|
|
|
6881
6912
|
// draw the tile if it has layer data
|
|
6882
6913
|
const d = this.getData(layerPos);
|
|
6883
|
-
if (d.tile
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6914
|
+
if (!d.tile) return;
|
|
6915
|
+
|
|
6916
|
+
const tileInfo = this.tileInfo && this.tileInfo.tile(d.tile);
|
|
6917
|
+
this.drawLayerTile(drawPos, drawSize, tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
6918
|
+
}
|
|
6919
|
+
|
|
6920
|
+
/** Draw the tile at a given position in the tile grid
|
|
6921
|
+
* This can be used to clear tiles when they are destroyed
|
|
6922
|
+
* For better performance use drawTileData inside a redrawStart/End block
|
|
6923
|
+
* @param {Vector2} layerPos
|
|
6924
|
+
* @param {boolean} [clear] - should the old tile be cleared
|
|
6925
|
+
*/
|
|
6926
|
+
redrawTileData(layerPos, clear=true)
|
|
6927
|
+
{
|
|
6928
|
+
if (!this.context) return;
|
|
6929
|
+
ASSERT(drawContext !== this.context, 'redrawStart() should not be active when calling redrawTileData(), instead use drawTileData()');
|
|
6930
|
+
|
|
6931
|
+
this.redrawStart();
|
|
6932
|
+
this.drawTileData(layerPos, clear);
|
|
6933
|
+
this.redrawEnd();
|
|
6934
|
+
}
|
|
6935
|
+
|
|
6936
|
+
/** Draw textured tile in layer space
|
|
6937
|
+
* @param {Vector2} pos - Position in pixel coordinates
|
|
6938
|
+
* @param {Vector2} [size=vec2(1)] - Size of the tile
|
|
6939
|
+
* @param {TileInfo} [tileInfo] - Tile info to use, untextured if undefined
|
|
6940
|
+
* @param {Color} [color=WHITE] - Color to modulate with
|
|
6941
|
+
* @param {number} [angle] - Angle to rotate by
|
|
6942
|
+
* @param {boolean} [mirror] - Is image flipped along the Y axis?
|
|
6943
|
+
* @param {Color} [additiveColor] - Additive color to be applied if any */
|
|
6944
|
+
drawLayerTile(pos, size=vec2(1), tileInfo, color=WHITE,
|
|
6945
|
+
angle=0, mirror, additiveColor)
|
|
6946
|
+
{
|
|
6947
|
+
const drawPos = pos.add(size.scale(.5));
|
|
6948
|
+
drawTile(drawPos, size, tileInfo, color, angle, mirror, additiveColor);
|
|
6949
|
+
}
|
|
6950
|
+
|
|
6951
|
+
/** Clear a rectangle in layer space
|
|
6952
|
+
* @param {Vector2} pos
|
|
6953
|
+
* @param {Vector2} size
|
|
6954
|
+
* @param {Color} [color=WHITE] - Color to modulate with
|
|
6955
|
+
* @param {number} [angle] - Angle to rotate by
|
|
6956
|
+
*/
|
|
6957
|
+
drawLayerRect(pos, size, color, angle=0)
|
|
6958
|
+
{ this.drawLayerTile(pos, size, undefined, color, angle); }
|
|
6959
|
+
|
|
6960
|
+
/** Clear a rectangle in layer space
|
|
6961
|
+
* @param {Vector2} pos - position in pixel coordinates
|
|
6962
|
+
* @param {Vector2} size
|
|
6963
|
+
*/
|
|
6964
|
+
clearLayerRect(pos, size)
|
|
6965
|
+
{
|
|
6966
|
+
ASSERT(drawContext === this.context, 'must call redrawStart() before clearing tiles');
|
|
6967
|
+
|
|
6968
|
+
const x = pos.x, y = this.canvas.height - pos.y - size.y;
|
|
6969
|
+
const useWebGL = this.hasWebGL();
|
|
6970
|
+
if (useWebGL)
|
|
6971
|
+
glClearRect(x, y, size.x, size.y);
|
|
6972
|
+
else
|
|
6973
|
+
this.context.clearRect(x, y, size.x, size.y);
|
|
6890
6974
|
}
|
|
6891
6975
|
}
|
|
6892
6976
|
|
|
@@ -6895,7 +6979,6 @@ class TileLayer extends CanvasLayer
|
|
|
6895
6979
|
* Tile Collision Layer - a tile layer with collision
|
|
6896
6980
|
* - adds collision data and functions to TileLayer
|
|
6897
6981
|
* - there can be multiple tile collision layers
|
|
6898
|
-
* - tile collision layers should not overlap each other
|
|
6899
6982
|
* @extends TileLayer
|
|
6900
6983
|
* @memberof TileLayers
|
|
6901
6984
|
*/
|
|
@@ -6967,7 +7050,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6967
7050
|
|
|
6968
7051
|
/** Check if collision with another object should occur
|
|
6969
7052
|
* @param {Vector2} pos
|
|
6970
|
-
* @param {Vector2} [size=(
|
|
7053
|
+
* @param {Vector2} [size=vec2()]
|
|
6971
7054
|
* @param {EngineObject} [object]
|
|
6972
7055
|
* @return {boolean} */
|
|
6973
7056
|
collisionTest(pos, size=new Vector2, object)
|
|
@@ -7287,7 +7370,7 @@ class ParticleEmitter extends EngineObject
|
|
|
7287
7370
|
particle.mirror = randBool();
|
|
7288
7371
|
|
|
7289
7372
|
// call particle create callback
|
|
7290
|
-
this.particleCreateCallback
|
|
7373
|
+
this.particleCreateCallback?.(particle);
|
|
7291
7374
|
|
|
7292
7375
|
// return the newly created particle
|
|
7293
7376
|
return particle;
|
|
@@ -7372,7 +7455,7 @@ class Particle extends EngineObject
|
|
|
7372
7455
|
const c = this.colorEnd;
|
|
7373
7456
|
this.color.set(c.r, c.g, c.b, c.a);
|
|
7374
7457
|
this.size.set(this.sizeEnd, this.sizeEnd);
|
|
7375
|
-
this.destroyCallback
|
|
7458
|
+
this.destroyCallback?.(this);
|
|
7376
7459
|
this.destroyed = 1;
|
|
7377
7460
|
}
|
|
7378
7461
|
}
|
|
@@ -7650,7 +7733,7 @@ let glContext;
|
|
|
7650
7733
|
let glAntialias = true;
|
|
7651
7734
|
|
|
7652
7735
|
// WebGL internal variables not exposed to documentation
|
|
7653
|
-
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos, glCanBeEnabled = true;
|
|
7736
|
+
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos, glInstancedVAO, glPolyVAO, glFramebuffer, glRenderTarget, glCanBeEnabled = true;
|
|
7654
7737
|
|
|
7655
7738
|
// WebGL internal constants
|
|
7656
7739
|
const gl_ARRAY_BUFFER_SIZE = 5e5;
|
|
@@ -7726,7 +7809,7 @@ function glInit(rootElement)
|
|
|
7726
7809
|
// setup instanced rendering shader program
|
|
7727
7810
|
glShader = glCreateProgram(
|
|
7728
7811
|
'#version 300 es\n' + // specify GLSL ES version
|
|
7729
|
-
'precision highp float;'+ // use highp for
|
|
7812
|
+
'precision highp float;'+ // use highp for accuracy
|
|
7730
7813
|
'uniform mat4 m;'+ // transform matrix
|
|
7731
7814
|
'in vec2 g;'+ // in: geometry
|
|
7732
7815
|
'in vec4 p,u,c,a;'+ // in: position/size, uvs, color, additiveColor
|
|
@@ -7741,7 +7824,7 @@ function glInit(rootElement)
|
|
|
7741
7824
|
'}' // end of shader
|
|
7742
7825
|
,
|
|
7743
7826
|
'#version 300 es\n' + // specify GLSL ES version
|
|
7744
|
-
'precision highp float;'+ // use highp for
|
|
7827
|
+
'precision highp float;'+ // use highp for accuracy
|
|
7745
7828
|
'uniform sampler2D s;'+ // texture
|
|
7746
7829
|
'in vec2 v;'+ // in: uv
|
|
7747
7830
|
'in vec4 d,e;'+ // in: color, additiveColor
|
|
@@ -7779,45 +7862,62 @@ function glInit(rootElement)
|
|
|
7779
7862
|
glColorData = new Uint32Array(glInstanceData);
|
|
7780
7863
|
glArrayBuffer = glContext.createBuffer();
|
|
7781
7864
|
glGeometryBuffer = glContext.createBuffer();
|
|
7865
|
+
glFramebuffer = glContext.createFramebuffer();
|
|
7866
|
+
glBatchCount = 0;
|
|
7782
7867
|
|
|
7783
7868
|
// create the geometry buffer, triangle strip square
|
|
7784
|
-
const geometry = new Float32Array([
|
|
7869
|
+
const geometry = new Float32Array([0,0,1,0,0,1,1,1]);
|
|
7785
7870
|
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
7786
7871
|
glContext.bufferData(glContext.ARRAY_BUFFER, geometry, glContext.STATIC_DRAW);
|
|
7872
|
+
|
|
7873
|
+
let offset, shader, stride;
|
|
7874
|
+
const initVertexAttrib = (name, type, typeSize, size, divisor=0)=>
|
|
7875
|
+
{
|
|
7876
|
+
const location = glContext.getAttribLocation(shader, name);
|
|
7877
|
+
const normalize = typeSize === 1;
|
|
7878
|
+
const fixedStride = typeSize && stride;
|
|
7879
|
+
glContext.enableVertexAttribArray(location);
|
|
7880
|
+
glContext.vertexAttribPointer(location, size, type, normalize, fixedStride, offset);
|
|
7881
|
+
glContext.vertexAttribDivisor(location, divisor);
|
|
7882
|
+
offset += size*typeSize;
|
|
7883
|
+
}
|
|
7884
|
+
|
|
7885
|
+
// setup VAO for instanced rendering
|
|
7886
|
+
glInstancedVAO = glContext.createVertexArray();
|
|
7887
|
+
glContext.bindVertexArray(glInstancedVAO);
|
|
7888
|
+
|
|
7889
|
+
// configure instanced vertex attributes
|
|
7890
|
+
offset = 0, shader = glShader, stride = gl_INSTANCE_BYTE_STRIDE;
|
|
7891
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
7892
|
+
initVertexAttrib('g', glContext.FLOAT, 0, 2); // geometry
|
|
7893
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
|
|
7894
|
+
glContext.bufferData(glContext.ARRAY_BUFFER, gl_ARRAY_BUFFER_SIZE, glContext.DYNAMIC_DRAW);
|
|
7895
|
+
initVertexAttrib('p', glContext.FLOAT, 4, 4, 1); // position & size
|
|
7896
|
+
initVertexAttrib('u', glContext.FLOAT, 4, 4, 1); // texture coords
|
|
7897
|
+
initVertexAttrib('c', glContext.UNSIGNED_BYTE, 1, 4, 1); // color
|
|
7898
|
+
initVertexAttrib('a', glContext.UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
7899
|
+
initVertexAttrib('r', glContext.FLOAT, 4, 1, 1); // rotation
|
|
7900
|
+
|
|
7901
|
+
// setup VAO for poly rendering
|
|
7902
|
+
glPolyVAO = glContext.createVertexArray();
|
|
7903
|
+
glContext.bindVertexArray(glPolyVAO);
|
|
7904
|
+
|
|
7905
|
+
// configure poly vertex attributes
|
|
7906
|
+
offset = 0, shader = glPolyShader, stride = gl_POLY_VERTEX_BYTE_STRIDE;
|
|
7907
|
+
initVertexAttrib('p', glContext.FLOAT, 4, 2); // position
|
|
7908
|
+
initVertexAttrib('c', glContext.UNSIGNED_BYTE, 1, 4); // color
|
|
7787
7909
|
}
|
|
7788
7910
|
}
|
|
7789
7911
|
|
|
7790
|
-
function glSetInstancedMode()
|
|
7912
|
+
function glSetInstancedMode(force=false)
|
|
7791
7913
|
{
|
|
7792
|
-
if (!glPolyMode) return;
|
|
7914
|
+
if (!force && !glPolyMode) return;
|
|
7793
7915
|
|
|
7794
7916
|
// setup instanced mode
|
|
7795
7917
|
glFlush();
|
|
7796
7918
|
glPolyMode = false;
|
|
7797
7919
|
glContext.useProgram(glShader);
|
|
7798
|
-
|
|
7799
|
-
// set vertex attributes
|
|
7800
|
-
let offset = 0;
|
|
7801
|
-
const initVertexAttribArray = (name, type, typeSize, size)=>
|
|
7802
|
-
{
|
|
7803
|
-
const location = glContext.getAttribLocation(glShader, name);
|
|
7804
|
-
const stride = typeSize && gl_INSTANCE_BYTE_STRIDE; // only if not geometry
|
|
7805
|
-
const divisor = typeSize && 1; // only if not geometry
|
|
7806
|
-
const normalize = typeSize === 1; // only if color
|
|
7807
|
-
glContext.enableVertexAttribArray(location);
|
|
7808
|
-
glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
|
|
7809
|
-
glContext.vertexAttribDivisor(location, divisor);
|
|
7810
|
-
offset += size*typeSize;
|
|
7811
|
-
}
|
|
7812
|
-
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
7813
|
-
initVertexAttribArray('g', glContext.FLOAT, 0, 2); // geometry
|
|
7814
|
-
glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
|
|
7815
|
-
glContext.bufferData(glContext.ARRAY_BUFFER, gl_ARRAY_BUFFER_SIZE, glContext.DYNAMIC_DRAW);
|
|
7816
|
-
initVertexAttribArray('p', glContext.FLOAT, 4, 4); // position & size
|
|
7817
|
-
initVertexAttribArray('u', glContext.FLOAT, 4, 4); // texture coords
|
|
7818
|
-
initVertexAttribArray('c', glContext.UNSIGNED_BYTE, 1, 4); // color
|
|
7819
|
-
initVertexAttribArray('a', glContext.UNSIGNED_BYTE, 1, 4); // additiveColor
|
|
7820
|
-
initVertexAttribArray('r', glContext.FLOAT, 4, 1); // rotation
|
|
7920
|
+
glContext.bindVertexArray(glInstancedVAO);
|
|
7821
7921
|
}
|
|
7822
7922
|
|
|
7823
7923
|
function glSetPolyMode()
|
|
@@ -7828,36 +7928,30 @@ function glSetPolyMode()
|
|
|
7828
7928
|
glFlush();
|
|
7829
7929
|
glPolyMode = true;
|
|
7830
7930
|
glContext.useProgram(glPolyShader);
|
|
7831
|
-
|
|
7832
|
-
// set vertex attributes
|
|
7833
|
-
let offset = 0;
|
|
7834
|
-
const initVertexAttribArray = (name, type, typeSize, size)=>
|
|
7835
|
-
{
|
|
7836
|
-
const location = glContext.getAttribLocation(glPolyShader, name);
|
|
7837
|
-
const normalize = typeSize === 1; // only normalize if color
|
|
7838
|
-
const stride = gl_POLY_VERTEX_BYTE_STRIDE;
|
|
7839
|
-
glContext.enableVertexAttribArray(location);
|
|
7840
|
-
glContext.vertexAttribPointer(location, size, type, normalize, stride, offset);
|
|
7841
|
-
glContext.vertexAttribDivisor(location, 0);
|
|
7842
|
-
offset += size*typeSize;
|
|
7843
|
-
}
|
|
7844
|
-
glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
|
|
7845
|
-
glContext.bufferData(glContext.ARRAY_BUFFER, gl_ARRAY_BUFFER_SIZE, glContext.DYNAMIC_DRAW);
|
|
7846
|
-
initVertexAttribArray('p', glContext.FLOAT, 4, 2); // position
|
|
7847
|
-
initVertexAttribArray('c', glContext.UNSIGNED_BYTE, 1, 4); // color
|
|
7931
|
+
glContext.bindVertexArray(glPolyVAO);
|
|
7848
7932
|
}
|
|
7849
7933
|
|
|
7850
7934
|
// Setup WebGL render each frame, called automatically by engine
|
|
7851
7935
|
// Also used by tile layer rendering when redrawing tiles
|
|
7852
|
-
function glPreRender()
|
|
7936
|
+
function glPreRender(clear=true)
|
|
7853
7937
|
{
|
|
7854
7938
|
if (!glEnable || !glContext) return;
|
|
7855
7939
|
|
|
7856
|
-
|
|
7857
|
-
|
|
7940
|
+
ASSERT(!glBatchCount, 'glPreRender called with unflushed batch.');
|
|
7941
|
+
|
|
7942
|
+
if (!glRenderTarget)
|
|
7943
|
+
{
|
|
7944
|
+
// set to same size as main canvas
|
|
7945
|
+
glCanvas.width = mainCanvasSize.x;
|
|
7946
|
+
glCanvas.height = mainCanvasSize.y;
|
|
7947
|
+
}
|
|
7948
|
+
glContext.viewport(0, 0, mainCanvasSize.x, mainCanvasSize.y);
|
|
7949
|
+
clear && glClearCanvas();
|
|
7858
7950
|
|
|
7859
7951
|
// build the transform matrix
|
|
7860
7952
|
const s = vec2(2*cameraScale).divide(mainCanvasSize);
|
|
7953
|
+
if (glRenderTarget)
|
|
7954
|
+
s.y = -s.y; // invert y when using render target
|
|
7861
7955
|
const rotatedCam = cameraPos.rotate(-cameraAngle);
|
|
7862
7956
|
const p = vec2(-1).subtract(rotatedCam.multiply(s));
|
|
7863
7957
|
const ca = cos(cameraAngle);
|
|
@@ -7886,12 +7980,14 @@ function glPreRender()
|
|
|
7886
7980
|
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
|
|
7887
7981
|
}
|
|
7888
7982
|
|
|
7983
|
+
// rebind the array buffer
|
|
7984
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
|
|
7985
|
+
|
|
7889
7986
|
// start with additive blending off
|
|
7890
7987
|
glAdditive = glBatchAdditive = false;
|
|
7891
7988
|
|
|
7892
|
-
// force it to set instanced mode
|
|
7893
|
-
|
|
7894
|
-
glSetInstancedMode();
|
|
7989
|
+
// force it to set instanced mode
|
|
7990
|
+
glSetInstancedMode(true);
|
|
7895
7991
|
}
|
|
7896
7992
|
|
|
7897
7993
|
/** Clear the canvas and setup the viewport
|
|
@@ -7900,13 +7996,9 @@ function glClearCanvas()
|
|
|
7900
7996
|
{
|
|
7901
7997
|
if (!glContext) return;
|
|
7902
7998
|
|
|
7903
|
-
// clear
|
|
7904
|
-
glCanvas.width = mainCanvasSize.x;
|
|
7905
|
-
glCanvas.height = mainCanvasSize.y;
|
|
7906
|
-
glContext.viewport(0, 0, glCanvas.width, glCanvas.height);
|
|
7999
|
+
// clear using the canvasClearColor
|
|
7907
8000
|
const color = canvasClearColor;
|
|
7908
|
-
|
|
7909
|
-
glContext.clearColor(color.r, color.g, color.b, color.a);
|
|
8001
|
+
glContext.clearColor(color.r, color.g, color.b, color.a);
|
|
7910
8002
|
glContext.clear(glContext.COLOR_BUFFER_BIT);
|
|
7911
8003
|
}
|
|
7912
8004
|
|
|
@@ -7983,7 +8075,7 @@ function glCreateTexture(image)
|
|
|
7983
8075
|
// build the texture
|
|
7984
8076
|
const texture = glContext.createTexture();
|
|
7985
8077
|
let mipMap = false;
|
|
7986
|
-
if (image
|
|
8078
|
+
if (image?.width)
|
|
7987
8079
|
{
|
|
7988
8080
|
glSetTextureData(texture, image);
|
|
7989
8081
|
glContext.bindTexture(glContext.TEXTURE_2D, texture);
|
|
@@ -8004,7 +8096,9 @@ function glCreateTexture(image)
|
|
|
8004
8096
|
glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MIN_FILTER, minFilter);
|
|
8005
8097
|
if (mipMap)
|
|
8006
8098
|
glContext.generateMipmap(glContext.TEXTURE_2D);
|
|
8007
|
-
|
|
8099
|
+
|
|
8100
|
+
// rebind active texture
|
|
8101
|
+
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
|
|
8008
8102
|
return texture;
|
|
8009
8103
|
}
|
|
8010
8104
|
|
|
@@ -8027,10 +8121,12 @@ function glSetTextureData(texture, image)
|
|
|
8027
8121
|
if (!glContext) return;
|
|
8028
8122
|
|
|
8029
8123
|
// build the texture
|
|
8030
|
-
ASSERT(
|
|
8124
|
+
ASSERT(image?.width > 0, 'Invalid image data.');
|
|
8031
8125
|
glContext.bindTexture(glContext.TEXTURE_2D, texture);
|
|
8032
8126
|
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, image);
|
|
8033
|
-
|
|
8127
|
+
|
|
8128
|
+
// rebind active texture
|
|
8129
|
+
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
|
|
8034
8130
|
}
|
|
8035
8131
|
|
|
8036
8132
|
/** Tells WebGL to create or update the glTexture and start tracking it
|
|
@@ -8249,6 +8345,48 @@ function glDrawColoredPoints(points, pointColors)
|
|
|
8249
8345
|
glBatchCount += vertCount;
|
|
8250
8346
|
}
|
|
8251
8347
|
|
|
8348
|
+
/** Set the WebGL render target to the given texture or back to the canvas
|
|
8349
|
+
* @param {WebGLTexture} [texture] - a texture or undefined to use normal glCanvas
|
|
8350
|
+
* @param {boolean} [clear] - should the render target be cleared
|
|
8351
|
+
* @memberof WebGL */
|
|
8352
|
+
function glSetRenderTarget(texture, clear=false)
|
|
8353
|
+
{
|
|
8354
|
+
if (texture)
|
|
8355
|
+
{
|
|
8356
|
+
glRenderTarget = texture;
|
|
8357
|
+
glContext.bindFramebuffer(glContext.FRAMEBUFFER, glFramebuffer);
|
|
8358
|
+
glContext.framebufferTexture2D(glContext.FRAMEBUFFER,
|
|
8359
|
+
glContext.COLOR_ATTACHMENT0, glContext.TEXTURE_2D, texture, 0);
|
|
8360
|
+
glPreRender(clear);
|
|
8361
|
+
}
|
|
8362
|
+
else
|
|
8363
|
+
{
|
|
8364
|
+
glFlush();
|
|
8365
|
+
glRenderTarget = undefined;
|
|
8366
|
+
glContext.bindFramebuffer(glContext.FRAMEBUFFER, null);
|
|
8367
|
+
}
|
|
8368
|
+
}
|
|
8369
|
+
|
|
8370
|
+
/** Clear out a rectangle area of the WebGL canvas or render target
|
|
8371
|
+
* @param {number} x
|
|
8372
|
+
* @param {number} y
|
|
8373
|
+
* @param {number} width
|
|
8374
|
+
* @param {number} height
|
|
8375
|
+
* @memberof WebGL */
|
|
8376
|
+
function glClearRect(x, y, width, height)
|
|
8377
|
+
{
|
|
8378
|
+
if (!glEnable) return;
|
|
8379
|
+
|
|
8380
|
+
// Enable scissor test to clear only the specified area
|
|
8381
|
+
glContext.enable(glContext.SCISSOR_TEST);
|
|
8382
|
+
glContext.scissor(x, y, width, height);
|
|
8383
|
+
glContext.clearColor(0, 0, 0, 0);
|
|
8384
|
+
glContext.clear(glContext.COLOR_BUFFER_BIT);
|
|
8385
|
+
glContext.disable(glContext.SCISSOR_TEST);
|
|
8386
|
+
}
|
|
8387
|
+
|
|
8388
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
8389
|
+
|
|
8252
8390
|
// WebGL internal function to convert polygon to outline triangle strip
|
|
8253
8391
|
function glMakeOutline(points, width, wrap=true)
|
|
8254
8392
|
{
|
|
@@ -8382,23 +8520,20 @@ function glPolyStrip(points)
|
|
|
8382
8520
|
const a = points[i0], b = points[i1], c = points[i2];
|
|
8383
8521
|
|
|
8384
8522
|
// check if convex
|
|
8385
|
-
if (cross(a, b, c) < e)
|
|
8386
|
-
continue;
|
|
8523
|
+
if (cross(a, b, c) < e) continue;
|
|
8387
8524
|
|
|
8388
8525
|
// check if any other point is inside
|
|
8389
8526
|
let hasInside = false;
|
|
8390
8527
|
for (let j = 0; j < indices.length; j++)
|
|
8391
8528
|
{
|
|
8392
8529
|
const k = indices[j];
|
|
8393
|
-
if (k === i0 || k === i1 || k === i2)
|
|
8394
|
-
|
|
8530
|
+
if (k === i0 || k === i1 || k === i2) continue;
|
|
8531
|
+
|
|
8395
8532
|
const p = points[k];
|
|
8396
8533
|
hasInside = pointInTriangle(p, a, b, c);
|
|
8397
|
-
if (hasInside)
|
|
8398
|
-
break;
|
|
8534
|
+
if (hasInside) break;
|
|
8399
8535
|
}
|
|
8400
|
-
if (hasInside)
|
|
8401
|
-
continue;
|
|
8536
|
+
if (hasInside) continue;
|
|
8402
8537
|
|
|
8403
8538
|
// found valid ear
|
|
8404
8539
|
triangles.push([i0, i1, i2]);
|
|
@@ -8423,8 +8558,7 @@ function glPolyStrip(points)
|
|
|
8423
8558
|
worstIndex = i;
|
|
8424
8559
|
}
|
|
8425
8560
|
}
|
|
8426
|
-
if (worstIndex < 0)
|
|
8427
|
-
break;
|
|
8561
|
+
if (worstIndex < 0) break;
|
|
8428
8562
|
|
|
8429
8563
|
const i0 = indices[(worstIndex + indices.length - 1) % indices.length];
|
|
8430
8564
|
const i1 = indices[worstIndex];
|
|
@@ -8655,14 +8789,16 @@ class PostProcessPlugin
|
|
|
8655
8789
|
{
|
|
8656
8790
|
/** Create global post processing shader
|
|
8657
8791
|
* @param {string} shaderCode
|
|
8658
|
-
* @param {boolean} [includeMainCanvas]
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8792
|
+
* @param {boolean} [includeMainCanvas] - combine mainCanvs onto glCanvas
|
|
8793
|
+
* @param {boolean} [feedbackTexture] - use glCanvas from previous frame as the texture
|
|
8794
|
+
* @example
|
|
8795
|
+
* // create the post process plugin object
|
|
8796
|
+
* new PostProcessPlugin(shaderCode);
|
|
8797
|
+
*/
|
|
8798
|
+
constructor(shaderCode, includeMainCanvas=false, feedbackTexture=false)
|
|
8664
8799
|
{
|
|
8665
8800
|
ASSERT(!postProcess, 'Post process already initialized');
|
|
8801
|
+
ASSERT(!(includeMainCanvas && feedbackTexture), 'Post process cannot both include main canvas and use feedback texture');
|
|
8666
8802
|
postProcess = this;
|
|
8667
8803
|
|
|
8668
8804
|
if (!shaderCode) // default shader pass through
|
|
@@ -8670,9 +8806,10 @@ class PostProcessPlugin
|
|
|
8670
8806
|
|
|
8671
8807
|
/** @property {WebGLProgram} - Shader for post processing */
|
|
8672
8808
|
this.shader = undefined;
|
|
8673
|
-
|
|
8674
8809
|
/** @property {WebGLTexture} - Texture for post processing */
|
|
8675
8810
|
this.texture = undefined;
|
|
8811
|
+
/** @property {WebGLVertexArrayObject} - Vertex array object */
|
|
8812
|
+
this.vao = undefined;
|
|
8676
8813
|
|
|
8677
8814
|
// setup the post processing plugin
|
|
8678
8815
|
initPostProcess();
|
|
@@ -8681,7 +8818,6 @@ class PostProcessPlugin
|
|
|
8681
8818
|
function initPostProcess()
|
|
8682
8819
|
{
|
|
8683
8820
|
if (headlessMode) return;
|
|
8684
|
-
|
|
8685
8821
|
if (!glEnable)
|
|
8686
8822
|
{
|
|
8687
8823
|
console.warn('PostProcessPlugin: WebGL not enabled!');
|
|
@@ -8692,14 +8828,14 @@ class PostProcessPlugin
|
|
|
8692
8828
|
postProcess.texture = glCreateTexture();
|
|
8693
8829
|
postProcess.shader = glCreateProgram(
|
|
8694
8830
|
'#version 300 es\n' + // specify GLSL ES version
|
|
8695
|
-
'precision highp float;'+ // use highp for
|
|
8831
|
+
'precision highp float;'+ // use highp for accuracy
|
|
8696
8832
|
'in vec2 p;'+ // position
|
|
8697
8833
|
'void main(){'+ // shader entry point
|
|
8698
8834
|
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
8699
8835
|
'}' // end of shader
|
|
8700
8836
|
,
|
|
8701
8837
|
'#version 300 es\n' + // specify GLSL ES version
|
|
8702
|
-
'precision highp float;'+ // use highp for
|
|
8838
|
+
'precision highp float;'+ // use highp for accuracy
|
|
8703
8839
|
'uniform sampler2D iChannel0;'+ // input texture
|
|
8704
8840
|
'uniform vec3 iResolution;'+ // size of output texture
|
|
8705
8841
|
'uniform float iTime;'+ // time
|
|
@@ -8710,6 +8846,17 @@ class PostProcessPlugin
|
|
|
8710
8846
|
'c.a=1.;'+ // always use full alpha
|
|
8711
8847
|
'}' // end of shader
|
|
8712
8848
|
);
|
|
8849
|
+
|
|
8850
|
+
// setup VAO for post processing
|
|
8851
|
+
postProcess.vao = glContext.createVertexArray();
|
|
8852
|
+
glContext.bindVertexArray(postProcess.vao);
|
|
8853
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
8854
|
+
|
|
8855
|
+
// configure vertex attributes
|
|
8856
|
+
const vertexByteStride = 8;
|
|
8857
|
+
const pLocation = glContext.getAttribLocation(postProcess.shader, 'p');
|
|
8858
|
+
glContext.enableVertexAttribArray(pLocation);
|
|
8859
|
+
glContext.vertexAttribPointer(pLocation, 2, glContext.FLOAT, false, vertexByteStride, 0);
|
|
8713
8860
|
}
|
|
8714
8861
|
function postProcessContextLost()
|
|
8715
8862
|
{
|
|
@@ -8724,14 +8871,17 @@ class PostProcessPlugin
|
|
|
8724
8871
|
}
|
|
8725
8872
|
function postProcessRender()
|
|
8726
8873
|
{
|
|
8727
|
-
if (headlessMode) return;
|
|
8728
|
-
|
|
8729
|
-
if (!glEnable)
|
|
8730
|
-
return;
|
|
8874
|
+
if (headlessMode || !glEnable) return;
|
|
8731
8875
|
|
|
8732
8876
|
// clear out the buffer
|
|
8733
8877
|
glFlush();
|
|
8734
|
-
|
|
8878
|
+
|
|
8879
|
+
// setup shader program to draw a quad
|
|
8880
|
+
glContext.useProgram(postProcess.shader);
|
|
8881
|
+
glContext.bindVertexArray(postProcess.vao);
|
|
8882
|
+
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, true);
|
|
8883
|
+
glContext.disable(glContext.BLEND);
|
|
8884
|
+
|
|
8735
8885
|
// setup texture
|
|
8736
8886
|
glContext.activeTexture(glContext.TEXTURE0);
|
|
8737
8887
|
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
@@ -8742,29 +8892,32 @@ class PostProcessPlugin
|
|
|
8742
8892
|
workCanvas.height = mainCanvasSize.y;
|
|
8743
8893
|
glCopyToContext(workContext);
|
|
8744
8894
|
workContext.drawImage(mainCanvas, 0, 0);
|
|
8895
|
+
mainCanvas.width |= 0
|
|
8745
8896
|
|
|
8746
8897
|
// copy work canvas to texture
|
|
8747
8898
|
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, workCanvas);
|
|
8748
8899
|
}
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
// set vertex position attribute
|
|
8757
|
-
const vertexByteStride = 8;
|
|
8758
|
-
const pLocation = glContext.getAttribLocation(postProcess.shader, 'p');
|
|
8759
|
-
glContext.enableVertexAttribArray(pLocation);
|
|
8760
|
-
glContext.vertexAttribPointer(pLocation, 2, glContext.FLOAT, false, vertexByteStride, 0);
|
|
8761
|
-
|
|
8900
|
+
else if (!feedbackTexture)
|
|
8901
|
+
{
|
|
8902
|
+
// copy glCanvas to texture
|
|
8903
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, glCanvas);
|
|
8904
|
+
}
|
|
8905
|
+
|
|
8762
8906
|
// set uniforms and draw
|
|
8763
8907
|
const uniformLocation = (name)=>glContext.getUniformLocation(postProcess.shader, name);
|
|
8764
8908
|
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
8765
8909
|
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
8766
8910
|
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
8767
8911
|
glContext.drawArrays(glContext.TRIANGLE_STRIP, 0, 4);
|
|
8912
|
+
|
|
8913
|
+
if (feedbackTexture)
|
|
8914
|
+
{
|
|
8915
|
+
// pass glCanvas back to overlay texture
|
|
8916
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, glCanvas);
|
|
8917
|
+
}
|
|
8918
|
+
|
|
8919
|
+
// force it to set instanced mode
|
|
8920
|
+
glSetInstancedMode(true);
|
|
8768
8921
|
}
|
|
8769
8922
|
}
|
|
8770
8923
|
}
|
|
@@ -9445,7 +9598,7 @@ class UISystemPlugin
|
|
|
9445
9598
|
const up = 'ArrowUp', down = 'ArrowDown', left = 'ArrowLeft', right = 'ArrowRight';
|
|
9446
9599
|
if (both)
|
|
9447
9600
|
{
|
|
9448
|
-
return keyIsDown(up) || keyIsDown(left) ? -1 :
|
|
9601
|
+
return keyIsDown(up) || keyIsDown(left) ? -1 :
|
|
9449
9602
|
keyIsDown(down) || keyIsDown(right) ? 1 : 0;
|
|
9450
9603
|
}
|
|
9451
9604
|
const back = vertical ? up : left;
|
|
@@ -9476,7 +9629,7 @@ class UISystemPlugin
|
|
|
9476
9629
|
* @return {boolean} */
|
|
9477
9630
|
getNavigationWasPressed()
|
|
9478
9631
|
{
|
|
9479
|
-
return isUsingGamepad ? gamepadWasPressed(0, gamepadPrimary) :
|
|
9632
|
+
return isUsingGamepad ? gamepadWasPressed(0, gamepadPrimary) :
|
|
9480
9633
|
keyWasPressed('Space') || keyWasPressed('Enter');
|
|
9481
9634
|
}
|
|
9482
9635
|
|
|
@@ -9524,7 +9677,7 @@ class UISystemPlugin
|
|
|
9524
9677
|
buttonYes.textHeight = 40;
|
|
9525
9678
|
buttonYes.navigationIndex = 1;
|
|
9526
9679
|
buttonYes.hoverColor = hsl(0,1,.5);
|
|
9527
|
-
buttonYes.onClick = ()=> { closeMenu(); yesCallback && yesCallback(); };
|
|
9680
|
+
buttonYes.onClick = ()=> { closeMenu(); yesCallback && yesCallback(); };
|
|
9528
9681
|
confirmMenu.addChild(buttonYes);
|
|
9529
9682
|
|
|
9530
9683
|
// no button
|
|
@@ -9554,8 +9707,8 @@ class UISystemPlugin
|
|
|
9554
9707
|
class UIObject
|
|
9555
9708
|
{
|
|
9556
9709
|
/** Create a UIObject
|
|
9557
|
-
* @param {Vector2} [pos=(
|
|
9558
|
-
* @param {Vector2} [size=(1
|
|
9710
|
+
* @param {Vector2} [pos=vec2()]
|
|
9711
|
+
* @param {Vector2} [size=vec2(1)]
|
|
9559
9712
|
*/
|
|
9560
9713
|
constructor(pos=vec2(), size=vec2())
|
|
9561
9714
|
{
|
|
@@ -9570,7 +9723,7 @@ class UIObject
|
|
|
9570
9723
|
this.size = size.copy();
|
|
9571
9724
|
/** @property {Color} - Color of the object */
|
|
9572
9725
|
this.color = uiSystem.defaultColor.copy();
|
|
9573
|
-
/** @property {Color} - Color of the object when active, uses
|
|
9726
|
+
/** @property {Color} - Color of the object when active, uses hoverColor if undefined */
|
|
9574
9727
|
this.activeColor = undefined;
|
|
9575
9728
|
/** @property {string} - Text for this ui object */
|
|
9576
9729
|
this.text = undefined;
|
|
@@ -9668,10 +9821,10 @@ class UIObject
|
|
|
9668
9821
|
|
|
9669
9822
|
// disconnect from parent and destroy children
|
|
9670
9823
|
this.destroyed = 1;
|
|
9671
|
-
this.parent
|
|
9824
|
+
this.parent?.removeChild(this);
|
|
9672
9825
|
for (const child of this.children)
|
|
9673
9826
|
{
|
|
9674
|
-
child.parent =
|
|
9827
|
+
child.parent = undefined;
|
|
9675
9828
|
child.destroy();
|
|
9676
9829
|
}
|
|
9677
9830
|
}
|
|
@@ -9767,10 +9920,10 @@ class UIObject
|
|
|
9767
9920
|
this.interactive && this.isActiveObject() && !this.disabled ?
|
|
9768
9921
|
this.color : this.lineColor;
|
|
9769
9922
|
const color = isNavigationObject ? this.hoverColor :
|
|
9770
|
-
this.disabled ? this.disabledColor :
|
|
9771
|
-
this.interactive ?
|
|
9772
|
-
this.
|
|
9773
|
-
this.
|
|
9923
|
+
this.disabled ? this.disabledColor :
|
|
9924
|
+
this.interactive ?
|
|
9925
|
+
this.isActiveObject() ? this.activeColor || this.hoverColor :
|
|
9926
|
+
this.isHoverObject() ? this.hoverColor :
|
|
9774
9927
|
this.color : this.color;
|
|
9775
9928
|
const lineWidth = this.lineWidth * (isNavigationObject ? 1.5 : 1);
|
|
9776
9929
|
|
|
@@ -9782,7 +9935,7 @@ class UIObject
|
|
|
9782
9935
|
getTextSize()
|
|
9783
9936
|
{
|
|
9784
9937
|
return vec2(
|
|
9785
|
-
this.textWidth || this.textFitScale * this.size.x,
|
|
9938
|
+
this.textWidth || this.textFitScale * this.size.x,
|
|
9786
9939
|
this.textHeight || this.textFitScale * this.size.y);
|
|
9787
9940
|
}
|
|
9788
9941
|
|
|
@@ -9830,9 +9983,9 @@ class UIObject
|
|
|
9830
9983
|
renderDebug(visible=true)
|
|
9831
9984
|
{
|
|
9832
9985
|
// apply color based on state
|
|
9833
|
-
const color =
|
|
9986
|
+
const color =
|
|
9834
9987
|
!visible ? GREEN :
|
|
9835
|
-
this.isHoverObject() ? YELLOW :
|
|
9988
|
+
this.isHoverObject() ? YELLOW :
|
|
9836
9989
|
this.disabled ? PURPLE :
|
|
9837
9990
|
this.interactive ? RED : BLUE;
|
|
9838
9991
|
uiSystem.drawRect(this.pos, this.size, CLEAR_BLACK, 4, color);
|
|
@@ -12354,357 +12507,349 @@ function drawThreeSlice(pos, size, startTile, color, borderSize=1, additiveColor
|
|
|
12354
12507
|
|
|
12355
12508
|
export
|
|
12356
12509
|
{
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
|
|
12364
|
-
|
|
12365
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
|
|
12465
|
-
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
|
|
12469
|
-
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
|
|
12487
|
-
|
|
12488
|
-
|
|
12489
|
-
|
|
12490
|
-
|
|
12491
|
-
|
|
12492
|
-
|
|
12493
|
-
|
|
12494
|
-
|
|
12495
|
-
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
|
|
12499
|
-
|
|
12500
|
-
|
|
12501
|
-
|
|
12502
|
-
|
|
12503
|
-
|
|
12504
|
-
|
|
12505
|
-
|
|
12506
|
-
|
|
12507
|
-
|
|
12508
|
-
|
|
12509
|
-
|
|
12510
|
-
|
|
12511
|
-
|
|
12512
|
-
|
|
12513
|
-
|
|
12514
|
-
|
|
12515
|
-
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
|
|
12519
|
-
|
|
12520
|
-
|
|
12521
|
-
|
|
12522
|
-
|
|
12523
|
-
|
|
12524
|
-
|
|
12525
|
-
|
|
12526
|
-
|
|
12527
|
-
|
|
12528
|
-
|
|
12529
|
-
|
|
12530
|
-
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
|
|
12536
|
-
|
|
12537
|
-
|
|
12538
|
-
|
|
12539
|
-
|
|
12540
|
-
|
|
12541
|
-
|
|
12542
|
-
|
|
12543
|
-
|
|
12544
|
-
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
12554
|
-
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
|
|
12560
|
-
|
|
12561
|
-
|
|
12562
|
-
|
|
12563
|
-
|
|
12564
|
-
|
|
12565
|
-
|
|
12566
|
-
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
|
|
12571
|
-
|
|
12572
|
-
|
|
12573
|
-
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
|
|
12579
|
-
|
|
12580
|
-
|
|
12581
|
-
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
|
|
12603
|
-
|
|
12604
|
-
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
|
|
12610
|
-
|
|
12611
|
-
|
|
12612
|
-
|
|
12613
|
-
|
|
12614
|
-
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
|
|
12621
|
-
|
|
12622
|
-
|
|
12623
|
-
|
|
12624
|
-
|
|
12625
|
-
|
|
12626
|
-
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
|
|
12637
|
-
|
|
12638
|
-
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
|
|
12664
|
-
|
|
12665
|
-
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12692
|
-
|
|
12693
|
-
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
|
|
12697
|
-
|
|
12698
|
-
|
|
12699
|
-
|
|
12700
|
-
ParticleEmitter,
|
|
12701
|
-
Particle,
|
|
12702
|
-
|
|
12703
|
-
// Medals
|
|
12704
|
-
medals,
|
|
12705
|
-
medalsPreventUnlock,
|
|
12706
|
-
medalsInit,
|
|
12707
|
-
Medal,
|
|
12510
|
+
// Engine
|
|
12511
|
+
engineName,
|
|
12512
|
+
engineVersion,
|
|
12513
|
+
frameRate,
|
|
12514
|
+
timeDelta,
|
|
12515
|
+
engineObjects,
|
|
12516
|
+
frame,
|
|
12517
|
+
time,
|
|
12518
|
+
timeReal,
|
|
12519
|
+
paused,
|
|
12520
|
+
getPaused,
|
|
12521
|
+
setPaused,
|
|
12522
|
+
engineInit,
|
|
12523
|
+
engineObjectsUpdate,
|
|
12524
|
+
engineObjectsDestroy,
|
|
12525
|
+
engineObjectsCollect,
|
|
12526
|
+
engineObjectsCallback,
|
|
12527
|
+
engineObjectsRaycast,
|
|
12528
|
+
engineAddPlugin,
|
|
12529
|
+
|
|
12530
|
+
// Globals
|
|
12531
|
+
debug,
|
|
12532
|
+
debugOverlay,
|
|
12533
|
+
debugWatermark,
|
|
12534
|
+
|
|
12535
|
+
// Debug
|
|
12536
|
+
ASSERT,
|
|
12537
|
+
LOG,
|
|
12538
|
+
debugRect,
|
|
12539
|
+
debugPoly,
|
|
12540
|
+
debugCircle,
|
|
12541
|
+
debugPoint,
|
|
12542
|
+
debugLine,
|
|
12543
|
+
debugOverlap,
|
|
12544
|
+
debugText,
|
|
12545
|
+
debugClear,
|
|
12546
|
+
debugScreenshot,
|
|
12547
|
+
debugShowErrors,
|
|
12548
|
+
debugVideoCaptureStart,
|
|
12549
|
+
debugVideoCaptureStop,
|
|
12550
|
+
debugVideoCaptureIsActive,
|
|
12551
|
+
|
|
12552
|
+
// Settings
|
|
12553
|
+
cameraPos,
|
|
12554
|
+
cameraAngle,
|
|
12555
|
+
cameraScale,
|
|
12556
|
+
canvasColorTiles,
|
|
12557
|
+
canvasClearColor,
|
|
12558
|
+
canvasMaxSize,
|
|
12559
|
+
canvasMinAspect,
|
|
12560
|
+
canvasMaxAspect,
|
|
12561
|
+
canvasFixedSize,
|
|
12562
|
+
canvasPixelated,
|
|
12563
|
+
tilesPixelated,
|
|
12564
|
+
fontDefault,
|
|
12565
|
+
showSplashScreen,
|
|
12566
|
+
headlessMode,
|
|
12567
|
+
tileDefaultSize,
|
|
12568
|
+
tileDefaultPadding,
|
|
12569
|
+
tileDefaultBleed,
|
|
12570
|
+
enablePhysicsSolver,
|
|
12571
|
+
objectDefaultMass,
|
|
12572
|
+
objectDefaultDamping,
|
|
12573
|
+
objectDefaultAngleDamping,
|
|
12574
|
+
objectDefaultRestitution,
|
|
12575
|
+
objectDefaultFriction,
|
|
12576
|
+
objectMaxSpeed,
|
|
12577
|
+
gravity,
|
|
12578
|
+
particleEmitRateScale,
|
|
12579
|
+
glEnable,
|
|
12580
|
+
gamepadsEnable,
|
|
12581
|
+
gamepadDirectionEmulateStick,
|
|
12582
|
+
inputWASDEmulateDirection,
|
|
12583
|
+
touchGamepadEnable,
|
|
12584
|
+
touchGamepadCenterButton,
|
|
12585
|
+
touchGamepadAnalog,
|
|
12586
|
+
touchGamepadSize,
|
|
12587
|
+
touchGamepadAlpha,
|
|
12588
|
+
vibrateEnable,
|
|
12589
|
+
soundEnable,
|
|
12590
|
+
soundVolume,
|
|
12591
|
+
soundDefaultRange,
|
|
12592
|
+
soundDefaultTaper,
|
|
12593
|
+
medalDisplayTime,
|
|
12594
|
+
medalDisplaySlideTime,
|
|
12595
|
+
medalDisplaySize,
|
|
12596
|
+
|
|
12597
|
+
// Setters for globals
|
|
12598
|
+
setCameraPos,
|
|
12599
|
+
setCameraAngle,
|
|
12600
|
+
setCameraScale,
|
|
12601
|
+
setCanvasColorTiles,
|
|
12602
|
+
setCanvasClearColor,
|
|
12603
|
+
setCanvasMaxSize,
|
|
12604
|
+
setCanvasMinAspect,
|
|
12605
|
+
setCanvasMaxAspect,
|
|
12606
|
+
setCanvasFixedSize,
|
|
12607
|
+
setCanvasPixelated,
|
|
12608
|
+
setTilesPixelated,
|
|
12609
|
+
setFontDefault,
|
|
12610
|
+
setShowSplashScreen,
|
|
12611
|
+
setHeadlessMode,
|
|
12612
|
+
setGLEnable,
|
|
12613
|
+
setTileDefaultSize,
|
|
12614
|
+
setTileDefaultPadding,
|
|
12615
|
+
setTileDefaultBleed,
|
|
12616
|
+
setEnablePhysicsSolver,
|
|
12617
|
+
setObjectDefaultMass,
|
|
12618
|
+
setObjectDefaultDamping,
|
|
12619
|
+
setObjectDefaultAngleDamping,
|
|
12620
|
+
setObjectDefaultRestitution,
|
|
12621
|
+
setObjectDefaultFriction,
|
|
12622
|
+
setObjectMaxSpeed,
|
|
12623
|
+
setGravity,
|
|
12624
|
+
setParticleEmitRateScale,
|
|
12625
|
+
setTouchInputEnable,
|
|
12626
|
+
setGamepadsEnable,
|
|
12627
|
+
setGamepadDirectionEmulateStick,
|
|
12628
|
+
setInputWASDEmulateDirection,
|
|
12629
|
+
setTouchGamepadEnable,
|
|
12630
|
+
setTouchGamepadCenterButton,
|
|
12631
|
+
setTouchGamepadButtonCount,
|
|
12632
|
+
setTouchGamepadAnalog,
|
|
12633
|
+
setTouchGamepadSize,
|
|
12634
|
+
setTouchGamepadAlpha,
|
|
12635
|
+
setVibrateEnable,
|
|
12636
|
+
setSoundEnable,
|
|
12637
|
+
setSoundVolume,
|
|
12638
|
+
setSoundDefaultRange,
|
|
12639
|
+
setSoundDefaultTaper,
|
|
12640
|
+
setMedalDisplayTime,
|
|
12641
|
+
setMedalDisplaySlideTime,
|
|
12642
|
+
setMedalDisplaySize,
|
|
12643
|
+
setMedalsPreventUnlock,
|
|
12644
|
+
setDebugWatermark,
|
|
12645
|
+
setDebugKey,
|
|
12646
|
+
|
|
12647
|
+
// Math
|
|
12648
|
+
PI,
|
|
12649
|
+
abs,
|
|
12650
|
+
floor,
|
|
12651
|
+
ceil,
|
|
12652
|
+
round,
|
|
12653
|
+
min,
|
|
12654
|
+
max,
|
|
12655
|
+
sign,
|
|
12656
|
+
hypot,
|
|
12657
|
+
log2,
|
|
12658
|
+
sin,
|
|
12659
|
+
cos,
|
|
12660
|
+
tan,
|
|
12661
|
+
atan2,
|
|
12662
|
+
mod,
|
|
12663
|
+
clamp,
|
|
12664
|
+
percent,
|
|
12665
|
+
distanceWrap,
|
|
12666
|
+
lerpWrap,
|
|
12667
|
+
distanceAngle,
|
|
12668
|
+
lerpAngle,
|
|
12669
|
+
lerp,
|
|
12670
|
+
smoothStep,
|
|
12671
|
+
nearestPowerOfTwo,
|
|
12672
|
+
isOverlapping,
|
|
12673
|
+
isIntersecting,
|
|
12674
|
+
wave,
|
|
12675
|
+
|
|
12676
|
+
// Utilities
|
|
12677
|
+
formatTime,
|
|
12678
|
+
fetchJSON,
|
|
12679
|
+
saveText,
|
|
12680
|
+
saveCanvas,
|
|
12681
|
+
saveDataURL,
|
|
12682
|
+
shareURL,
|
|
12683
|
+
|
|
12684
|
+
// Random
|
|
12685
|
+
rand,
|
|
12686
|
+
randInt,
|
|
12687
|
+
randBool,
|
|
12688
|
+
randSign,
|
|
12689
|
+
randInCircle,
|
|
12690
|
+
randVec2,
|
|
12691
|
+
randColor,
|
|
12692
|
+
|
|
12693
|
+
// Utility Classes
|
|
12694
|
+
RandomGenerator,
|
|
12695
|
+
Vector2,
|
|
12696
|
+
Color,
|
|
12697
|
+
Timer,
|
|
12698
|
+
vec2,
|
|
12699
|
+
rgb,
|
|
12700
|
+
hsl,
|
|
12701
|
+
isColor,
|
|
12702
|
+
isVector2,
|
|
12703
|
+
isNumber,
|
|
12704
|
+
isString,
|
|
12705
|
+
isArray,
|
|
12706
|
+
|
|
12707
|
+
// Default Colors
|
|
12708
|
+
WHITE,
|
|
12709
|
+
CLEAR_WHITE,
|
|
12710
|
+
BLACK,
|
|
12711
|
+
CLEAR_BLACK,
|
|
12712
|
+
GRAY,
|
|
12713
|
+
RED,
|
|
12714
|
+
ORANGE,
|
|
12715
|
+
YELLOW,
|
|
12716
|
+
GREEN,
|
|
12717
|
+
CYAN,
|
|
12718
|
+
BLUE,
|
|
12719
|
+
PURPLE,
|
|
12720
|
+
MAGENTA,
|
|
12721
|
+
|
|
12722
|
+
// Draw
|
|
12723
|
+
tile,
|
|
12724
|
+
TileInfo,
|
|
12725
|
+
TextureInfo,
|
|
12726
|
+
mainCanvas,
|
|
12727
|
+
mainContext,
|
|
12728
|
+
drawContext,
|
|
12729
|
+
workCanvas,
|
|
12730
|
+
workContext,
|
|
12731
|
+
workReadCanvas,
|
|
12732
|
+
workReadContext,
|
|
12733
|
+
mainCanvasSize,
|
|
12734
|
+
textureInfos,
|
|
12735
|
+
drawCount,
|
|
12736
|
+
screenToWorld,
|
|
12737
|
+
worldToScreen,
|
|
12738
|
+
screenToWorldDelta,
|
|
12739
|
+
worldToScreenDelta,
|
|
12740
|
+
screenToWorldTransform,
|
|
12741
|
+
drawTile,
|
|
12742
|
+
drawRect,
|
|
12743
|
+
drawRectGradient,
|
|
12744
|
+
drawLineList,
|
|
12745
|
+
drawLine,
|
|
12746
|
+
drawPoly,
|
|
12747
|
+
drawEllipse,
|
|
12748
|
+
drawCircle,
|
|
12749
|
+
drawCanvas2D,
|
|
12750
|
+
drawText,
|
|
12751
|
+
drawTextScreen,
|
|
12752
|
+
setBlendMode,
|
|
12753
|
+
combineCanvases,
|
|
12754
|
+
engineFontImage,
|
|
12755
|
+
FontImage,
|
|
12756
|
+
isFullscreen,
|
|
12757
|
+
toggleFullscreen,
|
|
12758
|
+
setCursor,
|
|
12759
|
+
getCameraSize,
|
|
12760
|
+
|
|
12761
|
+
// WebGL
|
|
12762
|
+
glCanvas,
|
|
12763
|
+
glContext,
|
|
12764
|
+
glAntialias,
|
|
12765
|
+
glClearCanvas,
|
|
12766
|
+
glSetTexture,
|
|
12767
|
+
glCompileShader,
|
|
12768
|
+
glCreateProgram,
|
|
12769
|
+
glCreateTexture,
|
|
12770
|
+
glDeleteTexture,
|
|
12771
|
+
glSetTextureData,
|
|
12772
|
+
glFlush,
|
|
12773
|
+
glCopyToContext,
|
|
12774
|
+
glSetAntialias,
|
|
12775
|
+
glDraw,
|
|
12776
|
+
glDrawPointsTransform,
|
|
12777
|
+
glDrawOutlineTransform,
|
|
12778
|
+
glDrawPoints,
|
|
12779
|
+
glDrawColoredPoints,
|
|
12780
|
+
glSetRenderTarget,
|
|
12781
|
+
glClearRect,
|
|
12782
|
+
|
|
12783
|
+
// Input
|
|
12784
|
+
keyIsDown,
|
|
12785
|
+
keyWasPressed,
|
|
12786
|
+
keyWasReleased,
|
|
12787
|
+
keyDirection,
|
|
12788
|
+
inputClear,
|
|
12789
|
+
inputClearKey,
|
|
12790
|
+
mouseIsDown,
|
|
12791
|
+
mouseWasPressed,
|
|
12792
|
+
mouseWasReleased,
|
|
12793
|
+
mousePos,
|
|
12794
|
+
mousePosScreen,
|
|
12795
|
+
mouseDelta,
|
|
12796
|
+
mouseDeltaScreen,
|
|
12797
|
+
mouseWheel,
|
|
12798
|
+
mouseInWindow,
|
|
12799
|
+
isUsingGamepad,
|
|
12800
|
+
inputPreventDefault,
|
|
12801
|
+
gamepadPrimary,
|
|
12802
|
+
isTouchDevice,
|
|
12803
|
+
setInputPreventDefault,
|
|
12804
|
+
gamepadIsDown,
|
|
12805
|
+
gamepadWasPressed,
|
|
12806
|
+
gamepadWasReleased,
|
|
12807
|
+
gamepadStick,
|
|
12808
|
+
gamepadDpad,
|
|
12809
|
+
gamepadConnected,
|
|
12810
|
+
vibrate,
|
|
12811
|
+
vibrateStop,
|
|
12812
|
+
pointerLockRequest,
|
|
12813
|
+
pointerLockExit,
|
|
12814
|
+
pointerLockIsActive,
|
|
12815
|
+
|
|
12816
|
+
// Audio
|
|
12817
|
+
audioContext,
|
|
12818
|
+
audioMasterGain,
|
|
12819
|
+
audioDefaultSampleRate,
|
|
12820
|
+
Sound,
|
|
12821
|
+
SoundWave,
|
|
12822
|
+
SoundInstance,
|
|
12823
|
+
speak,
|
|
12824
|
+
speakStop,
|
|
12825
|
+
getNoteFrequency,
|
|
12826
|
+
playSamples,
|
|
12827
|
+
zzfx,
|
|
12828
|
+
zzfxG,
|
|
12829
|
+
|
|
12830
|
+
// Base Object
|
|
12831
|
+
EngineObject,
|
|
12832
|
+
|
|
12833
|
+
// Tiles
|
|
12834
|
+
tileCollisionLayers,
|
|
12835
|
+
tileCollisionGetData,
|
|
12836
|
+
tileCollisionTest,
|
|
12837
|
+
tileCollisionRaycast,
|
|
12838
|
+
tileLayersLoad,
|
|
12839
|
+
TileLayerData,
|
|
12840
|
+
CanvasLayer,
|
|
12841
|
+
TileLayer,
|
|
12842
|
+
TileCollisionLayer,
|
|
12843
|
+
|
|
12844
|
+
// Particles
|
|
12845
|
+
ParticleEmitter,
|
|
12846
|
+
Particle,
|
|
12847
|
+
|
|
12848
|
+
// Medals
|
|
12849
|
+
medals,
|
|
12850
|
+
medalsPreventUnlock,
|
|
12851
|
+
medalsInit,
|
|
12852
|
+
Medal,
|
|
12708
12853
|
}
|
|
12709
12854
|
/**
|
|
12710
12855
|
* LittleJS Module Plugins Export
|