diff --git a/.playwright-mcp/drone-visor-com-feed-content.png b/.playwright-mcp/drone-visor-com-feed-content.png new file mode 100644 index 0000000..3cfd1e8 Binary files /dev/null and b/.playwright-mcp/drone-visor-com-feed-content.png differ diff --git a/.playwright-mcp/drone-visor-com-feed.png b/.playwright-mcp/drone-visor-com-feed.png new file mode 100644 index 0000000..4d33d41 Binary files /dev/null and b/.playwright-mcp/drone-visor-com-feed.png differ diff --git a/.playwright-mcp/drone-visor-dia-sem-nevoa-crop.png b/.playwright-mcp/drone-visor-dia-sem-nevoa-crop.png new file mode 100644 index 0000000..1a809fb Binary files /dev/null and b/.playwright-mcp/drone-visor-dia-sem-nevoa-crop.png differ diff --git a/.playwright-mcp/drone-visor-dia-sem-nevoa.png b/.playwright-mcp/drone-visor-dia-sem-nevoa.png new file mode 100644 index 0000000..ce811d0 Binary files /dev/null and b/.playwright-mcp/drone-visor-dia-sem-nevoa.png differ diff --git a/.playwright-mcp/drone-visor-feed-verificado-content.png b/.playwright-mcp/drone-visor-feed-verificado-content.png new file mode 100644 index 0000000..ee10e86 Binary files /dev/null and b/.playwright-mcp/drone-visor-feed-verificado-content.png differ diff --git a/.playwright-mcp/drone-visor-feed-verificado.png b/.playwright-mcp/drone-visor-feed-verificado.png new file mode 100644 index 0000000..f006975 Binary files /dev/null and b/.playwright-mcp/drone-visor-feed-verificado.png differ diff --git a/.playwright-mcp/drone-visor-final.png b/.playwright-mcp/drone-visor-final.png new file mode 100644 index 0000000..5d68582 Binary files /dev/null and b/.playwright-mcp/drone-visor-final.png differ diff --git a/.playwright-mcp/drone-visor-opaco-dia.png b/.playwright-mcp/drone-visor-opaco-dia.png new file mode 100644 index 0000000..501165a Binary files /dev/null and b/.playwright-mcp/drone-visor-opaco-dia.png differ diff --git a/.playwright-mcp/drone-visor-opaco-final.png b/.playwright-mcp/drone-visor-opaco-final.png new file mode 100644 index 0000000..f280291 Binary files /dev/null and b/.playwright-mcp/drone-visor-opaco-final.png differ diff --git a/.playwright-mcp/drone-visor-sem-nevoa-crop.png b/.playwright-mcp/drone-visor-sem-nevoa-crop.png new file mode 100644 index 0000000..979ab08 Binary files /dev/null and b/.playwright-mcp/drone-visor-sem-nevoa-crop.png differ diff --git a/.playwright-mcp/drone-visor-sem-nevoa.png b/.playwright-mcp/drone-visor-sem-nevoa.png new file mode 100644 index 0000000..82bcc83 Binary files /dev/null and b/.playwright-mcp/drone-visor-sem-nevoa.png differ diff --git a/.playwright-mcp/drone-visor-tela-opaca-crop.png b/.playwright-mcp/drone-visor-tela-opaca-crop.png new file mode 100644 index 0000000..4a80ca8 Binary files /dev/null and b/.playwright-mcp/drone-visor-tela-opaca-crop.png differ diff --git a/.playwright-mcp/drone-visor-tela-opaca.png b/.playwright-mcp/drone-visor-tela-opaca.png new file mode 100644 index 0000000..1901af8 Binary files /dev/null and b/.playwright-mcp/drone-visor-tela-opaca.png differ diff --git a/.playwright-mcp/firefox-not-ready.png b/.playwright-mcp/firefox-not-ready.png new file mode 100644 index 0000000..51f6b24 Binary files /dev/null and b/.playwright-mcp/firefox-not-ready.png differ diff --git a/public/js/city/app.js b/public/js/city/app.js index 6bc1cc1..c12c36f 100755 --- a/public/js/city/app.js +++ b/public/js/city/app.js @@ -30,7 +30,9 @@ scene.fog = new THREE.Fog(0x87ceeb, 120, 600); const camera = new THREE.PerspectiveCamera(72, 1, 0.1, 2000); camera.position.set(0, 1.7, 0); -const renderer = new THREE.WebGLRenderer({ antialias: true }); +// preserveDrawingBuffer: lets automated tests/screenshots read real +// frame pixels (the PIP visor proof); cost is negligible here. +const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; @@ -4347,6 +4349,18 @@ function renderDroneView() { drone.cam.aspect = w / h; drone.cam.updateProjectionMatrix(); + // Neutralise the city fog for the drone feed so the aerial view is + // never misted ("embassado"). Only the fog uniform values change for + // this pass — three refreshes them per render, so no material is + // recompiled and the ground view keeps its weather haze. + const fog = scene.fog; + const fogNear = fog ? fog.near : 0; + const fogFar = fog ? fog.far : 0; + if (fog) { + fog.near = 1e7; + fog.far = 2e7; // smoothstep → factor 0 at any real distance + } + renderer.setScissorTest(true); renderer.setScissor(x, y, w, h); renderer.setViewport(x, y, w, h); @@ -4354,6 +4368,12 @@ function renderDroneView() { renderer.setScissorTest(false); renderer.setViewport(0, 0, cr.width, cr.height); + if (fog) { + fog.near = fogNear; + fog.far = fogFar; + } + + drone.fogCleared = !!fog; drone.view = { x, y, w, h }; } @@ -7137,10 +7157,10 @@ function animate() { renderer.render(scene, camera); perfFrame.render = performance.now() - tRender; - // Drone command-panel feed: scissored second pass on alternate frames. + // Drone command-panel feed: scissored pass every frame so the visor + // never alternates between the drone view and the ground view. if (drone.active && drone.mesh) { - drone.frame += 1; - if (drone.frame % 2 === 0) renderDroneView(); + renderDroneView(); } } animate(); @@ -7626,6 +7646,26 @@ window.__city10 = { return out; }, playerCrossingAllowed, + /** Debug: the WebGL renderer (frame forensics for automated tests). */ + get gl() { + return renderer; + }, + /** Debug: live scene background/fog hex + camera pose. */ + get sceneInfo() { + return { + bg: scene.background ? scene.background.getHexString() : null, + fog: scene.fog ? scene.fog.color.getHexString() : null, + fogNear: scene.fog ? scene.fog.near : null, + fogFar: scene.fog ? scene.fog.far : null, + cam: { + x: +camera.position.x.toFixed(2), + y: +camera.position.y.toFixed(2), + z: +camera.position.z.toFixed(2), + }, + toneMapping: renderer.toneMapping, + exposure: renderer.toneMappingExposure, + }; + }, /** Current bump-to-talk peer (null when the chat is closed). */ get talkPeer() { return currentPeer; @@ -7684,6 +7724,8 @@ window.__city10 = { trafficLights: trafficSystem ? trafficSystem.heads.length / 2 : 0, crosswalks: crosswalkCount, sidewalkSegments: sidewalkSegmentCount, + hour: +(sim.hour ?? 0).toFixed(1), + night: +(sim.nightFactor ?? 0).toFixed(2), beach: beachSystem ? beachSystem.counts() : null, flights: airplaneSystem ? airplaneSystem.flights() : null, port: portSystem ? portSystem.counts() : null, @@ -7693,6 +7735,7 @@ window.__city10 = { y: +drone.pos.y.toFixed(1), z: +drone.pos.z.toFixed(1), yaw: +drone.yaw.toFixed(2), + fogCleared: !!drone.fogCleared, view: drone.view || { x: 0, y: 0, w: 0, h: 0 }, } : null, robots: pedestrianSystem diff --git a/resources/views/city/viewer.blade.php b/resources/views/city/viewer.blade.php index 3d40281..6f451bb 100755 --- a/resources/views/city/viewer.blade.php +++ b/resources/views/city/viewer.blade.php @@ -254,9 +254,13 @@ style="border:0; background:rgba(148,163,184,.18); color:#e2e8f0; border-radius:.35rem; cursor:pointer; padding:.1rem .45rem;">✕
+ style="height:200px; + background:repeating-linear-gradient(0deg, + rgba(0,0,0,.14) 0px, rgba(0,0,0,.14) 2px, + rgba(0,0,0,0) 2px, rgba(0,0,0,0) 4px); + border:7px solid #050a14; border-radius:.4rem; + box-shadow:inset 0 0 16px rgba(0,0,0,.75), + 0 0 0 2px rgba(148,163,184,.45);">
{{ __('city.drone_hint') }}