mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-11 13:52:19 +00:00
Merge branch 'german/refactor-navara-tiles-drape' into german/navara-refactor
This commit is contained in:
commit
2c62e1e378
5 files changed
+207
-42
No files matched your search
@@ -25,7 +25,11 @@ import {
|
||||
Uniform,
|
||||
} from 'three';
|
||||
import { DASH_EDGE, groundSeconds } from '../../ground.js';
|
||||
import { groundSurfaceUniforms, updateGroundSurface } from './surface.js';
|
||||
import {
|
||||
GROUND_SURFACE_GLSL,
|
||||
groundSurfaceUniforms,
|
||||
updateGroundSurface,
|
||||
} from './surface.js';
|
||||
import { withFrameUpdates, withPassDisposal } from '../frameDescriptor.js';
|
||||
import { REVEAL_OFFSET, ITEM_TEXELS, LOOK, MARGIN_RATIO } from './corridors.js';
|
||||
import {
|
||||
@@ -64,6 +68,7 @@ vec4 atSlot(highp sampler2D image, int slot) {
|
||||
}
|
||||
${DISPLAY_COLOR_GLSL}
|
||||
${VIEW_POSITION_GLSL}
|
||||
${GROUND_SURFACE_GLSL}
|
||||
// A corridor item over the colour, nearest metres from its line and along
|
||||
// metres from its start: its width, glow, pulse and dash, in LDR and
|
||||
// premultiplied, as Cesium writes ground polylines.
|
||||
@@ -119,7 +124,9 @@ vec3 blendCorridor(vec3 color, int item, float nearest, vec2 normal, float along
|
||||
}
|
||||
void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) {
|
||||
outputColor = inputColor;
|
||||
float depth = unpackRGBAToDepth(texture2D(tAllDepth, uv));
|
||||
// All three depths must name one pixel, including at tile boundaries.
|
||||
ivec2 depthPixel = ivec2(uv * vec2(textureSize(tAllDepth, 0)));
|
||||
float depth = unpackRGBAToDepth(texelFetch(tAllDepth, depthPixel, 0));
|
||||
float distance = viewDistance(depth, uLogDepthFC);
|
||||
vec4 ray = uInverseProjection * vec4(uv * 2.0 - 1.0, -1.0, 1.0);
|
||||
vec3 point = viewPosition(ray, distance);
|
||||
@@ -135,11 +142,10 @@ void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor)
|
||||
ivec2 tile = clamp(ivec2(floor(uv * vec2(${GRID_COLS}.0, ${GRID_ROWS}.0))), ivec2(0), ivec2(${GRID_COLS - 1}, ${GRID_ROWS - 1}));
|
||||
vec2 range = texelFetch(tGrid, tile, 0).xy;
|
||||
if (range.y < 0.5) return;
|
||||
float surface = texture2D(tSurfaceDepth, uv).r;
|
||||
// The packed depth also holds forward meshes, which are not ground.
|
||||
if (depth < surface - 2e-7) return;
|
||||
float globeDepth = unpackRGBAToDepth(texture2D(tGlobeDepth, uv));
|
||||
bool onTiles = surface < globeDepth - 2e-7;
|
||||
float surface = texelFetch(tSurfaceDepth, depthPixel, 0).r;
|
||||
float globeDepth = unpackRGBAToDepth(texelFetch(tGlobeDepth, depthPixel, 0));
|
||||
float surfaceType = groundSurfaceType(depth, surface, globeDepth);
|
||||
if (surfaceType == 0.0) return;
|
||||
vec3 color = toDisplay(inputColor.rgb);
|
||||
// The pixel's ground in world space, once: every corridor tile tests the
|
||||
// same value, so exactly one tile of a set holds it.
|
||||
@@ -154,7 +160,7 @@ void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor)
|
||||
vec4 east = atSlot(tFrames, id * ${TEXELS} + 1);
|
||||
vec4 north = atSlot(tFrames, id * ${TEXELS} + 2);
|
||||
vec4 surfaceFrame = atSlot(tFrames, id * ${TEXELS} + 3);
|
||||
if ((surfaceFrame.w == 1.0 && onTiles) || (surfaceFrame.w == 2.0 && !onTiles)) continue;
|
||||
if (surfaceFrame.w != 0.0 && surfaceFrame.w != surfaceType) continue;
|
||||
vec3 up = surfaceFrame.xyz;
|
||||
vec4 span = atSlot(tStyles, id * ${STYLE_TEXELS} + 3);
|
||||
vec3 delta = point - origin.xyz;
|
||||
@@ -453,7 +459,6 @@ class GroundEffectDesc extends withPassDisposal(withFrameUpdates(EffectDesc)) {
|
||||
textures.candidates.needsUpdate = true;
|
||||
}
|
||||
const u = this.effect.uniforms;
|
||||
u.get('tGlobeDepth').value = mrt.globeDepthBuffer;
|
||||
u.get('uViewToWorld').value.copy(camera.matrixWorld);
|
||||
u.get('uTime').value = groundSeconds();
|
||||
u.get('uPixelRatio').value = pixelRatio;
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
// Public depth inputs shared by actual terrain/tiles classifiers.
|
||||
import { Matrix4, Uniform, RGBADepthPacking } from 'three';
|
||||
|
||||
export const GROUND_SURFACE_GLSL = /* glsl */ `
|
||||
float groundSurfaceType(float depth, float surface, float globeDepth) {
|
||||
if (depth >= 1.0) return 0.0;
|
||||
// The scene target can be cleared after the globe is saved. Classify
|
||||
// the visible depth, not a difference between the two source buffers.
|
||||
if (globeDepth < 1.0 && abs(depth - globeDepth) <= 2e-7) return 1.0;
|
||||
if (surface < 1.0 && abs(depth - surface) <= 2e-7) return 2.0;
|
||||
// A forward mesh has depth in neither ground buffer.
|
||||
return 0.0;
|
||||
}
|
||||
`;
|
||||
|
||||
export function groundSurfaceUniforms({ surface = true } = {}) {
|
||||
return [
|
||||
['tAllDepth', new Uniform(null)],
|
||||
@@ -15,6 +28,11 @@ export function updateGroundSurface(effect, camera, context, mrt) {
|
||||
u.get('tAllDepth').value = mrt.depthBuffer;
|
||||
if (u.has('tSurfaceDepth'))
|
||||
u.get('tSurfaceDepth').value = context.getRenderTarget().depthTexture;
|
||||
if (u.has('tGlobeDepth')) {
|
||||
if (mrt.globeDepthBufferPacking !== RGBADepthPacking)
|
||||
throw new Error('Ground classification requires packed globe depth');
|
||||
u.get('tGlobeDepth').value = mrt.globeDepthBuffer;
|
||||
}
|
||||
u.get('uInverseProjection').value.copy(camera.projectionMatrixInverse);
|
||||
u.get('uLogDepthFC').value = 2 / Math.log2(camera.far + 1);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { BasicDepthPacking, Matrix4, RGBADepthPacking, Uniform } from 'three';
|
||||
import {
|
||||
GROUND_SURFACE_GLSL,
|
||||
groundSurfaceUniforms,
|
||||
updateGroundSurface,
|
||||
} from './surface.js';
|
||||
|
||||
// Execute the scalar shader decision with the same inputs as a depth texel.
|
||||
const classify = new Function(
|
||||
'abs',
|
||||
`${GROUND_SURFACE_GLSL.replace('float groundSurfaceType', 'function groundSurfaceType').replaceAll(/float (\w+)/g, '$1')}
|
||||
return groundSurfaceType;`,
|
||||
)(Math.abs);
|
||||
|
||||
for (const [name, depth, surface, globe, expected] of [
|
||||
['only globe depth after the scene depth is cleared', 0.6, 1, 0.6, 1],
|
||||
['only tile depth', 0.4, 0.4, 1, 2],
|
||||
['tiles in front of the globe', 0.4, 0.4, 0.6, 2],
|
||||
['globe in front of tiles', 0.4, 0.6, 0.4, 1],
|
||||
['globe retained in the scene depth', 0.6, 0.6, 0.6, 1],
|
||||
['visible globe with a different scene depth', 0.6, 0.4, 0.6, 1],
|
||||
[
|
||||
'globe with scene depth differing by one depth step',
|
||||
0.6,
|
||||
0.6 - 6e-8,
|
||||
0.6,
|
||||
1,
|
||||
],
|
||||
['forward mesh in front of the globe', 0.2, 0.6, 0.6, 0],
|
||||
['forward mesh in front of tiles', 0.2, 0.4, 0.6, 0],
|
||||
['no ground depth', 1, 1, 1, 0],
|
||||
['forward mesh without ground', 0.2, 1, 1, 0],
|
||||
]) {
|
||||
test(`ground classification: ${name}`, () => {
|
||||
assert.equal(classify(depth, surface, globe), expected);
|
||||
});
|
||||
}
|
||||
|
||||
test('ground depth bindings refresh together and require the globe packing they decode', () => {
|
||||
const effect = {
|
||||
uniforms: new Map([
|
||||
...groundSurfaceUniforms(),
|
||||
['tGlobeDepth', new Uniform(null)],
|
||||
]),
|
||||
};
|
||||
const camera = { projectionMatrixInverse: new Matrix4(), far: 1000 };
|
||||
const target = { depthTexture: {} };
|
||||
const context = { getRenderTarget: () => target };
|
||||
const mrt = {
|
||||
depthBuffer: {},
|
||||
depthBufferPacking: RGBADepthPacking,
|
||||
globeDepthBuffer: {},
|
||||
globeDepthBufferPacking: RGBADepthPacking,
|
||||
};
|
||||
for (let frame = 0; frame < 2; frame++) {
|
||||
mrt.depthBuffer = {};
|
||||
mrt.globeDepthBuffer = {};
|
||||
target.depthTexture = {};
|
||||
updateGroundSurface(effect, camera, context, mrt);
|
||||
assert.equal(effect.uniforms.get('tAllDepth').value, mrt.depthBuffer);
|
||||
assert.equal(
|
||||
effect.uniforms.get('tGlobeDepth').value,
|
||||
mrt.globeDepthBuffer,
|
||||
);
|
||||
assert.equal(
|
||||
effect.uniforms.get('tSurfaceDepth').value,
|
||||
target.depthTexture,
|
||||
);
|
||||
}
|
||||
mrt.globeDepthBufferPacking = BasicDepthPacking;
|
||||
assert.throws(
|
||||
() => updateGroundSurface(effect, camera, context, mrt),
|
||||
/packed globe depth/,
|
||||
);
|
||||
});
|
||||
@@ -1097,44 +1097,74 @@ export function groundBehaviourChecks(
|
||||
},
|
||||
],
|
||||
];
|
||||
if (fixtures?.tiles)
|
||||
checks.push([
|
||||
'ground.createSet: tiles drape ignores terrain and picks only tiles',
|
||||
async () => {
|
||||
await nadirView(10000);
|
||||
const set = engine.ground.createSet({
|
||||
id: 'contract-tiles-drape',
|
||||
kind: 'cell',
|
||||
drape: 'tiles',
|
||||
pickable: true,
|
||||
});
|
||||
try {
|
||||
set.replace([
|
||||
{
|
||||
west: 19.98,
|
||||
south: 9.98,
|
||||
east: 20.02,
|
||||
north: 10.02,
|
||||
fill: [1, 0, 1, 1],
|
||||
pickId: 'tiles-drape',
|
||||
},
|
||||
]);
|
||||
set.commit();
|
||||
await engine.maps.setPhotorealTiles(null);
|
||||
ensure(
|
||||
engine.picker.pick(centre())?.id !== 'tiles-drape',
|
||||
'tiles drape picked on terrain',
|
||||
checks.push([
|
||||
'ground.createSet: tiles drape ignores terrain and picks only tiles',
|
||||
async () => {
|
||||
await engine.maps.setPhotorealTiles(null);
|
||||
await nadirView(10000);
|
||||
const set = engine.ground.createSet({
|
||||
id: 'contract-tiles-drape',
|
||||
kind: 'cell',
|
||||
drape: 'tiles',
|
||||
pickable: true,
|
||||
});
|
||||
try {
|
||||
set.replace([
|
||||
{
|
||||
west: 19.98,
|
||||
south: 9.98,
|
||||
east: 20.02,
|
||||
north: 10.02,
|
||||
fill: [1, 0, 1, 1],
|
||||
pickId: 'tiles-drape',
|
||||
},
|
||||
]);
|
||||
set.commit();
|
||||
await until(() => set.ready(), 'tiles drape ready');
|
||||
ensure(
|
||||
engine.picker.pick(centre())?.id !== 'tiles-drape',
|
||||
'tiles drape picked on terrain',
|
||||
);
|
||||
if (readRegion) {
|
||||
set.show(false);
|
||||
await until(
|
||||
() => engine.surface.tilesLoaded?.() !== false,
|
||||
'terrain settles',
|
||||
);
|
||||
await settle(5);
|
||||
const sample = () => meanAt(engine, readRegion, 20, 10, 2);
|
||||
const baseline = sample();
|
||||
ensure(
|
||||
!nearColour(baseline, MAGENTA, 30),
|
||||
'terrain is already magenta',
|
||||
);
|
||||
set.show(true);
|
||||
await settle(5);
|
||||
const shown = sample();
|
||||
set.show(false);
|
||||
await settle(3);
|
||||
ensure(
|
||||
nearColour(sample(), baseline, 10),
|
||||
'terrain baseline changed',
|
||||
);
|
||||
ensure(
|
||||
nearColour(shown, baseline, 10),
|
||||
'tiles drape changed terrain pixels',
|
||||
);
|
||||
set.show(true);
|
||||
}
|
||||
if (fixtures?.tiles) {
|
||||
await engine.maps.setPhotorealTiles(mapSources.photoreal);
|
||||
ensure(
|
||||
engine.picker.pick(centre())?.id === 'tiles-drape',
|
||||
'tiles drape missed tiles',
|
||||
);
|
||||
} finally {
|
||||
set.destroy();
|
||||
await engine.maps.setPhotorealTiles(null);
|
||||
}
|
||||
},
|
||||
]);
|
||||
} finally {
|
||||
set.destroy();
|
||||
await engine.maps.setPhotorealTiles(null);
|
||||
}
|
||||
},
|
||||
]);
|
||||
return checks;
|
||||
}
|
||||
Reference in new issue
Block a user