Skip to content

Experimental: scalable iso interior view (HD/SSAA foundation)#314

Draft
noctonca wants to merge 6 commits into
mainfrom
feat/iso-brick-scale
Draft

Experimental: scalable iso interior view (HD/SSAA foundation)#314
noctonca wants to merge 6 commits into
mainfrom
feat/iso-brick-scale

Conversation

@noctonca

@noctonca noctonca commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

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 IsoScale primitive alone; this branch contains that commit plus everything built on it).

Default behaviour is unchanged: at IsoScale = 1.0 every 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:

  • Bricks (floor and walls) and the foreground brick re-blit over moving objects
  • 3D models (characters, projectile bodies, impact effects)
  • anim-3DS sprites (fans, vents)
  • TYPE_OBJ_SPRITE sprites (doors)
  • TYPE_EXTRA sprites (magic ball, item drops, effects)
  • Particle-flow dots
  • The object-to-brick projection pivot, so models stay aligned on the tile

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

noctonca added 6 commits June 7, 2026 13:23
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant