refactor: make camera aspect ratio auto-refresh in Camera class#8633
Conversation
Follow-up to #8632. Moves automatic aspect ratio handling into the Camera class so it stays up to date whenever inputs change, not just at onEnable. - Camera constructor now takes a GraphicsDevice (asserted non-null) - Camera owns calculateAspectRatio(rt); aspectRatio getter recomputes lazily in ASPECT_AUTO when rect / renderTarget / backbuffer change - Subscribes to graphicsDevice 'resizecanvas' and unsubscribes on destroy - CameraComponent.calculateAspectRatio forwards to Camera; the onEnable block added in #8632 is removed (lazy getter supersedes it) - All new Camera() sites plumbed with device (LightCamera.create, ShadowRenderer.createShadowCamera, cookie renderer, lightmapper) - Adds test/scene/camera.test.mjs and locks drag-helper test to ASPECT_MANUAL so its expectations don't drift with canvas size
There was a problem hiding this comment.
Pull request overview
Refactors camera aspect ratio auto-updating so Camera can lazily recompute its aspect ratio when inputs change (render target, rect, backbuffer resize, mode switches), rather than relying on CameraComponent.onEnable.
Changes:
Cameranow takes aGraphicsDevice, tracks aspect-ratio dirtiness, listens forresizecanvas, and exposescalculateAspectRatio.- Internal camera creation sites were updated to pass a device (camera component, cloning, light/shadow/cookie cameras, lightmapper).
- New unit tests cover AUTO/MANUAL aspect behavior and cleanup; an existing UI drag test locks aspect ratio to avoid resolution-dependent expectations.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| test/scene/camera.test.mjs | Adds coverage for AUTO recomputation triggers, MANUAL preservation, destroy/unsubscribe, clone, and component forwarding. |
| test/framework/components/element/element-drag-helper.test.mjs | Locks camera aspect to MANUAL 16:9 to make drag math independent of canvas size. |
| src/scene/camera.js | Moves aspect auto-refresh logic into Camera (device-backed) with dirty flag + resize listener + forwarding method. |
| src/framework/components/camera/component.js | Instantiates Camera with a device; removes onEnable aspect refresh; forwards calculateAspectRatio to Camera. |
| src/scene/renderer/light-camera.js | Updates LightCamera.create to accept a device and constructs Camera(device). |
| src/scene/renderer/shadow-renderer.js | Threads device into shadow camera creation via LightCamera.create(device, ...). |
| src/scene/renderer/render-pass-cookie-renderer.js | Updates cookie renderer to pass device into temporary LightCamera creation. |
| src/scene/light.js | Updates shadow camera creation call to pass light.device. |
| src/framework/lightmapper/lightmapper.js | Updates baking camera creation to new Camera(this.device). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Follow-up to #8632. Moves the automatic aspect ratio handling from
CameraComponentinto theCameraclass itself, so it stays up to date whenever any of its inputs change — not just atonEnabletime.Motivation:
#8632 fixed the "stale aspect ratio before the first frame" problem inside
CameraComponent.onEnable, but the same staleness still occurred when:renderTargetwas assigned at runtime,rectwas changed at runtime,ASPECT_MANUALback toASPECT_AUTO,onEnablenever ran).Changes:
Cameraconstructor now takes aGraphicsDevice(asserted non-null) and stores it oncamera.device, so the camera can fall back to the backbuffer size when no render target is assigned.Cameranow ownscalculateAspectRatio(rt); whenrtis omitted it falls back to the camera's ownrenderTarget, then the backbuffer.rect.z/rect.wis included so the aspect matches the actually-rendered viewport.get aspectRatiorecomputes on every read inASPECT_AUTO(the calculation is trivially cheap — a few multiplies and a divide — and this avoids any need for a device-event subscription). If the recomputed value differs from the cached one,_projMatDirtyis flipped so the projection matrix is rebuilt on its next read._evaluateProjectionMatrixnow force-readsaspectRatiobefore checking the dirty flag, so a backbuffer resize (with no camera setter touched) is picked up on the nextprojectionMatrixaccess.CameraComponent.calculateAspectRationow forwards to the underlyingCamera, with clarified JSDoc (falls back to the camera's render target before the backbuffer, includesrect). TheonEnableblock added in Calculate camera aspect ration in CameraComponent onEnable #8632 is removed (the lazy getter supersedes it).new Camera()sites updated to pass a device:CameraComponent,Camera.clone(),LightCamera.create(and its callers inShadowRenderer.createShadowCamera,render-pass-cookie-renderer,evalSpotCookieMatrix), andLightmapper.API Changes:
Cameraconstructor —new Camera()→new Camera(graphicsDevice).Camerais internal, so this is not a public API break, but any code instantiatingCameradirectly must pass aGraphicsDevice.LightCamera.create(name, lightType, face)→LightCamera.create(device, name, lightType, face)(internal).ShadowRenderer.createShadowCamera(shadowType, type, face)→ShadowRenderer.createShadowCamera(device, shadowType, type, face)(internal).Tests:
test/scene/camera.test.mjscovering AUTO recompute onrenderTarget,rect, backbuffer resize, MANUAL→AUTO switch, MANUAL preservation,projectionMatrixrefresh after backbuffer resize,clone(), and the component-level forwarders.element-drag-helper.test.mjsupdated to explicitly lockASPECT_MANUAL+16/9, so its expected values don't drift with device dimensions.