@driftengine/core 4.5.0 → 4.6.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/CHANGELOG.json +62 -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 +249 -128
- package/dist/render/backend/webgpu/buffers.d.ts +17 -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/motionPass.js +2 -1
- 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 +478 -214
- 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 +23 -24
- package/dist/render/mesh.js +56 -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 +311 -150
- package/src/render/backend/webgpu/buffers.ts +49 -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/motionPass.ts +2 -1
- package/src/render/backend/webgpu/renderer.ts +556 -239
- 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 +64 -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,160 @@
|
|
|
1
|
+
import { boxInFrustum } from '../math/frustum.js';
|
|
2
|
+
/** How many planes a frustum holds, four floats each. */
|
|
3
|
+
const PLANES = 6;
|
|
4
|
+
/**
|
|
5
|
+
* Indices a culling batch must draw, all its instances together, before culling it instance by
|
|
6
|
+
* instance pays; below it the batch is drawn whole once its box is seen (`batchBoxVisible`).
|
|
7
|
+
*
|
|
8
|
+
* **What a per-instance cull costs is fixed a batch, and what it saves grows with the batch.** On
|
|
9
|
+
* WebGPU it is a dispatch and an indirect draw, and Chrome validates every indirect draw in its GPU
|
|
10
|
+
* process — measured at 8 µs apiece in a city frame whose six hundred small prop batches took one
|
|
11
|
+
* each, 52 fps against 70 with those draws gone. On WebGL2 it is a loop over the instances and an
|
|
12
|
+
* upload of the survivors. A few thousand indices cost a device less than either.
|
|
13
|
+
*
|
|
14
|
+
* What it gives up is the instances of a small batch that stand out of view once its box is in it,
|
|
15
|
+
* which the device transforms and clips for nothing. What would make it wrong is a device whose
|
|
16
|
+
* vertex stage limits the frame while the CPU idles — where this wants to come down.
|
|
17
|
+
*/
|
|
18
|
+
export const INSTANCE_CULL_MIN_INDICES = 1 << 14;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a culling batch of `count` instances of a mesh of `indexCount` indices is culled instance
|
|
21
|
+
* by instance, or drawn whole once its box is seen. See `INSTANCE_CULL_MIN_INDICES`.
|
|
22
|
+
*/
|
|
23
|
+
export function cullsInstances(count, indexCount) {
|
|
24
|
+
return count * indexCount >= INSTANCE_CULL_MIN_INDICES;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Keep the instances of `data` whose spheres meet `frustum`, compacted in order into `out`, and
|
|
28
|
+
* return how many. `out` must hold as many as `data` does; its count is set.
|
|
29
|
+
*/
|
|
30
|
+
export function cullInstances(data, local, frustum, out) {
|
|
31
|
+
const count = Math.min(data.count, data.capacity, out.capacity);
|
|
32
|
+
const models = data.models;
|
|
33
|
+
const tints = data.tints;
|
|
34
|
+
let kept = 0;
|
|
35
|
+
for (let i = 0; i < count; i++) {
|
|
36
|
+
sphereOf(models, i, local, scratch);
|
|
37
|
+
if (!sphereMeets(frustum, scratch))
|
|
38
|
+
continue;
|
|
39
|
+
/* Copied float by float: a subarray would be a view allocated per instance, per frame. */
|
|
40
|
+
const from = i * 16;
|
|
41
|
+
const to = kept * 16;
|
|
42
|
+
for (let k = 0; k < 16; k++)
|
|
43
|
+
out.models[to + k] = models[from + k];
|
|
44
|
+
out.tints[kept * 3] = tints[i * 3];
|
|
45
|
+
out.tints[kept * 3 + 1] = tints[i * 3 + 1];
|
|
46
|
+
out.tints[kept * 3 + 2] = tints[i * 3 + 2];
|
|
47
|
+
kept++;
|
|
48
|
+
}
|
|
49
|
+
out.count = kept;
|
|
50
|
+
return kept;
|
|
51
|
+
}
|
|
52
|
+
/** `sphereInFrustum` over a packed (x, y, z, radius): kept unless wholly beyond one plane. */
|
|
53
|
+
function sphereMeets(frustum, sphere) {
|
|
54
|
+
const x = sphere[0];
|
|
55
|
+
const y = sphere[1];
|
|
56
|
+
const z = sphere[2];
|
|
57
|
+
const radius = sphere[3];
|
|
58
|
+
for (let plane = 0; plane < PLANES; plane++) {
|
|
59
|
+
const at = plane * 4;
|
|
60
|
+
const distance = frustum[at] * x +
|
|
61
|
+
frustum[at + 1] * y +
|
|
62
|
+
frustum[at + 2] * z +
|
|
63
|
+
frustum[at + 3];
|
|
64
|
+
if (distance < -radius)
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The world box around every instance's sphere, for testing a whole batch at once: (minX, minY,
|
|
71
|
+
* minZ, maxX, maxY, maxZ) into `out`. A batch holding nothing writes a min above its max.
|
|
72
|
+
*
|
|
73
|
+
* **A box, not a sphere, and the difference is the whole point for a flat batch.** A region of props
|
|
74
|
+
* a hundred metres square has a sphere seventy metres deep underground, where nothing occludes it,
|
|
75
|
+
* so a sphere test never finds a region hidden. The box stays as flat as the props are.
|
|
76
|
+
*/
|
|
77
|
+
export function instancesBox(data, local, out) {
|
|
78
|
+
const count = Math.min(data.count, data.capacity);
|
|
79
|
+
if (count === 0) {
|
|
80
|
+
out[0] = 1;
|
|
81
|
+
out[1] = 1;
|
|
82
|
+
out[2] = 1;
|
|
83
|
+
out[3] = -1;
|
|
84
|
+
out[4] = -1;
|
|
85
|
+
out[5] = -1;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
let minX = Infinity;
|
|
89
|
+
let minY = Infinity;
|
|
90
|
+
let minZ = Infinity;
|
|
91
|
+
let maxX = -Infinity;
|
|
92
|
+
let maxY = -Infinity;
|
|
93
|
+
let maxZ = -Infinity;
|
|
94
|
+
for (let i = 0; i < count; i++) {
|
|
95
|
+
sphereOf(data.models, i, local, scratch);
|
|
96
|
+
const r = scratch[3];
|
|
97
|
+
minX = Math.min(minX, scratch[0] - r);
|
|
98
|
+
minY = Math.min(minY, scratch[1] - r);
|
|
99
|
+
minZ = Math.min(minZ, scratch[2] - r);
|
|
100
|
+
maxX = Math.max(maxX, scratch[0] + r);
|
|
101
|
+
maxY = Math.max(maxY, scratch[1] + r);
|
|
102
|
+
maxZ = Math.max(maxZ, scratch[2] + r);
|
|
103
|
+
}
|
|
104
|
+
out[0] = minX;
|
|
105
|
+
out[1] = minY;
|
|
106
|
+
out[2] = minZ;
|
|
107
|
+
out[3] = maxX;
|
|
108
|
+
out[4] = maxY;
|
|
109
|
+
out[5] = maxZ;
|
|
110
|
+
}
|
|
111
|
+
const scratch = new Float32Array(4);
|
|
112
|
+
/** Instance `i`'s world sphere into `out` as (x, y, z, radius). */
|
|
113
|
+
function sphereOf(models, i, local, out) {
|
|
114
|
+
const m = i * 16;
|
|
115
|
+
const cx = local.centre[0] ?? 0;
|
|
116
|
+
const cy = local.centre[1] ?? 0;
|
|
117
|
+
const cz = local.centre[2] ?? 0;
|
|
118
|
+
const m0 = models[m];
|
|
119
|
+
const m1 = models[m + 1];
|
|
120
|
+
const m2 = models[m + 2];
|
|
121
|
+
const m4 = models[m + 4];
|
|
122
|
+
const m5 = models[m + 5];
|
|
123
|
+
const m6 = models[m + 6];
|
|
124
|
+
const m8 = models[m + 8];
|
|
125
|
+
const m9 = models[m + 9];
|
|
126
|
+
const m10 = models[m + 10];
|
|
127
|
+
out[0] = m0 * cx + m4 * cy + m8 * cz + models[m + 12];
|
|
128
|
+
out[1] = m1 * cx + m5 * cy + m9 * cz + models[m + 13];
|
|
129
|
+
out[2] = m2 * cx + m6 * cy + m10 * cz + models[m + 14];
|
|
130
|
+
out[3] =
|
|
131
|
+
local.radius *
|
|
132
|
+
Math.sqrt(Math.max(m0 * m0 + m1 * m1 + m2 * m2, m4 * m4 + m5 * m5 + m6 * m6, m8 * m8 + m9 * m9 + m10 * m10));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Whether a batch's box (`instancesBox`) can be seen at all: inside the frustum, and not behind the
|
|
136
|
+
* declared occluders. One test for a whole region's props, run before any instance is looked at.
|
|
137
|
+
*/
|
|
138
|
+
export function batchBoxVisible(box, frustum, occluder) {
|
|
139
|
+
const minX = box[0];
|
|
140
|
+
const minY = box[1];
|
|
141
|
+
const minZ = box[2];
|
|
142
|
+
const maxX = box[3];
|
|
143
|
+
const maxY = box[4];
|
|
144
|
+
const maxZ = box[5];
|
|
145
|
+
if (minX > maxX)
|
|
146
|
+
return false;
|
|
147
|
+
if (!boxInFrustum(frustum, minX, minY, minZ, maxX, maxY, maxZ))
|
|
148
|
+
return false;
|
|
149
|
+
if (occluder === null)
|
|
150
|
+
return true;
|
|
151
|
+
boxMin[0] = minX;
|
|
152
|
+
boxMin[1] = minY;
|
|
153
|
+
boxMin[2] = minZ;
|
|
154
|
+
boxMax[0] = maxX;
|
|
155
|
+
boxMax[1] = maxY;
|
|
156
|
+
boxMax[2] = maxZ;
|
|
157
|
+
return !occluder.occludedBox(boxMin, boxMax);
|
|
158
|
+
}
|
|
159
|
+
const boxMin = new Float32Array(3);
|
|
160
|
+
const boxMax = new Float32Array(3);
|
|
@@ -67,3 +67,18 @@ export declare const INSTANCE_STRIDE: number;
|
|
|
67
67
|
* Writes nothing beyond `count`, and allocates nothing: `out` is the caller's staging array.
|
|
68
68
|
*/
|
|
69
69
|
export declare function packInstances(instances: MeshInstances, out: Float32Array): void;
|
|
70
|
+
/** How a batch made by `createInstanced` behaves. */
|
|
71
|
+
export interface InstancedOptions {
|
|
72
|
+
/**
|
|
73
|
+
* Cull the camera draw: the whole batch skipped when its instances' sphere is out of view or behind
|
|
74
|
+
* the declared occluders, and otherwise each instance kept only where its sphere meets the view —
|
|
75
|
+
* on the CPU on WebGL2, as compute into an indirect draw on WebGPU — once the batch is large enough
|
|
76
|
+
* to repay that (`cullsInstances`); a smaller one is drawn whole. Shadow, mirror-free motion and
|
|
77
|
+
* blended draws still see every instance. Off by default, because a batch small enough to draw whole
|
|
78
|
+
* pays for nothing it would save.
|
|
79
|
+
*
|
|
80
|
+
* **For a batch spread over space** — a prop across a region, a crowd, traffic. A batch in one
|
|
81
|
+
* place gains only the whole-batch test, which it could as well do itself.
|
|
82
|
+
*/
|
|
83
|
+
readonly cull?: boolean;
|
|
84
|
+
}
|
|
@@ -131,22 +131,20 @@ export declare const ENVIRONMENT_TEXTURE_UNIT: number;
|
|
|
131
131
|
*/
|
|
132
132
|
export declare const CLUSTER_TABLE_TEXTURE_UNIT: number;
|
|
133
133
|
/**
|
|
134
|
-
* Where the
|
|
135
|
-
*
|
|
136
|
-
* **
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* declared in every profile and a declared sampler needs
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* The tenth of sixteen. `CAPABILITIES.md` §3 keeps the running count.
|
|
134
|
+
* Where the fixture atlas binds: a fixture's spot cookies and its photometric profiles, one texture.
|
|
135
|
+
*
|
|
136
|
+
* **Two tables in one binding since 2026-09-30**, which is the fold `textureUnitBudget.test.ts` asks
|
|
137
|
+
* the next sampler for: the widest lit shader bound fifteen of the sixteen fragment samplers WebGL2
|
|
138
|
+
* guarantees, and the cookie atlas and the photometric atlas were two small float tables read with
|
|
139
|
+
* `textureLod`, both 128 texels across. See `fixtureAtlas.ts` for the layout and why cookies sit on
|
|
140
|
+
* top.
|
|
141
|
+
*
|
|
142
|
+
* **Spent unconditionally, for the reason the froxel table is**: both lookups are branches on values
|
|
143
|
+
* rather than permutations, so the sampler is declared in every profile and a declared sampler needs
|
|
144
|
+
* a complete texture. With nothing loaded that is a single row of ones — the multiplicative
|
|
145
|
+
* identity, since a row of zeros would switch off every light that reached it.
|
|
148
146
|
*/
|
|
149
|
-
export declare const
|
|
147
|
+
export declare const FIXTURE_ATLAS_TEXTURE_UNIT: number;
|
|
150
148
|
/**
|
|
151
149
|
* The joint palette, read by the **vertex** stage.
|
|
152
150
|
*
|
|
@@ -173,21 +171,6 @@ export declare const SKIN_PALETTE_TEXTURE_UNIT: number;
|
|
|
173
171
|
* of sixteen spoken for, three free.
|
|
174
172
|
*/
|
|
175
173
|
export declare const MORPH_DELTA_TEXTURE_UNIT: number;
|
|
176
|
-
/**
|
|
177
|
-
* Where the spot-light cookie atlas binds: one row of square tiles, one tile a cookie.
|
|
178
|
-
*
|
|
179
|
-
* **The fourteenth of sixteen, and two are left.** `CAPABILITIES.md` §3 keeps the running count —
|
|
180
|
-
* and had been three units stale for as long as the IES atlas, the skin palette and the morph
|
|
181
|
-
* deltas have existed, which is why the number is derived in `textureUnitBudget.test.ts` and only
|
|
182
|
-
* quoted in prose.
|
|
183
|
-
*
|
|
184
|
-
* **Spent unconditionally, for the reason the froxel table and the IES atlas are**: a declared
|
|
185
|
-
* sampler needs a complete texture bound whether or not the branch reads it, so a one-texel white
|
|
186
|
-
* placeholder stands in it when no consumer has loaded a cookie. White rather than black, because
|
|
187
|
-
* it multiplies a light's colour and a black stand-in would switch off every light that reached
|
|
188
|
-
* it — the identity `packIesAtlas` chooses for the same reason.
|
|
189
|
-
*/
|
|
190
|
-
export declare const COOKIE_ATLAS_TEXTURE_UNIT: number;
|
|
191
174
|
/**
|
|
192
175
|
* The colour the frame had already drawn, for a refracting surface to sample.
|
|
193
176
|
*
|
|
@@ -219,11 +202,17 @@ export declare const DRIFT_LIGHT_ATLAS_TEXTURE_UNIT: number;
|
|
|
219
202
|
*/
|
|
220
203
|
export declare const SUN_GLASS_TINT_TEXTURE_UNIT: number;
|
|
221
204
|
/**
|
|
222
|
-
* What each lamp keeps through glass (`pointGlassTint.ts`), the lit program
|
|
223
|
-
*
|
|
224
|
-
*
|
|
205
|
+
* What each lamp keeps through glass (`pointGlassTint.ts`), a fragment sampler of the lit program.
|
|
206
|
+
* A unit of the combined pool, which WebGL2 guarantees thirty-two of; what a stage may declare is
|
|
207
|
+
* sixteen, which `textureUnitBudget.test.ts` counts from the shader source.
|
|
225
208
|
*/
|
|
226
209
|
export declare const POINT_GLASS_TINT_TEXTURE_UNIT: number;
|
|
210
|
+
/**
|
|
211
|
+
* Each surface layer's effects table (`surfaceEffects.ts`), bound beside the albedo array it
|
|
212
|
+
* describes. **Paid for by folding**: the cookie and photometric atlases became one fixture atlas
|
|
213
|
+
* to free the sampler this takes, so the lit stage still keeps one of its sixteen spare.
|
|
214
|
+
*/
|
|
215
|
+
export declare const SURFACE_EFFECTS_TEXTURE_UNIT: number;
|
|
227
216
|
/**
|
|
228
217
|
* A cookie's tile, in texels a side.
|
|
229
218
|
*
|
|
@@ -129,22 +129,20 @@ export const ENVIRONMENT_TEXTURE_UNIT = EMISSIVE_TEXTURE_UNIT + 1;
|
|
|
129
129
|
*/
|
|
130
130
|
export const CLUSTER_TABLE_TEXTURE_UNIT = ENVIRONMENT_TEXTURE_UNIT + 1;
|
|
131
131
|
/**
|
|
132
|
-
* Where the
|
|
133
|
-
*
|
|
134
|
-
* **
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* declared in every profile and a declared sampler needs
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* The tenth of sixteen. `CAPABILITIES.md` §3 keeps the running count.
|
|
132
|
+
* Where the fixture atlas binds: a fixture's spot cookies and its photometric profiles, one texture.
|
|
133
|
+
*
|
|
134
|
+
* **Two tables in one binding since 2026-09-30**, which is the fold `textureUnitBudget.test.ts` asks
|
|
135
|
+
* the next sampler for: the widest lit shader bound fifteen of the sixteen fragment samplers WebGL2
|
|
136
|
+
* guarantees, and the cookie atlas and the photometric atlas were two small float tables read with
|
|
137
|
+
* `textureLod`, both 128 texels across. See `fixtureAtlas.ts` for the layout and why cookies sit on
|
|
138
|
+
* top.
|
|
139
|
+
*
|
|
140
|
+
* **Spent unconditionally, for the reason the froxel table is**: both lookups are branches on values
|
|
141
|
+
* rather than permutations, so the sampler is declared in every profile and a declared sampler needs
|
|
142
|
+
* a complete texture. With nothing loaded that is a single row of ones — the multiplicative
|
|
143
|
+
* identity, since a row of zeros would switch off every light that reached it.
|
|
146
144
|
*/
|
|
147
|
-
export const
|
|
145
|
+
export const FIXTURE_ATLAS_TEXTURE_UNIT = CLUSTER_TABLE_TEXTURE_UNIT + 1;
|
|
148
146
|
/**
|
|
149
147
|
* The joint palette, read by the **vertex** stage.
|
|
150
148
|
*
|
|
@@ -162,7 +160,7 @@ export const IES_ATLAS_TEXTURE_UNIT = CLUSTER_TABLE_TEXTURE_UNIT + 1;
|
|
|
162
160
|
* What it costs is one of the five units left after the IES atlas. What would make it wrong is
|
|
163
161
|
* the lit pass growing past sixteen, which `textureUnitBudget.test.ts` asserts against.
|
|
164
162
|
*/
|
|
165
|
-
export const SKIN_PALETTE_TEXTURE_UNIT =
|
|
163
|
+
export const SKIN_PALETTE_TEXTURE_UNIT = FIXTURE_ATLAS_TEXTURE_UNIT + 1;
|
|
166
164
|
/**
|
|
167
165
|
* A mesh's morph deltas, also read by the vertex stage, and also out of this same sixteen.
|
|
168
166
|
*
|
|
@@ -171,21 +169,6 @@ export const SKIN_PALETTE_TEXTURE_UNIT = IES_ATLAS_TEXTURE_UNIT + 1;
|
|
|
171
169
|
* of sixteen spoken for, three free.
|
|
172
170
|
*/
|
|
173
171
|
export const MORPH_DELTA_TEXTURE_UNIT = SKIN_PALETTE_TEXTURE_UNIT + 1;
|
|
174
|
-
/**
|
|
175
|
-
* Where the spot-light cookie atlas binds: one row of square tiles, one tile a cookie.
|
|
176
|
-
*
|
|
177
|
-
* **The fourteenth of sixteen, and two are left.** `CAPABILITIES.md` §3 keeps the running count —
|
|
178
|
-
* and had been three units stale for as long as the IES atlas, the skin palette and the morph
|
|
179
|
-
* deltas have existed, which is why the number is derived in `textureUnitBudget.test.ts` and only
|
|
180
|
-
* quoted in prose.
|
|
181
|
-
*
|
|
182
|
-
* **Spent unconditionally, for the reason the froxel table and the IES atlas are**: a declared
|
|
183
|
-
* sampler needs a complete texture bound whether or not the branch reads it, so a one-texel white
|
|
184
|
-
* placeholder stands in it when no consumer has loaded a cookie. White rather than black, because
|
|
185
|
-
* it multiplies a light's colour and a black stand-in would switch off every light that reached
|
|
186
|
-
* it — the identity `packIesAtlas` chooses for the same reason.
|
|
187
|
-
*/
|
|
188
|
-
export const COOKIE_ATLAS_TEXTURE_UNIT = MORPH_DELTA_TEXTURE_UNIT + 1;
|
|
189
172
|
/**
|
|
190
173
|
* The colour the frame had already drawn, for a refracting surface to sample.
|
|
191
174
|
*
|
|
@@ -199,7 +182,7 @@ export const COOKIE_ATLAS_TEXTURE_UNIT = MORPH_DELTA_TEXTURE_UNIT + 1;
|
|
|
199
182
|
* cubemaps became one array, which is what freed the units this one is spending.
|
|
200
183
|
* `textureUnitBudget.test.ts` carries the phone that budget was written for.
|
|
201
184
|
*/
|
|
202
|
-
export const REFRACT_SCENE_TEXTURE_UNIT =
|
|
185
|
+
export const REFRACT_SCENE_TEXTURE_UNIT = MORPH_DELTA_TEXTURE_UNIT + 1;
|
|
203
186
|
/**
|
|
204
187
|
* DriftLight's brick index and atlas, the lit program's fourteenth and fifteenth fragment samplers.
|
|
205
188
|
*
|
|
@@ -217,11 +200,17 @@ export const DRIFT_LIGHT_ATLAS_TEXTURE_UNIT = DRIFT_LIGHT_INDEX_TEXTURE_UNIT + 1
|
|
|
217
200
|
*/
|
|
218
201
|
export const SUN_GLASS_TINT_TEXTURE_UNIT = DRIFT_LIGHT_ATLAS_TEXTURE_UNIT + 1;
|
|
219
202
|
/**
|
|
220
|
-
* What each lamp keeps through glass (`pointGlassTint.ts`), the lit program
|
|
221
|
-
*
|
|
222
|
-
*
|
|
203
|
+
* What each lamp keeps through glass (`pointGlassTint.ts`), a fragment sampler of the lit program.
|
|
204
|
+
* A unit of the combined pool, which WebGL2 guarantees thirty-two of; what a stage may declare is
|
|
205
|
+
* sixteen, which `textureUnitBudget.test.ts` counts from the shader source.
|
|
223
206
|
*/
|
|
224
207
|
export const POINT_GLASS_TINT_TEXTURE_UNIT = SUN_GLASS_TINT_TEXTURE_UNIT + 1;
|
|
208
|
+
/**
|
|
209
|
+
* Each surface layer's effects table (`surfaceEffects.ts`), bound beside the albedo array it
|
|
210
|
+
* describes. **Paid for by folding**: the cookie and photometric atlases became one fixture atlas
|
|
211
|
+
* to free the sampler this takes, so the lit stage still keeps one of its sixteen spare.
|
|
212
|
+
*/
|
|
213
|
+
export const SURFACE_EFFECTS_TEXTURE_UNIT = POINT_GLASS_TINT_TEXTURE_UNIT + 1;
|
|
225
214
|
/**
|
|
226
215
|
* A cookie's tile, in texels a side.
|
|
227
216
|
*
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A world's fixed lights bucketed once, so the frame's exact choice looks only at the lights near
|
|
3
|
+
* the eye rather than at every one of them.
|
|
4
|
+
*
|
|
5
|
+
* **The choice must be the full scan's, exactly, and needs no sort to be.** Both of the choice's
|
|
6
|
+
* lists break a tie in distance by source index, so the order the lights are met in cannot reach the
|
|
7
|
+
* result: what reaches it is the full scan's candidates minus only lights no query could keep —
|
|
8
|
+
* every light whose cell lies within `viewRange + maxRadius` of the eye is gathered, and the
|
|
9
|
+
* selection's own `viewRange + radius` test does the rest.
|
|
10
|
+
*
|
|
11
|
+
* **Cost stated**: built once, from where the lights stood. A light that moves or changes its reach
|
|
12
|
+
* must be re-bucketed by the caller — this is for the city's fixed thousands, and a scene's handful
|
|
13
|
+
* of movers stays with the plain scan.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here allocates after construction; the gather writes into the grid's own scratch.
|
|
16
|
+
*/
|
|
17
|
+
import type { PointLightSource } from './pointLightSelection.ts';
|
|
18
|
+
/** The most cells a grid may span, which keeps a stray light far away from costing a gigabyte. */
|
|
19
|
+
export declare const LIGHT_GRID_MAX_CELLS: number;
|
|
20
|
+
export interface LightGrid {
|
|
21
|
+
readonly sources: readonly PointLightSource[];
|
|
22
|
+
/** Metres a cell spans on each axis. */
|
|
23
|
+
readonly cell: number;
|
|
24
|
+
/** The world position of cell (0, 0, 0)'s corner. */
|
|
25
|
+
readonly origin: readonly [number, number, number];
|
|
26
|
+
readonly dims: readonly [number, number, number];
|
|
27
|
+
/** Where cell `c`'s lights start in `lights`; `start[c + 1]` is where they end. */
|
|
28
|
+
readonly start: Uint32Array;
|
|
29
|
+
/** Source indices by cell, ascending within each. */
|
|
30
|
+
readonly lights: Uint32Array;
|
|
31
|
+
/** The largest reach of any source, which widens every query. */
|
|
32
|
+
readonly maxRadius: number;
|
|
33
|
+
/** The last gather, cell by cell: `gathered[0..count)`. */
|
|
34
|
+
readonly gathered: Int32Array;
|
|
35
|
+
}
|
|
36
|
+
/** Bucket `sources` into cells of `cell` metres. Built once; not a frame path. */
|
|
37
|
+
export declare function createLightGrid(sources: readonly PointLightSource[], cell: number): LightGrid;
|
|
38
|
+
/**
|
|
39
|
+
* Gather into `grid.gathered`, cell by cell, every light whose cell lies within `reach` metres of
|
|
40
|
+
* (x, y, z) on each axis, and answer how many.
|
|
41
|
+
*/
|
|
42
|
+
export declare function gatherLights(grid: LightGrid, x: number, y: number, z: number, reach: number): number;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** The most cells a grid may span, which keeps a stray light far away from costing a gigabyte. */
|
|
2
|
+
export const LIGHT_GRID_MAX_CELLS = 1 << 22;
|
|
3
|
+
/** Bucket `sources` into cells of `cell` metres. Built once; not a frame path. */
|
|
4
|
+
export function createLightGrid(sources, cell) {
|
|
5
|
+
if (!(cell > 0))
|
|
6
|
+
throw new Error(`createLightGrid: a cell of ${String(cell)} metres`);
|
|
7
|
+
let minX = Infinity;
|
|
8
|
+
let minY = Infinity;
|
|
9
|
+
let minZ = Infinity;
|
|
10
|
+
let maxX = -Infinity;
|
|
11
|
+
let maxY = -Infinity;
|
|
12
|
+
let maxZ = -Infinity;
|
|
13
|
+
let maxRadius = 0;
|
|
14
|
+
for (const light of sources) {
|
|
15
|
+
minX = Math.min(minX, light.x);
|
|
16
|
+
minY = Math.min(minY, light.y);
|
|
17
|
+
minZ = Math.min(minZ, light.z);
|
|
18
|
+
maxX = Math.max(maxX, light.x);
|
|
19
|
+
maxY = Math.max(maxY, light.y);
|
|
20
|
+
maxZ = Math.max(maxZ, light.z);
|
|
21
|
+
maxRadius = Math.max(maxRadius, light.radius);
|
|
22
|
+
}
|
|
23
|
+
if (sources.length === 0)
|
|
24
|
+
minX = minY = minZ = maxX = maxY = maxZ = 0;
|
|
25
|
+
const dims = [
|
|
26
|
+
Math.floor((maxX - minX) / cell) + 1,
|
|
27
|
+
Math.floor((maxY - minY) / cell) + 1,
|
|
28
|
+
Math.floor((maxZ - minZ) / cell) + 1,
|
|
29
|
+
];
|
|
30
|
+
const cells = dims[0] * dims[1] * dims[2];
|
|
31
|
+
if (cells > LIGHT_GRID_MAX_CELLS) {
|
|
32
|
+
throw new Error(`createLightGrid: these lights span ${dims.join(' × ')} cells of ${cell} m, past ` +
|
|
33
|
+
`${LIGHT_GRID_MAX_CELLS}; a larger cell fits them.`);
|
|
34
|
+
}
|
|
35
|
+
const cellOf = (light) => Math.floor((light.x - minX) / cell) +
|
|
36
|
+
dims[0] * (Math.floor((light.y - minY) / cell) + dims[1] * Math.floor((light.z - minZ) / cell));
|
|
37
|
+
const start = new Uint32Array(cells + 1);
|
|
38
|
+
for (const light of sources) {
|
|
39
|
+
const c = cellOf(light);
|
|
40
|
+
start[c + 1] = start[c + 1] + 1;
|
|
41
|
+
}
|
|
42
|
+
for (let c = 0; c < cells; c++)
|
|
43
|
+
start[c + 1] = start[c + 1] + start[c];
|
|
44
|
+
const lights = new Uint32Array(sources.length);
|
|
45
|
+
const cursor = start.slice(0, cells);
|
|
46
|
+
for (let index = 0; index < sources.length; index++) {
|
|
47
|
+
const c = cellOf(sources[index]);
|
|
48
|
+
lights[cursor[c]] = index;
|
|
49
|
+
cursor[c] = cursor[c] + 1;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
sources,
|
|
53
|
+
cell,
|
|
54
|
+
origin: [minX, minY, minZ],
|
|
55
|
+
dims,
|
|
56
|
+
start,
|
|
57
|
+
lights,
|
|
58
|
+
maxRadius,
|
|
59
|
+
gathered: new Int32Array(sources.length),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Gather into `grid.gathered`, cell by cell, every light whose cell lies within `reach` metres of
|
|
64
|
+
* (x, y, z) on each axis, and answer how many.
|
|
65
|
+
*/
|
|
66
|
+
export function gatherLights(grid, x, y, z, reach) {
|
|
67
|
+
const { cell, origin, dims, start, lights, gathered } = grid;
|
|
68
|
+
const x0 = Math.max(0, Math.floor((x - reach - origin[0]) / cell));
|
|
69
|
+
const x1 = Math.min(dims[0] - 1, Math.floor((x + reach - origin[0]) / cell));
|
|
70
|
+
const y0 = Math.max(0, Math.floor((y - reach - origin[1]) / cell));
|
|
71
|
+
const y1 = Math.min(dims[1] - 1, Math.floor((y + reach - origin[1]) / cell));
|
|
72
|
+
const z0 = Math.max(0, Math.floor((z - reach - origin[2]) / cell));
|
|
73
|
+
const z1 = Math.min(dims[2] - 1, Math.floor((z + reach - origin[2]) / cell));
|
|
74
|
+
let count = 0;
|
|
75
|
+
for (let cz = z0; cz <= z1; cz++) {
|
|
76
|
+
for (let cy = y0; cy <= y1; cy++) {
|
|
77
|
+
const row = dims[0] * (cy + dims[1] * cz);
|
|
78
|
+
for (let cx = x0; cx <= x1; cx++) {
|
|
79
|
+
const c = row + cx;
|
|
80
|
+
for (let at = start[c]; at < start[c + 1]; at++) {
|
|
81
|
+
gathered[count++] = lights[at];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return count;
|
|
87
|
+
}
|
package/dist/render/mesh.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ import type { MeshData } from '@driftengine/drft';
|
|
|
7
7
|
export declare class Mesh {
|
|
8
8
|
private readonly vao;
|
|
9
9
|
private readonly buffers;
|
|
10
|
+
/** Every attribute bound from a buffer, so a batch can build a vertex array of its own. */
|
|
11
|
+
private readonly layout;
|
|
12
|
+
private indexBuffer;
|
|
10
13
|
/**
|
|
11
14
|
* The position and normal buffers, kept so `update` can rewrite them.
|
|
12
15
|
*
|
|
@@ -16,7 +19,13 @@ export declare class Mesh {
|
|
|
16
19
|
private readonly deformable;
|
|
17
20
|
/** How many floats the position buffer holds, so an update of the wrong size is refused. */
|
|
18
21
|
private readonly positionFloats;
|
|
19
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Indices one draw of it issues; the batch-size rule reads it (`cullsInstances`). **At zero the
|
|
24
|
+
* mesh has nothing to draw and no verb draws it**, as on WebGPU (`GpuMesh.indexCount`): a draw of
|
|
25
|
+
* nothing is silent here but is still a call and a counted draw, and the two backends count the
|
|
26
|
+
* same frame.
|
|
27
|
+
*/
|
|
28
|
+
readonly indexCount: number;
|
|
20
29
|
/**
|
|
21
30
|
* The constant values this mesh's *absent* attributes are shaded with, re-applied on
|
|
22
31
|
* every draw.
|
|
@@ -94,30 +103,18 @@ export declare class Mesh {
|
|
|
94
103
|
/** Caller is responsible for having the flat program + uniforms bound. */
|
|
95
104
|
draw(gl: WebGL2RenderingContext): void;
|
|
96
105
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* batch
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* which wants a vertex array per batch rather than a flag here.
|
|
106
|
-
*/
|
|
107
|
-
attachInstances(gl: WebGL2RenderingContext, buffer: WebGLBuffer, stride: number): void;
|
|
108
|
-
/** The instance columns, bound. Called at attach and again after a plain draw disabled them. */
|
|
109
|
-
private bindInstanceColumns;
|
|
110
|
-
private instanceBuffer;
|
|
111
|
-
private instanceStride;
|
|
112
|
-
/** Whether `attachInstances` has already claimed locations 11 to 15 on this mesh's array. */
|
|
113
|
-
private instanced;
|
|
114
|
-
/**
|
|
115
|
-
* Draw `count` instances through the attributes `attachInstances` bound.
|
|
116
|
-
*
|
|
117
|
-
* The constants are re-applied exactly as `draw` does — a vertex array restores the arrays and
|
|
118
|
-
* not the constant attributes, and an absent optional attribute is a constant here.
|
|
106
|
+
* A vertex array for one instanced batch: every attribute this mesh binds from a buffer, its index
|
|
107
|
+
* buffer, and the batch's instance columns — the model matrix at 11 to 14, the tint at 15.
|
|
108
|
+
*
|
|
109
|
+
* **A batch owns its own array**, which is what lets a mesh have more than one batch: a prop drawn
|
|
110
|
+
* once per region is one mesh and a batch per region. With one array a mesh had one batch, since a
|
|
111
|
+
* second would rebind the columns the first draws through and place one batch's instances by the
|
|
112
|
+
* other's matrices. The arrays share every buffer, so a batch costs its instances and nothing of
|
|
113
|
+
* the geometry. The caller deletes what this returns.
|
|
119
114
|
*/
|
|
120
|
-
|
|
115
|
+
createInstanceArray(gl: WebGL2RenderingContext, instances: WebGLBuffer, stride: number): WebGLVertexArrayObject;
|
|
116
|
+
/** Draw `count` instances through a batch's own vertex array. See `createInstanceArray`. */
|
|
117
|
+
drawInstancesThrough(gl: WebGL2RenderingContext, vao: WebGLVertexArrayObject, count: number): void;
|
|
121
118
|
dispose(gl: WebGL2RenderingContext): void;
|
|
122
119
|
/**
|
|
123
120
|
* Rewrite this mesh's positions, and its normals where the caller has them.
|
|
@@ -179,3 +176,5 @@ export interface IncrementalMesh {
|
|
|
179
176
|
* them before a byte of it has landed.
|
|
180
177
|
*/
|
|
181
178
|
export declare function createMeshIncremental(gl: WebGL2RenderingContext, data: MeshData, dynamic?: boolean): IncrementalMesh;
|
|
179
|
+
/** Texture coordinates and layers interleaved as (u, v, layer), one row a vertex. */
|
|
180
|
+
export declare function packUvLayers(uvs: Float32Array, layers: Float32Array): Float32Array;
|