@driftengine/core 4.8.5 → 4.8.6
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 +4321 -4250
- package/dist/index.d.ts +10 -2
- package/dist/index.js +11 -1
- package/dist/render/ambientHarmonics.d.ts +46 -0
- package/dist/render/ambientHarmonics.js +80 -0
- package/dist/render/areaLights.d.ts +19 -0
- package/dist/render/areaLights.js +20 -0
- package/dist/render/backend/api.d.ts +13 -2
- package/dist/render/backend/webgl2/instanced.d.ts +2 -0
- package/dist/render/backend/webgl2/instanced.js +11 -2
- package/dist/render/backend/webgl2/renderer.d.ts +74 -1
- package/dist/render/backend/webgl2/renderer.js +229 -37
- package/dist/render/backend/webgl2/skinScatter.d.ts +14 -0
- package/dist/render/backend/webgl2/skinScatter.js +88 -26
- package/dist/render/backend/webgpu/buffers.js +2 -1
- package/dist/render/backend/webgpu/clusterBinner.js +3 -2
- package/dist/render/backend/webgpu/flatPass.d.ts +7 -5
- package/dist/render/backend/webgpu/flatPass.js +15 -13
- package/dist/render/backend/webgpu/particlePass.d.ts +37 -4
- package/dist/render/backend/webgpu/particlePass.js +80 -12
- package/dist/render/backend/webgpu/postPass.d.ts +6 -1
- package/dist/render/backend/webgpu/postPass.js +7 -2
- package/dist/render/backend/webgpu/renderer.d.ts +58 -7
- package/dist/render/backend/webgpu/renderer.js +329 -78
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.js +41 -1
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +40 -28
- package/dist/render/backend/webgpu/skinPaletteRing.js +69 -32
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +10 -2
- package/dist/render/backend/webgpu/skinScatterPass.js +59 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +2 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +18 -4
- package/dist/render/bloomChain.d.ts +44 -0
- package/dist/render/bloomChain.js +28 -0
- package/dist/render/bloomPass.d.ts +3 -1
- package/dist/render/bloomPass.js +16 -2
- package/dist/render/clusteredLights.d.ts +55 -1
- package/dist/render/clusteredLights.js +182 -8
- package/dist/render/fixtureAtlas.js +1 -1
- package/dist/render/instanceCull.js +7 -0
- package/dist/render/instances.d.ts +32 -10
- package/dist/render/instances.js +17 -8
- package/dist/render/lightBudget.d.ts +26 -1
- package/dist/render/lightBudget.js +26 -3
- package/dist/render/lightmap.d.ts +102 -0
- package/dist/render/lightmap.js +118 -0
- package/dist/render/mesh.js +2 -1
- package/dist/render/particleBatch.d.ts +59 -3
- package/dist/render/particleBatch.js +53 -2
- package/dist/render/particleMaterial.d.ts +6 -1
- package/dist/render/particleMaterial.js +10 -2
- package/dist/render/particlePool.d.ts +11 -0
- package/dist/render/particlePool.js +2 -0
- package/dist/render/particleSort.d.ts +34 -0
- package/dist/render/particleSort.js +96 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +8 -1
- package/dist/render/shaders/bloom.d.ts +8 -4
- package/dist/render/shaders/bloom.js +20 -3
- package/dist/render/shaders/flat/areaLight.d.ts +10 -0
- package/dist/render/shaders/flat/areaLight.js +96 -0
- package/dist/render/shaders/flat/index.js +30 -4
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +107 -105
- package/dist/render/shaders/flat/models.d.ts +16 -4
- package/dist/render/shaders/flat/models.js +64 -4
- package/dist/render/shaders/flat/normalMap.d.ts +10 -0
- package/dist/render/shaders/flat/normalMap.js +25 -0
- package/dist/render/shaders/flat/preamble.js +74 -37
- package/dist/render/shaders/flat/skin.js +6 -5
- package/dist/render/shaders/generated/bloom.wgsl.d.ts +13 -3
- package/dist/render/shaders/generated/bloom.wgsl.js +13 -3
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1631 -2044
- package/dist/render/shaders/generated/flat.wgsl.js +1669 -2082
- package/dist/render/shaders/generated/particle.wgsl.d.ts +115 -1
- package/dist/render/shaders/generated/particle.wgsl.js +115 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/skinBlur.wgsl.js +12 -2
- package/dist/render/shaders/particle.d.ts +17 -1
- package/dist/render/shaders/particle.js +133 -1
- package/dist/render/shaders/skinBlur.js +25 -3
- package/dist/render/skinBlur.d.ts +8 -5
- package/dist/render/skinBlur.js +8 -5
- package/dist/render/surfaceModel.d.ts +21 -1
- package/dist/render/surfaceModel.js +22 -0
- package/dist/render/surfaceTexture.d.ts +14 -1
- package/dist/render/surfaceTexture.js +15 -2
- package/package.json +6 -6
- package/src/index.ts +20 -1
- package/src/render/ambientHarmonics.ts +92 -0
- package/src/render/areaLights.ts +39 -0
- package/src/render/backend/api.ts +14 -1
- package/src/render/backend/webgl2/instanced.ts +12 -2
- package/src/render/backend/webgl2/renderer.ts +292 -35
- package/src/render/backend/webgl2/skinScatter.ts +120 -43
- package/src/render/backend/webgpu/buffers.ts +2 -1
- package/src/render/backend/webgpu/clusterBinner.ts +3 -1
- package/src/render/backend/webgpu/flatPass.ts +18 -13
- package/src/render/backend/webgpu/particlePass.ts +118 -12
- package/src/render/backend/webgpu/postPass.ts +7 -2
- package/src/render/backend/webgpu/renderer.ts +396 -95
- package/src/render/backend/webgpu/shaders/clusterBin.wgsl.ts +41 -1
- package/src/render/backend/webgpu/skinPaletteRing.ts +65 -33
- package/src/render/backend/webgpu/skinScatterPass.ts +59 -9
- package/src/render/backend/webgpu/surfaceTexturePass.ts +26 -5
- package/src/render/bloomChain.ts +69 -0
- package/src/render/bloomPass.ts +26 -2
- package/src/render/clusteredLights.ts +221 -7
- package/src/render/fixtureAtlas.ts +1 -1
- package/src/render/instanceCull.ts +5 -0
- package/src/render/instances.ts +40 -11
- package/src/render/lightBudget.ts +48 -2
- package/src/render/lightmap.ts +202 -0
- package/src/render/mesh.ts +2 -1
- package/src/render/particleBatch.ts +95 -4
- package/src/render/particleMaterial.ts +11 -1
- package/src/render/particlePool.ts +13 -0
- package/src/render/particleSort.ts +133 -0
- package/src/render/sceneTarget.ts +18 -1
- package/src/render/shaders/bloom.ts +20 -3
- package/src/render/shaders/flat/areaLight.ts +96 -0
- package/src/render/shaders/flat/index.ts +30 -4
- package/src/render/shaders/flat/main.ts +107 -105
- package/src/render/shaders/flat/models.ts +73 -8
- package/src/render/shaders/flat/normalMap.ts +25 -0
- package/src/render/shaders/flat/preamble.ts +74 -37
- package/src/render/shaders/flat/skin.ts +6 -5
- package/src/render/shaders/generated/bloom.wgsl.ts +13 -3
- package/src/render/shaders/generated/flat.wgsl.ts +1669 -2082
- package/src/render/shaders/generated/particle.wgsl.ts +116 -1
- package/src/render/shaders/generated/skinBlur.wgsl.ts +12 -2
- package/src/render/shaders/particle.ts +134 -1
- package/src/render/shaders/skinBlur.ts +25 -3
- package/src/render/skinBlur.ts +8 -5
- package/src/render/surfaceModel.ts +36 -1
- package/src/render/surfaceTexture.ts +41 -3
|
@@ -2,6 +2,7 @@ import { layerSize, refuseArrayUpdate, sourceSize } from './textureSource.js';
|
|
|
2
2
|
import { compressedLayers, planBlocks, refuseBlockUpdate } from './compressedSource.js';
|
|
3
3
|
import { uploadCompressedArray } from './glCompressed.js';
|
|
4
4
|
import { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from './surfaceEffects.js';
|
|
5
|
+
import { isLightmapTexels } from './lightmap.js';
|
|
5
6
|
const DEFAULT_ANISOTROPY = 4;
|
|
6
7
|
/**
|
|
7
8
|
* An image uploaded once and bound per draw.
|
|
@@ -29,7 +30,9 @@ export class SurfaceTexture {
|
|
|
29
30
|
* where `compressed` — the formats this context samples — has theirs; see `compressedSource.ts`.
|
|
30
31
|
*/
|
|
31
32
|
constructor(gl, source, options = {}, compressed = []) {
|
|
32
|
-
|
|
33
|
+
/* A lightmap's page: half floats, uploaded as they are. See `lightmap.ts`. */
|
|
34
|
+
const baked = isLightmapTexels(source) ? source : null;
|
|
35
|
+
const listed = (baked !== null ? [] : Array.isArray(source) ? source : [source]);
|
|
33
36
|
const blocks = compressedLayers(listed);
|
|
34
37
|
this.srgb = (options.colorSpace ?? 'linear') === 'srgb';
|
|
35
38
|
/* Refused before anything is allocated, so a refusal leaves no texture behind. */
|
|
@@ -40,7 +43,17 @@ export class SurfaceTexture {
|
|
|
40
43
|
this.texture = texture;
|
|
41
44
|
const wrap = (options.wrap ?? 'repeat') === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
42
45
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
43
|
-
if (
|
|
46
|
+
if (baked !== null) {
|
|
47
|
+
/* No chain: a page is read at its own resolution, and a level of it would blend regions. */
|
|
48
|
+
this.layers = baked.layers;
|
|
49
|
+
this.blockFormat = null;
|
|
50
|
+
this.mipmapped = false;
|
|
51
|
+
/* A typed array refuses either flag set, so both are put down here rather than assumed. */
|
|
52
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
53
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
54
|
+
gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, gl.RGBA16F, baked.width, baked.height, baked.layers, 0, gl.RGBA, gl.HALF_FLOAT, baked.texels);
|
|
55
|
+
}
|
|
56
|
+
else if (blocks !== null && plan !== null) {
|
|
44
57
|
/* The stored chain, or level 0 alone where no chain was asked for. Nothing is generated:
|
|
45
58
|
`generateMipmap` cannot write a compressed format. */
|
|
46
59
|
this.layers = blocks.length;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/core",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.6",
|
|
4
4
|
"description": "The runtime of DriftEngine, a WebGPU game engine with a WebGL2 fallback: renderer, loop, input, cameras, scene graph and physics",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@driftengine/drft": "4.8.
|
|
33
|
-
"@driftengine/physics": "4.8.
|
|
32
|
+
"@driftengine/drft": "4.8.6",
|
|
33
|
+
"@driftengine/physics": "4.8.6",
|
|
34
34
|
"gl-matrix": "^3.4.3"
|
|
35
35
|
},
|
|
36
36
|
"author": "Drift Technologies",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"prepack": "node ../../scripts/build.mjs @driftengine/core"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@driftengine/nav": "4.8.
|
|
71
|
-
"@driftengine/network": "4.8.
|
|
72
|
-
"@driftengine/texture": "4.8.
|
|
70
|
+
"@driftengine/nav": "4.8.6",
|
|
71
|
+
"@driftengine/network": "4.8.6",
|
|
72
|
+
"@driftengine/texture": "4.8.6"
|
|
73
73
|
}
|
|
74
74
|
}
|
package/src/index.ts
CHANGED
|
@@ -454,6 +454,12 @@ export type { ReflectionOptions, ReflectiveSample } from './render/gi/reflection
|
|
|
454
454
|
*/
|
|
455
455
|
export { GRADE_PLACEHOLDER_SIZE, MAX_GRADE_SIZE, identityGradeLut } from './render/colourGrade.ts';
|
|
456
456
|
export type { ColourGradeLut } from './render/colourGrade.ts';
|
|
457
|
+
/**
|
|
458
|
+
* How bloom answers past its threshold — a ramp in place of the subtraction, and a tint for each
|
|
459
|
+
* of the pyramid's `BLOOM_LEVELS` octaves — the third argument of `setBloom`.
|
|
460
|
+
*/
|
|
461
|
+
export { BLOOM_LEVELS } from './render/bloomChain.ts';
|
|
462
|
+
export type { BloomResponse } from './render/bloomChain.ts';
|
|
457
463
|
export { probeDevice } from './render/backend/probe.ts';
|
|
458
464
|
export type { ProbeVerdict } from './render/backend/probe.ts';
|
|
459
465
|
/*
|
|
@@ -491,15 +497,28 @@ export type { ClothBindingData } from './render/clothBindingData.ts';
|
|
|
491
497
|
* A frozen descriptor made by a factory and set as `SurfaceMaterial.model`; each is a pipeline of
|
|
492
498
|
* its own, compiled the first time a draw asks.
|
|
493
499
|
*/
|
|
494
|
-
export {
|
|
500
|
+
export {
|
|
501
|
+
anisotropicModel,
|
|
502
|
+
eyeModel,
|
|
503
|
+
hairModel,
|
|
504
|
+
lightmapModel,
|
|
505
|
+
skinModel,
|
|
506
|
+
} from './render/surfaceModel.ts';
|
|
495
507
|
export type {
|
|
496
508
|
AnisotropicModel,
|
|
497
509
|
EyeModel,
|
|
498
510
|
HairModel,
|
|
511
|
+
LightmapModel,
|
|
499
512
|
SkinModel,
|
|
500
513
|
SurfaceModel,
|
|
501
514
|
SurfaceModelKind,
|
|
502
515
|
} from './render/surfaceModel.ts';
|
|
516
|
+
/*
|
|
517
|
+
* A baked lightmap: the page a consumer decoded, which `createLightmap` uploads as a lightmapped
|
|
518
|
+
* material's `modelMap`, and the region of it a material or an instance reads.
|
|
519
|
+
*/
|
|
520
|
+
export { WHOLE_PAGE } from './render/lightmap.ts';
|
|
521
|
+
export type { LightmapPage, LightmapRegion } from './render/lightmap.ts';
|
|
503
522
|
/*
|
|
504
523
|
* A skinned cloth solved where the renderer can: compute on WebGPU, the CPU under WebGL2. A function
|
|
505
524
|
* rather than a renderer method so only a game that imports it carries the solver.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A draw's own ambient light, as nine spherical-harmonic coefficients: what `setAmbientSH` takes,
|
|
3
|
+
* how both backends pack it, and the arithmetic the lit stage evaluates, which its tests hold.
|
|
4
|
+
*
|
|
5
|
+
* **Why a draw needs its own.** The engine's diffuse ambient is one answer for a whole frame — the
|
|
6
|
+
* sky-and-ground gradient, or the probe grid's irradiance — so everything standing in one place is
|
|
7
|
+
* lit alike. A stage with a baked volume of indirect light (Unreal's volumetric lightmap) lights a
|
|
8
|
+
* character by sampling that volume where the character stands; a consumer that samples it each
|
|
9
|
+
* frame hands the result here, around that character's draws, and the character takes the light of
|
|
10
|
+
* the spot it is in rather than the frame's average.
|
|
11
|
+
*
|
|
12
|
+
* **The coefficients are of incoming radiance**, `L(ω) ≈ Σ c_lm Y_lm(ω)`, on the standard real
|
|
13
|
+
* basis in its usual order — `Y00`, then `Y1,-1 Y1,0 Y1,1`, then `Y2,-2 Y2,-1 Y2,0 Y2,1 Y2,2` —
|
|
14
|
+
* evaluated on the engine's own world axes as written: `Y1,-1 = 0.488603 y`, `Y1,0 = 0.488603 z`,
|
|
15
|
+
* `Y1,1 = 0.488603 x`, `Y2,-2 = 1.092548 xy`, `Y2,-1 = 1.092548 yz`, `Y2,0 = 0.315392 (3z² − 1)`,
|
|
16
|
+
* `Y2,1 = 1.092548 xz`, `Y2,2 = 0.546274 (x² − y²)`, with y up. Three numbers a coefficient, red
|
|
17
|
+
* green blue, twenty-seven in all. A source in another engine's axes is rotated into these first.
|
|
18
|
+
*
|
|
19
|
+
* **What the surface receives is the cosine convolution**, Ramamoorthi and Hanrahan's: the bands
|
|
20
|
+
* scaled by π, 2π/3 and π/4 and divided by π, which is the irradiance over π a Lambertian surface
|
|
21
|
+
* multiplies its albedo by — the same quantity the gradient and the probes hand the shader. A
|
|
22
|
+
* constant radiance `L` everywhere is `c00 = L / 0.282095` and lights a surface exactly as an
|
|
23
|
+
* ambient of `L` does. Negative ringing, which a second-order fit of a sharp source has, is clamped
|
|
24
|
+
* to zero.
|
|
25
|
+
*
|
|
26
|
+
* **What it gives up** is the reflections: these replace the diffuse ambient only, and a glossy
|
|
27
|
+
* surface still reflects the frame's probes or sky. **What would make it wrong** is light the
|
|
28
|
+
* volume holds at a frequency nine coefficients cannot carry — a sharp sun patch — which is the
|
|
29
|
+
* stage's own direct light's job and not this.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/** Numbers `setAmbientSH` takes: nine coefficients of three channels. */
|
|
33
|
+
export const AMBIENT_SH_VALUES = 27;
|
|
34
|
+
|
|
35
|
+
/** Floats the lit stage's `uAmbientSH` holds: the coefficients, and whether they are on. */
|
|
36
|
+
export const AMBIENT_SH_FLOATS = 28;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The coefficients as the lit stage reads them, into `out` of `AMBIENT_SH_FLOATS`: the twenty-seven
|
|
40
|
+
* numbers in order and a last one that is 1, or every float zero for `null` — the frame's own
|
|
41
|
+
* ambient. Refuses a list that is not twenty-seven numbers, naming its length.
|
|
42
|
+
*/
|
|
43
|
+
export function packAmbientSH(coefficients: ArrayLike<number> | null, out: Float32Array): void {
|
|
44
|
+
if (coefficients === null) {
|
|
45
|
+
out.fill(0);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (coefficients.length !== AMBIENT_SH_VALUES) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`setAmbientSH: ${coefficients.length} values, where nine coefficients of red, green and ` +
|
|
51
|
+
`blue are ${AMBIENT_SH_VALUES}`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
for (let i = 0; i < AMBIENT_SH_VALUES; i++) {
|
|
55
|
+
const value = coefficients[i] as number;
|
|
56
|
+
out[i] = Number.isFinite(value) ? value : 0;
|
|
57
|
+
}
|
|
58
|
+
out[AMBIENT_SH_VALUES] = 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The band factors of the cosine convolution, over π: 1, 2/3 and 1/4. */
|
|
62
|
+
const BAND = [1, 2 / 3, 1 / 4] as const;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The ambient a surface facing `nx, ny, nz` (unit) receives from these coefficients, into `out`
|
|
66
|
+
* (three floats): what `ambientHarmonics` in the lit stage computes. Run at no point by the engine;
|
|
67
|
+
* the reference its tests hold.
|
|
68
|
+
*/
|
|
69
|
+
export function ambientFromSH(
|
|
70
|
+
coefficients: ArrayLike<number>,
|
|
71
|
+
nx: number,
|
|
72
|
+
ny: number,
|
|
73
|
+
nz: number,
|
|
74
|
+
out: Float32Array,
|
|
75
|
+
): void {
|
|
76
|
+
const basis = [
|
|
77
|
+
0.282095 * BAND[0],
|
|
78
|
+
0.488603 * ny * BAND[1],
|
|
79
|
+
0.488603 * nz * BAND[1],
|
|
80
|
+
0.488603 * nx * BAND[1],
|
|
81
|
+
1.092548 * nx * ny * BAND[2],
|
|
82
|
+
1.092548 * ny * nz * BAND[2],
|
|
83
|
+
0.315392 * (3 * nz * nz - 1) * BAND[2],
|
|
84
|
+
1.092548 * nx * nz * BAND[2],
|
|
85
|
+
0.546274 * (nx * nx - ny * ny) * BAND[2],
|
|
86
|
+
];
|
|
87
|
+
for (let c = 0; c < 3; c++) {
|
|
88
|
+
let sum = 0;
|
|
89
|
+
for (let k = 0; k < 9; k++) sum += (coefficients[k * 3 + c] as number) * (basis[k] as number);
|
|
90
|
+
out[c] = Math.max(sum, 0);
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/render/areaLights.ts
CHANGED
|
@@ -88,8 +88,20 @@ export interface AreaLightSource {
|
|
|
88
88
|
* every caster, not just the fixture.
|
|
89
89
|
*/
|
|
90
90
|
shadowNear?: number;
|
|
91
|
+
/**
|
|
92
|
+
* How far the rectangle's light reaches, in metres from its centre: past it the light is zero,
|
|
93
|
+
* windowed down to it, so a clustered rectangle can be binned into the froxels it reaches and no
|
|
94
|
+
* others. **Read only where the rectangle is shaded through the froxel table** — the ones past
|
|
95
|
+
* `maxAreaLights` with `clusteredLights` on; the fixed four light everything they face, as they
|
|
96
|
+
* always have. Absent, it is where the rectangle's irradiance on its axis falls to a thousandth
|
|
97
|
+
* of a scene unit, and never inside the rectangle itself. Unreal's `AttenuationRadius`, in metres.
|
|
98
|
+
*/
|
|
99
|
+
range?: number;
|
|
91
100
|
}
|
|
92
101
|
|
|
102
|
+
/** The irradiance a derived `range` ends at, in scene units. See `AreaLightSource.range`. */
|
|
103
|
+
export const AREA_RANGE_CUTOFF = 1e-3;
|
|
104
|
+
|
|
93
105
|
/** The five arrays the shader declares, filled once a frame from whatever a caller supplied. */
|
|
94
106
|
export interface AreaLightBuffer {
|
|
95
107
|
count: number;
|
|
@@ -99,6 +111,8 @@ export interface AreaLightBuffer {
|
|
|
99
111
|
readonly up: Float32Array;
|
|
100
112
|
readonly sizes: Float32Array;
|
|
101
113
|
readonly twoSided: Float32Array;
|
|
114
|
+
/** One a rectangle: how far its light reaches. See `AreaLightSource.range`. */
|
|
115
|
+
readonly ranges?: Float32Array;
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
export function createAreaLightBuffer(capacity: number = MAX_AREA_LIGHTS): AreaLightBuffer {
|
|
@@ -110,9 +124,28 @@ export function createAreaLightBuffer(capacity: number = MAX_AREA_LIGHTS): AreaL
|
|
|
110
124
|
up: new Float32Array(capacity * 3),
|
|
111
125
|
sizes: new Float32Array(capacity * 2),
|
|
112
126
|
twoSided: new Float32Array(capacity),
|
|
127
|
+
ranges: new Float32Array(capacity),
|
|
113
128
|
};
|
|
114
129
|
}
|
|
115
130
|
|
|
131
|
+
/**
|
|
132
|
+
* How far a rectangle's light reaches when it names no range: where its irradiance on its own axis,
|
|
133
|
+
* `L · A / d²` for a rectangle of radiance `L` and area `A` seen from far, falls to
|
|
134
|
+
* `AREA_RANGE_CUTOFF`. Never less than the rectangle's own half-diagonal.
|
|
135
|
+
*/
|
|
136
|
+
export function areaRange(
|
|
137
|
+
r: number,
|
|
138
|
+
g: number,
|
|
139
|
+
b: number,
|
|
140
|
+
halfWidth: number,
|
|
141
|
+
halfHeight: number,
|
|
142
|
+
): number {
|
|
143
|
+
const radiance = Math.max(r, g, b, 0);
|
|
144
|
+
const area = 4 * halfWidth * halfHeight;
|
|
145
|
+
const diagonal = Math.hypot(halfWidth, halfHeight);
|
|
146
|
+
return Math.max(Math.sqrt((radiance * area) / AREA_RANGE_CUTOFF), diagonal);
|
|
147
|
+
}
|
|
148
|
+
|
|
116
149
|
/**
|
|
117
150
|
* Fill the buffer from a caller's list, normalising the axes and orthogonalising them.
|
|
118
151
|
*
|
|
@@ -188,6 +221,12 @@ export function selectAreaLights(
|
|
|
188
221
|
out.sizes[slot * 2] = Math.max(1e-4, light.halfWidth);
|
|
189
222
|
out.sizes[slot * 2 + 1] = Math.max(1e-4, light.halfHeight);
|
|
190
223
|
out.twoSided[slot] = light.twoSided === true ? 1 : 0;
|
|
224
|
+
if (out.ranges !== undefined) {
|
|
225
|
+
out.ranges[slot] =
|
|
226
|
+
light.range !== undefined && light.range > 0
|
|
227
|
+
? light.range
|
|
228
|
+
: areaRange(light.r, light.g, light.b, light.halfWidth, light.halfHeight);
|
|
229
|
+
}
|
|
191
230
|
}
|
|
192
231
|
|
|
193
232
|
return out;
|
|
@@ -7,6 +7,7 @@ import type { Renderer, TranslucentMeshOptions } from './webgl2/renderer.ts';
|
|
|
7
7
|
import type { InstancedOptions, MeshInstances } from '../instances.ts';
|
|
8
8
|
import type { SurfaceMaterial } from '../surfaceTexture.ts';
|
|
9
9
|
import type { SurfaceSource } from '../compressedSource.ts';
|
|
10
|
+
import type { LightmapPage } from '../lightmap.ts';
|
|
10
11
|
import type { ClothBindingData } from '../clothBindingData.ts';
|
|
11
12
|
import type { TextStyle } from '../textLayout.ts';
|
|
12
13
|
import type { SdfFont } from '../sdfFont.ts';
|
|
@@ -300,6 +301,7 @@ export type RendererApi = Omit<
|
|
|
300
301
|
| 'disposeSdfText'
|
|
301
302
|
| 'createSurfaceTexture'
|
|
302
303
|
| 'createSurfaceTextureArray'
|
|
304
|
+
| 'createLightmap'
|
|
303
305
|
| 'updateSurfaceTexture'
|
|
304
306
|
| 'disposeSurfaceTexture'
|
|
305
307
|
| 'setSurfaceTexture'
|
|
@@ -361,6 +363,15 @@ export type RendererApi = Omit<
|
|
|
361
363
|
options?: Parameters<Renderer['createSurfaceTexture']>[1],
|
|
362
364
|
): SurfaceTextureHandle;
|
|
363
365
|
|
|
366
|
+
/**
|
|
367
|
+
* A baked lightmap page on the device, as the texture a `lightmapModel` material takes for its
|
|
368
|
+
* `modelMap`: two layers of half floats, the irradiance and the direction, filtered linearly and
|
|
369
|
+
* clamped at its edges, with no mip chain. Disposed as any texture is, with
|
|
370
|
+
* `disposeSurfaceTexture`; `updateSurfaceTexture` refuses it, as it refuses any array. See
|
|
371
|
+
* `lightmap.ts` for what a surface does with it.
|
|
372
|
+
*/
|
|
373
|
+
createLightmap(page: LightmapPage): SurfaceTextureHandle;
|
|
374
|
+
|
|
364
375
|
/**
|
|
365
376
|
* Replace a texture's pixels, keeping the GPU object and its sampler state.
|
|
366
377
|
*
|
|
@@ -708,8 +719,10 @@ export type RendererApi = Omit<
|
|
|
708
719
|
*/
|
|
709
720
|
createParticles(
|
|
710
721
|
capacity: number,
|
|
711
|
-
options: Omit<Parameters<Renderer['createParticles']>[1], 'reuse'> & {
|
|
722
|
+
options: Omit<Parameters<Renderer['createParticles']>[1], 'reuse' | 'texture'> & {
|
|
712
723
|
readonly reuse?: ParticleHandle;
|
|
724
|
+
/** A `'sprite'`'s image. Re-typed to the handle for `reuse`'s reason. */
|
|
725
|
+
readonly texture?: SurfaceTextureHandle;
|
|
713
726
|
},
|
|
714
727
|
): ParticleHandle;
|
|
715
728
|
|
|
@@ -34,6 +34,8 @@ export class InstancedBatch {
|
|
|
34
34
|
private readonly buffer: WebGLBuffer;
|
|
35
35
|
private readonly vao: WebGLVertexArrayObject;
|
|
36
36
|
private readonly staging: Float32Array;
|
|
37
|
+
/** The survivors of a lightmapped batch's cull, made the first time one is culled. */
|
|
38
|
+
private keptRegioned: MeshInstances | null = null;
|
|
37
39
|
private readonly kept: {
|
|
38
40
|
readonly buffer: WebGLBuffer;
|
|
39
41
|
readonly vao: WebGLVertexArrayObject;
|
|
@@ -80,9 +82,17 @@ export class InstancedBatch {
|
|
|
80
82
|
drawCulled(gl: WebGL2RenderingContext, data: MeshInstances, frustum: FrustumPlanes): number {
|
|
81
83
|
const kept = this.kept;
|
|
82
84
|
if (kept === null) return 0;
|
|
83
|
-
|
|
85
|
+
/* A lightmapped batch's survivors keep their regions, in a copy that carries them. */
|
|
86
|
+
const into =
|
|
87
|
+
data.lightmapRegions === undefined
|
|
88
|
+
? kept.instances
|
|
89
|
+
: (this.keptRegioned ??= {
|
|
90
|
+
...createMeshInstances(this.capacity),
|
|
91
|
+
lightmapRegions: new Float32Array(this.capacity * 4),
|
|
92
|
+
});
|
|
93
|
+
const count = cullInstances(data, this.mesh.bounds, frustum, into);
|
|
84
94
|
if (count === 0) return 0;
|
|
85
|
-
packInstances(
|
|
95
|
+
packInstances(into, this.staging);
|
|
86
96
|
gl.bindBuffer(gl.ARRAY_BUFFER, kept.buffer);
|
|
87
97
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.staging, 0, count * INSTANCE_FLOATS);
|
|
88
98
|
this.mesh.drawInstancesThrough(gl, kept.vao, count);
|