@driftengine/core 4.5.0 → 4.6.0
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 +51 -0
- package/README.md +1 -1
- package/dist/geometry/boxCsg.d.ts +28 -0
- package/dist/geometry/boxCsg.js +139 -0
- package/dist/geometry/csg.d.ts +36 -0
- package/dist/geometry/csg.js +329 -0
- package/dist/geometry/meshBuilder.js +13 -0
- package/dist/geometry/meshConcat.js +1 -0
- package/dist/geometry/pixelFont.js +2 -0
- package/dist/geometry/solid.d.ts +61 -0
- package/dist/geometry/solid.js +152 -0
- package/dist/geometry/solidFlat.d.ts +35 -0
- package/dist/geometry/solidFlat.js +428 -0
- package/dist/geometry/solidHarness.d.ts +19 -0
- package/dist/geometry/solidHarness.js +66 -0
- package/dist/geometry/solidNormals.d.ts +17 -0
- package/dist/geometry/solidNormals.js +86 -0
- package/dist/geometry/solidRound.d.ts +41 -0
- package/dist/geometry/solidRound.js +323 -0
- package/dist/geometry/solidWriter.d.ts +16 -0
- package/dist/geometry/solidWriter.js +42 -0
- package/dist/index.d.ts +23 -2
- package/dist/index.js +13 -1
- package/dist/render/backend/api.d.ts +17 -3
- package/dist/render/backend/drawFog.d.ts +26 -0
- package/dist/render/backend/drawFog.js +30 -0
- package/dist/render/backend/materialChanges.d.ts +2 -1
- package/dist/render/backend/materialChanges.js +6 -1
- package/dist/render/backend/webgl2/instanced.d.ts +24 -5
- package/dist/render/backend/webgl2/instanced.js +72 -15
- package/dist/render/backend/webgl2/renderer.d.ts +122 -8
- package/dist/render/backend/webgl2/renderer.js +238 -121
- package/dist/render/backend/webgpu/buffers.d.ts +10 -0
- package/dist/render/backend/webgpu/buffers.js +34 -3
- package/dist/render/backend/webgpu/depthPass.js +5 -1
- package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +4 -1
- package/dist/render/backend/webgpu/flatPass.js +38 -9
- package/dist/render/backend/webgpu/glassTintPass.js +2 -1
- package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
- package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
- package/dist/render/backend/webgpu/instanced.d.ts +11 -1
- package/dist/render/backend/webgpu/instanced.js +21 -3
- package/dist/render/backend/webgpu/latePass.d.ts +6 -2
- package/dist/render/backend/webgpu/latePass.js +11 -4
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -10
- package/dist/render/backend/webgpu/renderer.js +462 -201
- package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +17 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +77 -38
- package/dist/render/driftLight/bake.d.ts +7 -0
- package/dist/render/driftLight/bake.js +61 -56
- package/dist/render/driftLight/denseField.d.ts +46 -0
- package/dist/render/driftLight/denseField.js +109 -0
- package/dist/render/driftLight/lightField.d.ts +10 -5
- package/dist/render/driftLight/lightField.js +25 -19
- package/dist/render/driftLight/presence.d.ts +50 -0
- package/dist/render/driftLight/presence.js +43 -0
- package/dist/render/driftLight/uniforms.d.ts +3 -3
- package/dist/render/driftLight/uniforms.js +5 -6
- package/dist/render/driftLight/worldLightField.d.ts +41 -0
- package/dist/render/driftLight/worldLightField.js +66 -0
- package/dist/render/fixtureAtlas.d.ts +20 -0
- package/dist/render/fixtureAtlas.js +49 -0
- package/dist/render/instanceCull.d.ts +63 -0
- package/dist/render/instanceCull.js +160 -0
- package/dist/render/instances.d.ts +15 -0
- package/dist/render/lightBudget.d.ts +22 -33
- package/dist/render/lightBudget.js +24 -35
- package/dist/render/lightGrid.d.ts +42 -0
- package/dist/render/lightGrid.js +87 -0
- package/dist/render/mesh.d.ts +18 -24
- package/dist/render/mesh.js +51 -68
- package/dist/render/occlusion.d.ts +10 -0
- package/dist/render/occlusion.js +36 -7
- package/dist/render/pointLightSelection.d.ts +7 -0
- package/dist/render/pointLightSelection.js +29 -4
- package/dist/render/rainField.d.ts +13 -0
- package/dist/render/rainField.js +27 -5
- package/dist/render/recon/lateDepth.d.ts +11 -0
- package/dist/render/recon/lateDepth.js +70 -0
- package/dist/render/recon/resolve.d.ts +34 -4
- package/dist/render/recon/resolve.js +161 -20
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +2 -0
- package/dist/render/shaders/depth.js +12 -9
- package/dist/render/shaders/flat/driftLight.d.ts +1 -1
- package/dist/render/shaders/flat/driftLight.js +32 -7
- package/dist/render/shaders/flat/index.js +5 -3
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +75 -23
- package/dist/render/shaders/flat/preamble.js +75 -20
- package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
- package/dist/render/shaders/flat/surfaceEffects.js +152 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +8 -8
- package/dist/render/shaders/generated/depth.wgsl.js +8 -8
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1320 -1320
- package/dist/render/shaders/generated/flat.wgsl.js +1359 -1345
- package/dist/render/shaders/generated/lightVolume.wgsl.js +18 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
- package/dist/render/shaders/generated/rush.wgsl.js +21 -16
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
- package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
- package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/text.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
- package/dist/render/shaders/recon/lateDepth.wgsl.js +38 -13
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
- package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
- package/dist/render/shaders/rush.js +11 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/sdfText.js +3 -2
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +18 -9
- package/dist/render/surfaceEffects.d.ts +83 -0
- package/dist/render/surfaceEffects.js +75 -0
- package/dist/render/surfaceScene.d.ts +8 -0
- package/dist/render/surfaceScene.js +8 -0
- package/dist/render/surfaceTexture.d.ts +18 -2
- package/dist/render/surfaceTexture.js +68 -15
- package/dist/render/temporalAa.d.ts +25 -6
- package/dist/render/temporalAa.js +45 -15
- package/dist/render/textureSource.d.ts +30 -0
- package/dist/render/textureSource.js +53 -0
- package/dist/render/vertexDefaults.js +2 -0
- package/dist/world/hlod.d.ts +104 -0
- package/dist/world/hlod.js +214 -0
- package/package.json +6 -6
- package/src/geometry/boxCsg.ts +186 -0
- package/src/geometry/csg.ts +384 -0
- package/src/geometry/meshBuilder.ts +15 -0
- package/src/geometry/meshConcat.ts +1 -0
- package/src/geometry/pixelFont.ts +2 -0
- package/src/geometry/solid.ts +189 -0
- package/src/geometry/solidFlat.ts +522 -0
- package/src/geometry/solidHarness.ts +83 -0
- package/src/geometry/solidNormals.ts +103 -0
- package/src/geometry/solidRound.ts +443 -0
- package/src/geometry/solidWriter.ts +58 -0
- package/src/index.ts +51 -1
- package/src/render/backend/api.ts +21 -2
- package/src/render/backend/drawFog.ts +31 -0
- package/src/render/backend/materialChanges.ts +9 -2
- package/src/render/backend/webgl2/instanced.ts +80 -14
- package/src/render/backend/webgl2/renderer.ts +300 -143
- package/src/render/backend/webgpu/buffers.ts +42 -3
- package/src/render/backend/webgpu/depthPass.ts +5 -1
- package/src/render/backend/webgpu/drawCommand.ts +6 -0
- package/src/render/backend/webgpu/flatPass.ts +41 -11
- package/src/render/backend/webgpu/glassTintPass.ts +2 -1
- package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
- package/src/render/backend/webgpu/instanced.ts +21 -3
- package/src/render/backend/webgpu/latePass.ts +12 -3
- package/src/render/backend/webgpu/renderer.ts +540 -226
- package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
- package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +90 -50
- package/src/render/driftLight/bake.ts +85 -62
- package/src/render/driftLight/denseField.ts +149 -0
- package/src/render/driftLight/lightField.ts +33 -18
- package/src/render/driftLight/presence.ts +69 -0
- package/src/render/driftLight/uniforms.ts +8 -9
- package/src/render/driftLight/worldLightField.ts +83 -0
- package/src/render/fixtureAtlas.ts +63 -0
- package/src/render/instanceCull.ts +205 -0
- package/src/render/instances.ts +16 -0
- package/src/render/lightBudget.ts +25 -36
- package/src/render/lightGrid.ts +130 -0
- package/src/render/mesh.ts +59 -72
- package/src/render/occlusion.ts +51 -7
- package/src/render/pointLightSelection.ts +72 -4
- package/src/render/rainField.ts +39 -5
- package/src/render/recon/lateDepth.ts +83 -0
- package/src/render/recon/resolve.ts +206 -20
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneTarget.ts +4 -0
- package/src/render/shaders/depth.ts +12 -9
- package/src/render/shaders/flat/driftLight.ts +32 -7
- package/src/render/shaders/flat/index.ts +5 -3
- package/src/render/shaders/flat/main.ts +75 -23
- package/src/render/shaders/flat/preamble.ts +75 -20
- package/src/render/shaders/flat/surfaceEffects.ts +152 -0
- package/src/render/shaders/generated/depth.wgsl.ts +8 -8
- package/src/render/shaders/generated/flat.wgsl.ts +1361 -1345
- package/src/render/shaders/generated/lightVolume.wgsl.ts +20 -4
- package/src/render/shaders/generated/rush.wgsl.ts +21 -16
- package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
- package/src/render/shaders/generated/text.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
- package/src/render/shaders/recon/lateDepth.wgsl.ts +38 -13
- package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
- package/src/render/shaders/rush.ts +11 -1
- package/src/render/shaders/sdfText.ts +3 -2
- package/src/render/shaders/text.ts +18 -9
- package/src/render/surfaceEffects.ts +132 -0
- package/src/render/surfaceScene.ts +16 -0
- package/src/render/surfaceTexture.ts +110 -19
- package/src/render/temporalAa.ts +78 -14
- package/src/render/textureSource.ts +72 -0
- package/src/render/vertexDefaults.ts +2 -0
- package/src/world/hlod.ts +290 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which level of detail each region of a world is drawn at, and the crossfade between two.
|
|
3
|
+
*
|
|
4
|
+
* **A region is a box and one geometric error per level**, finest first: how far, in metres, that
|
|
5
|
+
* level's surface may stand from the real one. A level is good enough once its error projects under
|
|
6
|
+
* `pixelTolerance` pixels, so the distance it takes over at is `error · projectionScale / tolerance`
|
|
7
|
+
* — the rule 3D Tiles uses, and the reason the file carries errors rather than distances: a
|
|
8
|
+
* distance is a decision about one screen, and an error is a fact about the geometry.
|
|
9
|
+
*
|
|
10
|
+
* **A band around every switch, so a still eye can never flip.** A region coarsens only once the
|
|
11
|
+
* eye is `1 + hysteresis` past the level's distance and refines only once it is `1 − hysteresis`
|
|
12
|
+
* inside it, so an eye standing on a threshold — or wobbling across it by less than the band —
|
|
13
|
+
* keeps the level it has. Distance is to the region's **box**, so the region under the eye is
|
|
14
|
+
* always at its finest, whatever its size.
|
|
15
|
+
*
|
|
16
|
+
* **A change crossfades rather than pops.** Each draw carries a dither for `setDitherFade`: the
|
|
17
|
+
* level arriving at `+t`, the one leaving at `−t`, which cover every pixel exactly once. `t` runs
|
|
18
|
+
* over `fadeSec` of the caller's `dtSec`, never a clock — so **a held clock never finishes a fade**,
|
|
19
|
+
* and a capture wants `fadeSec: 0`. A region nobody can see finishes any fade at once, and a region
|
|
20
|
+
* just added fades in from nothing.
|
|
21
|
+
*
|
|
22
|
+
* What it gives up: a region is one decision, so a long one seen end-on is drawn at the level its
|
|
23
|
+
* nearest corner asks for all the way along. Smaller regions are the lever.
|
|
24
|
+
*
|
|
25
|
+
* Nothing here allocates after construction except `add`, which is not per frame.
|
|
26
|
+
*/
|
|
27
|
+
import type { FrustumPlanes } from '../math/frustum.ts';
|
|
28
|
+
import { batchBoxVisible } from '../render/instanceCull.ts';
|
|
29
|
+
import type { BoxOccluder } from '../render/instanceCull.ts';
|
|
30
|
+
|
|
31
|
+
/** The level of a region drawn at none of its own: past `farDistance`, or not arrived yet. */
|
|
32
|
+
export const HLOD_NONE = 255;
|
|
33
|
+
|
|
34
|
+
export interface HlodOptions {
|
|
35
|
+
/** The most regions the set holds at once. */
|
|
36
|
+
readonly capacity: number;
|
|
37
|
+
/** The most levels a region may carry. 4 unless stated. */
|
|
38
|
+
readonly maxLevels?: number;
|
|
39
|
+
/** How many pixels of geometric error a level may show before a finer one is drawn. 1.5 unless stated. */
|
|
40
|
+
readonly pixelTolerance?: number;
|
|
41
|
+
/** The band around each switch, as a fraction of its distance. 0.15 unless stated. */
|
|
42
|
+
readonly hysteresis?: number;
|
|
43
|
+
/** Seconds a crossfade takes. 0.4 unless stated; 0 switches at once. */
|
|
44
|
+
readonly fadeSec?: number;
|
|
45
|
+
/** Beyond this, a region is drawn at no level at all. Unbounded unless stated. */
|
|
46
|
+
readonly farDistance?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** What `select` hands back: one entry per draw, a crossfading region taking two. */
|
|
50
|
+
export interface HlodDraws {
|
|
51
|
+
count: number;
|
|
52
|
+
/** The region's id, as it was added. */
|
|
53
|
+
readonly region: Uint32Array;
|
|
54
|
+
/** Which of its levels to draw. */
|
|
55
|
+
readonly level: Uint8Array;
|
|
56
|
+
/** What to pass `setDitherFade` for this draw: 0 whole, `+t` arriving, `−t` leaving. */
|
|
57
|
+
readonly dither: Float32Array;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Room for every region of a set of this capacity, each crossfading at once. */
|
|
61
|
+
export function createHlodDraws(capacity: number): HlodDraws {
|
|
62
|
+
return {
|
|
63
|
+
count: 0,
|
|
64
|
+
region: new Uint32Array(capacity * 2),
|
|
65
|
+
level: new Uint8Array(capacity * 2),
|
|
66
|
+
dither: new Float32Array(capacity * 2),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Pixels a metre spans at one metre's distance: the screen's half of the switch distance. */
|
|
71
|
+
export function projectionScaleOf(fovYDeg: number, heightPx: number): number {
|
|
72
|
+
return heightPx / (2 * Math.tan((fovYDeg * Math.PI) / 360));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class HlodSet {
|
|
76
|
+
readonly capacity: number;
|
|
77
|
+
readonly maxLevels: number;
|
|
78
|
+
private readonly tolerance: number;
|
|
79
|
+
private readonly band: number;
|
|
80
|
+
private readonly fadeSec: number;
|
|
81
|
+
private readonly far: number;
|
|
82
|
+
private readonly slots = new Map<number, number>();
|
|
83
|
+
private readonly ids: Uint32Array;
|
|
84
|
+
private readonly boxes: Float32Array;
|
|
85
|
+
private readonly errors: Float32Array;
|
|
86
|
+
private readonly levels: Uint8Array;
|
|
87
|
+
/** The level each region is at, or arriving at. */
|
|
88
|
+
private readonly level: Uint8Array;
|
|
89
|
+
/** The level a crossfade is leaving. */
|
|
90
|
+
private readonly from: Uint8Array;
|
|
91
|
+
/** How far through its crossfade each region is; 1 is settled. */
|
|
92
|
+
private readonly fade: Float32Array;
|
|
93
|
+
private readonly box = new Float32Array(6);
|
|
94
|
+
private count = 0;
|
|
95
|
+
|
|
96
|
+
constructor(options: HlodOptions) {
|
|
97
|
+
this.capacity = options.capacity;
|
|
98
|
+
this.maxLevels = options.maxLevels ?? 4;
|
|
99
|
+
if (this.maxLevels < 1 || this.maxLevels >= HLOD_NONE) {
|
|
100
|
+
throw new Error(`HlodSet: maxLevels must be 1 to ${HLOD_NONE - 1}, not ${this.maxLevels}`);
|
|
101
|
+
}
|
|
102
|
+
this.tolerance = options.pixelTolerance ?? 1.5;
|
|
103
|
+
this.band = options.hysteresis ?? 0.15;
|
|
104
|
+
this.fadeSec = options.fadeSec ?? 0.4;
|
|
105
|
+
this.far = options.farDistance ?? Number.POSITIVE_INFINITY;
|
|
106
|
+
this.ids = new Uint32Array(this.capacity);
|
|
107
|
+
this.boxes = new Float32Array(this.capacity * 6);
|
|
108
|
+
this.errors = new Float32Array(this.capacity * this.maxLevels);
|
|
109
|
+
this.levels = new Uint8Array(this.capacity);
|
|
110
|
+
this.level = new Uint8Array(this.capacity);
|
|
111
|
+
this.from = new Uint8Array(this.capacity);
|
|
112
|
+
this.fade = new Float32Array(this.capacity);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get size(): number {
|
|
116
|
+
return this.count;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
has(id: number): boolean {
|
|
120
|
+
return this.slots.has(id);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The level a region is at or arriving at, or `HLOD_NONE`. -1 for a region not in the set. */
|
|
124
|
+
levelOf(id: number): number {
|
|
125
|
+
const slot = this.slots.get(id);
|
|
126
|
+
return slot === undefined ? -1 : (this.level[slot] as number);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Add a region: its box as min then max, and one geometric error per level, finest first and
|
|
131
|
+
* never shrinking. It arrives at no level and fades into the one the next `select` picks.
|
|
132
|
+
*/
|
|
133
|
+
add(id: number, box: ArrayLike<number>, errors: ArrayLike<number>): void {
|
|
134
|
+
if (this.slots.has(id)) throw new Error(`HlodSet: region ${id} is already in the set`);
|
|
135
|
+
if (this.count >= this.capacity) {
|
|
136
|
+
throw new Error(`HlodSet: the set holds ${this.capacity} regions and is full`);
|
|
137
|
+
}
|
|
138
|
+
if (errors.length < 1 || errors.length > this.maxLevels) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`HlodSet: region ${id} has ${errors.length} levels; 1 to ${this.maxLevels} are allowed`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
for (let i = 1; i < errors.length; i++) {
|
|
144
|
+
if ((errors[i] as number) < (errors[i - 1] as number)) {
|
|
145
|
+
throw new Error(`HlodSet: region ${id}'s level ${i} is finer than level ${i - 1}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const slot = this.count++;
|
|
149
|
+
this.slots.set(id, slot);
|
|
150
|
+
this.ids[slot] = id;
|
|
151
|
+
for (let i = 0; i < 6; i++) this.boxes[slot * 6 + i] = box[i] as number;
|
|
152
|
+
for (let i = 0; i < errors.length; i++) {
|
|
153
|
+
this.errors[slot * this.maxLevels + i] = errors[i] as number;
|
|
154
|
+
}
|
|
155
|
+
this.levels[slot] = errors.length;
|
|
156
|
+
this.level[slot] = HLOD_NONE;
|
|
157
|
+
this.from[slot] = HLOD_NONE;
|
|
158
|
+
this.fade[slot] = 1;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Remove a region; nothing if it is not there. The last region takes its slot. */
|
|
162
|
+
remove(id: number): void {
|
|
163
|
+
const slot = this.slots.get(id);
|
|
164
|
+
if (slot === undefined) return;
|
|
165
|
+
this.slots.delete(id);
|
|
166
|
+
const last = --this.count;
|
|
167
|
+
if (slot === last) return;
|
|
168
|
+
const moved = this.ids[last] as number;
|
|
169
|
+
this.slots.set(moved, slot);
|
|
170
|
+
this.ids[slot] = moved;
|
|
171
|
+
this.boxes.copyWithin(slot * 6, last * 6, last * 6 + 6);
|
|
172
|
+
this.errors.copyWithin(
|
|
173
|
+
slot * this.maxLevels,
|
|
174
|
+
last * this.maxLevels,
|
|
175
|
+
(last + 1) * this.maxLevels,
|
|
176
|
+
);
|
|
177
|
+
this.levels[slot] = this.levels[last] as number;
|
|
178
|
+
this.level[slot] = this.level[last] as number;
|
|
179
|
+
this.from[slot] = this.from[last] as number;
|
|
180
|
+
this.fade[slot] = this.fade[last] as number;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Decide every region's level for this view and write what to draw into `out`, returning how
|
|
185
|
+
* many draws that is. `projectionScale` is `projectionScaleOf(fov, height)`; `occluder` may be
|
|
186
|
+
* the renderer, asked after this frame's occluders are declared.
|
|
187
|
+
*/
|
|
188
|
+
select(
|
|
189
|
+
eye: ArrayLike<number>,
|
|
190
|
+
projectionScale: number,
|
|
191
|
+
frustum: FrustumPlanes,
|
|
192
|
+
occluder: BoxOccluder | null,
|
|
193
|
+
dtSec: number,
|
|
194
|
+
out: HlodDraws,
|
|
195
|
+
): number {
|
|
196
|
+
const ex = eye[0] as number;
|
|
197
|
+
const ey = eye[1] as number;
|
|
198
|
+
const ez = eye[2] as number;
|
|
199
|
+
const perError = projectionScale / this.tolerance;
|
|
200
|
+
const step = this.fadeSec > 0 ? dtSec / this.fadeSec : 1;
|
|
201
|
+
let n = 0;
|
|
202
|
+
for (let slot = 0; slot < this.count; slot++) {
|
|
203
|
+
const b = slot * 6;
|
|
204
|
+
const dx = Math.max((this.boxes[b] as number) - ex, 0, ex - (this.boxes[b + 3] as number));
|
|
205
|
+
const dy = Math.max(
|
|
206
|
+
(this.boxes[b + 1] as number) - ey,
|
|
207
|
+
0,
|
|
208
|
+
ey - (this.boxes[b + 4] as number),
|
|
209
|
+
);
|
|
210
|
+
const dz = Math.max(
|
|
211
|
+
(this.boxes[b + 2] as number) - ez,
|
|
212
|
+
0,
|
|
213
|
+
ez - (this.boxes[b + 5] as number),
|
|
214
|
+
);
|
|
215
|
+
const d = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
216
|
+
const target = this.levelFor(slot, d, perError);
|
|
217
|
+
this.move(slot, target);
|
|
218
|
+
|
|
219
|
+
for (let i = 0; i < 6; i++) this.box[i] = this.boxes[b + i] as number;
|
|
220
|
+
if (!batchBoxVisible(this.box, frustum, occluder)) {
|
|
221
|
+
this.fade[slot] = 1;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
let t = this.fade[slot] as number;
|
|
225
|
+
if (t < 1) {
|
|
226
|
+
t = Math.min(1, t + step);
|
|
227
|
+
this.fade[slot] = t;
|
|
228
|
+
}
|
|
229
|
+
const level = this.level[slot] as number;
|
|
230
|
+
const id = this.ids[slot] as number;
|
|
231
|
+
if (t >= 1) {
|
|
232
|
+
if (level !== HLOD_NONE) n = this.emit(out, n, id, level, 0);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
/* Nothing of the arriving level at t = 0, and a dither of 0 would draw it whole. */
|
|
236
|
+
if (level !== HLOD_NONE && t > 0) n = this.emit(out, n, id, level, t);
|
|
237
|
+
const from = this.from[slot] as number;
|
|
238
|
+
if (from !== HLOD_NONE) n = this.emit(out, n, id, from, -t);
|
|
239
|
+
}
|
|
240
|
+
out.count = n;
|
|
241
|
+
return n;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** The level a region at distance `d` should be at, from where it is, through the bands. */
|
|
245
|
+
private levelFor(slot: number, d: number, perError: number): number {
|
|
246
|
+
const count = this.levels[slot] as number;
|
|
247
|
+
const current = this.level[slot] as number;
|
|
248
|
+
/* NONE counts as the level past the coarsest, whose distance is the far limit. */
|
|
249
|
+
let at = current === HLOD_NONE ? count : current;
|
|
250
|
+
while (at < count && d > this.switchAt(slot, at + 1, count, perError) * (1 + this.band)) at++;
|
|
251
|
+
while (at > 0 && d < this.switchAt(slot, at, count, perError) * (1 - this.band)) at--;
|
|
252
|
+
return at === count ? HLOD_NONE : at;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** The distance level `index` takes over at; the level past the coarsest is the far limit. */
|
|
256
|
+
private switchAt(slot: number, index: number, count: number, perError: number): number {
|
|
257
|
+
if (index >= count) return this.far;
|
|
258
|
+
return (this.errors[slot * this.maxLevels + index] as number) * perError;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Begin, reverse or redirect a crossfade toward `target`. */
|
|
262
|
+
private move(slot: number, target: number): void {
|
|
263
|
+
const level = this.level[slot] as number;
|
|
264
|
+
if (target === level) return;
|
|
265
|
+
const fade = this.fade[slot] as number;
|
|
266
|
+
if (fade >= 1) {
|
|
267
|
+
this.from[slot] = level;
|
|
268
|
+
this.fade[slot] = 0;
|
|
269
|
+
} else if (target === this.from[slot]) {
|
|
270
|
+
/* Back the way it came, at the share each level already covers rather than from the start.
|
|
271
|
+
The cells swap — the arriving level always takes the low ones — so a reversal changes the
|
|
272
|
+
grain for a frame and never the balance. */
|
|
273
|
+
this.from[slot] = level;
|
|
274
|
+
this.fade[slot] = 1 - fade;
|
|
275
|
+
} else {
|
|
276
|
+
/* A third level: leave from whichever of the two covers more of the region. */
|
|
277
|
+
this.from[slot] = fade < 0.5 ? (this.from[slot] as number) : level;
|
|
278
|
+
this.fade[slot] = 0;
|
|
279
|
+
}
|
|
280
|
+
this.level[slot] = target;
|
|
281
|
+
if (this.fadeSec <= 0) this.fade[slot] = 1;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private emit(out: HlodDraws, n: number, id: number, level: number, dither: number): number {
|
|
285
|
+
out.region[n] = id;
|
|
286
|
+
out.level[n] = level;
|
|
287
|
+
out.dither[n] = dither;
|
|
288
|
+
return n + 1;
|
|
289
|
+
}
|
|
290
|
+
}
|