littlejsengine 1.24.2 → 1.24.3
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/CHANGELOG.md +19 -0
- package/REFERENCE.md +2 -1
- package/dist/littlejs.d.ts +131 -109
- package/dist/littlejs.esm.js +95 -55
- package/dist/littlejs.esm.min.js +3 -3
- package/dist/littlejs.js +95 -55
- package/dist/littlejs.min.js +2 -2
- package/dist/littlejs.release.js +95 -55
- package/package.json +1 -1
- package/plugins/box2d.js +9 -5
- package/plugins/gltf.js +6 -1
- package/plugins/render3dExtras.js +12 -8
- package/plugins/textureSheet.js +15 -7
- package/src/engine.js +31 -17
- package/src/engineAudio.js +3 -3
- package/src/engineBuild.mjs +49 -2
- package/src/engineDraw.js +11 -9
- package/src/engineInput.js +6 -3
- package/src/engineWebGL.js +3 -3
package/dist/littlejs.esm.js
CHANGED
|
@@ -35,7 +35,7 @@ const engineName = 'LittleJS';
|
|
|
35
35
|
* @type {string}
|
|
36
36
|
* @default
|
|
37
37
|
* @memberof Engine */
|
|
38
|
-
const engineVersion = '1.24.
|
|
38
|
+
const engineVersion = '1.24.3';
|
|
39
39
|
|
|
40
40
|
/** Frames per second to update
|
|
41
41
|
* @type {number}
|
|
@@ -168,7 +168,7 @@ let timeFixedStart = 0, frameFixedStart = 0;
|
|
|
168
168
|
let windowWidthLast = 0, windowHeightLast = 0, windowPixelRatioLast = 0;
|
|
169
169
|
let engineUpdateInternal; // assigned by engineInit so engineStep can drive it
|
|
170
170
|
let engineFrameScheduled = false; // a frame of the loop is asked for and has not run yet
|
|
171
|
-
let
|
|
171
|
+
let engineFrameErrorLast; // the last error a release build's loop went on past, as text
|
|
172
172
|
|
|
173
173
|
// the pairs of objects asked about a collision this update, so the other's own physics does not ask again: each
|
|
174
174
|
// asker's others, with true for a pair both said to resolve, and false for one left overlapping, ignored or only
|
|
@@ -422,20 +422,30 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
422
422
|
frameTimeBufferMS = 0;
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
-
// update multiple frames if necessary in case of slow framerate
|
|
426
|
-
|
|
425
|
+
// update multiple frames if necessary in case of slow framerate; a tick that throws still spends its time
|
|
426
|
+
// and the smoothing still comes back, so a release build going on past an error each tick keeps its rate
|
|
427
|
+
try
|
|
427
428
|
{
|
|
428
|
-
|
|
429
|
-
|
|
429
|
+
while (frameTimeBufferMS >= 0)
|
|
430
|
+
{
|
|
431
|
+
try
|
|
432
|
+
{
|
|
433
|
+
// read again each tick, so a pause set by the game stops the rest of this frame's catch-up ticks
|
|
434
|
+
const frozenTick = paused || !(timeScale * debugScale);
|
|
430
435
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
436
|
+
// increment frame and update time, frozen does not advance time
|
|
437
|
+
if (!frozenTick)
|
|
438
|
+
time = timeFixedStart + (frame++ - frameFixedStart) / frameRate;
|
|
439
|
+
engineTick(frozenTick);
|
|
440
|
+
}
|
|
441
|
+
finally { frameTimeBufferMS -= 1e3 / frameRate; }
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
finally
|
|
445
|
+
{
|
|
446
|
+
// add the time smoothing back in
|
|
447
|
+
frameTimeBufferMS += deltaSmooth;
|
|
435
448
|
}
|
|
436
|
-
|
|
437
|
-
// add the time smoothing back in
|
|
438
|
-
frameTimeBufferMS += deltaSmooth;
|
|
439
449
|
}
|
|
440
450
|
|
|
441
451
|
// one tick of the loop: update game and objects, when frozen update everything except them
|
|
@@ -732,9 +742,13 @@ function engineScheduleFrame()
|
|
|
732
742
|
try { engineUpdateInternal(frameTimeMS); }
|
|
733
743
|
catch (error)
|
|
734
744
|
{
|
|
735
|
-
// a release build goes on past an error in a frame, a frozen game is the worst a player can get;
|
|
736
|
-
//
|
|
737
|
-
|
|
745
|
+
// a release build goes on past an error in a frame, a frozen game is the worst a player can get; an error
|
|
746
|
+
// is logged when it is not the last one again, one every frame would flood the console
|
|
747
|
+
const text = String(error);
|
|
748
|
+
text === engineFrameErrorLast || console.error(error);
|
|
749
|
+
engineFrameErrorLast = text;
|
|
750
|
+
// the frame's input is cleared as its tick would have, or a key press that threw would be pressed again
|
|
751
|
+
inputUpdatePost();
|
|
738
752
|
engineScheduleFrame();
|
|
739
753
|
}
|
|
740
754
|
};
|
|
@@ -5187,10 +5201,13 @@ function tile(index=0, size=tileDefaultSize, texture=0, padding=tileDefaultPaddi
|
|
|
5187
5201
|
// create tile info object
|
|
5188
5202
|
const textureInfo = typeof texture === 'number' ?
|
|
5189
5203
|
textureInfos[texture] : texture;
|
|
5190
|
-
if (
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5204
|
+
if (!textureInfo?.size.x)
|
|
5205
|
+
{
|
|
5206
|
+
// no image: headless loads none and keeps the size; an image that failed to load, whose warning named it, gives
|
|
5207
|
+
// a tile of no size, which draws nothing; a slot never given an image is a mistake a debug build points out
|
|
5208
|
+
ASSERT(headlessMode || textureInfo instanceof TextureInfo, 'tile texture is not loaded', texture);
|
|
5209
|
+
return new TileInfo(new Vector2, headlessMode ? size.copy() : new Vector2, textureInfo, padding, bleed);
|
|
5210
|
+
}
|
|
5194
5211
|
|
|
5195
5212
|
// get the position of the tile
|
|
5196
5213
|
const sizePaddedX = size.x + padding*2;
|
|
@@ -5658,8 +5675,8 @@ function drawTile(pos, size=vec2(1), tileInfo, color=WHITE,
|
|
|
5658
5675
|
if (headlessMode && !context) return; // headless has no canvas, only a context passed in is drawn to
|
|
5659
5676
|
|
|
5660
5677
|
const textureInfo = tileInfo?.textureInfo;
|
|
5661
|
-
if (textureInfo && !(tileInfo.size.x && tileInfo.size.y))
|
|
5662
|
-
return; // a tile with no area draws nothing, like a sprite still loading
|
|
5678
|
+
if (textureInfo && !(tileInfo.size.x && tileInfo.size.y && textureInfo.size.x))
|
|
5679
|
+
return; // a tile with no area draws nothing, like a sprite still loading, and nor does an image that failed
|
|
5663
5680
|
const bleed = tileInfo?.bleed ?? 0;
|
|
5664
5681
|
if (useWebGL && glEnable)
|
|
5665
5682
|
{
|
|
@@ -5753,7 +5770,7 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
5753
5770
|
* @param {boolean} [screenSpace=drawScreenSpace]
|
|
5754
5771
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
5755
5772
|
* @memberof Draw */
|
|
5756
|
-
function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=CLEAR_WHITE, angle=0, useWebGL=glEnable, screenSpace=drawScreenSpace, context)
|
|
5773
|
+
function drawRectGradient(pos, size=vec2(1), colorTop=WHITE, colorBottom=CLEAR_WHITE, angle=0, useWebGL=glEnable, screenSpace=drawScreenSpace, context)
|
|
5757
5774
|
{
|
|
5758
5775
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
5759
5776
|
ASSERT(isVector2(size), 'size must be a vec2');
|
|
@@ -6748,8 +6765,7 @@ function bakeTintedImage(image, color, additiveColor)
|
|
|
6748
6765
|
* @param {number} dHeight
|
|
6749
6766
|
* @param {Color} color
|
|
6750
6767
|
* @param {Color} [additiveColor]
|
|
6751
|
-
* @param {number} [bleed] - How many pixels to shrink the source, used to fix bleeding
|
|
6752
|
-
* @memberof Draw */
|
|
6768
|
+
* @param {number} [bleed] - How many pixels to shrink the source, used to fix bleeding */
|
|
6753
6769
|
function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, color, additiveColor, bleed=0)
|
|
6754
6770
|
{
|
|
6755
6771
|
const sx2 = bleed;
|
|
@@ -7575,8 +7591,9 @@ function inputInit()
|
|
|
7575
7591
|
if (soundEnable && !headlessMode && audioContext && !audioIsRunning())
|
|
7576
7592
|
audioContext.resume();
|
|
7577
7593
|
|
|
7578
|
-
// a press in the bars around a letterboxed canvas is not one on its edge; a drag out of it still moves
|
|
7579
|
-
|
|
7594
|
+
// a press in the bars around a letterboxed canvas is not one on its edge; a drag out of it still moves; under
|
|
7595
|
+
// pointer lock the mouse stays where the lock began, which may be in a bar, and every click is the game's
|
|
7596
|
+
if (!pointerLockIsActive() && !inCanvas(e.x, e.y))
|
|
7580
7597
|
return;
|
|
7581
7598
|
inputData[0][e.button] = 3;
|
|
7582
7599
|
|
|
@@ -7993,9 +8010,11 @@ function inputUpdate()
|
|
|
7993
8010
|
(gamepadIsDown(12,i)&&1) - (gamepadIsDown(13,i)&&1));
|
|
7994
8011
|
}
|
|
7995
8012
|
|
|
7996
|
-
// copy dpad to left analog stick when pressed
|
|
8013
|
+
// copy dpad to left analog stick when pressed; a gamepad with no sticks has that one, at rest when not
|
|
7997
8014
|
if (gamepadDirectionEmulateStick && (dpad.x || dpad.y))
|
|
7998
8015
|
sticks[0] = dpad.clampLength();
|
|
8016
|
+
else if (gamepadDirectionEmulateStick && !sticks.length)
|
|
8017
|
+
sticks[0] = vec2();
|
|
7999
8018
|
}
|
|
8000
8019
|
|
|
8001
8020
|
// disable touch gamepad if using real gamepad
|
|
@@ -8742,7 +8761,7 @@ class Sound
|
|
|
8742
8761
|
*/
|
|
8743
8762
|
constructor(asset, randomness, range, taper=soundDefaultTaper, onloadCallback)
|
|
8744
8763
|
{
|
|
8745
|
-
if (!soundEnable || headlessMode)
|
|
8764
|
+
if (!soundEnable || headlessMode || !audioContext) // a browser with no audio makes none either
|
|
8746
8765
|
{
|
|
8747
8766
|
// no sound is made: it counts as loaded, so a game that waits for its sounds goes on
|
|
8748
8767
|
this.loadedPercent = 1;
|
|
@@ -8812,8 +8831,8 @@ class Sound
|
|
|
8812
8831
|
// load the audio file, a URL object as bundlers give works like its string;
|
|
8813
8832
|
// report failures rather than leaving an unhandled rejection, the sound just stays unloaded and silent
|
|
8814
8833
|
const filename = asset + '';
|
|
8815
|
-
engineAddLoad(this.loadSound(filename).catch(e=>
|
|
8816
|
-
|
|
8834
|
+
engineAddLoad(this.loadSound(filename).catch(e=> // startup waits for it, and a release build says why too
|
|
8835
|
+
console.warn('Sound load failed for', filename, '-', e?.message ?? e)));
|
|
8817
8836
|
}
|
|
8818
8837
|
}
|
|
8819
8838
|
|
|
@@ -11862,7 +11881,7 @@ function glSetTextureWrap(texture, wrap=true)
|
|
|
11862
11881
|
/** Compile WebGL shader of the given type, will throw errors if in debug mode
|
|
11863
11882
|
* @param {string} source
|
|
11864
11883
|
* @param {number} type
|
|
11865
|
-
* @return {WebGLShader}
|
|
11884
|
+
* @return {WebGLShader|undefined} - undefined with no WebGL
|
|
11866
11885
|
* @memberof WebGL */
|
|
11867
11886
|
function glCompileShader(source, type)
|
|
11868
11887
|
{
|
|
@@ -11882,7 +11901,7 @@ function glCompileShader(source, type)
|
|
|
11882
11901
|
/** Create WebGL program with given shaders
|
|
11883
11902
|
* @param {string} vsSource
|
|
11884
11903
|
* @param {string} fsSource
|
|
11885
|
-
* @return {WebGLProgram}
|
|
11904
|
+
* @return {WebGLProgram|undefined} - undefined with no WebGL
|
|
11886
11905
|
* @memberof WebGL */
|
|
11887
11906
|
function glCreateProgram(vsSource, fsSource)
|
|
11888
11907
|
{
|
|
@@ -11930,7 +11949,7 @@ function glShaderProgram(shader)
|
|
|
11930
11949
|
* @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} [image]
|
|
11931
11950
|
* @param {boolean|Array<number>} [wrap] - true for REPEAT, false for CLAMP_TO_EDGE, or the WebGL modes across and down
|
|
11932
11951
|
* @param {boolean} [pixelated] - Hard edged or smooth, undefined follows tilesPixelated
|
|
11933
|
-
* @return {WebGLTexture}
|
|
11952
|
+
* @return {WebGLTexture|undefined} - undefined with no WebGL
|
|
11934
11953
|
* @memberof WebGL */
|
|
11935
11954
|
function glCreateTexture(image, wrap=false, pixelated=tilesPixelated)
|
|
11936
11955
|
{
|
|
@@ -17790,7 +17809,7 @@ class Box2dObject extends EngineObject
|
|
|
17790
17809
|
* @param {number} angle */
|
|
17791
17810
|
setTransform(pos, angle)
|
|
17792
17811
|
{
|
|
17793
|
-
this.pos
|
|
17812
|
+
this.pos.set(pos.x, pos.y); // its own pos kept, as an EngineObject's is
|
|
17794
17813
|
this.angle = angle;
|
|
17795
17814
|
// box2d uses reverse angle
|
|
17796
17815
|
const x = pos.x, y = pos.y;
|
|
@@ -17806,7 +17825,7 @@ class Box2dObject extends EngineObject
|
|
|
17806
17825
|
* @param {Vector2} pos */
|
|
17807
17826
|
setPosition(pos)
|
|
17808
17827
|
{
|
|
17809
|
-
this.pos
|
|
17828
|
+
this.pos.set(pos.x, pos.y); // its own pos kept, as an EngineObject's is
|
|
17810
17829
|
const x = pos.x, y = pos.y;
|
|
17811
17830
|
box2dWhenUnlocked(()=>
|
|
17812
17831
|
{
|
|
@@ -19307,7 +19326,8 @@ class Box2dPlugin
|
|
|
19307
19326
|
return box2d.raycastAll(start, end, includeSensors)[0];
|
|
19308
19327
|
}
|
|
19309
19328
|
|
|
19310
|
-
/** box
|
|
19329
|
+
/** Every object whose shapes' bounding boxes overlap a box, not the shapes themselves, so near the corner of a
|
|
19330
|
+
* turned box or a circle it finds one the box does not touch; use pointCast or a raycast for exact
|
|
19311
19331
|
* @param {Vector2} pos
|
|
19312
19332
|
* @param {Vector2} size
|
|
19313
19333
|
* @param {boolean} [includeSensors] - Also find sensors, trigger zones are passed through by default
|
|
@@ -19337,7 +19357,8 @@ class Box2dPlugin
|
|
|
19337
19357
|
return queryObjects;
|
|
19338
19358
|
}
|
|
19339
19359
|
|
|
19340
|
-
/** box
|
|
19360
|
+
/** The first object whose shapes' bounding boxes overlap a box, not the shapes themselves, so near the corner of
|
|
19361
|
+
* a turned box or a circle it finds one the box does not touch; use pointCast or a raycast for exact
|
|
19341
19362
|
* @param {Vector2} pos
|
|
19342
19363
|
* @param {Vector2} size
|
|
19343
19364
|
* @param {boolean} [includeSensors] - Also find sensors, trigger zones are passed through by default
|
|
@@ -19597,8 +19618,10 @@ async function box2dInit()
|
|
|
19597
19618
|
{
|
|
19598
19619
|
if (o.body)
|
|
19599
19620
|
{
|
|
19621
|
+
// moved in place, as the engine moves an EngineObject's pos, so what holds it follows the body;
|
|
19600
19622
|
// box2d uses reverse angle
|
|
19601
|
-
|
|
19623
|
+
const p = o.body.GetPosition();
|
|
19624
|
+
o.pos.set(p.get_x(), p.get_y());
|
|
19602
19625
|
o.angle = -o.body.GetAngle();
|
|
19603
19626
|
}
|
|
19604
19627
|
}
|
|
@@ -20094,6 +20117,8 @@ let textureSheetPendingCount = 0;
|
|
|
20094
20117
|
const textureSheetLoaded = new Map;
|
|
20095
20118
|
// a load's key: what it was given, a vector by its numbers
|
|
20096
20119
|
const textureSheetLoadKey = (...parts)=> parts.map((p)=> isVector2(p) ? p.x + ',' + p.y : String(p)).join('|');
|
|
20120
|
+
// a load whose file failed is forgotten, so loading it again tries again, as when the server comes back
|
|
20121
|
+
const textureSheetLoadFailed = (key, loaded)=> textureSheetLoaded.get(key) === loaded && textureSheetLoaded.delete(key);
|
|
20097
20122
|
|
|
20098
20123
|
/**
|
|
20099
20124
|
* Texture Sheet - A texture that images are packed into as they load
|
|
@@ -20250,7 +20275,8 @@ class TextureSheet
|
|
|
20250
20275
|
* - Pass frameSize for animations, then step through them with TileInfo.frame
|
|
20251
20276
|
* - Grid images keep their layout and frames wrap down to the next row
|
|
20252
20277
|
* - Pass sourcePadding if the source image has padding baked in around frames
|
|
20253
|
-
* - The same image loaded again with the same settings gives back what the first load did, packed once
|
|
20278
|
+
* - The same image loaded again with the same settings gives back what the first load did, packed once, unless
|
|
20279
|
+
* that load failed
|
|
20254
20280
|
* @param {string} src - Image source path
|
|
20255
20281
|
* @param {Vector2|number} [frameSize] - Size of each animation frame in pixels, or the whole image less its
|
|
20256
20282
|
* source padding if not passed
|
|
@@ -20305,7 +20331,7 @@ function loadSprite(src, frameSize, padding=textureSheetPadding, sourcePadding=0
|
|
|
20305
20331
|
if (!added)
|
|
20306
20332
|
{
|
|
20307
20333
|
// leave the tile empty, no sheet can hold it
|
|
20308
|
-
|
|
20334
|
+
console.warn('loadSprite image is too large to fit on a texture sheet:', src);
|
|
20309
20335
|
return;
|
|
20310
20336
|
}
|
|
20311
20337
|
Object.assign(tileInfo, added.tile);
|
|
@@ -20314,7 +20340,8 @@ function loadSprite(src, frameSize, padding=textureSheetPadding, sourcePadding=0
|
|
|
20314
20340
|
else
|
|
20315
20341
|
{
|
|
20316
20342
|
// leave the tile empty if the image failed to load
|
|
20317
|
-
|
|
20343
|
+
console.warn('loadSprite failed to load image:', src);
|
|
20344
|
+
textureSheetLoadFailed(key, tileInfo);
|
|
20318
20345
|
}
|
|
20319
20346
|
});
|
|
20320
20347
|
|
|
@@ -20332,7 +20359,8 @@ function loadSprite(src, frameSize, padding=textureSheetPadding, sourcePadding=0
|
|
|
20332
20359
|
* is not read, cut it into its tiles first
|
|
20333
20360
|
* - An image that fails to load, or that no sheet can hold, adds no tiles and says so in the console, so the tiles
|
|
20334
20361
|
* of the images after it move up
|
|
20335
|
-
* - The same images loaded again with the same settings give back what the first load did, packed once
|
|
20362
|
+
* - The same images loaded again with the same settings give back what the first load did, packed once, unless
|
|
20363
|
+
* an image of it failed
|
|
20336
20364
|
* @param {Array<string>} sources - Image source paths
|
|
20337
20365
|
* @param {Vector2|number} [tileSize] - Size of a tile in pixels
|
|
20338
20366
|
* @param {number} [padding] - How many pixels padding around each tile on the sheet
|
|
@@ -20374,6 +20402,7 @@ function loadTiles(sources, tileSize=tileDefaultSize, padding=textureSheetPaddin
|
|
|
20374
20402
|
// the whole tiles of it, an edge past the last one left out
|
|
20375
20403
|
const columns = image.width / size.x | 0, rows = image.height / size.y | 0, count = columns * rows;
|
|
20376
20404
|
const added = count && textureSheetAdd(vec2(columns * size.x, rows * size.y), size, padding, 0);
|
|
20405
|
+
image.width || textureSheetLoadFailed(key, set);
|
|
20377
20406
|
if (!added)
|
|
20378
20407
|
return console.warn('loadTiles: ' + src + (count ? ' does not fit on a texture sheet' :
|
|
20379
20408
|
' failed to load, or is smaller than a tile') + ', its tiles are left out');
|
|
@@ -20392,7 +20421,8 @@ function loadTiles(sources, tileSize=tileDefaultSize, padding=textureSheetPaddin
|
|
|
20392
20421
|
* - Aseprite frame tags become animations, so do names like run_0, run_1
|
|
20393
20422
|
* - Trimmed frames are restored to their full source size when packed
|
|
20394
20423
|
* - Rotated frames are rotated back upright when packed
|
|
20395
|
-
* - The same atlas loaded again by its paths, with the same padding, gives back what the first load did, packed
|
|
20424
|
+
* - The same atlas loaded again by its paths, with the same padding, gives back what the first load did, packed
|
|
20425
|
+
* once, unless that load failed
|
|
20396
20426
|
* @param {string} imageSrc - Atlas image path
|
|
20397
20427
|
* @param {string|Object} jsonSrc - Atlas json path, or already parsed json data
|
|
20398
20428
|
* @param {number} [padding] - How many pixels padding around each frame
|
|
@@ -20447,7 +20477,7 @@ function loadAtlas(imageSrc, jsonSrc, padding=textureSheetPadding)
|
|
|
20447
20477
|
const added = textureSheetAdd(blockSize, sourceSize, padding);
|
|
20448
20478
|
if (!added)
|
|
20449
20479
|
{
|
|
20450
|
-
|
|
20480
|
+
console.warn('loadAtlas frames are too large to fit on a texture sheet:', group.name);
|
|
20451
20481
|
continue;
|
|
20452
20482
|
}
|
|
20453
20483
|
const {sheet, tile} = added;
|
|
@@ -20481,7 +20511,8 @@ function loadAtlas(imageSrc, jsonSrc, padding=textureSheetPadding)
|
|
|
20481
20511
|
else
|
|
20482
20512
|
{
|
|
20483
20513
|
// leave the atlas empty if either file failed to load
|
|
20484
|
-
|
|
20514
|
+
console.warn('loadAtlas failed to load:', imageSrc, jsonSrc);
|
|
20515
|
+
textureSheetLoadFailed(key, atlas);
|
|
20485
20516
|
}
|
|
20486
20517
|
});
|
|
20487
20518
|
|
|
@@ -28924,9 +28955,9 @@ class HeightMap extends EngineObject3D
|
|
|
28924
28955
|
levelSegment3D(from, to)
|
|
28925
28956
|
{
|
|
28926
28957
|
const m = this.pos3D, size = this.mapSize;
|
|
28927
|
-
if (abs(to.x - m.x) > size.x / 2 || abs(to.z - m.z) > size.y / 2) return; // off the map
|
|
28958
|
+
if (abs(to.x - m.x) > size.x / 2 || abs(to.z - m.z) > size.y / 2) return undefined; // off the map
|
|
28928
28959
|
const a = from.y - this.getHeight(from.x, from.z), b = to.y - this.getHeight(to.x, to.z);
|
|
28929
|
-
if (a < 0 || b >= 0) return; // above all the way, or under from the start
|
|
28960
|
+
if (a < 0 || b >= 0) return undefined; // above all the way, or under from the start
|
|
28930
28961
|
const distance = a / (a - b);
|
|
28931
28962
|
return {distance, normal: this.getNormal(from.x + (to.x - from.x) * distance, from.z + (to.z - from.z) * distance)};
|
|
28932
28963
|
}
|
|
@@ -29964,9 +29995,11 @@ class LensFlare3D extends EngineObject3D
|
|
|
29964
29995
|
return this.made;
|
|
29965
29996
|
}
|
|
29966
29997
|
|
|
29967
|
-
|
|
29968
|
-
|
|
29969
|
-
|
|
29998
|
+
/** What the flare is of, seen from the camera: the way to it, how far it is, Infinity for the sun and for a
|
|
29999
|
+
* directional light, which shines from its place toward the origin, and a point to find it on the screen by;
|
|
30000
|
+
* undefined with no direction, or a light that is gone or at the camera
|
|
30001
|
+
* @return {{direction: Vector3, distance: number, pos: Vector3}|undefined}
|
|
30002
|
+
* @ignore */
|
|
29970
30003
|
flareSource()
|
|
29971
30004
|
{
|
|
29972
30005
|
const camera = render3D.camera.pos, light = this.light;
|
|
@@ -29982,9 +30015,11 @@ class LensFlare3D extends EngineObject3D
|
|
|
29982
30015
|
return distance ? {direction: offset.scale(1 / distance), distance, pos} : undefined;
|
|
29983
30016
|
}
|
|
29984
30017
|
|
|
29985
|
-
|
|
29986
|
-
|
|
29987
|
-
|
|
30018
|
+
/** How the flare would show with nothing in the way: where its source is on the screen, how strong it is there,
|
|
30019
|
+
* fading as it leaves the screen, its tint and the height its sizes are parts of; undefined when it would not
|
|
30020
|
+
* show at all, behind the camera, off the screen, or of a light that is off or seen from outside its cone
|
|
30021
|
+
* @return {{sun: Vector2, center: Vector2, strength: number, tint: Color, height: number}|undefined}
|
|
30022
|
+
* @ignore */
|
|
29988
30023
|
flareLook()
|
|
29989
30024
|
{
|
|
29990
30025
|
const source = this.flareSource(), center = mainCanvasSize.scale(.5);
|
|
@@ -33285,11 +33320,16 @@ function gltfFetch(uri, baseUrl, files)
|
|
|
33285
33320
|
const path = clean(baseUrl + name);
|
|
33286
33321
|
if (path === undefined)
|
|
33287
33322
|
return Promise.reject(new Error('glTF needs ' + name + ', which is above the files given'));
|
|
33288
|
-
const lower = path.toLowerCase(), file =
|
|
33323
|
+
const lower = path.toLowerCase(), file = lower.slice(lower.lastIndexOf('/') + 1);
|
|
33289
33324
|
const byPath = new Map([...files].map(([key, value])=> [clean(key)?.toLowerCase(), value]));
|
|
33290
33325
|
const found = files.get(path) ?? byPath.get(lower), keys = [...byPath.keys()];
|
|
33291
33326
|
if (found)
|
|
33292
33327
|
return Promise.resolve(new Response(found));
|
|
33328
|
+
// what is in a folder dropped, not the folder, leaves the folder's own name out of the paths: the longest key
|
|
33329
|
+
// the path ends with, a folder and more, as a name alone is only taken when one file has it
|
|
33330
|
+
const end = keys.filter((k)=> k?.includes('/') && lower.endsWith('/' + k)).sort((a, b)=> b.length - a.length)[0];
|
|
33331
|
+
if (end)
|
|
33332
|
+
return Promise.resolve(new Response(byPath.get(end)));
|
|
33293
33333
|
const named = new Set(keys.filter((k)=> k?.slice(k.lastIndexOf('/') + 1) === file).map((k)=> byPath.get(k)));
|
|
33294
33334
|
if (named.size === 1)
|
|
33295
33335
|
return Promise.resolve(new Response([...named][0]));
|