Files
sacred_k/docs/widescreen.md
T
khallaandClaude Opus 5.5 df31c9c105 Widescreen: keep original size, extend view horizontally; fix player/camera
- Default render size is 768 high and as wide as the monitor aspect
  (1835x768 on 3440x1440), scaled up: everything keeps the size players
  know and the view only grows sideways. Height=0 keeps native 1:1.
- Ground: 818/888 clip limits, screen centre floats and integer
  immediates, ground quad list (capped at 1749) moved with the tile list.
- 3D: cGranny ortho (+-512 x +-384, incl. HUD items) scaled; inverse
  projection 0x623a20 (1/1024, 1/768) follows the render size, which fixes
  the camera following the player ("outside world") and the missing
  player / ESC menu.
- tools: game-input.ps1 + autotest-load.ps1 drive the game (load the first
  save game, screenshots in-game and with the ESC menu).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 01:48:08 +02:00

5.9 KiB

Widescreen: how the game sizes its view

Goal: render the world 1:1 at the monitor's native resolution, showing more of the world instead of scaling the 1024x768 image. Render and activity (animation/AI) distance must grow with it. UI keeps its 1024x768 layout.

Frame (render thread)

cEngine::renderThread loop, 0x0060e3f0, per frame:

  1. clear (D3D device +0x28)
  2. updates: 0x401660, 0x640b50, 0x634210, weather 0x417e50/0x41a4d0
  3. world: view->vf14(device) where view = engine[+8 + idx*4] (cWorldView0)
  4. UI: 0x756820()->0x7587b0(device), debug overlays 0x60f3e0/0x60f500
  5. FPS overlay 0x60aa90, DxDriver7::Flip

Skipped entirely while g_appFlags & 0x20000 (app inactive).

World view (cWorldView0, vtable 0x008900f8)

Offset Meaning
+0x14/+0x18 camera position (world pixels)
+0x1c/+0x20 requested camera position (scroll target)
+0xb60 zoom (float, clamped to 0.5 .. 2.0)
+0x96ab0 view width = zoom * 1024.0
+0x96ab4 view height = zoom * 768.0
+0x96ac0 extra rows (4)
+0x96ac8 columns = width/96 + 12
+0x96acc rows = height/48 + 24

Functions:

Address Name Notes
0x00624ae0 cWorldView0::SetupView computes the size fields above from zoom and the float constants 1024.0 (0x0088e6f4) and 768.0 (0x00890040); tile corner offsets ±48.2/zoom, ±24.2/zoom
0x006322b0 cWorldView0::Render (vf14) iso tile grid: tiles 96x48, two staggered half-rows per row (0x62b000), first tile = camera - (width/192, height/96)
0x0062a730 cWorldView0::UpdateCamera (vf0c) scroll blocking against sector edges uses width/2, height/2
0x00628210 cWorldView0::SetZoom clamps zoom, sets the ortho projection for 3D models: x ±267zoom, y ±200zoom, z -600..2500, then calls SetupView
0x00623c40 world -> screen projection x * 1024.0 / (2/P00), y * 768.0 / (2/P11)
0x006426a0 build ortho matrix (l, r, b, t, n, f)

All tile/screen positions are pre-transformed (FVF 0x1c4: XYZRHW, diffuse, specular, tex1); 3D models use the ortho projection.

Users of the 1024.0 / 768.0 float constants

World view (should follow the new size): 0x61e5c2, 0x61e5ec, 0x623c82, 0x623c9c, 0x624af2, 0x624b03, 0x628f65, 0x628f8f, 0x629294, 0x6292bc, 0x62b43a, 0x62dc32, 0x62ea2b, 0x62ea5e, 0x62f30d, 0x62f345, 0x630656, 0x63068a, 0x630f3f, 0x630f77 (mostly on-screen clip tests).

Elsewhere (unknown / UI, leave alone for now): 0x40694a, 0x4ec036, 0x593c25, 0x593c3a, 0x6ccb87, 0x6ce156, 0x758a34, 0x75f79a, 0x75f7b8.

Ortho projection extents (±267 / ±200 pushed as floats)

0x60c1f7, 0x60da54, 0x625039, 0x6283b3, 0x62858d, 0x628d3b.

Other frame producers (laid out for 1024x768: "classic" frames)

Address What Thread
0x00756940 cUI_Manager thread loop: main menu (clear, update, BeginScene 0x646f50, UI render 0x7587b0 incl. the menu's 3D scene, EndScene 0x646fd0, Flip) own UI thread
0x006469d0 loading screen: GDI BitBlt of bitmap + progress bar + text into the back buffer (fixed 1024x768) render thread
0x006a0c60 video/intro playback (unexamined)

The widescreen mod gives these frames a 1024x768 D3D viewport (set at BeginScene when the scene depth +0x1d8 is 0) and the borderless mod presents only that area, scaled to the monitor.

Direct3D 7 render target limit

Windows' D3D7 runtime refuses IDirect3D7::CreateDevice on any render target wider or taller than 2048 (DDERR_INVALIDOBJECT, 0x88760082), measured with a standalone test: 2048x1440 works, 2049x1440 / 2560x1080 / 3440x1440 fail. Native ultrawide therefore needs dxwrapper (Dd7to9, D3D7 -> D3D9), see src/mods/dxwrapper.cpp; without it the render size is clamped to 2048.

Renderer

DxDriver7::SetDisplayMode (0x00645390) creates back buffer, z-buffer and D3D viewport from DxDisplayMode::width/height (hardcoded 1024x768 by InitGame, 0x00815620, which also calls ChangeDisplaySettings(1024x768) in fullscreen).

Fixed 1024x768 elsewhere (later)

  • 0x00613710 engine capture (save-game thumbnail), 0x006483f0 TGA writer
  • 0x0060e3f0 GDI BitBlt of a 1024x768 bitmap (startup/movie path)
  • 0x00627260 name/HP labels while Alt is held (no constants, uses view)

Render size

Default: 768 world pixels high (the original vertical view), width from the monitor aspect (3440x1440 -> 1835x768), scaled to the monitor by the borderless mod. Everything keeps the on-screen size players know; the view only grows horizontally. [widescreen] Height=0 renders natively 1:1 (everything about half size on a 1440p monitor).

Things that had to follow the render size

  • view size / centre / clip floats and their integer immediates (world view)
  • tile draw list and ground quad list (fixed 1750 / 1749 entries) moved into an enlarged view object
  • 3D ortho extents: world (+-267 x +-200, zoomed and unzoomed) and cGranny (+-512 x +-384, incl. cUI_NetGranny for HUD items)
  • the inverse projection 0x623a20 (screen -> view) uses 1/1024 and 1/768: game logic (camera follow 0x44d9f0) converts world -> screen -> view with the device's current projection; unpatched it sent the camera off-world

Open questions

  • Above ~2300 px render width (between 2304 and 2816) the player is not drawn although monsters are (cCreature::vf14 still called). Not hit at the default size; some per-view limit of ~40 tile columns, not found yet.

  • HUD: drawn in the top-left 1024x768 of the render target; 3D belt items use the scaled HUD projection and sit off their slots.

  • Activity radius: which creatures/objects are updated and animated (cWorld / cSector, 0x00603e90 = player?, 0x005fe060 = object by id).

  • Fixed-size per-view buffers (e.g. +0x80e40, 5 vectors at +0x96ad0 with capacity 0x200) - do they overflow at 3440x1440 (47 columns x 54 rows)?

  • Mouse picking (screen -> world).