@driftengine/core 4.8.3 → 4.8.5
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 +82 -0
- package/README.md +1 -1
- package/dist/build/shaderComments.js +0 -0
- package/dist/geometry/meshConcat.js +2 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +16 -1
- package/dist/math/scalar.js +1 -1
- package/dist/render/anisotropicLobe.d.ts +7 -0
- package/dist/render/anisotropicLobe.js +33 -0
- package/dist/render/backend/api.d.ts +41 -4
- package/dist/render/backend/select.js +8 -0
- package/dist/render/backend/webgl2/clothTextures.d.ts +27 -0
- package/dist/render/backend/webgl2/clothTextures.js +93 -0
- package/dist/render/backend/webgl2/renderer.d.ts +136 -3
- package/dist/render/backend/webgl2/renderer.js +604 -63
- package/dist/render/backend/webgl2/skinScatter.d.ts +50 -0
- package/dist/render/backend/webgl2/skinScatter.js +256 -0
- package/dist/render/backend/webgpu/buffers.d.ts +25 -11
- package/dist/render/backend/webgpu/buffers.js +98 -49
- package/dist/render/backend/webgpu/clothSolvePack.d.ts +27 -0
- package/dist/render/backend/webgpu/clothSolvePack.js +129 -0
- package/dist/render/backend/webgpu/clothTextures.d.ts +68 -0
- package/dist/render/backend/webgpu/clothTextures.js +145 -0
- package/dist/render/backend/webgpu/compressedUpload.d.ts +18 -0
- package/dist/render/backend/webgpu/compressedUpload.js +38 -0
- package/dist/render/backend/webgpu/computeHost.d.ts +21 -0
- package/dist/render/backend/webgpu/computeHost.js +5 -0
- package/dist/render/backend/webgpu/depthPass.d.ts +16 -6
- package/dist/render/backend/webgpu/depthPass.js +39 -9
- package/dist/render/backend/webgpu/drawCommand.js +6 -2
- package/dist/render/backend/webgpu/flatPass.d.ts +38 -4
- package/dist/render/backend/webgpu/flatPass.js +109 -12
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +74 -0
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +452 -0
- package/dist/render/backend/webgpu/motionPass.d.ts +20 -0
- package/dist/render/backend/webgpu/motionPass.js +106 -9
- package/dist/render/backend/webgpu/postPass.d.ts +3 -1
- package/dist/render/backend/webgpu/postPass.js +6 -3
- package/dist/render/backend/webgpu/renderer.d.ts +113 -5
- package/dist/render/backend/webgpu/renderer.js +498 -48
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +33 -0
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +469 -0
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +51 -0
- package/dist/render/backend/webgpu/skinScatterPass.js +253 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +37 -12
- package/dist/render/clothBindingData.d.ts +41 -0
- package/dist/render/clothBindingData.js +72 -0
- package/dist/render/clothPlacement.d.ts +25 -0
- package/dist/render/clothPlacement.js +85 -0
- package/dist/render/compressedSource.d.ts +66 -0
- package/dist/render/compressedSource.js +138 -0
- package/dist/render/cutoutCaster.d.ts +2 -0
- package/dist/render/cutoutCaster.js +2 -0
- package/dist/render/cutoutDither.d.ts +51 -0
- package/dist/render/cutoutDither.js +90 -0
- package/dist/render/eyeRefraction.d.ts +35 -0
- package/dist/render/eyeRefraction.js +40 -0
- package/dist/render/glCompressed.d.ts +27 -0
- package/dist/render/glCompressed.js +53 -0
- package/dist/render/hairLobes.d.ts +70 -0
- package/dist/render/hairLobes.js +120 -0
- package/dist/render/lightBudget.d.ts +22 -0
- package/dist/render/lightBudget.js +22 -0
- package/dist/render/mesh.d.ts +7 -0
- package/dist/render/mesh.js +24 -0
- package/dist/render/recon/motionVectors.d.ts +4 -2
- package/dist/render/recon/motionVectors.js +6 -3
- package/dist/render/renderQuality.d.ts +20 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneTarget.d.ts +12 -0
- package/dist/render/sceneTarget.js +14 -0
- package/dist/render/shaders/clothBinding.d.ts +22 -0
- package/dist/render/shaders/clothBinding.js +80 -0
- package/dist/render/shaders/depth.js +42 -6
- package/dist/render/shaders/flat/anisotropic.d.ts +15 -0
- package/dist/render/shaders/flat/anisotropic.js +68 -0
- package/dist/render/shaders/flat/eye.d.ts +1 -0
- package/dist/render/shaders/flat/eye.js +81 -0
- package/dist/render/shaders/flat/hair.d.ts +1 -0
- package/dist/render/shaders/flat/hair.js +101 -0
- package/dist/render/shaders/flat/index.js +36 -2
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +113 -17
- package/dist/render/shaders/flat/models.d.ts +14 -0
- package/dist/render/shaders/flat/models.js +273 -0
- package/dist/render/shaders/flat/preamble.js +26 -24
- package/dist/render/shaders/flat/skin.d.ts +1 -0
- package/dist/render/shaders/flat/skin.js +114 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +38 -16
- package/dist/render/shaders/generated/depth.wgsl.js +38 -16
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1790 -1704
- package/dist/render/shaders/generated/flat.wgsl.js +1828 -1742
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +48 -0
- package/dist/render/shaders/generated/skinBlur.wgsl.js +54 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +102 -0
- package/dist/render/shaders/skinBlur.d.ts +1 -0
- package/dist/render/shaders/skinBlur.js +103 -0
- package/dist/render/shaders/skinning.d.ts +3 -1
- package/dist/render/shaders/skinning.js +31 -4
- package/dist/render/shadowCasters.d.ts +13 -1
- package/dist/render/skinBlur.d.ts +41 -0
- package/dist/render/skinBlur.js +49 -0
- package/dist/render/skinScatter.d.ts +67 -0
- package/dist/render/skinScatter.js +80 -0
- package/dist/render/skinnedClothRun.d.ts +26 -0
- package/dist/render/skinnedClothRun.js +84 -0
- package/dist/render/surfaceModel.d.ts +129 -0
- package/dist/render/surfaceModel.js +152 -0
- package/dist/render/surfaceTexture.d.ts +44 -4
- package/dist/render/surfaceTexture.js +60 -46
- package/dist/render/vertexDefaults.js +7 -0
- package/package.json +6 -6
- package/src/build/shaderComments.ts +0 -0
- package/src/geometry/meshConcat.ts +2 -0
- package/src/index.ts +40 -1
- package/src/math/scalar.ts +1 -1
- package/src/render/anisotropicLobe.ts +41 -0
- package/src/render/backend/api.ts +52 -3
- package/src/render/backend/select.ts +8 -0
- package/src/render/backend/webgl2/clothTextures.ts +120 -0
- package/src/render/backend/webgl2/renderer.ts +693 -64
- package/src/render/backend/webgl2/skinScatter.ts +309 -0
- package/src/render/backend/webgpu/buffers.ts +137 -63
- package/src/render/backend/webgpu/clothSolvePack.ts +161 -0
- package/src/render/backend/webgpu/clothTextures.ts +194 -0
- package/src/render/backend/webgpu/compressedUpload.ts +65 -0
- package/src/render/backend/webgpu/computeHost.ts +25 -0
- package/src/render/backend/webgpu/depthPass.ts +39 -7
- package/src/render/backend/webgpu/drawCommand.ts +6 -2
- package/src/render/backend/webgpu/flatPass.ts +145 -7
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +502 -0
- package/src/render/backend/webgpu/motionPass.ts +128 -9
- package/src/render/backend/webgpu/postPass.ts +5 -1
- package/src/render/backend/webgpu/renderer.ts +663 -43
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +470 -0
- package/src/render/backend/webgpu/skinScatterPass.ts +309 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +42 -12
- package/src/render/clothBindingData.ts +89 -0
- package/src/render/clothPlacement.ts +114 -0
- package/src/render/compressedSource.ts +198 -0
- package/src/render/cutoutCaster.ts +5 -0
- package/src/render/cutoutDither.ts +99 -0
- package/src/render/eyeRefraction.ts +72 -0
- package/src/render/glCompressed.ts +86 -0
- package/src/render/hairLobes.ts +188 -0
- package/src/render/lightBudget.ts +25 -0
- package/src/render/mesh.ts +23 -0
- package/src/render/recon/motionVectors.ts +6 -3
- package/src/render/renderQuality.ts +28 -0
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneTarget.ts +21 -0
- package/src/render/shaders/clothBinding.ts +81 -0
- package/src/render/shaders/depth.ts +42 -6
- package/src/render/shaders/flat/anisotropic.ts +68 -0
- package/src/render/shaders/flat/eye.ts +82 -0
- package/src/render/shaders/flat/hair.ts +102 -0
- package/src/render/shaders/flat/index.ts +36 -2
- package/src/render/shaders/flat/main.ts +113 -17
- package/src/render/shaders/flat/models.ts +286 -0
- package/src/render/shaders/flat/preamble.ts +26 -24
- package/src/render/shaders/flat/skin.ts +116 -0
- package/src/render/shaders/generated/depth.wgsl.ts +38 -16
- package/src/render/shaders/generated/flat.wgsl.ts +1828 -1742
- package/src/render/shaders/generated/skinBlur.wgsl.ts +56 -0
- package/src/render/shaders/recon/motion.wgsl.ts +102 -0
- package/src/render/shaders/skinBlur.ts +105 -0
- package/src/render/shaders/skinning.ts +31 -4
- package/src/render/shadowCasters.ts +13 -0
- package/src/render/skinBlur.ts +53 -0
- package/src/render/skinScatter.ts +92 -0
- package/src/render/skinnedClothRun.ts +128 -0
- package/src/render/surfaceModel.ts +274 -0
- package/src/render/surfaceTexture.ts +123 -63
- package/src/render/vertexDefaults.ts +7 -0
- package/dist/math/exact.d.ts +0 -76
- package/dist/math/exact.js +0 -327
- package/src/math/exact.ts +0 -340
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A material's shading model: how its surface answers light, when the standard model is not what
|
|
3
|
+
* it is made of — brushed metal, hair, skin, an eye.
|
|
4
|
+
*
|
|
5
|
+
* **A frozen descriptor, made once by a factory, carrying a kind and that model's numbers.** The
|
|
6
|
+
* standard model is `null`, exactly today's shader, and a scene that never names a model compiles
|
|
7
|
+
* nothing of any of them: each is a pipeline constant of its own (`MODEL_ANISOTROPIC` and the rest
|
|
8
|
+
* in `shaders/flat/models.ts`), off in every pipeline but the ones a material asks for.
|
|
9
|
+
*
|
|
10
|
+
* **Two vectors of numbers a material, and no more.** The lit stage's eight-light build is 254 of
|
|
11
|
+
* the 256 uniform vectors an Adreno 740 offers, so a model's numbers are packed into
|
|
12
|
+
* `uModelParams[2]` — the last component of the second says whether a model map is bound — and
|
|
13
|
+
* every model fits. See `packModel` for each layout.
|
|
14
|
+
*
|
|
15
|
+
* **The eye's axis is the one number that moves.** It is the eye's forward direction in the world,
|
|
16
|
+
* which a turning head or a glance changes every frame; the lit stage has no room left for another
|
|
17
|
+
* varying (sixteen, WebGPU's ceiling), so the renderer turns the descriptor's mesh-local axis by
|
|
18
|
+
* the draw's model matrix and the eye's joint each draw, with `eyeAxisInWorld`.
|
|
19
|
+
*/
|
|
20
|
+
import type { Vec3 } from '../math/color.ts';
|
|
21
|
+
|
|
22
|
+
/** Strands of brushed metal, satin or hair-like fibre: a highlight stretched across them. */
|
|
23
|
+
export interface AnisotropicModel {
|
|
24
|
+
readonly kind: 'anisotropic';
|
|
25
|
+
/** 0 to 1: how far the highlight is stretched along the tangent. */
|
|
26
|
+
readonly strength: number;
|
|
27
|
+
/** Radians, turning the stretch's direction from the tangent toward the bitangent. */
|
|
28
|
+
readonly rotation: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Hair: three lobes along the strand, which is the mesh's tangent, running from root to tip
|
|
33
|
+
* (`hairLobes.ts`). A model map's green turns each strand's tilt by up to half of it either way and
|
|
34
|
+
* its blue is occlusion.
|
|
35
|
+
*/
|
|
36
|
+
export interface HairModel {
|
|
37
|
+
readonly kind: 'hair';
|
|
38
|
+
/**
|
|
39
|
+
* Radians the cuticle's scales tilt the strand's surface: positive moves the white highlight
|
|
40
|
+
* toward the root and the coloured one toward the tip. A card whose tangent runs tip to root
|
|
41
|
+
* takes it negative.
|
|
42
|
+
*/
|
|
43
|
+
readonly shift: number;
|
|
44
|
+
/** 0 to 1: the light scattered through many strands, tinted by their colour. */
|
|
45
|
+
readonly scatter: number;
|
|
46
|
+
/** How strongly light from behind glows through: the transmitted lobe. */
|
|
47
|
+
readonly backlit: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Skin: light scattered beneath the surface, reddening its terminator and its thin parts. */
|
|
51
|
+
export interface SkinModel {
|
|
52
|
+
readonly kind: 'skin';
|
|
53
|
+
/** The colour light keeps travelling beneath the surface — the mean free path, as a colour. */
|
|
54
|
+
readonly scatterColor: Vec3;
|
|
55
|
+
/** Metres: how far light travels beneath it before it leaves. */
|
|
56
|
+
readonly radius: number;
|
|
57
|
+
/** 0 to 1: how much a light behind a thin part shows through it. */
|
|
58
|
+
readonly transmission: number;
|
|
59
|
+
/** Which of up to eight profiles the screen-space scattering blurs this skin by. */
|
|
60
|
+
readonly profile: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** An eye: an iris seen through a cornea, which refracts it, under a mirror-smooth highlight. */
|
|
64
|
+
export interface EyeModel {
|
|
65
|
+
readonly kind: 'eye';
|
|
66
|
+
/** The iris's radius, in texture coordinates from the centre of the eye's map. */
|
|
67
|
+
readonly irisRadius: number;
|
|
68
|
+
/** Metres from the cornea down to the iris. */
|
|
69
|
+
readonly irisDepth: number;
|
|
70
|
+
/** The cornea's index of refraction. */
|
|
71
|
+
readonly ior: number;
|
|
72
|
+
/** How smooth the cornea's highlight is. */
|
|
73
|
+
readonly corneaRoughness: number;
|
|
74
|
+
/** The eye's forward direction, in the mesh's own space at its bind pose. */
|
|
75
|
+
readonly axis: Vec3;
|
|
76
|
+
/** The joint the eye turns with, or -1 for an eye placed by its model matrix alone. */
|
|
77
|
+
readonly joint: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Every model a material can name, by its `kind`: see `SurfaceMaterial.model`. */
|
|
81
|
+
export type SurfaceModel = AnisotropicModel | HairModel | SkinModel | EyeModel;
|
|
82
|
+
/** A model's name, which also picks its pipeline switch: `SURFACE_MODEL_SWITCH`. */
|
|
83
|
+
export type SurfaceModelKind = SurfaceModel['kind'];
|
|
84
|
+
|
|
85
|
+
/** Every kind, in the order the lit stage declares their switches. */
|
|
86
|
+
export const SURFACE_MODEL_KINDS: readonly SurfaceModelKind[] = [
|
|
87
|
+
'anisotropic',
|
|
88
|
+
'hair',
|
|
89
|
+
'skin',
|
|
90
|
+
'eye',
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
/** The lit stage's switch for each kind. */
|
|
94
|
+
export const SURFACE_MODEL_SWITCH: Readonly<Record<SurfaceModelKind, string>> = {
|
|
95
|
+
anisotropic: 'MODEL_ANISOTROPIC',
|
|
96
|
+
hair: 'MODEL_HAIR',
|
|
97
|
+
skin: 'MODEL_SKIN',
|
|
98
|
+
eye: 'MODEL_EYE',
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/** Floats a material's model takes: `uModelParams[2]`. */
|
|
102
|
+
export const MODEL_PARAM_FLOATS = 8;
|
|
103
|
+
|
|
104
|
+
function check(model: string, name: string, value: number, low: number, high: number): number {
|
|
105
|
+
if (!Number.isFinite(value) || value < low || value > high) {
|
|
106
|
+
throw new Error(`${model}: ${name} must be from ${low} to ${high}, not ${String(value)}`);
|
|
107
|
+
}
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function unit(model: string, name: string, v: Vec3): Vec3 {
|
|
112
|
+
const length = Math.hypot(v[0], v[1], v[2]);
|
|
113
|
+
if (!(length > 1e-6))
|
|
114
|
+
throw new Error(`${model}: ${name} must be a direction, not [${v.join(', ')}]`);
|
|
115
|
+
return [v[0] / length, v[1] / length, v[2] / length];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Brushed metal and the like, after glTF's `KHR_materials_anisotropy`: a strength and a turn. */
|
|
119
|
+
export function anisotropicModel(
|
|
120
|
+
options: { strength?: number; rotation?: number } = {},
|
|
121
|
+
): AnisotropicModel {
|
|
122
|
+
return Object.freeze({
|
|
123
|
+
kind: 'anisotropic',
|
|
124
|
+
strength: check('anisotropicModel', 'strength', options.strength ?? 1, 0, 1),
|
|
125
|
+
rotation: check(
|
|
126
|
+
'anisotropicModel',
|
|
127
|
+
'rotation',
|
|
128
|
+
options.rotation ?? 0,
|
|
129
|
+
-2 * Math.PI,
|
|
130
|
+
2 * Math.PI,
|
|
131
|
+
),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Hair, along the mesh's tangent from root to tip. A tilt of 3°, within the 2.5° to 5° measured. */
|
|
136
|
+
export function hairModel(
|
|
137
|
+
options: { shift?: number; scatter?: number; backlit?: number } = {},
|
|
138
|
+
): HairModel {
|
|
139
|
+
return Object.freeze({
|
|
140
|
+
kind: 'hair',
|
|
141
|
+
shift: check('hairModel', 'shift', options.shift ?? (3 * Math.PI) / 180, -0.5, 0.5),
|
|
142
|
+
scatter: check('hairModel', 'scatter', options.scatter ?? 0.7, 0, 1),
|
|
143
|
+
backlit: check('hairModel', 'backlit', options.backlit ?? 1, 0, 4),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Skin. The defaults are a fair skin's: a red mean free path a little over a centimetre long. */
|
|
148
|
+
export function skinModel(
|
|
149
|
+
options: { scatterColor?: Vec3; radius?: number; transmission?: number; profile?: number } = {},
|
|
150
|
+
): SkinModel {
|
|
151
|
+
const color = options.scatterColor ?? [0.85, 0.35, 0.22];
|
|
152
|
+
for (let c = 0; c < 3; c++) check('skinModel', 'scatterColor', color[c] as number, 0, 1);
|
|
153
|
+
const profile = check('skinModel', 'profile', options.profile ?? 0, 0, 7);
|
|
154
|
+
if (!Number.isInteger(profile))
|
|
155
|
+
throw new Error(`skinModel: profile must be a whole number, not ${profile}`);
|
|
156
|
+
return Object.freeze({
|
|
157
|
+
kind: 'skin',
|
|
158
|
+
scatterColor: Object.freeze([color[0], color[1], color[2]]) as unknown as Vec3,
|
|
159
|
+
radius: check('skinModel', 'radius', options.radius ?? 0.012, 0, 0.1),
|
|
160
|
+
transmission: check('skinModel', 'transmission', options.transmission ?? 0.5, 0, 1),
|
|
161
|
+
profile,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** An eye. The defaults are a human eye's, its axis along the mesh's +Z. */
|
|
166
|
+
export function eyeModel(
|
|
167
|
+
options: {
|
|
168
|
+
irisRadius?: number;
|
|
169
|
+
irisDepth?: number;
|
|
170
|
+
ior?: number;
|
|
171
|
+
corneaRoughness?: number;
|
|
172
|
+
axis?: Vec3;
|
|
173
|
+
joint?: number;
|
|
174
|
+
} = {},
|
|
175
|
+
): EyeModel {
|
|
176
|
+
const joint = options.joint ?? -1;
|
|
177
|
+
if (!Number.isInteger(joint) || joint < -1) {
|
|
178
|
+
throw new Error(`eyeModel: joint must be a joint index or -1, not ${String(joint)}`);
|
|
179
|
+
}
|
|
180
|
+
return Object.freeze({
|
|
181
|
+
kind: 'eye',
|
|
182
|
+
irisRadius: check('eyeModel', 'irisRadius', options.irisRadius ?? 0.15, 0.001, 0.5),
|
|
183
|
+
irisDepth: check('eyeModel', 'irisDepth', options.irisDepth ?? 0.003, 0, 0.02),
|
|
184
|
+
ior: check('eyeModel', 'ior', options.ior ?? 1.336, 1, 2),
|
|
185
|
+
corneaRoughness: check('eyeModel', 'corneaRoughness', options.corneaRoughness ?? 0.02, 0, 1),
|
|
186
|
+
axis: Object.freeze(unit('eyeModel', 'axis', options.axis ?? [0, 0, 1])) as unknown as Vec3,
|
|
187
|
+
joint,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* A model's numbers into `out` — eight floats, `uModelParams[0]` then `[1]` — and whether a model
|
|
193
|
+
* map is bound into the last. Zeros for the standard model. An eye's axis goes in with the
|
|
194
|
+
* descriptor's own until the draw turns it: see `eyeAxisInWorld`.
|
|
195
|
+
*
|
|
196
|
+
* - anisotropic: strength, cos and sin of the rotation · −, −, −, map
|
|
197
|
+
* - hair: shift, scatter, backlit, − · −, −, −, map
|
|
198
|
+
* - skin: the scatter colour, radius · transmission, profile, −, map
|
|
199
|
+
* - eye: iris radius, iris depth, ior, cornea roughness · the axis, map
|
|
200
|
+
*/
|
|
201
|
+
export function packModel(model: SurfaceModel | null, mapped: boolean, out: Float32Array): void {
|
|
202
|
+
out.fill(0);
|
|
203
|
+
if (model === null) return;
|
|
204
|
+
out[7] = mapped ? 1 : 0;
|
|
205
|
+
switch (model.kind) {
|
|
206
|
+
case 'anisotropic':
|
|
207
|
+
out[0] = model.strength;
|
|
208
|
+
out[1] = Math.cos(model.rotation);
|
|
209
|
+
out[2] = Math.sin(model.rotation);
|
|
210
|
+
return;
|
|
211
|
+
case 'hair':
|
|
212
|
+
out[0] = model.shift;
|
|
213
|
+
out[1] = model.scatter;
|
|
214
|
+
out[2] = model.backlit;
|
|
215
|
+
return;
|
|
216
|
+
case 'skin':
|
|
217
|
+
out[0] = model.scatterColor[0];
|
|
218
|
+
out[1] = model.scatterColor[1];
|
|
219
|
+
out[2] = model.scatterColor[2];
|
|
220
|
+
out[3] = model.radius;
|
|
221
|
+
out[4] = model.transmission;
|
|
222
|
+
out[5] = model.profile;
|
|
223
|
+
return;
|
|
224
|
+
case 'eye':
|
|
225
|
+
out[0] = model.irisRadius;
|
|
226
|
+
out[1] = model.irisDepth;
|
|
227
|
+
out[2] = model.ior;
|
|
228
|
+
out[3] = model.corneaRoughness;
|
|
229
|
+
out[4] = model.axis[0];
|
|
230
|
+
out[5] = model.axis[1];
|
|
231
|
+
out[6] = model.axis[2];
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* An eye's axis in the world for one draw, into `out[at…at+2]`: the descriptor's mesh-local axis
|
|
238
|
+
* turned by its joint's palette entry, where it names one and a palette is set, and then by the
|
|
239
|
+
* draw's model matrix — the turn its vertices take, without the translation. Normalised, because
|
|
240
|
+
* a palette carries a joint's scale.
|
|
241
|
+
*/
|
|
242
|
+
export function eyeAxisInWorld(
|
|
243
|
+
model: EyeModel,
|
|
244
|
+
placement: ArrayLike<number>,
|
|
245
|
+
palette: ArrayLike<number> | null,
|
|
246
|
+
out: Float32Array,
|
|
247
|
+
at: number,
|
|
248
|
+
): void {
|
|
249
|
+
let [x, y, z] = model.axis;
|
|
250
|
+
const j = model.joint * 16;
|
|
251
|
+
if (palette !== null && model.joint >= 0 && j + 16 <= palette.length) {
|
|
252
|
+
const px =
|
|
253
|
+
(palette[j] as number) * x + (palette[j + 4] as number) * y + (palette[j + 8] as number) * z;
|
|
254
|
+
const py =
|
|
255
|
+
(palette[j + 1] as number) * x +
|
|
256
|
+
(palette[j + 5] as number) * y +
|
|
257
|
+
(palette[j + 9] as number) * z;
|
|
258
|
+
const pz =
|
|
259
|
+
(palette[j + 2] as number) * x +
|
|
260
|
+
(palette[j + 6] as number) * y +
|
|
261
|
+
(palette[j + 10] as number) * z;
|
|
262
|
+
x = px;
|
|
263
|
+
y = py;
|
|
264
|
+
z = pz;
|
|
265
|
+
}
|
|
266
|
+
const m = placement;
|
|
267
|
+
const wx = (m[0] as number) * x + (m[4] as number) * y + (m[8] as number) * z;
|
|
268
|
+
const wy = (m[1] as number) * x + (m[5] as number) * y + (m[9] as number) * z;
|
|
269
|
+
const wz = (m[2] as number) * x + (m[6] as number) * y + (m[10] as number) * z;
|
|
270
|
+
const length = Math.hypot(wx, wy, wz) || 1;
|
|
271
|
+
out[at] = wx / length;
|
|
272
|
+
out[at + 1] = wy / length;
|
|
273
|
+
out[at + 2] = wz / length;
|
|
274
|
+
}
|
|
@@ -8,9 +8,19 @@
|
|
|
8
8
|
* array samples exactly as a 2D texture does. A mesh names its layer per vertex (`MeshData.layers`),
|
|
9
9
|
* so a merged block wearing forty facades is one draw.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import type { SurfaceModel } from './surfaceModel.ts';
|
|
12
|
+
import { layerSize, refuseArrayUpdate, sourceSize } from './textureSource.ts';
|
|
13
|
+
import { compressedLayers, planBlocks, refuseBlockUpdate } from './compressedSource.ts';
|
|
14
|
+
import type {
|
|
15
|
+
BlockFormat,
|
|
16
|
+
CompressedTextureFormat,
|
|
17
|
+
CompressedTextureSource,
|
|
18
|
+
SurfaceSource,
|
|
19
|
+
} from './compressedSource.ts';
|
|
20
|
+
import { uploadCompressedArray } from './glCompressed.ts';
|
|
12
21
|
import { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from './surfaceEffects.ts';
|
|
13
22
|
import type { SurfaceLayerEffect } from './surfaceEffects.ts';
|
|
23
|
+
import type { CutoutMode } from './cutoutDither.ts';
|
|
14
24
|
|
|
15
25
|
/**
|
|
16
26
|
* How a texture behaves past its edges and between its texels.
|
|
@@ -81,8 +91,20 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
81
91
|
*/
|
|
82
92
|
uOffset?: number;
|
|
83
93
|
vOffset?: number;
|
|
84
|
-
/** Alpha below which a fragment is discarded. See `
|
|
94
|
+
/** Alpha below which a fragment is discarded. See `uCutout`. */
|
|
85
95
|
cutout?: number;
|
|
96
|
+
/**
|
|
97
|
+
* How the cutout's edge is drawn: `'hard'`, the default and every cutout before 4.8.4, or
|
|
98
|
+
* `'dithered'`, which keeps a pixel by the share of it the texture covers so a strand of hair, a
|
|
99
|
+
* lash or a fringe of leaves has a soft edge instead of a stair of pixels.
|
|
100
|
+
*
|
|
101
|
+
* **The frame decides how a dithered edge is resolved**, because the material cannot know: under a
|
|
102
|
+
* temporal resolve (TAA or DriftTR) the pattern moves every frame and the resolve averages it;
|
|
103
|
+
* multisampled, the share goes to the GPU as alpha-to-coverage; with neither, the test is hard,
|
|
104
|
+
* since a dither nothing averages is grain. A translucent draw tests hard whatever this says. See
|
|
105
|
+
* `cutoutDither.ts`.
|
|
106
|
+
*/
|
|
107
|
+
cutoutMode?: CutoutMode;
|
|
86
108
|
/**
|
|
87
109
|
* Whether both faces are seen: glTF's `doubleSided`, what a curtain or a leaf card is. Drawn
|
|
88
110
|
* without culling, and a back face is lit as its front, with the normal turned to the viewer.
|
|
@@ -123,6 +145,19 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
|
|
|
123
145
|
* consumer who wants a bound map twice as bright asks here rather than rebuilding the mesh.
|
|
124
146
|
*/
|
|
125
147
|
emissiveScale?: readonly [number, number, number];
|
|
148
|
+
/**
|
|
149
|
+
* How the surface answers light, when the standard model is not what it is made of: brushed
|
|
150
|
+
* metal, hair, skin, an eye — `anisotropicModel`, `hairModel`, `skinModel`, `eyeModel`. Null or
|
|
151
|
+
* absent is the standard model, exactly as before. **A pipeline of its own**, compiled the first
|
|
152
|
+
* time a draw asks, so a scene that names no model pays for none. See `surfaceModel.ts`.
|
|
153
|
+
*/
|
|
154
|
+
model?: SurfaceModel | null;
|
|
155
|
+
/**
|
|
156
|
+
* The model's own channels, in the albedo's coordinates and layers; what each channel means is
|
|
157
|
+
* the model's to say. Absent, each model takes its own neutral. Sampled linear, like any map
|
|
158
|
+
* that is not a colour.
|
|
159
|
+
*/
|
|
160
|
+
modelMap?: Texture | null;
|
|
126
161
|
}
|
|
127
162
|
|
|
128
163
|
export interface SurfaceTextureOptions {
|
|
@@ -222,79 +257,45 @@ export class SurfaceTexture {
|
|
|
222
257
|
readonly layers: number;
|
|
223
258
|
/** The per-layer effects table, or null for a texture given none. See `surfaceEffects.ts`. */
|
|
224
259
|
private effectsTable: WebGLTexture | null = null;
|
|
260
|
+
/** The block format a compressed texture holds, or null for one uploaded from images. */
|
|
261
|
+
private readonly blockFormat: BlockFormat | null;
|
|
225
262
|
|
|
226
263
|
/**
|
|
227
264
|
* One image, or an array of images that must share one size (`layerSize` refuses otherwise).
|
|
228
|
-
* Uploaded unflipped either way; see the constructor body for why.
|
|
265
|
+
* Uploaded unflipped either way; see the constructor body for why. Or blocks, uploaded as they are
|
|
266
|
+
* where `compressed` — the formats this context samples — has theirs; see `compressedSource.ts`.
|
|
229
267
|
*/
|
|
230
268
|
constructor(
|
|
231
269
|
gl: WebGL2RenderingContext,
|
|
232
|
-
source:
|
|
270
|
+
source: SurfaceSource | readonly SurfaceSource[],
|
|
233
271
|
options: SurfaceTextureOptions = {},
|
|
272
|
+
compressed: readonly CompressedTextureFormat[] = [],
|
|
234
273
|
) {
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
this.
|
|
274
|
+
const listed = (Array.isArray(source) ? source : [source]) as readonly SurfaceSource[];
|
|
275
|
+
const blocks = compressedLayers(listed);
|
|
276
|
+
this.srgb = (options.colorSpace ?? 'linear') === 'srgb';
|
|
277
|
+
/* Refused before anything is allocated, so a refusal leaves no texture behind. */
|
|
278
|
+
const plan = blocks === null ? null : planBlocks(blocks, this.srgb, compressed);
|
|
238
279
|
const texture = gl.createTexture();
|
|
239
280
|
if (texture === null) throw new Error('SurfaceTexture: createTexture failed');
|
|
240
281
|
this.texture = texture;
|
|
241
|
-
this.mipmapped = options.mipmap ?? true;
|
|
242
|
-
this.srgb = (options.colorSpace ?? 'linear') === 'srgb';
|
|
243
|
-
|
|
244
282
|
const wrap = (options.wrap ?? 'repeat') === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
245
|
-
|
|
246
283
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
247
|
-
/*
|
|
248
|
-
* **Not flipped, and this used to be, which is the bug it is written down for.**
|
|
249
|
-
*
|
|
250
|
-
* The flip was here with a comment saying every source is a 2D drawing surface whose Y
|
|
251
|
-
* runs down from the top-left, so without it "every texture arrives mirrored". That is
|
|
252
|
-
* true of a canvas and **WebGL ignores `UNPACK_FLIP_Y_WEBGL` entirely for an
|
|
253
|
-
* `ImageBitmap`**, which carries its own orientation. So the two source types the same
|
|
254
|
-
* method accepts arrived the opposite way up from each other, and nothing caught it
|
|
255
|
-
* because every canvas-sourced texture in this repository was symmetric under a vertical
|
|
256
|
-
* flip: eroded noise, a radial halo, a sleeve blurred to 24 pixels and back. One consumer
|
|
257
|
-
* painted lettering to a canvas beside a sleeve decoded as a bitmap and the text was
|
|
258
|
-
* upside down.
|
|
259
|
-
*
|
|
260
|
-
* **Made to agree by dropping the flip rather than by extending it**, and the direction
|
|
261
|
-
* is a deliberate choice about blast radius rather than a coin toss: `drftLoader` builds
|
|
262
|
-
* every model texture from an `ImageBitmap`, so flipping bitmaps to match canvases would
|
|
263
|
-
* turn over every painted surface on every loaded model. Dropping it changes only
|
|
264
|
-
* canvas-sourced textures, and every one of those that exists today is symmetric.
|
|
265
|
-
*
|
|
266
|
-
* A caller that was mirroring its own canvas to cancel this must stop.
|
|
267
|
-
*/
|
|
268
|
-
/* Allocated mutable rather than with texStorage3D, so `update` can hand a one-layer texture a
|
|
269
|
-
new size, as texImage2D always could. Each layer is then written in place. */
|
|
270
|
-
gl.texImage3D(
|
|
271
|
-
gl.TEXTURE_2D_ARRAY,
|
|
272
|
-
0,
|
|
273
|
-
this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA,
|
|
274
|
-
width,
|
|
275
|
-
height,
|
|
276
|
-
sources.length,
|
|
277
|
-
0,
|
|
278
|
-
gl.RGBA,
|
|
279
|
-
gl.UNSIGNED_BYTE,
|
|
280
|
-
null,
|
|
281
|
-
);
|
|
282
|
-
for (let layer = 0; layer < sources.length; layer++) {
|
|
283
|
-
gl.texSubImage3D(
|
|
284
|
-
gl.TEXTURE_2D_ARRAY,
|
|
285
|
-
0,
|
|
286
|
-
0,
|
|
287
|
-
0,
|
|
288
|
-
layer,
|
|
289
|
-
width,
|
|
290
|
-
height,
|
|
291
|
-
1,
|
|
292
|
-
gl.RGBA,
|
|
293
|
-
gl.UNSIGNED_BYTE,
|
|
294
|
-
sources[layer] as TexImageSource,
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
284
|
|
|
285
|
+
if (blocks !== null && plan !== null) {
|
|
286
|
+
/* The stored chain, or level 0 alone where no chain was asked for. Nothing is generated:
|
|
287
|
+
`generateMipmap` cannot write a compressed format. */
|
|
288
|
+
this.layers = blocks.length;
|
|
289
|
+
this.blockFormat = (blocks[0] as CompressedTextureSource).format;
|
|
290
|
+
this.mipmapped = (options.mipmap ?? true) && plan.levels > 1;
|
|
291
|
+
uploadCompressedArray(gl, plan.name, blocks, this.mipmapped ? plan.levels : 1);
|
|
292
|
+
} else {
|
|
293
|
+
const sources = listed as readonly TexImageSource[];
|
|
294
|
+
this.layers = sources.length;
|
|
295
|
+
this.blockFormat = null;
|
|
296
|
+
this.mipmapped = options.mipmap ?? true;
|
|
297
|
+
uploadImages(gl, sources, this.srgb);
|
|
298
|
+
}
|
|
298
299
|
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, wrap);
|
|
299
300
|
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, wrap);
|
|
300
301
|
/* Nearest where the caller said their pixels are the subject; see `filter`. The mip chain is
|
|
@@ -314,7 +315,7 @@ export class SurfaceTexture {
|
|
|
314
315
|
: gl.LINEAR,
|
|
315
316
|
);
|
|
316
317
|
|
|
317
|
-
if (this.mipmapped) gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
318
|
+
if (this.mipmapped && this.blockFormat === null) gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
318
319
|
applyAnisotropy(gl, options.anisotropy ?? DEFAULT_ANISOTROPY);
|
|
319
320
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
320
321
|
|
|
@@ -345,7 +346,6 @@ export class SurfaceTexture {
|
|
|
345
346
|
}
|
|
346
347
|
}
|
|
347
348
|
|
|
348
|
-
/** Bind the effects table to `unit`, or `fallback` for a texture given none. */
|
|
349
349
|
/** Whether this texture carries an effects table, which the lit program reads only once one exists. */
|
|
350
350
|
get hasEffects(): boolean {
|
|
351
351
|
return this.effectsTable !== null;
|
|
@@ -370,6 +370,7 @@ export class SurfaceTexture {
|
|
|
370
370
|
update(gl: WebGL2RenderingContext, source: TexImageSource): void {
|
|
371
371
|
if (this.texture === null) return;
|
|
372
372
|
refuseArrayUpdate(this.layers);
|
|
373
|
+
refuseBlockUpdate(this.blockFormat);
|
|
373
374
|
const { width, height } = sourceSize(source);
|
|
374
375
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
|
|
375
376
|
/* Unflipped, matching the constructor. See it for why, and for what it cost. */
|
|
@@ -404,6 +405,65 @@ export class SurfaceTexture {
|
|
|
404
405
|
}
|
|
405
406
|
}
|
|
406
407
|
|
|
408
|
+
/** Images into the bound array, level 0 of each layer; the chain, if any, is generated after. */
|
|
409
|
+
function uploadImages(
|
|
410
|
+
gl: WebGL2RenderingContext,
|
|
411
|
+
sources: readonly TexImageSource[],
|
|
412
|
+
srgb: boolean,
|
|
413
|
+
): void {
|
|
414
|
+
const { width, height } = layerSize(sources);
|
|
415
|
+
/*
|
|
416
|
+
* **Not flipped, and this used to be, which is the bug it is written down for.**
|
|
417
|
+
*
|
|
418
|
+
* The flip was here with a comment saying every source is a 2D drawing surface whose Y
|
|
419
|
+
* runs down from the top-left, so without it "every texture arrives mirrored". That is
|
|
420
|
+
* true of a canvas and **WebGL ignores `UNPACK_FLIP_Y_WEBGL` entirely for an
|
|
421
|
+
* `ImageBitmap`**, which carries its own orientation. So the two source types the same
|
|
422
|
+
* method accepts arrived the opposite way up from each other, and nothing caught it
|
|
423
|
+
* because every canvas-sourced texture in this repository was symmetric under a vertical
|
|
424
|
+
* flip: eroded noise, a radial halo, a sleeve blurred to 24 pixels and back. One consumer
|
|
425
|
+
* painted lettering to a canvas beside a sleeve decoded as a bitmap and the text was
|
|
426
|
+
* upside down.
|
|
427
|
+
*
|
|
428
|
+
* **Made to agree by dropping the flip rather than by extending it**, and the direction
|
|
429
|
+
* is a deliberate choice about blast radius rather than a coin toss: `drftLoader` builds
|
|
430
|
+
* every model texture from an `ImageBitmap`, so flipping bitmaps to match canvases would
|
|
431
|
+
* turn over every painted surface on every loaded model. Dropping it changes only
|
|
432
|
+
* canvas-sourced textures, and every one of those that exists today is symmetric.
|
|
433
|
+
*
|
|
434
|
+
* A caller that was mirroring its own canvas to cancel this must stop.
|
|
435
|
+
*/
|
|
436
|
+
/* Allocated mutable rather than with texStorage3D, so `update` can hand a one-layer texture a
|
|
437
|
+
new size, as texImage2D always could. Each layer is then written in place. */
|
|
438
|
+
gl.texImage3D(
|
|
439
|
+
gl.TEXTURE_2D_ARRAY,
|
|
440
|
+
0,
|
|
441
|
+
srgb ? gl.SRGB8_ALPHA8 : gl.RGBA,
|
|
442
|
+
width,
|
|
443
|
+
height,
|
|
444
|
+
sources.length,
|
|
445
|
+
0,
|
|
446
|
+
gl.RGBA,
|
|
447
|
+
gl.UNSIGNED_BYTE,
|
|
448
|
+
null,
|
|
449
|
+
);
|
|
450
|
+
for (let layer = 0; layer < sources.length; layer++) {
|
|
451
|
+
gl.texSubImage3D(
|
|
452
|
+
gl.TEXTURE_2D_ARRAY,
|
|
453
|
+
0,
|
|
454
|
+
0,
|
|
455
|
+
0,
|
|
456
|
+
layer,
|
|
457
|
+
width,
|
|
458
|
+
height,
|
|
459
|
+
1,
|
|
460
|
+
gl.RGBA,
|
|
461
|
+
gl.UNSIGNED_BYTE,
|
|
462
|
+
sources[layer] as TexImageSource,
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
407
467
|
/**
|
|
408
468
|
* Set anisotropic filtering when the driver offers it, and stay silent when it does not.
|
|
409
469
|
*
|
|
@@ -67,6 +67,13 @@ export const ABSENT_ATTRIBUTE: Readonly<Record<string, readonly number[]>> = {
|
|
|
67
67
|
*/
|
|
68
68
|
joints: [0, 0, 0, 0],
|
|
69
69
|
weights: [1, 0, 0, 0],
|
|
70
|
+
/**
|
|
71
|
+
* The second four influences, absent: all zero, so a four-influence mesh drawn through a pipeline
|
|
72
|
+
* built for eight adds nothing. **Not the first set's trap**: there a zero sum collapses the
|
|
73
|
+
* vertex, here the first set already sums to one and zero is exactly "no more influences".
|
|
74
|
+
*/
|
|
75
|
+
joints2: [0, 0, 0, 0],
|
|
76
|
+
weights2: [0, 0, 0, 0],
|
|
70
77
|
/**
|
|
71
78
|
* The four-lane channel: planted, fully sunlit, opaque, and a reserved lane.
|
|
72
79
|
*
|
package/dist/math/exact.d.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transcendentals that give the same bits on every JavaScript engine.
|
|
3
|
-
*
|
|
4
|
-
* **Not more accurate than `Math`, and that is not what they are for.** These are within a couple
|
|
5
|
-
* of units in the last place of correctly rounded, which is about what a platform `sin` gives you.
|
|
6
|
-
* What they are is *reproducible*: ECMAScript declines to specify `sin`, `cos`, `exp`, `acos` and
|
|
7
|
-
* eighteen more, so two engines may differ by an ulp, and an ulp is enough to make two peers running
|
|
8
|
-
* the same simulation disagree — first invisibly, then completely.
|
|
9
|
-
*
|
|
10
|
-
* `scripts/determinism.mjs` is the gate that refuses the unspecified ones inside the simulation set
|
|
11
|
-
* and names the twenty-two. Its header carries the other half of the argument: `+ - * /` are IEEE
|
|
12
|
-
* 754 operations by specification, JavaScript permits no fused-multiply-add contraction, and
|
|
13
|
-
* `Math.sqrt` is required correctly rounded and is *not* on that list. **So everything below uses
|
|
14
|
-
* only those five operations**, which is the whole design: reproducibility by construction rather
|
|
15
|
-
* than by hoping two engines chose the same algorithm.
|
|
16
|
-
*
|
|
17
|
-
* ---
|
|
18
|
-
*
|
|
19
|
-
* ## What is here, and what is deliberately not
|
|
20
|
-
*
|
|
21
|
-
* `exactSin`, `exactCos`, `exactExp`, `exactAcos` and `exactLog`. Five, because five is what the
|
|
22
|
-
* engine and its first consumer actually reach for: the terrain binding's slope angle needs `acos`,
|
|
23
|
-
* a lockstep fixture rotating anything needs `sin` and `cos`, exponential decay needs `exp`, and a
|
|
24
|
-
* detector's box prior is a logit of the patch grid, which needs `log`.
|
|
25
|
-
*
|
|
26
|
-
* **`atan2` is absent.** It is the obvious fifth and nothing here needs one: `shape.ts` sorts by a
|
|
27
|
-
* comparison key computed from two in-plane axes rather than by an angle, and `joints.ts` compares
|
|
28
|
-
* a quaternion component against a half-angle's sine. *Reversed by* a site that genuinely needs a
|
|
29
|
-
* signed angle from two components, at which point it arrives with its own golden vectors.
|
|
30
|
-
*
|
|
31
|
-
* ## Nothing in core imports these
|
|
32
|
-
*
|
|
33
|
-
* Deliberately. `core-only` is the one size figure in this repository that every consumer pays, and
|
|
34
|
-
* a module reachable from `createRenderer` is in it whether anybody calls it or not. These are
|
|
35
|
-
* reached by the script bindings and by consumers who ask for them, so tree-shaking keeps them out
|
|
36
|
-
* of a bundle that does not, which is measured rather than assumed.
|
|
37
|
-
*
|
|
38
|
-
* ## The coefficients
|
|
39
|
-
*
|
|
40
|
-
* The polynomial and rational coefficients are fdlibm's, which is the ancestor of most platform
|
|
41
|
-
* libms and is public domain. They are written as literals: **a literal is the same number on
|
|
42
|
-
* every engine, which is the only property being bought.** Their provenance matters less than their
|
|
43
|
-
* fixity — a coefficient somebody re-derived to one more digit is a different function, and
|
|
44
|
-
* `exact.test.ts`'s golden bit patterns are what would catch that.
|
|
45
|
-
*/
|
|
46
|
-
/** `Math.sin`, reproducibly. NaN in, NaN out; an infinity answers NaN, as `Math.sin` does. */
|
|
47
|
-
export declare function exactSin(x: number): number;
|
|
48
|
-
/** `Math.cos`, reproducibly. */
|
|
49
|
-
export declare function exactCos(x: number): number;
|
|
50
|
-
/**
|
|
51
|
-
* `Math.exp`, reproducibly.
|
|
52
|
-
*
|
|
53
|
-
* Reduce by a multiple of ln 2 into [-ln2/2, ln2/2], sum the Taylor series there, and scale by a
|
|
54
|
-
* power of two. **A degree-14 series and not a shorter minimax**, because on that interval the
|
|
55
|
-
* fifteenth term is about 4e-20 — below the precision of a double — so the series is exact to the
|
|
56
|
-
* representation and there is nothing a fitted polynomial would buy.
|
|
57
|
-
*/
|
|
58
|
-
export declare function exactExp(x: number): number;
|
|
59
|
-
/**
|
|
60
|
-
* `Math.log`, reproducibly: fdlibm's algorithm, which is also V8's.
|
|
61
|
-
*
|
|
62
|
-
* Take the power of two out as `k` and leave a significand in [√2/2, √2), so that `f = m - 1` is
|
|
63
|
-
* small either side of zero; then log(1 + f) = f - f²/2 + s·(f²/2 + R(s²)) with s = f/(2 + f), and
|
|
64
|
-
* `k·ln 2` added back in two parts so the large part loses nothing. The exponent is read and the
|
|
65
|
-
* significand rescaled through the double's own words — integer operations, not transcendentals.
|
|
66
|
-
*/
|
|
67
|
-
export declare function exactLog(x: number): number;
|
|
68
|
-
/**
|
|
69
|
-
* `Math.acos`, reproducibly. Outside [-1, 1] this answers NaN, as `Math.acos` does.
|
|
70
|
-
*
|
|
71
|
-
* Three branches rather than one, and the split at ±0.5 is the reason it is accurate near the ends:
|
|
72
|
-
* `acos` has infinite derivative at ±1, so a polynomial in `x` loses its footing there. Substituting
|
|
73
|
-
* `s = sqrt((1 ∓ x)/2)` moves the awkward point to zero, where a polynomial is comfortable — which
|
|
74
|
-
* is also why `sqrt` being exactly rounded matters here rather than being a convenience.
|
|
75
|
-
*/
|
|
76
|
-
export declare function exactAcos(x: number): number;
|