Experimental: scalable iso interior view (HD/SSAA foundation)#314
Draft
noctonca wants to merge 6 commits into
Draft
Experimental: scalable iso interior view (HD/SSAA foundation)#314noctonca wants to merge 6 commits into
noctonca wants to merge 6 commits into
Conversation
The iso projection had no scale parameter (SetIsoProjection takes only a centre), so raising the resolution revealed more room instead of putting more pixels on models. Add a single fixed-point IsoScale global, applied at every iso scale site so the whole model scales coherently: - ProjectListIso (object/character geometry) - LongProjectPointIso (single points: anchors, shadows, bboxes) - Map2Screen (brick positions) - the sphere radius in Sphere/Sphere_Transp (hands, joints and heads carry a separate radius scalar, not just a projected centre) This is the shared primitive for dynamic zoom, supersampling, and HD interior fidelity. At the default ISO_SCALE_ONE the multiply/shift cancels exactly ((v*256)>>(n+8) == v>>n), so the faithful path is bit-for-bit unchanged. The ASM equivalence tests test_lprojiso, test_prliiso, test_grille and test_aff_obj all pass at identity. A dev console verb 'iso scale <factor>' / 'iso reset' drives it (also via --exec for headless captures). It forces a full redraw (FirstTime = AFF_ALL_FLIP) so the cached brick floor re-lays at the new scale, not just the per-frame 3D objects. Only iso interior scenes are affected; exterior 3D uses the perspective projection. Known limitation: bricks are fixed-size sprite tiles, so Map2Screen scales their positions but AffBrickBlock still blits at native size, leaving gaps between floor bricks at scales > 1. See docs/ISO_SCALE.md.
Under IsoScale > 1 the brick grid spacing (Map2Screen) grows but the tiles were still blitted at native size by AffGraph, leaving gaps between floor and wall bricks. Bricks are RLE-compressed, so ScaleSprite (which needs raw pixels) cannot draw them directly. Add DecodeBrickToRaw (brick RLE -> raw Struc_Sprite buffer) and a BlitBrickIso helper: at ISO_SCALE_ONE it calls the original AffGraph (byte-identical), otherwise it decodes and lets ScaleSprite draw the tile at the matching factor. Wired into AffBrickBlock and AffBrickBlockOnly. This makes the initial scene draw (and any full redraw, which the iso scale console verb forces) render bricks scaled with no gaps. The re-blit of bricks over moving objects (DrawOverBrick -> CopyMask -> Screen) is a separate path and is not yet scaled, so objects in motion at scale > 1 still leave brick artifacts; that is the next step.
Phase A scaled the initial brick draw; this scales the re-blit of bricks over moving objects and the bucketing/clip constants that decide which tile to re-blit. - ReBlitBrickIso replaces the 6 CopyMask re-blit calls in DrawOverBrick / DrawOverBrick3 / DrawOverBrickCage. At ISO_SCALE_ONE it calls the original CopyMask (bit-exact); otherwise it re-draws the scaled tile via BlitBrickIso, the same path as the initial draw, so placement matches. - New ISO_PX(n) helper (CAMERA.H) scales a native iso pixel by IsoScale, identity at ISO_SCALE_ONE. The brick column divisor (24), the clip bounds (-47/-38/-24) and the re-blit vertical overlap (38) now go through it, so the bucketing granularity tracks the scaled tile. With the divisor scaled, the back-scan bias returns to its native -2 / -1 (one full / half brick of columns). This is the start of the single-source approach from docs/ISO_SPACE_AUDIT.md: one scale factor applied to every iso pixel dimension instead of each carrying its own. Sprite sizing (group D) is the remaining unscaled surface. Bit-exact at ISO_SCALE_ONE (ISO_PX(n) == n); static zoom verified gap-free.
SDL PresentFrame uploads Log to the display, so Log is the real composited frame. Screen is a secondary buffer holding only the clean brick background (source for CopyMask re-blit and BackupScreen), so it misses the per-frame 3D models and the foreground-brick re-blit. The --screenshot harness flag and the console screenshot were saving Screen, so 3D models and movers did not appear in captures. Capture Log instead, matching the display and the existing Dial / menu / UI captures which already use Log.
Completes the per-element iso view scaling started by the IsoScale primitive and the brick Phase A/B. The whole interior view now scales coherently at a non-default IsoScale, which is the foundation an HD/SSAA pass needs (render at S x with IsoScale = S, then downsample). Bit-exact at ISO_SCALE_ONE, so the faithful path and the ASM equivalence tests are unaffected; the scale stays a dev-only console knob. - Re-blit foreground bricks by copying the composited Screen buffer masked by the brick silhouette (ScaledReBlitFromScreen), instead of re-drawing one decoded brick, which lost the front-to-back compositing where bricks overlap. - Use each brick's real scaled vertical extent in the DrawOverBrick overlap test instead of a single hardcoded height. - Scale the object-vs-brick projection pivot so models stay aligned on the tile (the pivot offset, not just the per-brick step, has to scale). - Scale sprite objects at the gameplay call site, leaving the UI-shared PtrAffGraph untouched: anim-3DS sprites (fans), TYPE_OBJ_SPRITE (doors), and TYPE_EXTRA sprites (magic ball, item drops). Projection-derived sprite scales are left alone (they already scale through the projection). - Scale particle-flow dots in size and keep them centred on their position. - Add an `iso probe` console diagnostic for the projector deltas; drop the temporary debug overlay. Experimental: not yet verified in-game for transient effects (magic ball in flight, item drop, door open/close, live particle bursts), and the S x buffer + downsample HD pass is not built. See docs/ISO_SCALE_FINDINGS.md.
Post-implementation companion to ISO_SCALE.md (the primitive) and the iso-space audit. Records the main lesson (an interior frame is drawn by ~7 independent render classes, each needing its own scaling), the two projection insights (scale the pivot not just the step; projection-derived sizes scale for free), the bugs and fixes, the verification method, and what it would take to ship. Framed as exploratory findings, not a shipped feature.
This was referenced Jun 8, 2026
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.
Status: experimental, Draft, not for merge. This is the per-element foundation for HD interior models, kept as a reviewable record of the work and the findings. It supersedes #311 (the
IsoScaleprimitive alone; this branch contains that commit plus everything built on it).Default behaviour is unchanged: at
IsoScale = 1.0every change is bit-exact, and the scale is a dev-only console knob (iso scale <f>/iso reset/iso probe). So it is safe in the tree, but it does not ship a user-facing feature on its own.What this is
HD interior models means rendering the iso scene at a higher scale (and, for SSAA, downsampling). That only renders correctly if every part of an interior frame honors the scale. An interior frame turns out to be drawn by about seven independent render classes that share no code, so this makes each one scale with
IsoScale:TYPE_OBJ_SPRITEsprites (doors)TYPE_EXTRAsprites (magic ball, item drops, effects)What is done vs not
Done and verified by static capture and per-class isolation diff: all seven classes scale coherently at a static scale, bit-exact at 1.0.
Not done: the S-times render buffer plus downsample pass (the actual HD/SSAA step), camera integration for smooth dynamic zoom, scoping the scale away from the UI, fractional scales, and any performance work. Not yet verified in-game for transient effects (magic ball in flight, item drop, door open and close, live particle bursts).
Notes
docs/ISO_SCALE_FINDINGS.md.