@driftengine/core 4.11.1 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +102 -0
- package/README.md +18 -9
- package/dist/geometry/meshBuilder.d.ts +10 -0
- package/dist/geometry/meshBuilder.js +11 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -2
- package/dist/math/color.d.ts +25 -0
- package/dist/math/color.js +37 -0
- package/dist/render/backend/webgl2/renderer.d.ts +33 -5
- package/dist/render/backend/webgl2/renderer.js +123 -49
- package/dist/render/backend/webgl2/skinScatter.d.ts +3 -1
- package/dist/render/backend/webgl2/skinScatter.js +5 -1
- package/dist/render/backend/webgpu/boltPass.d.ts +1 -1
- package/dist/render/backend/webgpu/causticsPass.d.ts +1 -1
- package/dist/render/backend/webgpu/filmPass.d.ts +1 -1
- package/dist/render/backend/webgpu/flockPass.d.ts +1 -1
- package/dist/render/backend/webgpu/giFieldPass.d.ts +3 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +10 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.js +28 -11
- package/dist/render/backend/webgpu/panelPass.d.ts +1 -1
- package/dist/render/backend/webgpu/postPass.d.ts +2 -2
- package/dist/render/backend/webgpu/renderer.d.ts +21 -8
- package/dist/render/backend/webgpu/renderer.js +103 -38
- package/dist/render/backend/webgpu/scatterPass.d.ts +1 -1
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +5 -1
- package/dist/render/backend/webgpu/skinScatterPass.js +9 -3
- package/dist/render/backend/webgpu/skyPass.d.ts +11 -1
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/backend/webgpu/windStreakPass.d.ts +1 -1
- package/dist/render/causticsRenderer.d.ts +3 -1
- package/dist/render/causticsRenderer.js +5 -1
- package/dist/render/decalPass.d.ts +3 -1
- package/dist/render/decalPass.js +4 -1
- package/dist/render/flockRenderer.d.ts +1 -1
- package/dist/render/flockRenderer.js +4 -1
- package/dist/render/globalMediumPass.d.ts +3 -1
- package/dist/render/globalMediumPass.js +5 -1
- package/dist/render/gradeColor.d.ts +21 -0
- package/dist/render/gradeColor.js +64 -0
- package/dist/render/lightMatrix.d.ts +13 -0
- package/dist/render/lightMatrix.js +17 -0
- package/dist/render/passGrade.d.ts +13 -0
- package/dist/render/passGrade.js +47 -0
- package/dist/render/plumeRenderer.d.ts +1 -1
- package/dist/render/plumeRenderer.js +4 -1
- package/dist/render/renderQuality.d.ts +57 -18
- package/dist/render/renderQuality.js +3 -3
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -0
- package/dist/render/sdfTextRenderer.d.ts +1 -1
- package/dist/render/sdfTextRenderer.js +1 -1
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/arcane.js +5 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +4 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/caustics.js +4 -1
- package/dist/render/shaders/decalProject.js +12 -2
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/film.js +22 -9
- package/dist/render/shaders/fire.d.ts +1 -1
- package/dist/render/shaders/fire.js +4 -1
- package/dist/render/shaders/flat/areaLight.d.ts +1 -1
- package/dist/render/shaders/flat/areaLight.js +1 -1
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +1 -0
- package/dist/render/shaders/flat/index.d.ts +5 -0
- package/dist/render/shaders/flat/index.js +1 -1
- package/dist/render/shaders/flat/layerLooks.d.ts +16 -0
- package/dist/render/shaders/flat/layerLooks.js +108 -0
- package/dist/render/shaders/flat/layered.d.ts +6 -4
- package/dist/render/shaders/flat/layered.js +77 -13
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +7 -2
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/preamble.js +6 -0
- package/dist/render/shaders/flock.d.ts +1 -1
- package/dist/render/shaders/flock.js +4 -1
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/arcane.wgsl.js +11 -1
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/bolt.wgsl.js +12 -2
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/caustics.wgsl.js +12 -2
- package/dist/render/shaders/generated/decalProject.wgsl.d.ts +6 -1
- package/dist/render/shaders/generated/decalProject.wgsl.js +6 -1
- package/dist/render/shaders/generated/film.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/film.wgsl.js +12 -2
- package/dist/render/shaders/generated/fire.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/fire.wgsl.js +11 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +144 -16
- package/dist/render/shaders/generated/flat.wgsl.js +183 -55
- package/dist/render/shaders/generated/flock.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/flock.wgsl.js +12 -2
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/globalMedium.wgsl.js +12 -2
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +36 -16
- package/dist/render/shaders/generated/lightVolume.wgsl.js +39 -19
- package/dist/render/shaders/generated/panel.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/panel.wgsl.js +12 -2
- package/dist/render/shaders/generated/rush.wgsl.d.ts +31 -26
- package/dist/render/shaders/generated/rush.wgsl.js +31 -26
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/scatter.wgsl.js +12 -2
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.js +11 -1
- package/dist/render/shaders/generated/sky.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/sky.wgsl.js +12 -2
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/smoke.wgsl.js +11 -1
- package/dist/render/shaders/generated/text.wgsl.d.ts +11 -1
- package/dist/render/shaders/generated/text.wgsl.js +11 -1
- package/dist/render/shaders/generated/water.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/water.wgsl.js +12 -2
- package/dist/render/shaders/generated/windStreaks.wgsl.d.ts +12 -2
- package/dist/render/shaders/generated/windStreaks.wgsl.js +12 -2
- package/dist/render/shaders/globalMedium.js +14 -1
- package/dist/render/shaders/gpudriven/present.wgsl.d.ts +15 -6
- package/dist/render/shaders/gpudriven/present.wgsl.js +71 -8
- package/dist/render/shaders/lightVolume.js +4 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +8 -2
- package/dist/render/shaders/outputTransform.js +18 -11
- package/dist/render/shaders/panel.d.ts +1 -1
- package/dist/render/shaders/panel.js +5 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/rush.js +8 -0
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/scatter.js +4 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/skinBlur.js +5 -1
- package/dist/render/shaders/sky.d.ts +1 -1
- package/dist/render/shaders/sky.js +5 -1
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/smoke.js +4 -1
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +4 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -2
- package/dist/render/shaders/windStreaks.d.ts +1 -1
- package/dist/render/shaders/windStreaks.js +4 -1
- package/dist/render/surfaceLayerLooks.d.ts +92 -0
- package/dist/render/surfaceLayerLooks.js +102 -0
- package/dist/render/surfaceLayers.d.ts +47 -6
- package/dist/render/surfaceLayers.js +5 -1
- package/dist/render/textRenderer.d.ts +3 -1
- package/dist/render/textRenderer.js +7 -1
- package/dist/render/vertexDefaults.d.ts +1 -1
- package/dist/render/vertexDefaults.js +1 -1
- package/dist/render/waterRenderer.d.ts +1 -1
- package/dist/render/waterRenderer.js +4 -1
- package/dist/render/windStreakRenderer.d.ts +3 -1
- package/dist/render/windStreakRenderer.js +5 -1
- package/package.json +6 -6
- package/scripts/browser.d.mts +5 -0
- package/scripts/browser.mjs +68 -14
- package/scripts/look.d.mts +81 -0
- package/scripts/look.mjs +380 -0
- package/src/geometry/meshBuilder.ts +21 -2
- package/src/index.ts +13 -2
- package/src/math/color.ts +39 -0
- package/src/render/backend/webgl2/renderer.ts +174 -46
- package/src/render/backend/webgl2/skinScatter.ts +5 -0
- package/src/render/backend/webgpu/giFieldPass.ts +3 -1
- package/src/render/backend/webgpu/gpuDrivenPass.ts +29 -13
- package/src/render/backend/webgpu/renderer.ts +112 -37
- package/src/render/backend/webgpu/skinScatterPass.ts +17 -1
- package/src/render/causticsRenderer.ts +5 -0
- package/src/render/decalPass.ts +3 -0
- package/src/render/flockRenderer.ts +5 -0
- package/src/render/globalMediumPass.ts +10 -1
- package/src/render/gradeColor.ts +90 -0
- package/src/render/lightMatrix.ts +32 -0
- package/src/render/passGrade.ts +58 -0
- package/src/render/plumeRenderer.ts +5 -0
- package/src/render/renderQuality.ts +60 -21
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/sdfTextRenderer.ts +1 -1
- package/src/render/shaders/arcane.ts +5 -1
- package/src/render/shaders/bolt.ts +4 -1
- package/src/render/shaders/caustics.ts +4 -1
- package/src/render/shaders/decalProject.ts +12 -2
- package/src/render/shaders/film.ts +22 -9
- package/src/render/shaders/fire.ts +4 -1
- package/src/render/shaders/flat/areaLight.ts +1 -1
- package/src/render/shaders/flat/glassTint.ts +3 -1
- package/src/render/shaders/flat/index.ts +6 -1
- package/src/render/shaders/flat/layerLooks.ts +108 -0
- package/src/render/shaders/flat/layered.ts +78 -13
- package/src/render/shaders/flat/main.ts +7 -2
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/preamble.ts +6 -0
- package/src/render/shaders/flock.ts +4 -1
- package/src/render/shaders/generated/arcane.wgsl.ts +11 -1
- package/src/render/shaders/generated/bolt.wgsl.ts +12 -2
- package/src/render/shaders/generated/caustics.wgsl.ts +12 -2
- package/src/render/shaders/generated/decalProject.wgsl.ts +6 -1
- package/src/render/shaders/generated/film.wgsl.ts +12 -2
- package/src/render/shaders/generated/fire.wgsl.ts +11 -1
- package/src/render/shaders/generated/flat.wgsl.ts +183 -55
- package/src/render/shaders/generated/flock.wgsl.ts +12 -2
- package/src/render/shaders/generated/globalMedium.wgsl.ts +12 -2
- package/src/render/shaders/generated/lightVolume.wgsl.ts +39 -19
- package/src/render/shaders/generated/panel.wgsl.ts +12 -2
- package/src/render/shaders/generated/rush.wgsl.ts +31 -26
- package/src/render/shaders/generated/scatter.wgsl.ts +12 -2
- package/src/render/shaders/generated/skinBlur.wgsl.ts +11 -1
- package/src/render/shaders/generated/sky.wgsl.ts +12 -2
- package/src/render/shaders/generated/smoke.wgsl.ts +11 -1
- package/src/render/shaders/generated/text.wgsl.ts +11 -1
- package/src/render/shaders/generated/water.wgsl.ts +12 -2
- package/src/render/shaders/generated/windStreaks.wgsl.ts +12 -2
- package/src/render/shaders/globalMedium.ts +14 -1
- package/src/render/shaders/gpudriven/present.wgsl.ts +72 -8
- package/src/render/shaders/lightVolume.ts +4 -1
- package/src/render/shaders/outputTransform.ts +19 -11
- package/src/render/shaders/panel.ts +5 -1
- package/src/render/shaders/rush.ts +8 -0
- package/src/render/shaders/scatter.ts +4 -1
- package/src/render/shaders/skinBlur.ts +5 -1
- package/src/render/shaders/sky.ts +5 -1
- package/src/render/shaders/smoke.ts +4 -1
- package/src/render/shaders/text.ts +4 -1
- package/src/render/shaders/water.ts +12 -2
- package/src/render/shaders/windStreaks.ts +4 -1
- package/src/render/surfaceLayerLooks.ts +186 -0
- package/src/render/surfaceLayers.ts +52 -7
- package/src/render/textRenderer.ts +6 -0
- package/src/render/vertexDefaults.ts +1 -1
- package/src/render/waterRenderer.ts +5 -0
- package/src/render/windStreakRenderer.ts +5 -0
|
@@ -25,7 +25,9 @@ export declare class TextRenderer {
|
|
|
25
25
|
*
|
|
26
26
|
* Answers whether it drew, which the renderer counts on its frame budget.
|
|
27
27
|
*/
|
|
28
|
-
draw(viewportWidth: number, viewportHeight: number, originX: number, originY: number, style: TextStyle, timeSec: number
|
|
28
|
+
draw(viewportWidth: number, viewportHeight: number, originX: number, originY: number, style: TextStyle, timeSec: number,
|
|
29
|
+
/** The renderer's `passGrade`: the frame's, but none in a probe's face or a capture. */
|
|
30
|
+
outputTransform?: number, outputExposure?: number): boolean;
|
|
29
31
|
dispose(): void;
|
|
30
32
|
/** Both instance buffers, from the arrays the layout filled in place. */
|
|
31
33
|
private upload;
|
|
@@ -67,11 +67,17 @@ export class TextRenderer {
|
|
|
67
67
|
*
|
|
68
68
|
* Answers whether it drew, which the renderer counts on its frame budget.
|
|
69
69
|
*/
|
|
70
|
-
draw(viewportWidth, viewportHeight, originX, originY, style, timeSec
|
|
70
|
+
draw(viewportWidth, viewportHeight, originX, originY, style, timeSec,
|
|
71
|
+
/** The renderer's `passGrade`: the frame's, but none in a probe's face or a capture. */
|
|
72
|
+
outputTransform = 0, outputExposure = 1) {
|
|
71
73
|
if (this.layout.instanceCount === 0 || style.alpha <= 0)
|
|
72
74
|
return false;
|
|
73
75
|
const gl = this.gl;
|
|
74
76
|
gl.useProgram(this.program);
|
|
77
|
+
const grade = this.uniforms['uOutputTransform'];
|
|
78
|
+
if (grade !== undefined)
|
|
79
|
+
gl.uniform1i(grade, outputTransform);
|
|
80
|
+
setFloat(gl, this.uniforms['uOutputExposure'], outputExposure);
|
|
75
81
|
gl.bindVertexArray(this.vao);
|
|
76
82
|
/*
|
|
77
83
|
* Text gets a fresh depth buffer rather than no depth test at all.
|
|
@@ -41,7 +41,7 @@ export declare const OUTPUT_TRANSFORM_CODE: Readonly<Record<string, number>>;
|
|
|
41
41
|
/**
|
|
42
42
|
* The code a **forward** pass grades with: the transform's own, except `filmic`, which only the
|
|
43
43
|
* composite carries the constants for, and which a pass grading itself therefore draws as `aces`.
|
|
44
|
-
*
|
|
44
|
+
* `passGrade.ts` reads this for both backends, so neither can hand a forward stage a code it lacks.
|
|
45
45
|
*/
|
|
46
46
|
export declare function forwardTransformCode(transform: string): number;
|
|
47
47
|
/**
|
|
@@ -115,7 +115,7 @@ export const OUTPUT_TRANSFORM_CODE = {
|
|
|
115
115
|
/**
|
|
116
116
|
* The code a **forward** pass grades with: the transform's own, except `filmic`, which only the
|
|
117
117
|
* composite carries the constants for, and which a pass grading itself therefore draws as `aces`.
|
|
118
|
-
*
|
|
118
|
+
* `passGrade.ts` reads this for both backends, so neither can hand a forward stage a code it lacks.
|
|
119
119
|
*/
|
|
120
120
|
export function forwardTransformCode(transform) {
|
|
121
121
|
return transform === 'filmic' ? 2 : (OUTPUT_TRANSFORM_CODE[transform] ?? 0);
|
|
@@ -158,6 +158,6 @@ export declare class WaterRenderer {
|
|
|
158
158
|
* by geometry in front of it, but its far edge fades out, and that fade needs
|
|
159
159
|
* the sky already behind it to blend into.
|
|
160
160
|
*/
|
|
161
|
-
draw(gl: WebGL2RenderingContext, camera: FrameView, timeSeconds: number, settings: WaterBody, directionalDir: Vec3, directionalColor: Vec3, ambient: Vec3, atmosphere: Atmosphere, underwaterEnabled: boolean, reflection: PlanarReflection | null, lights?: PointLightSet | null, lightFalloff?: 'smooth' | 'inverseSquare', windX?: number, windZ?: number): void;
|
|
161
|
+
draw(gl: WebGL2RenderingContext, camera: FrameView, timeSeconds: number, settings: WaterBody, directionalDir: Vec3, directionalColor: Vec3, ambient: Vec3, atmosphere: Atmosphere, underwaterEnabled: boolean, reflection: PlanarReflection | null, lights?: PointLightSet | null, lightFalloff?: 'smooth' | 'inverseSquare', windX?: number, windZ?: number, outputTransform?: number, outputExposure?: number): void;
|
|
162
162
|
dispose(gl: WebGL2RenderingContext): void;
|
|
163
163
|
}
|
|
@@ -95,10 +95,13 @@ export class WaterRenderer {
|
|
|
95
95
|
* by geometry in front of it, but its far edge fades out, and that fade needs
|
|
96
96
|
* the sky already behind it to blend into.
|
|
97
97
|
*/
|
|
98
|
-
draw(gl, camera, timeSeconds, settings, directionalDir, directionalColor, ambient, atmosphere, underwaterEnabled, reflection, lights = null, lightFalloff = 'smooth', windX = 0, windZ = 0) {
|
|
98
|
+
draw(gl, camera, timeSeconds, settings, directionalDir, directionalColor, ambient, atmosphere, underwaterEnabled, reflection, lights = null, lightFalloff = 'smooth', windX = 0, windZ = 0, outputTransform = 0, outputExposure = 1) {
|
|
99
99
|
const u = this.uniforms;
|
|
100
100
|
const reflectionReady = reflection?.isReadyFor(settings.level) ?? false;
|
|
101
101
|
gl.useProgram(this.program);
|
|
102
|
+
/* The renderer's `passGrade`: the frame's, but none in a probe's face or a capture. */
|
|
103
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, outputTransform);
|
|
104
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, outputExposure);
|
|
102
105
|
gl.uniformMatrix4fv(u['uViewProj'] ?? null, false, camera.viewProjection);
|
|
103
106
|
gl.uniformMatrix4fv(u['uReflectionViewProj'] ?? null, false, reflection?.viewProjection ?? camera.viewProjection);
|
|
104
107
|
gl.uniform3fv(u['uCameraPos'] ?? null, camera.position);
|
|
@@ -33,6 +33,8 @@ export declare class WindStreakRenderer {
|
|
|
33
33
|
/** Answers whether it drew, which the renderer counts on its frame budget. */
|
|
34
34
|
draw(gl: WebGL2RenderingContext, camera: FrameView, wind: WindField, timeSeconds: number, tint: Vec3,
|
|
35
35
|
/** True when the camera is below the waterline. */
|
|
36
|
-
submerged?: boolean
|
|
36
|
+
submerged?: boolean,
|
|
37
|
+
/** The renderer's `passGrade`: the frame's, but none in a probe's face or a capture. */
|
|
38
|
+
outputTransform?: number, outputExposure?: number): boolean;
|
|
37
39
|
dispose(gl: WebGL2RenderingContext): void;
|
|
38
40
|
}
|
|
@@ -51,7 +51,9 @@ export class WindStreakRenderer {
|
|
|
51
51
|
/** Answers whether it drew, which the renderer counts on its frame budget. */
|
|
52
52
|
draw(gl, camera, wind, timeSeconds, tint,
|
|
53
53
|
/** True when the camera is below the waterline. */
|
|
54
|
-
submerged = false
|
|
54
|
+
submerged = false,
|
|
55
|
+
/** The renderer's `passGrade`: the frame's, but none in a probe's face or a capture. */
|
|
56
|
+
outputTransform = 0, outputExposure = 1) {
|
|
55
57
|
/* Chosen in `windStreakDraw.ts` so both backends show the same weather. */
|
|
56
58
|
const settled = resolveWindStreaks(wind.speed, wind.driftX, wind.driftZ, this.onsetSpeed, this.fullSpeed, submerged, this.resolved);
|
|
57
59
|
if (!settled.visible)
|
|
@@ -68,6 +70,8 @@ export class WindStreakRenderer {
|
|
|
68
70
|
gl.uniform1f(u['uCellSize'] ?? null, this.cellSize);
|
|
69
71
|
gl.uniform1f(u['uTime'] ?? null, timeSeconds);
|
|
70
72
|
gl.uniform3fv(u['uTint'] ?? null, tint);
|
|
73
|
+
gl.uniform1i(u['uOutputTransform'] ?? null, outputTransform);
|
|
74
|
+
gl.uniform1f(u['uOutputExposure'] ?? null, outputExposure);
|
|
71
75
|
gl.enable(gl.BLEND);
|
|
72
76
|
blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
73
77
|
gl.depthMask(false);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.13.0",
|
|
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.
|
|
33
|
-
"@driftengine/physics": "4.
|
|
32
|
+
"@driftengine/drft": "4.13.0",
|
|
33
|
+
"@driftengine/physics": "4.13.0",
|
|
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.
|
|
71
|
-
"@driftengine/network": "4.
|
|
72
|
-
"@driftengine/texture": "4.
|
|
70
|
+
"@driftengine/nav": "4.13.0",
|
|
71
|
+
"@driftengine/network": "4.13.0",
|
|
72
|
+
"@driftengine/texture": "4.13.0"
|
|
73
73
|
}
|
|
74
74
|
}
|
package/scripts/browser.d.mts
CHANGED
|
@@ -7,6 +7,11 @@ export interface LaunchedBrowser {
|
|
|
7
7
|
close(): Promise<void>;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/** Where a Chromium-based browser lives on a platform, in the order `chromePath` tries them. */
|
|
11
|
+
export function browserCandidates(
|
|
12
|
+
platform?: string,
|
|
13
|
+
env?: Readonly<Record<string, string | undefined>>,
|
|
14
|
+
): string[];
|
|
10
15
|
export function chromePath(): string;
|
|
11
16
|
export function launch(options?: {
|
|
12
17
|
flags?: readonly string[];
|
package/scripts/browser.mjs
CHANGED
|
@@ -27,22 +27,64 @@
|
|
|
27
27
|
* it, and it is what turns "leaks accumulate forever" into "at most one leak per still-running
|
|
28
28
|
* process."
|
|
29
29
|
*/
|
|
30
|
-
import { spawn } from 'node:child_process';
|
|
30
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
31
31
|
import { once } from 'node:events';
|
|
32
32
|
import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
33
33
|
import { tmpdir } from 'node:os';
|
|
34
34
|
import path from 'node:path';
|
|
35
35
|
import { sleep } from './cdp.mjs';
|
|
36
36
|
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Where a Chromium-based browser lives on each platform, in the order worth trying.
|
|
39
|
+
*
|
|
40
|
+
* **Edge is last on every list and the only thing on Windows that is always there.** It is
|
|
41
|
+
* Chromium, it speaks the same DevTools protocol, and it ships with Windows 10 and 11, so a person
|
|
42
|
+
* who never installed Chrome still has a browser this module can drive. Chrome comes first where it
|
|
43
|
+
* exists because it is what the engine's captures are measured on.
|
|
44
|
+
*
|
|
45
|
+
* Windows paths are built from the environment rather than written out, because `Program Files`
|
|
46
|
+
* is wherever that machine put it and a per-user install sits under `LOCALAPPDATA`; a Mac's
|
|
47
|
+
* per-user install sits under `~/Applications`. Nothing here has been run on Windows or macOS by
|
|
48
|
+
* this repository yet; the lists are the browsers' documented install locations.
|
|
49
|
+
*/
|
|
50
|
+
export function browserCandidates(platform = process.platform, env = process.env) {
|
|
51
|
+
if (platform === 'win32') {
|
|
52
|
+
const roots = [env['PROGRAMFILES'], env['PROGRAMFILES(X86)'], env['LOCALAPPDATA']].filter(
|
|
53
|
+
(root) => typeof root === 'string' && root !== '',
|
|
54
|
+
);
|
|
55
|
+
return [
|
|
56
|
+
...roots.map((root) =>
|
|
57
|
+
path.win32.join(root, 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
|
58
|
+
),
|
|
59
|
+
...roots.map((root) =>
|
|
60
|
+
path.win32.join(root, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
|
|
61
|
+
),
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
if (platform === 'darwin') {
|
|
65
|
+
/* `/Applications` for an install by an administrator, `~/Applications` for one without. */
|
|
66
|
+
const roots = [
|
|
67
|
+
'/Applications',
|
|
68
|
+
typeof env['HOME'] === 'string' && env['HOME'] !== ''
|
|
69
|
+
? path.posix.join(env['HOME'], 'Applications')
|
|
70
|
+
: null,
|
|
71
|
+
].filter((root) => root !== null);
|
|
72
|
+
const apps = [
|
|
73
|
+
'Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
74
|
+
'Chromium.app/Contents/MacOS/Chromium',
|
|
75
|
+
'Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
|
|
76
|
+
];
|
|
77
|
+
return apps.flatMap((app) => roots.map((root) => path.posix.join(root, app)));
|
|
78
|
+
}
|
|
79
|
+
return [
|
|
80
|
+
'/opt/google/chrome/chrome',
|
|
81
|
+
'/usr/bin/google-chrome',
|
|
82
|
+
'/usr/bin/google-chrome-stable',
|
|
83
|
+
'/usr/bin/chromium',
|
|
84
|
+
'/usr/bin/chromium-browser',
|
|
85
|
+
'/usr/bin/microsoft-edge',
|
|
86
|
+
];
|
|
87
|
+
}
|
|
46
88
|
|
|
47
89
|
/**
|
|
48
90
|
* What it takes to reach the real card from a headless process.
|
|
@@ -80,7 +122,7 @@ const PROFILE_PREFIX = 'driftengine-shots-';
|
|
|
80
122
|
const OWNER_FILE = 'driftengine-owner.json';
|
|
81
123
|
|
|
82
124
|
/**
|
|
83
|
-
*
|
|
125
|
+
* Stop a browser and every process it started, escalating if it does not go.
|
|
84
126
|
*
|
|
85
127
|
* **Chrome is a tree, not a process.** It forks a zygote, a GPU process and one renderer per
|
|
86
128
|
* page, and signalling the parent alone leaves the rest running with the port closed and the
|
|
@@ -89,9 +131,20 @@ const OWNER_FILE = 'driftengine-owner.json';
|
|
|
89
131
|
*
|
|
90
132
|
* SIGTERM first because a browser asked politely flushes and exits; SIGKILL after a grace period
|
|
91
133
|
* because one that is wedged never will, and a wedged browser is exactly the case this exists for.
|
|
134
|
+
*
|
|
135
|
+
* **Windows has no process groups to signal**, so there `taskkill /T` walks the tree from the
|
|
136
|
+
* browser's own PID and `/F` ends it at once. Synchronous, because this also runs from an `exit`
|
|
137
|
+
* handler, where nothing asynchronous gets to finish.
|
|
92
138
|
*/
|
|
93
139
|
function killTree(pid, graceMs = 2000) {
|
|
94
140
|
if (!Number.isInteger(pid) || pid <= 0) return;
|
|
141
|
+
if (process.platform === 'win32') {
|
|
142
|
+
spawnSync('taskkill', ['/pid', String(pid), '/T', '/F'], {
|
|
143
|
+
stdio: 'ignore',
|
|
144
|
+
windowsHide: true,
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
95
148
|
try {
|
|
96
149
|
process.kill(-pid, 'SIGTERM');
|
|
97
150
|
} catch {
|
|
@@ -194,10 +247,11 @@ function defaultIsAlive(pid) {
|
|
|
194
247
|
export function chromePath() {
|
|
195
248
|
const asked = process.env['CHROME_PATH'];
|
|
196
249
|
if (asked !== undefined && asked !== '') return asked;
|
|
197
|
-
const
|
|
250
|
+
const candidates = browserCandidates();
|
|
251
|
+
const found = candidates.find((candidate) => existsSync(candidate));
|
|
198
252
|
if (found === undefined) {
|
|
199
253
|
throw new Error(
|
|
200
|
-
`no Chrome or
|
|
254
|
+
`no Chrome, Chromium or Edge found. Tried:\n ${candidates.join('\n ')}\nSet CHROME_PATH to one.`,
|
|
201
255
|
);
|
|
202
256
|
}
|
|
203
257
|
return found;
|
|
@@ -227,7 +281,7 @@ export async function launch({ flags = GPU_FLAGS, headless = true, timeoutMs = 2
|
|
|
227
281
|
process and every renderer with one signal to the negative PID. `unref()` is deliberately
|
|
228
282
|
not called: the parent must stay attached, or its own exit and signal handlers below never
|
|
229
283
|
fire, and those are half of what closes the other three holes. */
|
|
230
|
-
const child = spawn(binary, args, { stdio: 'ignore', detached: true });
|
|
284
|
+
const child = spawn(binary, args, { stdio: 'ignore', detached: true, windowsHide: true });
|
|
231
285
|
writeFileSync(
|
|
232
286
|
path.join(profile, OWNER_FILE),
|
|
233
287
|
JSON.stringify({ ownerPid: process.pid, chromePid: child.pid }),
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { RgbaImage } from './png.d.mts';
|
|
2
|
+
|
|
3
|
+
/** The backends `look` photographs by default, in the order a consumer runs them. */
|
|
4
|
+
export const LOOK_BACKENDS: readonly ['webgpu', 'webgl2'];
|
|
5
|
+
|
|
6
|
+
/** At or above this share of one colour, a canvas is called empty. */
|
|
7
|
+
export const EMPTY_SHARE: number;
|
|
8
|
+
|
|
9
|
+
/** What a frame is made of. */
|
|
10
|
+
export interface FrameStats {
|
|
11
|
+
readonly width: number;
|
|
12
|
+
readonly height: number;
|
|
13
|
+
/** The most common colour, as `#rrggbb`. */
|
|
14
|
+
readonly dominant: string;
|
|
15
|
+
/** The share of the frame that colour covers, 0 to 1. */
|
|
16
|
+
readonly dominantShare: number;
|
|
17
|
+
/** Rec. 709 luminance averaged over the frame, 0 to 255. */
|
|
18
|
+
readonly meanLuminance: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** One backend's photograph and everything the page said while it was taken. */
|
|
22
|
+
export interface LookCapture {
|
|
23
|
+
readonly backend: string;
|
|
24
|
+
readonly url: string;
|
|
25
|
+
/** What a player sees, interface included. */
|
|
26
|
+
readonly file: string;
|
|
27
|
+
/**
|
|
28
|
+
* The backend that actually drew, read from the context the page's canvas asked for: `'webgpu'`,
|
|
29
|
+
* `'webgl2'`, or `null` when no canvas in the page asked for either.
|
|
30
|
+
*/
|
|
31
|
+
readonly drawnWith: 'webgpu' | 'webgl2' | null;
|
|
32
|
+
/** Measured over the canvas alone; `null` when the page never booted. */
|
|
33
|
+
readonly stats: FrameStats | null;
|
|
34
|
+
/** The share of the canvas that changed between two frames; `null` when the page never booted. */
|
|
35
|
+
readonly moved: number | null;
|
|
36
|
+
readonly errors: readonly string[];
|
|
37
|
+
readonly warnings: readonly string[];
|
|
38
|
+
/** Every console line, `level: text`. */
|
|
39
|
+
readonly logs: readonly string[];
|
|
40
|
+
readonly problems: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface LookReport {
|
|
44
|
+
readonly url: string;
|
|
45
|
+
/** The renderer string of the GPU that drew, read from a WebGL2 context. */
|
|
46
|
+
readonly gpu: string;
|
|
47
|
+
readonly captures: readonly LookCapture[];
|
|
48
|
+
readonly problems: readonly string[];
|
|
49
|
+
/** True when no capture found a problem. */
|
|
50
|
+
readonly ok: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface LookOptions {
|
|
54
|
+
/** The address of the running game. */
|
|
55
|
+
readonly url: string;
|
|
56
|
+
readonly backends?: readonly string[];
|
|
57
|
+
/** Where the PNGs and `look.json` go. Defaults to `.driftengine/look`. */
|
|
58
|
+
readonly out?: string;
|
|
59
|
+
readonly width?: number;
|
|
60
|
+
readonly height?: number;
|
|
61
|
+
/** How long to wait for a canvas in the page to ask for a WebGPU or WebGL2 context. */
|
|
62
|
+
readonly bootMs?: number;
|
|
63
|
+
/** A pause after that for the first frames. */
|
|
64
|
+
readonly settleMs?: number;
|
|
65
|
+
/** How long to wait after that for the canvas to show more than one colour. */
|
|
66
|
+
readonly drawMs?: number;
|
|
67
|
+
/** The gap between the two frames compared to tell whether the game moves. */
|
|
68
|
+
readonly moveMs?: number;
|
|
69
|
+
/** Browser flags; defaults to `gpuFlagsFor()`. */
|
|
70
|
+
readonly flags?: readonly string[] | null;
|
|
71
|
+
/** `false` opens a real window, for a machine whose headless browser cannot reach its GPU. */
|
|
72
|
+
readonly headless?: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function gpuFlagsFor(platform?: string): string[] | null;
|
|
76
|
+
export function lookUrl(url: string, backend: string): string;
|
|
77
|
+
export function frameStats(image: RgbaImage): FrameStats;
|
|
78
|
+
export function changedShare(a: RgbaImage, b: RgbaImage, tolerance?: number): number;
|
|
79
|
+
export function sortConsole(lines: readonly string[]): { errors: string[]; warnings: string[] };
|
|
80
|
+
export function look(options: LookOptions): Promise<LookReport>;
|
|
81
|
+
export function formatLook(report: LookReport): string;
|