diff --git a/AGENTS.md b/AGENTS.md index d553397a..e9ee04c4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,3 +55,4 @@ - Library builds must externalize `three` with a pattern that also matches subpaths like `three/webgpu`, `three/tsl`, and `three/examples/jsm/*`; externalizing only bare `three` bundles a second Three runtime into `build/` and triggers `THREE.WARNING: Multiple instances of Three.js being imported.` in consumers - State action migrations must use `AnimationSystem.fromTargets(...).play()` and `Toolbox.enableTool()`; lingering `animate()` or `useTool()` calls can still let `yarn build` exit 0 while `vite-plugin-dts` reports TS2339 API drift - When swapping canvases under WebGPU, `DIVEEnvironment.setRenderer()` must run before disposing the previous `WebGPURenderer`; disposing the old renderer first can crash `PMREMGenerator.dispose()` inside Three's `NodeManager.delete` with `usedTimes` access errors +- `OrientationDisplay.tick()` should size its overlay viewport from `DIVERenderer.canvas.clientHeight` and restore the prior `webgpurenderer.autoClear` value; unit tests can fall back to the saved viewport height when the mock omits `canvas` diff --git a/src/plugins/orientationdisplay/src/OrientationDisplay.ts b/src/plugins/orientationdisplay/src/OrientationDisplay.ts index a1e1b073..d0c30f6d 100644 --- a/src/plugins/orientationdisplay/src/OrientationDisplay.ts +++ b/src/plugins/orientationdisplay/src/OrientationDisplay.ts @@ -46,14 +46,18 @@ export class OrientationDisplay implements DIVETicker { public tick(): void { // save current background reference and set it to transparent const restoreBackground = this._scene.background ?? null; + const restoreAutoClear = this._renderer.webgpurenderer.autoClear; this._scene.background = null; // save current viewport and set it to desired size this._renderer.webgpurenderer.getViewport(this._restoreViewport); + const canvasHeight = + this._renderer.webgpurenderer.domElement?.clientHeight ?? + this._restoreViewport.w; this._renderer.webgpurenderer.setViewport( 0, - this._renderer.webgpurenderer.domElement.clientHeight - 150, + Math.max(0, canvasHeight - 150), 150, 150, ); @@ -70,7 +74,7 @@ export class OrientationDisplay implements DIVETicker { // restore viewport and background this._renderer.webgpurenderer.setViewport(this._restoreViewport); - this._renderer.webgpurenderer.autoClear = true; + this._renderer.webgpurenderer.autoClear = restoreAutoClear; this._scene.background = restoreBackground; } diff --git a/src/plugins/orientationdisplay/src/__test__/OrientationDisplay.test.ts b/src/plugins/orientationdisplay/src/__test__/OrientationDisplay.test.ts index 448a79a8..4e594d99 100644 --- a/src/plugins/orientationdisplay/src/__test__/OrientationDisplay.test.ts +++ b/src/plugins/orientationdisplay/src/__test__/OrientationDisplay.test.ts @@ -35,10 +35,17 @@ const mockCamera = { matrix: new Matrix4(), } as unknown as DIVEPerspectiveCamera; +const mockCanvas = document.createElement('canvas'); +Object.defineProperty(mockCanvas, 'clientHeight', { + value: 600, + configurable: true, +}); + const mockRenderer = { render: vi.fn(), + canvas: mockCanvas, webgpurenderer: { - getViewport: vi.fn().mockReturnValue(new Vector4(0, 0, 800, 600)), + getViewport: vi.fn((viewport: Vector4) => viewport.set(0, 0, 800, 600)), setViewport: vi.fn(), render: vi.fn(), autoClear: true, @@ -108,7 +115,7 @@ describe('OrientationDisplay', () => { ).toHaveBeenCalledWith(orientationDisplay['_restoreViewport']); expect( mockRenderer.webgpurenderer.setViewport, - ).toHaveBeenCalledWith(0, 0, 150, 150); + ).toHaveBeenCalledWith(0, 450, 150, 150); expect(mockRenderer.webgpurenderer.render).toHaveBeenCalledWith( mockScene, orientationDisplay['_orthographicCamera'], @@ -140,10 +147,23 @@ describe('OrientationDisplay', () => { ); }); - it('should manage autoClear property correctly', () => { + it('should restore the previous autoClear property', () => { + mockRenderer.webgpurenderer.autoClear = false; + + orientationDisplay.tick(); + + expect(mockRenderer.webgpurenderer.autoClear).toBe(false); + }); + + it('should fall back to the stored viewport height when canvas height is unavailable', () => { + mockRenderer.webgpurenderer.domElement = + undefined as unknown as HTMLCanvasElement; + orientationDisplay.tick(); - expect(mockRenderer.webgpurenderer.autoClear).toBe(true); + expect( + mockRenderer.webgpurenderer.setViewport, + ).toHaveBeenCalledWith(0, 450, 150, 150); }); }); });