mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
fix(sky): restore the Milky Way on teardown, tidy the anime QA script
- VisualEffects.destroy() puts skyBox.milkyWayVisibility back to 1, as it already restores the borrowed bloom stage, so a surviving viewer does not keep a faded sky. - Rename scripts/qa-anime-space-mask.mjs to qa-anime-sky.mjs: rewrite the header for the current approach, drop --space-expr and --repo, import puppeteer as a package, reject unknown cases and count the milkyway case as reached only while the anime stage is on. - Put both sky QA scripts under format:check. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
65b98b4971
commit
8deb47560e
4 files changed
+77
-41
No files matched your search
@@ -1076,5 +1076,7 @@
|
|||||||
"src/sky/stars.test.mjs",
|
"src/sky/stars.test.mjs",
|
||||||
"src/sky/starrySky.test.mjs",
|
"src/sky/starrySky.test.mjs",
|
||||||
"src/tooling/skyAssets.test.mjs",
|
"src/tooling/skyAssets.test.mjs",
|
||||||
"src/tooling/skyAssets.paintOut.test.mjs"
|
"src/tooling/skyAssets.paintOut.test.mjs",
|
||||||
|
"scripts/qa-sky-sun-moon.mjs",
|
||||||
|
"scripts/qa-anime-sky.mjs"
|
||||||
]
|
]
|
||||||
@@ -1,44 +1,51 @@
|
|||||||
// Repro for revmain-opus-1 (+ revmain-opus-6, + a guard for the fix).
|
#!/usr/bin/env node
|
||||||
// Anime exempts "space" pixels from cel-shading (src/styles/anime.js). This
|
/**
|
||||||
// script compiles the CURRENT anime fragment shader from the checkout with its
|
* qa-anime-sky — the anime style must cel-shade the Earth, and leave the sky
|
||||||
// last line swapped for a debug output (R = pixel exempt from quantization,
|
* without bands.
|
||||||
// G = raw depth 1.0), runs it as an extra post-process stage in the running app
|
*
|
||||||
// (headless) and counts misclassified pixels.
|
* Earlier the anime shader exempted the far plane, judged from the
|
||||||
//
|
* post-process depth, from its luminance bands. That depth cannot tell the
|
||||||
// Cases:
|
* sky from far geometry, so the Earth lost its bands. The shader now matches
|
||||||
// satellites whole globe from 20 000 km with the satellites layer on
|
* main, and src/ui/visualEffects.js fades the Milky Way out with the style.
|
||||||
// (near clamps to 0.1 m): Earth pixels must NOT be exempt. [revmain-opus-1]
|
* Each case runs headless against a Vite dev server:
|
||||||
// keyless keyless stack, Denver 1700 m looking west: terrain above the
|
*
|
||||||
// ellipsoid horizon must NOT be exempt. [revmain-opus-6]
|
* satellites whole globe from 20 000 km with the satellites layer on
|
||||||
// milkyway the real anime style, looking away from the Earth at the
|
* (near clamps to 0.1 m): no Earth pixel may skip the bands.
|
||||||
// Galactic Centre: the sky must show no cel bands (the faint
|
* keyless keyless stack, Denver 1700 m looking west: no terrain pixel
|
||||||
// glow used to turn into blotches; the glow now fades out
|
* above the ellipsoid horizon may skip the bands.
|
||||||
// with the style, src/ui/visualEffects.js).
|
* milkyway the real anime style, looking away from the Earth at the
|
||||||
//
|
* Galactic Centre: at most 0.5 % of the sky (the stars) may
|
||||||
// Usage: node scripts/qa-anime-space-mask.mjs --url http://localhost:4333/ [--case satellites|keyless|milkyway|all]
|
* reach the first band.
|
||||||
// [--space-expr "<GLSL bool expression>"] (try a candidate fix without editing anime.js)
|
*
|
||||||
// Exit: 0 all pass, 1 a case failed, 2 a scenario was not reached.
|
* The first two cases compile the checkout's anime shader with a debug
|
||||||
|
* output (R = pixel skipped the bands, G = raw depth 1.0) as an extra stage.
|
||||||
|
*
|
||||||
|
* Usage: node scripts/qa-anime-sky.mjs [--url http://localhost:4333/] [--case satellites|keyless|milkyway|all]
|
||||||
|
* Exit: 0 all pass, 1 a case failed, 2 a scenario was not reached or the case is unknown.
|
||||||
|
*/
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import path from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
const opt = (name, fallback) => {
|
const opt = (name, fallback) => {
|
||||||
const i = process.argv.indexOf(`--${name}`);
|
const i = process.argv.indexOf(`--${name}`);
|
||||||
return i > 0 && process.argv[i + 1] ? process.argv[i + 1] : fallback;
|
return i > 0 && process.argv[i + 1] ? process.argv[i + 1] : fallback;
|
||||||
};
|
};
|
||||||
const ROOT = path.resolve(opt('repo', process.cwd()));
|
|
||||||
const URL_ = opt('url', 'http://localhost:4333/');
|
const URL_ = opt('url', 'http://localhost:4333/');
|
||||||
const CASES =
|
const ALL_CASES = ['satellites', 'keyless', 'milkyway'];
|
||||||
opt('case', 'all') === 'all'
|
const requested = opt('case', 'all');
|
||||||
? ['satellites', 'keyless', 'milkyway']
|
const CASES = requested === 'all' ? ALL_CASES : [requested];
|
||||||
: [opt('case')];
|
if (!CASES.every((c) => ALL_CASES.includes(c))) {
|
||||||
const SPACE_EXPR = opt('space-expr');
|
console.error(
|
||||||
// Run from the repository root, or pass --repo.
|
`unknown case ${requested}; expected ${ALL_CASES.join('|')}|all`,
|
||||||
const { default: puppeteer } = await import(
|
|
||||||
path.join(ROOT, 'node_modules/puppeteer/lib/puppeteer/puppeteer.js')
|
|
||||||
);
|
);
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
let fs = readFileSync(path.join(ROOT, 'src/styles/anime.js'), 'utf8');
|
let fs = readFileSync(
|
||||||
|
fileURLToPath(new URL('../src/styles/anime.js', import.meta.url)),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
fs = fs
|
fs = fs
|
||||||
.slice(fs.indexOf('/* glsl */ `') + 12, fs.lastIndexOf('`'))
|
.slice(fs.indexOf('/* glsl */ `') + 12, fs.lastIndexOf('`'))
|
||||||
.replace('uniform float intensity;', 'const float intensity = 1.0;')
|
.replace('uniform float intensity;', 'const float intensity = 1.0;')
|
||||||
@@ -49,11 +56,6 @@ if (!/uniform\s+sampler2D\s+depthTexture/.test(fs))
|
|||||||
'uniform sampler2D colorTexture;',
|
'uniform sampler2D colorTexture;',
|
||||||
'uniform sampler2D colorTexture;\n uniform sampler2D depthTexture;',
|
'uniform sampler2D colorTexture;\n uniform sampler2D depthTexture;',
|
||||||
);
|
);
|
||||||
if (SPACE_EXPR) {
|
|
||||||
const m = fs.match(/bool space = ([^;]+);/);
|
|
||||||
if (!m) throw new Error('--space-expr: no `bool space = …;` in anime.js');
|
|
||||||
fs = fs.replace(m[0], `bool space = ${SPACE_EXPR};`);
|
|
||||||
}
|
|
||||||
if (!/float bands\b/.test(fs))
|
if (!/float bands\b/.test(fs))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'anime.js has no `bands` variable: adapt the debug line of this script',
|
'anime.js has no `bands` variable: adapt the debug line of this script',
|
||||||
@@ -289,7 +291,15 @@ try {
|
|||||||
total: px.length / 4,
|
total: px.length / 4,
|
||||||
banded,
|
banded,
|
||||||
visibility: s.skyBox.milkyWayVisibility,
|
visibility: s.skyBox.milkyWayVisibility,
|
||||||
anime: g.styleManager.currentStyle ?? null,
|
animeOn: (() => {
|
||||||
|
const stages = s.postProcessStages;
|
||||||
|
for (let i = 0; i < stages.length; i++) {
|
||||||
|
const stage = stages.get(i);
|
||||||
|
if (stage.name === 'godsEyeView_anime')
|
||||||
|
return stage.enabled && stage.uniforms.intensity > 0.99;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})(),
|
||||||
};
|
};
|
||||||
g.styleManager.setStyle('normal', { userInitiated: true });
|
g.styleManager.setStyle('normal', { userInitiated: true });
|
||||||
window.__dbg.enabled = true;
|
window.__dbg.enabled = true;
|
||||||
@@ -298,7 +308,7 @@ try {
|
|||||||
verdict(
|
verdict(
|
||||||
'milkyway',
|
'milkyway',
|
||||||
r.banded <= 0.005 * r.total,
|
r.banded <= 0.005 * r.total,
|
||||||
r.total > 0,
|
r.total > 0 && r.animeOn,
|
||||||
`${r.banded} of ${r.total} sky pixels at or above the first cel band (luma >= 0.2)`,
|
`${r.banded} of ${r.total} sky pixels at or above the first cel band (luma >= 0.2)`,
|
||||||
r,
|
r,
|
||||||
);
|
);
|
||||||
@@ -115,7 +115,7 @@ export class VisualEffects {
|
|||||||
// Anime's four luminance bands turn the faint Milky Way into blotches, and
|
// Anime's four luminance bands turn the faint Milky Way into blotches, and
|
||||||
// a post-process cannot tell the sky from far geometry reliably (near
|
// a post-process cannot tell the sky from far geometry reliably (near
|
||||||
// plane clamps, keyless terrain above the ellipsoid horizon): fade the
|
// plane clamps, keyless terrain above the ellipsoid horizon): fade the
|
||||||
// glow out with the style instead. Stars survive as banded points.
|
// glow out with the style instead. Bright stars survive as banded points.
|
||||||
if (stage === this.stages.anime) {
|
if (stage === this.stages.anime) {
|
||||||
const sky = this.viewer.scene?.skyBox;
|
const sky = this.viewer.scene?.skyBox;
|
||||||
if (sky && 'milkyWayVisibility' in sky)
|
if (sky && 'milkyWayVisibility' in sky)
|
||||||
@@ -228,7 +228,7 @@ export class VisualEffects {
|
|||||||
this.transitions.clear();
|
this.transitions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove owned stages and restore the borrowed bloom stage after callers release it. */
|
/** Remove owned stages and restore the borrowed bloom stage and sky after callers release them. */
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.destroyed) return;
|
if (this.destroyed) return;
|
||||||
this.stop();
|
this.stop();
|
||||||
@@ -240,5 +240,11 @@ export class VisualEffects {
|
|||||||
Object.assign(this.bloomStage.uniforms, this.previousBloom.uniforms);
|
Object.assign(this.bloomStage.uniforms, this.previousBloom.uniforms);
|
||||||
this.bloomStage.enabled = this.previousBloom.enabled;
|
this.bloomStage.enabled = this.previousBloom.enabled;
|
||||||
}
|
}
|
||||||
|
// Anime may have faded the Milky Way out; a surviving viewer gets it back.
|
||||||
|
const sky = this.viewer.scene.skyBox;
|
||||||
|
if (sky && 'milkyWayVisibility' in sky) {
|
||||||
|
sky.milkyWayVisibility = 1;
|
||||||
|
this.requestRender('style-stage');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,6 +179,24 @@ test('the anime stage fades the Milky Way out with its own intensity', () => {
|
|||||||
f.effects.destroy();
|
f.effects.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('destroying the effects gives a surviving viewer its Milky Way back', () => {
|
||||||
|
const f = fixture();
|
||||||
|
const sky = { milkyWayVisibility: 1 };
|
||||||
|
f.effects.viewer.scene.skyBox = sky;
|
||||||
|
f.effects.initStyles();
|
||||||
|
f.effects.startTransition('anime', 0, 1);
|
||||||
|
f.tick(250);
|
||||||
|
f.effects.stop();
|
||||||
|
const frozen = sky.milkyWayVisibility;
|
||||||
|
assert.ok(
|
||||||
|
frozen > 0 && frozen < 1,
|
||||||
|
'stop keeps the stages, and the fade, as they are',
|
||||||
|
);
|
||||||
|
f.effects.destroy();
|
||||||
|
assert.equal(sky.milkyWayVisibility, 1);
|
||||||
|
assert.equal(f.requests.at(-1), 'style-stage');
|
||||||
|
});
|
||||||
|
|
||||||
test('bloom retains its dead zone, normalized intensity and independent toggle', () => {
|
test('bloom retains its dead zone, normalized intensity and independent toggle', () => {
|
||||||
const f = fixture();
|
const f = fixture();
|
||||||
f.effects.initPostProcess();
|
f.effects.initPostProcess();
|
||||||
|
|||||||
Reference in new issue
Block a user