@driftengine/core 4.8.1 → 4.8.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.json +142 -0
- package/README.md +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -0
- package/dist/input/actionMap.d.ts +6 -1
- package/dist/input/actionMap.js +20 -7
- package/dist/input/input.d.ts +28 -0
- package/dist/input/input.js +71 -9
- package/dist/input/inputHarness.d.ts +9 -0
- package/dist/input/inputHarness.js +34 -2
- package/dist/render/backend/materialChanges.d.ts +7 -2
- package/dist/render/backend/materialChanges.js +8 -3
- package/dist/render/backend/webgl2/renderer.d.ts +75 -3
- package/dist/render/backend/webgl2/renderer.js +209 -22
- package/dist/render/backend/webgpu/buffers.d.ts +2 -0
- package/dist/render/backend/webgpu/buffers.js +2 -0
- package/dist/render/backend/webgpu/causticsPass.js +2 -1
- package/dist/render/backend/webgpu/depthPass.d.ts +13 -3
- package/dist/render/backend/webgpu/depthPass.js +4 -3
- package/dist/render/backend/webgpu/filmPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +5 -0
- package/dist/render/backend/webgpu/flatPass.js +18 -8
- package/dist/render/backend/webgpu/linePass.js +2 -1
- package/dist/render/backend/webgpu/panelPass.js +2 -1
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/pipelineCache.d.ts +40 -8
- package/dist/render/backend/webgpu/pipelineCache.js +76 -9
- package/dist/render/backend/webgpu/plumePass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +12 -2
- package/dist/render/backend/webgpu/postPass.js +19 -5
- package/dist/render/backend/webgpu/renderer.d.ts +66 -2
- package/dist/render/backend/webgpu/renderer.js +189 -38
- package/dist/render/backend/webgpu/sdfTextPass.js +2 -1
- package/dist/render/backend/webgpu/textPass.js +2 -1
- package/dist/render/backend/webgpu/waterPass.js +2 -1
- package/dist/render/backend/webgpu/windStreakPass.js +2 -1
- package/dist/render/boltBatch.js +2 -1
- package/dist/render/causticsRenderer.js +2 -1
- package/dist/render/cutoutCaster.d.ts +3 -1
- package/dist/render/cutoutCaster.js +4 -0
- package/dist/render/decalPass.js +2 -1
- package/dist/render/globalMediumPass.js +3 -2
- package/dist/render/lineBatch.js +5 -1
- package/dist/render/mapsWithoutUvs.d.ts +24 -0
- package/dist/render/mapsWithoutUvs.js +24 -0
- package/dist/render/mesh.d.ts +2 -0
- package/dist/render/mesh.js +3 -0
- package/dist/render/oitPass.js +2 -1
- package/dist/render/particleBatch.js +3 -2
- package/dist/render/particlePool.d.ts +21 -2
- package/dist/render/particlePool.js +72 -37
- package/dist/render/pass.d.ts +12 -0
- package/dist/render/plumeRenderer.js +3 -2
- package/dist/render/renderQuality.d.ts +16 -8
- package/dist/render/sceneCoverage.d.ts +54 -0
- package/dist/render/sceneCoverage.js +72 -0
- package/dist/render/sdfTextRenderer.js +2 -1
- package/dist/render/shaders/depth.js +3 -2
- package/dist/render/shaders/flat/glassTint.d.ts +28 -0
- package/dist/render/shaders/flat/glassTint.js +31 -0
- package/dist/render/shaders/flat/index.d.ts +12 -0
- package/dist/render/shaders/flat/index.js +13 -2
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +60 -25
- package/dist/render/shaders/flat/surfaceEffects.d.ts +1 -1
- package/dist/render/shaders/flat/surfaceEffects.js +6 -2
- package/dist/render/shaders/generated/depth.wgsl.d.ts +32 -12
- package/dist/render/shaders/generated/depth.wgsl.js +32 -12
- package/dist/render/shaders/generated/flat.wgsl.d.ts +89 -0
- package/dist/render/shaders/generated/flat.wgsl.js +127 -38
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +3 -3
- package/dist/render/shaders/generated/particle.wgsl.js +3 -3
- package/dist/render/shaders/generated/rush.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/rush.wgsl.js +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.js +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +14 -1
- package/dist/render/shaders/outputTransform.js +23 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/recon/resolve.wgsl.js +15 -1
- package/dist/render/shaders/rush.js +13 -2
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/temporalResolve.js +12 -5
- package/dist/render/ssrPass.js +2 -1
- package/dist/render/surfaceTexture.d.ts +13 -0
- package/dist/render/surfaceTexture.js +4 -0
- package/dist/render/textRenderer.js +2 -1
- package/dist/render/vertexDefaults.js +1 -0
- package/dist/render/viewModel.d.ts +29 -0
- package/dist/render/viewModel.js +35 -0
- package/dist/render/waterRenderer.js +2 -1
- package/dist/render/windStreakRenderer.js +2 -1
- package/package.json +6 -6
- package/src/index.ts +22 -1
- package/src/input/actionMap.ts +23 -8
- package/src/input/input.ts +79 -9
- package/src/input/inputHarness.ts +27 -2
- package/src/render/backend/materialChanges.ts +8 -3
- package/src/render/backend/webgl2/renderer.ts +223 -29
- package/src/render/backend/webgpu/buffers.ts +5 -0
- package/src/render/backend/webgpu/causticsPass.ts +2 -1
- package/src/render/backend/webgpu/depthPass.ts +4 -3
- package/src/render/backend/webgpu/filmPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +43 -26
- package/src/render/backend/webgpu/linePass.ts +2 -1
- package/src/render/backend/webgpu/panelPass.ts +2 -1
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/pipelineCache.ts +82 -8
- package/src/render/backend/webgpu/plumePass.ts +2 -1
- package/src/render/backend/webgpu/postPass.ts +20 -5
- package/src/render/backend/webgpu/renderer.ts +191 -37
- package/src/render/backend/webgpu/sdfTextPass.ts +2 -1
- package/src/render/backend/webgpu/textPass.ts +2 -1
- package/src/render/backend/webgpu/waterPass.ts +2 -1
- package/src/render/backend/webgpu/windStreakPass.ts +2 -1
- package/src/render/boltBatch.ts +2 -1
- package/src/render/causticsRenderer.ts +2 -1
- package/src/render/cutoutCaster.ts +15 -2
- package/src/render/decalPass.ts +2 -1
- package/src/render/globalMediumPass.ts +3 -2
- package/src/render/lineBatch.ts +3 -1
- package/src/render/mapsWithoutUvs.ts +43 -0
- package/src/render/mesh.ts +3 -0
- package/src/render/oitPass.ts +2 -1
- package/src/render/particleBatch.ts +3 -2
- package/src/render/particlePool.ts +107 -56
- package/src/render/pass.ts +12 -0
- package/src/render/plumeRenderer.ts +3 -2
- package/src/render/renderQuality.ts +16 -8
- package/src/render/sceneCoverage.ts +78 -0
- package/src/render/sdfTextRenderer.ts +2 -1
- package/src/render/shaders/depth.ts +3 -2
- package/src/render/shaders/flat/glassTint.ts +48 -0
- package/src/render/shaders/flat/index.ts +25 -2
- package/src/render/shaders/flat/main.ts +60 -25
- package/src/render/shaders/flat/surfaceEffects.ts +6 -2
- package/src/render/shaders/generated/depth.wgsl.ts +32 -12
- package/src/render/shaders/generated/flat.wgsl.ts +127 -38
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +3 -3
- package/src/render/shaders/generated/rush.wgsl.ts +1 -1
- package/src/render/shaders/generated/sdfText.wgsl.ts +1 -1
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
- package/src/render/shaders/outputTransform.ts +24 -1
- package/src/render/shaders/recon/resolve.wgsl.ts +15 -1
- package/src/render/shaders/rush.ts +13 -2
- package/src/render/shaders/temporalResolve.ts +12 -5
- package/src/render/ssrPass.ts +2 -1
- package/src/render/surfaceTexture.ts +16 -0
- package/src/render/textRenderer.ts +2 -1
- package/src/render/vertexDefaults.ts +1 -0
- package/src/render/viewModel.ts +41 -0
- package/src/render/waterRenderer.ts +2 -1
- package/src/render/windStreakRenderer.ts +2 -1
package/src/index.ts
CHANGED
|
@@ -743,6 +743,21 @@ export {
|
|
|
743
743
|
REVERSED_DEPTH,
|
|
744
744
|
conventionalDepth,
|
|
745
745
|
} from './render/depthConvention.ts';
|
|
746
|
+
/*
|
|
747
|
+
* **What a contributed pass's blended draw does to the scene's alpha**, which carries how much of
|
|
748
|
+
* each pixel is still the opaque surface: the share the composite's ambient occlusion may darken.
|
|
749
|
+
* A pass blending into the scene states one of these for its alpha. See `sceneCoverage.ts`.
|
|
750
|
+
*/
|
|
751
|
+
export {
|
|
752
|
+
SCENE_ALPHA_COVERS,
|
|
753
|
+
SCENE_ALPHA_KEEPS,
|
|
754
|
+
SCENE_ALPHA_TRANSMITS,
|
|
755
|
+
blendCovering,
|
|
756
|
+
blendKeeping,
|
|
757
|
+
blendTransmitting,
|
|
758
|
+
} from './render/sceneCoverage.ts';
|
|
759
|
+
/* How much of the depth range `beginViewModel` takes when it is given no share. See `viewModel.ts`. */
|
|
760
|
+
export { VIEW_MODEL_DEPTH_SHARE } from './render/viewModel.ts';
|
|
746
761
|
export type { ColliderSurfaceOptions } from './physics/colliderSurface.ts';
|
|
747
762
|
export { BoxSurface } from './physics/boxSurface.ts';
|
|
748
763
|
export { heightSurface } from './physics/heightSurface.ts';
|
|
@@ -864,7 +879,13 @@ export {
|
|
|
864
879
|
export { InputSource } from './input/input.ts';
|
|
865
880
|
export { isTypingTarget } from './input/typingTarget.ts';
|
|
866
881
|
export type { TouchPoint, InputCallbacks } from './input/input.ts';
|
|
867
|
-
export type {
|
|
882
|
+
export type {
|
|
883
|
+
GamepadIdentity,
|
|
884
|
+
GamepadView,
|
|
885
|
+
InputOptions,
|
|
886
|
+
InputSourceName,
|
|
887
|
+
MouseButton,
|
|
888
|
+
} from './input/input.ts';
|
|
868
889
|
export type { InputDevice } from './input/activeDevice.ts';
|
|
869
890
|
export type { GamepadFamily } from './input/gamepadIdentity.ts';
|
|
870
891
|
/* Positions rather than lettering, and the constant a consumer overrides to taste. */
|
package/src/input/actionMap.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { KeyValueStore } from '../core/storage.ts';
|
|
2
|
-
import type { InputSource } from './input.ts';
|
|
2
|
+
import type { InputSource, MouseButton } from './input.ts';
|
|
3
3
|
import type { GamepadButton } from './gamepadMapping.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -18,12 +18,15 @@ import type { GamepadButton } from './gamepadMapping.ts';
|
|
|
18
18
|
/** One thing that can satisfy an action. */
|
|
19
19
|
export type Binding =
|
|
20
20
|
| { readonly device: 'keyboard'; readonly code: string }
|
|
21
|
-
| { readonly device: 'gamepad'; readonly button: GamepadButton }
|
|
21
|
+
| { readonly device: 'gamepad'; readonly button: GamepadButton }
|
|
22
|
+
| { readonly device: 'mouse'; readonly button: MouseButton };
|
|
22
23
|
|
|
23
24
|
/** Jump, fire, pause: a state, bound to keys and buttons. */
|
|
24
25
|
export interface DigitalAction {
|
|
25
26
|
readonly keys?: readonly string[];
|
|
26
27
|
readonly buttons?: readonly GamepadButton[];
|
|
28
|
+
/** Mouse buttons, pressed over the input source's target: fire on the left, aim on the right. */
|
|
29
|
+
readonly mouseButtons?: readonly MouseButton[];
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
/**
|
|
@@ -92,7 +95,9 @@ export class ActionMap {
|
|
|
92
95
|
return this.any(action, (binding) =>
|
|
93
96
|
binding.device === 'keyboard'
|
|
94
97
|
? this.input.isDown(binding.code)
|
|
95
|
-
:
|
|
98
|
+
: binding.device === 'mouse'
|
|
99
|
+
? this.input.mouseDown(binding.button)
|
|
100
|
+
: (this.input.pad(0)?.down(binding.button) ?? false),
|
|
96
101
|
);
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -101,7 +106,9 @@ export class ActionMap {
|
|
|
101
106
|
return this.any(action, (binding) =>
|
|
102
107
|
binding.device === 'keyboard'
|
|
103
108
|
? this.input.keyPressed(binding.code)
|
|
104
|
-
:
|
|
109
|
+
: binding.device === 'mouse'
|
|
110
|
+
? this.input.mousePressed(binding.button)
|
|
111
|
+
: (this.input.pad(0)?.pressed(binding.button) ?? false),
|
|
105
112
|
);
|
|
106
113
|
}
|
|
107
114
|
|
|
@@ -118,6 +125,8 @@ export class ActionMap {
|
|
|
118
125
|
for (const binding of bindings) {
|
|
119
126
|
if (binding.device === 'keyboard') {
|
|
120
127
|
if (this.input.consumeKeyPress(binding.code)) return true;
|
|
128
|
+
} else if (binding.device === 'mouse') {
|
|
129
|
+
if (this.input.consumeMousePress(binding.button)) return true;
|
|
121
130
|
} else if (this.input.pad(0)?.consumePress(binding.button) === true) {
|
|
122
131
|
return true;
|
|
123
132
|
}
|
|
@@ -354,16 +363,19 @@ function bindingsOf(definition: DigitalAction): Binding[] {
|
|
|
354
363
|
const list: Binding[] = [];
|
|
355
364
|
for (const code of definition.keys ?? []) list.push({ device: 'keyboard', code });
|
|
356
365
|
for (const button of definition.buttons ?? []) list.push({ device: 'gamepad', button });
|
|
366
|
+
for (const button of definition.mouseButtons ?? []) list.push({ device: 'mouse', button });
|
|
357
367
|
return list;
|
|
358
368
|
}
|
|
359
369
|
|
|
360
370
|
function sameBinding(a: Binding, b: Binding): boolean {
|
|
361
|
-
if (a.device
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
371
|
+
if (a.device === 'keyboard' || b.device === 'keyboard') {
|
|
372
|
+
return a.device === 'keyboard' && b.device === 'keyboard' && a.code === b.code;
|
|
373
|
+
}
|
|
374
|
+
return a.device === b.device && a.button === b.button;
|
|
365
375
|
}
|
|
366
376
|
|
|
377
|
+
const MOUSE_BUTTONS: ReadonlySet<string> = new Set(['left', 'middle', 'right', 'back', 'forward']);
|
|
378
|
+
|
|
367
379
|
function sameList(a: readonly Binding[], b: readonly Binding[]): boolean {
|
|
368
380
|
return a.length === b.length && a.every((entry, at) => sameBinding(entry, b[at] as Binding));
|
|
369
381
|
}
|
|
@@ -373,5 +385,8 @@ function isBinding(value: unknown): value is Binding {
|
|
|
373
385
|
if (typeof value !== 'object' || value === null) return false;
|
|
374
386
|
const candidate = value as { device?: unknown; code?: unknown; button?: unknown };
|
|
375
387
|
if (candidate.device === 'keyboard') return typeof candidate.code === 'string';
|
|
388
|
+
if (candidate.device === 'mouse') {
|
|
389
|
+
return typeof candidate.button === 'string' && MOUSE_BUTTONS.has(candidate.button);
|
|
390
|
+
}
|
|
376
391
|
return candidate.device === 'gamepad' && typeof candidate.button === 'string';
|
|
377
392
|
}
|
package/src/input/input.ts
CHANGED
|
@@ -104,6 +104,23 @@ export interface TouchPoint {
|
|
|
104
104
|
/** The devices this source watches, and the granularity a consumer may switch off. */
|
|
105
105
|
export type InputSourceName = 'keyboard' | 'mouse' | 'touch' | 'gamepad';
|
|
106
106
|
|
|
107
|
+
/** A mouse button by what it is, in `MouseEvent.button` order. */
|
|
108
|
+
export type MouseButton = 'left' | 'middle' | 'right' | 'back' | 'forward';
|
|
109
|
+
|
|
110
|
+
/** One bit a button, so the held, pressed and claimed states are integers and a read allocates nothing. */
|
|
111
|
+
const MOUSE_BIT: Readonly<Record<MouseButton, number>> = {
|
|
112
|
+
left: 1,
|
|
113
|
+
middle: 2,
|
|
114
|
+
right: 4,
|
|
115
|
+
back: 8,
|
|
116
|
+
forward: 16,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** `MouseEvent.button` as a bit, or 0 for a button past the five this names. */
|
|
120
|
+
function mouseBit(button: number): number {
|
|
121
|
+
return button >= 0 && button < 5 ? 1 << button : 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
107
124
|
export interface InputOptions {
|
|
108
125
|
/**
|
|
109
126
|
* Turn a device off. Absent means on.
|
|
@@ -420,6 +437,19 @@ export class InputSource {
|
|
|
420
437
|
private mouseDx = 0;
|
|
421
438
|
private mouseDy = 0;
|
|
422
439
|
private mouseButtonDown = false;
|
|
440
|
+
/**
|
|
441
|
+
* Each mouse button's own state, as the keyboard's sets are for keys: held, pressed since the
|
|
442
|
+
* last poll, claimed this frame, and waiting for somebody to act on it. Bits, by `MOUSE_BIT`.
|
|
443
|
+
*
|
|
444
|
+
* **Beside `mouseButtonDown`, not instead of it.** That flag is "a button is held", which is what
|
|
445
|
+
* an unlocked drag-to-look reads; a game binding fire to the left button and aim to the right
|
|
446
|
+
* needs to know which, and to see a release of one while the other stays down.
|
|
447
|
+
*/
|
|
448
|
+
private mouseHeld = 0;
|
|
449
|
+
private mousePressedNow = 0;
|
|
450
|
+
private mousePressedNext = 0;
|
|
451
|
+
private mouseClaimed = 0;
|
|
452
|
+
private mouseUnclaimed = 0;
|
|
423
453
|
private readonly disposers: Array<() => void> = [];
|
|
424
454
|
|
|
425
455
|
/** Which device the player last actually used, and who wants telling when that changes. */
|
|
@@ -531,13 +561,21 @@ export class InputSource {
|
|
|
531
561
|
this.keysUnclaimed.clear();
|
|
532
562
|
});
|
|
533
563
|
|
|
534
|
-
this.listen(target, 'mousedown', () => {
|
|
564
|
+
this.listen(target, 'mousedown', (e: MouseEvent) => {
|
|
535
565
|
if (!this.enabled.mouse) return;
|
|
536
566
|
this.mouseButtonDown = true;
|
|
567
|
+
const bit = mouseBit(e.button);
|
|
568
|
+
this.mouseHeld |= bit;
|
|
569
|
+
this.mousePressedNext |= bit;
|
|
570
|
+
this.mouseUnclaimed |= bit;
|
|
537
571
|
this.active.use('mouse');
|
|
538
572
|
});
|
|
539
|
-
|
|
573
|
+
/* On the window, so a press that becomes a drag off the canvas still comes back up. */
|
|
574
|
+
this.listen(window, 'mouseup', (e: MouseEvent) => {
|
|
540
575
|
this.mouseButtonDown = false;
|
|
576
|
+
const bit = mouseBit(e.button);
|
|
577
|
+
this.mouseHeld &= ~bit;
|
|
578
|
+
this.mouseUnclaimed &= ~bit;
|
|
541
579
|
});
|
|
542
580
|
/*
|
|
543
581
|
* **Released by anything that can eat the mouseup, exactly as the keys are.**
|
|
@@ -559,15 +597,11 @@ export class InputSource {
|
|
|
559
597
|
* `visibilitychange` as well as `blur`, because a tab switched away with a button held
|
|
560
598
|
* comes back to a document that never saw the release either.
|
|
561
599
|
*/
|
|
562
|
-
this.listen(window, 'blur', () =>
|
|
563
|
-
|
|
564
|
-
});
|
|
565
|
-
this.listen(window, 'pointercancel', () => {
|
|
566
|
-
this.mouseButtonDown = false;
|
|
567
|
-
});
|
|
600
|
+
this.listen(window, 'blur', () => this.releaseMouse());
|
|
601
|
+
this.listen(window, 'pointercancel', () => this.releaseMouse());
|
|
568
602
|
/* Not in `GlobalEventHandlersEventMap`, so it is bound directly and disposed by hand. */
|
|
569
603
|
const onHidden = (): void => {
|
|
570
|
-
if (document.visibilityState !== 'visible') this.
|
|
604
|
+
if (document.visibilityState !== 'visible') this.releaseMouse();
|
|
571
605
|
};
|
|
572
606
|
document.addEventListener('visibilitychange', onHidden);
|
|
573
607
|
this.disposers.push(() => document.removeEventListener('visibilitychange', onHidden));
|
|
@@ -676,6 +710,10 @@ export class InputSource {
|
|
|
676
710
|
spent.clear();
|
|
677
711
|
this.keysPressedNext = spent;
|
|
678
712
|
this.keysClaimed.clear();
|
|
713
|
+
/* The mouse buttons' frame boundary, on the same poll and by the same rule. */
|
|
714
|
+
this.mousePressedNow = this.mousePressedNext;
|
|
715
|
+
this.mousePressedNext = 0;
|
|
716
|
+
this.mouseClaimed = 0;
|
|
679
717
|
|
|
680
718
|
if (!this.enabled.gamepad) return;
|
|
681
719
|
const source = (globalThis as { navigator?: { getGamepads?: () => (RawPad | null)[] } })
|
|
@@ -768,6 +806,38 @@ export class InputSource {
|
|
|
768
806
|
return true;
|
|
769
807
|
}
|
|
770
808
|
|
|
809
|
+
/**
|
|
810
|
+
* Whether a mouse button is held: pressed over the target and not yet released anywhere.
|
|
811
|
+
*
|
|
812
|
+
* A browser opens its context menu on the right button's release; a game binding that button
|
|
813
|
+
* cancels `contextmenu` on its canvas, which is a decision about the page and not this source's.
|
|
814
|
+
*/
|
|
815
|
+
mouseDown(button: MouseButton): boolean {
|
|
816
|
+
return (this.mouseHeld & MOUSE_BIT[button]) !== 0;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/** Whether a mouse button went down since the last poll, as `keyPressed` is for a key. */
|
|
820
|
+
mousePressed(button: MouseButton): boolean {
|
|
821
|
+
const bit = MOUSE_BIT[button];
|
|
822
|
+
return (this.mousePressedNow & bit) !== 0 && (this.mouseClaimed & bit) === 0;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
/** The same edge, claimed, as `consumeKeyPress` is for a key: true for exactly one caller. */
|
|
826
|
+
consumeMousePress(button: MouseButton): boolean {
|
|
827
|
+
const bit = MOUSE_BIT[button];
|
|
828
|
+
if ((this.mouseUnclaimed & bit) === 0) return false;
|
|
829
|
+
this.mouseUnclaimed &= ~bit;
|
|
830
|
+
this.mouseClaimed |= bit;
|
|
831
|
+
return true;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/** Every button up and every waiting press gone: what a lost focus or a cancelled pointer means. */
|
|
835
|
+
private releaseMouse(): void {
|
|
836
|
+
this.mouseButtonDown = false;
|
|
837
|
+
this.mouseHeld = 0;
|
|
838
|
+
this.mouseUnclaimed = 0;
|
|
839
|
+
}
|
|
840
|
+
|
|
771
841
|
/**
|
|
772
842
|
* Add a device-event subscriber. Multiple reusable control layers may listen
|
|
773
843
|
* without overwriting one another; interpretation still happens outside the
|
|
@@ -96,9 +96,19 @@ export function fakeBrowser(id = 'Xbox 360 Controller (XInput STANDARD GAMEPAD)'
|
|
|
96
96
|
const frameCallbacks: ((time: number) => void)[] = [];
|
|
97
97
|
let nextFrameHandle = 1;
|
|
98
98
|
|
|
99
|
+
const targetListeners = new Map<string, ((event: unknown) => void)[]>();
|
|
99
100
|
const target = {
|
|
100
|
-
addEventListener: () => {
|
|
101
|
-
|
|
101
|
+
addEventListener: (type: string, fn: (event: unknown) => void) => {
|
|
102
|
+
const existing = targetListeners.get(type);
|
|
103
|
+
if (existing === undefined) targetListeners.set(type, [fn]);
|
|
104
|
+
else existing.push(fn);
|
|
105
|
+
},
|
|
106
|
+
removeEventListener: (type: string, fn: (event: unknown) => void) => {
|
|
107
|
+
const existing = targetListeners.get(type);
|
|
108
|
+
if (existing === undefined) return;
|
|
109
|
+
const at = existing.indexOf(fn);
|
|
110
|
+
if (at >= 0) existing.splice(at, 1);
|
|
111
|
+
},
|
|
102
112
|
requestPointerLock: () => undefined,
|
|
103
113
|
getBoundingClientRect: () => ({ left: 0, top: 0, width: 640, height: 480 }),
|
|
104
114
|
} as unknown as HTMLElement;
|
|
@@ -204,6 +214,21 @@ export function fakeBrowser(id = 'Xbox 360 Controller (XInput STANDARD GAMEPAD)'
|
|
|
204
214
|
keyUp(code: string): void {
|
|
205
215
|
for (const fn of windowListeners.get('keyup') ?? []) fn({ code });
|
|
206
216
|
},
|
|
217
|
+
/**
|
|
218
|
+
* Press a mouse button over the target, by `MouseEvent.button`: 0 left, 1 middle, 2 right.
|
|
219
|
+
* Pressed on the target and released on the window, as a browser delivers a drag that ends
|
|
220
|
+
* off the canvas.
|
|
221
|
+
*/
|
|
222
|
+
mouseDown(button: number): void {
|
|
223
|
+
for (const fn of targetListeners.get('mousedown') ?? []) fn({ button, movementX: 0 });
|
|
224
|
+
},
|
|
225
|
+
mouseUp(button: number): void {
|
|
226
|
+
for (const fn of windowListeners.get('mouseup') ?? []) fn({ button });
|
|
227
|
+
},
|
|
228
|
+
/** The window losing focus, which eats every release that would have followed. */
|
|
229
|
+
blur(): void {
|
|
230
|
+
for (const fn of windowListeners.get('blur') ?? []) fn({});
|
|
231
|
+
},
|
|
207
232
|
/** Run exactly one animation frame, which is what a poll rides on. */
|
|
208
233
|
frame(): void {
|
|
209
234
|
const pending = frameCallbacks.splice(0, frameCallbacks.length);
|
|
@@ -31,12 +31,17 @@ export interface DrawMaterialOptions {
|
|
|
31
31
|
readonly refracting: boolean;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/**
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Whether a draw's options differ from the pass's, so it takes a material and leaves one to restore.
|
|
36
|
+
*
|
|
37
|
+
* `passFog` is how the surfaces around it meet the medium — `FOG_RECEDE` unless `setSurfaceFog`
|
|
38
|
+
* turned it off — so a translucent draw asking for what the pass already has takes nothing.
|
|
39
|
+
*/
|
|
40
|
+
export function ownsMaterial(options: DrawMaterialOptions, passFog = FOG_RECEDE): boolean {
|
|
36
41
|
return (
|
|
37
42
|
options.opacity < 1 ||
|
|
38
43
|
!options.lit ||
|
|
39
|
-
options.fog !==
|
|
44
|
+
options.fog !== passFog ||
|
|
40
45
|
!options.toneMapped ||
|
|
41
46
|
options.refracting
|
|
42
47
|
);
|