Repository navigation
feat(rendering)!: add camera views and cull sprites outside them - #714
Merged
Merged
Conversation
A camera's view (the world area it shows, its pixels per unit and conversions to and from the canvas in CSS pixels) is now computed in one place, computeCameraView/getCameraView, instead of being repeated by the render, terrain and UI systems and recomputed from pieces by game code. - The render and terrain systems project each camera from its view; createProjectionMatrix takes the world-space rect to show. - UI layout, safe area and pointer resolution use the canvas camera's view, so 'screenPixels' sizes follow the camera's zoom and a screen-space canvas fills its camera's view. - The render system skips sprites, nine-slice regions and glyphs whose quads are outside a camera's view. - calculateVisibleWorldSize, calculatePixelsPerUnit, screenToWorldSpace, worldToScreenSpace, canvasToWorldSpace and the unused CameraEcsComponent.scissorRect are removed; demos, docs, e2e scenes and /demo use the view. A render-target camera keeps the canvas's aspect, since the present pass stretches every target over the canvas; giving targets their own aspect belongs with render-target sizing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GCQ9KLWEmo3mFAR7W2gMS7
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…7dc6d4 # Conflicts: # CHANGELOG.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GCQ9KLWEmo3mFAR7W2gMS7
stormmuller
pushed a commit
that referenced
this pull request
Oct 6, 2026
…j6xxo Brings in camera views (#714). The game states demo sizes its play area with getCameraView(...).size, since calculateVisibleWorldSize is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAAGHGoEyhYQhFcyTw37jE
3 of 5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements
design/camera-views.md.Camera views (phase 1)
computeCameraView(camera, position, renderContext)andgetCameraView(world, camera, renderContext)(src/rendering/camera-view.ts). Each returns aCameraView:boundsandsizein world units, accounting for the camera's position and zoom;pixelsPerUnitin CSS pixels;worldToViewportandviewportToWorld, converting to and from CSS pixels from the canvas's top-left, Y-down. These invert each other exactly. The oldworldToScreenSpacedidn't flip Y.createProjectionMatrixnow takes the world-spaceRectto show.resolveCanvasPointerPositionall go through the canvas camera's view. As a result:'screenPixels'sizes and safe-area insets follow the camera's zoom, so they keep their on-screen size on a world-space canvas whose camera zooms;calculateVisibleWorldSize,calculatePixelsPerUnit,screenToWorldSpace,worldToScreenSpace,canvasToWorldSpace, and the unusedCameraEcsComponent.scissorRect./demo,physics/forces.md,ecs/game.mdand thehigh-dpi-canvase2e scene.rendering/world-units-and-cameras.mdis rewritten around views.View culling (phase 2)
computeSpriteInstanceBounds, sits next tobindSpriteInstanceData. A test checks it against the cornerssprite.vertcomputes from the bound instance data, so the two can't drift apart.Where this differs from the design (agreed with the author before implementing)
render-resolution.md.msdf-effects.fragdraws both inside the same glyph quad, so culling the exact quads already keeps them.sprite-textures.md, as decided. A custom vertex shader that moves vertices outside the sprite's quad may be culled while partly visible. The guide and the changelog both say so.Performance (CPU, render system only, jsdom with a mocked GL, 20k sprites, 200 frames, two runs each)
Not done here
glyph-quad.tsadds each glyph's offset without rotating or scaling it. That's a pre-existing defect; culling uses the quads as they're drawn and the tests avoid locking it in.Forge-Game-Engine/demo) follows on its own branch and needs a Forge release containing this.Related issue(s)
Implements
design/camera-views.md.Verification checklist
npm run check-typespasses with 0 errorsnpm testpasses (1786 tests)npm run lintpasses with 0 errorsnpm run cspellpasses with 0 errorsnpm run check-exportspassesindex.ts/documentation-site/docs/docsis updatednpm run build, then the docs site'stypecheckandbuild;camera-pan-zoomtest: after zooming and panning, the mouse is moved onto the rendered green square, and the scene checks it converts back inside that square in world space.Changelog
## [Unreleased]inCHANGELOG.md:createProjectionMatrix, UI zoom behavior;🤖 Generated with Claude Code
https://claude.ai/code/session_01GCQ9KLWEmo3mFAR7W2gMS7
Generated by Claude Code