Skip to content

World view rendering (RenderWorldView 0x419dd0) — exact maths, models, sort rules ​

Reference implementation: tools/view.py (integer, bit-level port) and tools/models.py (extracts all 188 model parts to out/models/models.json). Sample frames: out/view/*.png (RFMAP001_bunker.png/_drive.png = level 1 home pad, RFMAP051_town_{bunker,drive,heli,lift,intro}.png = building-dense compound of "No Gas Up").

All values are 16.16 fixed point unless stated. FixMul(a,b) = (a*b)>>16 (64-bit, floor; 0x438300, __fastcall ECX,EDX). FixDiv(a,b) = trunc((a/65536)/(b/65536)*65536) (x87 + _ftol, 0x438390). CosFixed/SinFixed(a) (0x438360/0x438520) = trunc(cos(a/65536 * 2π/256) * 65536): full circle 0x1000000. ISqrt 0x438550, FixSqrt 0x438580. / below is C division (truncate toward 0).

1. Tables (InitProjectionAndRotTables 0x408440, arg F = DAT_004438a8 = 0x12c0000 = 300.0) ​

tableaddresscontents
projection T[i], i = -1024..5110x483780 (= PTR_DAT_00440c30; first entry at -0x1000)T[i] = F / (300 - i) (int div; 0 at i=300) = 300/(300-i) in 16.16
yaw Y[k], k=0..630x481780 (9 ints, stride 0x24)a=k*0x40000: [c, s, 0, -s, c, 0, 0, 0, 1] (rotation about z)
pitch P[k]0x48a7b0[1, 0, 0, 0, c, s, 0, -s, c] (rotation about x)
heading dir0x482480 (3 ints)(0,-1,0)*Y[k] = (sin, -cos, 0); heading 0 = north (-y)
reciprocals0x482080FixDiv(1.0, n) n=1..255
triangular0x48b0f0sum(0..n)

Vectors are row vectors: v' = v*M (VecMulMat3 0x42bb00, Mat_TransformVerts 0x4383c0, each term FixMuled separately); Mat_Mul3x3(out,A,B) 0x438460 = A·B (so v*(A*B) applies A first). World axes: x east, y south (row 0 = north), z up; 1 cell = 32 px = 0x200000.

2. Camera / view struct (0x48bd90 + i*0x24c, dword index [n] or byte offset +0x..) ​

fieldmeaning
[2],[3]view w,h (1P lo-res 320x152, clip rect (0,0)-(319,151))
[4],[5]screen centre = (w/2)<<16, (h/2)<<16 → (160,76)
[6],[7]camera world x,y (the point that projects to the centre at z=0)
[8] +0x20camera height offset H (smoothed value +0x114, target +0x134)
[9] +0x24pitch p (target +0x144), cache key +0x104
[10] +0x28-cos p; [11] +0x2c = sin p
[12],[13]left = world X of the top row's left end, top = world Y of the top screen row (integer px, relative to camera)
+0x3c..+0x5ccamera matrix C = [1,0,0, 0,s,c, 0,-c,s] (c=cos p, s=sin p)
+0x10c/+0x110smoothed target, +0x12c/+0x130 target, +0x128 target z, +0x138/+0x13c velocity
+0x14c/+0x150/+0x154per-frame pointers to the winning tracker's {accel, maxaccel, gain} triples for pos / height / pitch
+0x158tracker list (nodes of 0x60 bytes at 0x4568c8 + view*0x60, update fn CamTracker_Update 0x402d90)
+0x1c4/+0x1c8screen-window offset (Camera_SetScreenWindow 0x403550; 1P: (0,12), window 288x140)
[0x2f],[0x30]zoom-in ramp / view callback (ViewModeZoomIn → RenderWorldView)

View space of a world point (X,Y,Z relative to the camera): x' = X, y' = Y*s - Z*c, z' = Y*c + Z*s (z' grows toward the viewer). Projection uses zr = z' - H: scale = T[zr>>16] = 300/(300 - zr); the eye is at zr = 300.

Float equivalent: sx = 160 + X*k, sy = 76 + (Y*sin p - Z*cos p)*k, k = 300/(300 + H - Y*cos p - Z*sin p).

top/left (ViewInit 0x403750, recomputed by Camera_Update when p or H change):

hh = -h>>1                                   // -76
top  = FixDiv(H + F, FixMul(F, s)/hh - [10]) // Y where screen y = -h/2 (16.16)
inv  = FixDiv(H + FixMul([10], top) + F, F)  // 1/scale at that row
[13] = top >> 16 ;  [12] = (inv * (-w>>1)) >> 16

Camera modes (trackers created by the game; values = ViewAddTracker args) ​

situationcreatorpitch +0x144H +0x134target z offsetcentre scale
intro zoom startViewInit0x200000 (45°)270–0.53
bunker vehicle selectView_EnterBunkerCam 0x404ee0 (pad pos)0x180000 (33.75°)001.00
lift risingLift_SetTopDownCam 0x4181b00x400000 (90°, straight down)250100.55
driving tank/jeep/MSVSpawnVehicle 0x427cd0 / 0x4182000x180000g_VehCamHeight 0x44afb0[type] = -170102.31
driving helisame0x180000-100101.50
vehicle destroyedVehicleDestroy (call at 0x42840e)0x3e0000-120 (0xff880000)10–

Default node (CamTracker_Default 0x402bf0, template 0x43eea8): H target 0, pitch 0x200000, params {0.1, 0.05, 4.0}, {0.1, 0.05, 3.0}, {0.04, 0.01, 1.0}.

Camera_Update 0x403a40 (follow) ​

  1. Walk tracker list; first node whose update returns 1 wins. A node sets (if still 0) +0x14c/150/154 to its param triples and writes targets: +0x12c,+0x130 = obj.x,y, +0x128 = obj.z + zoff, +0x134 = H, +0x144 = pitch.
  2. Pitch: Camera_StepAxis(+0x24 → +0x144). StepAxis(cur,target,vel,{a0,a1,gain}): if the integer parts match stop; else desired speed min(FixSqrt(FixMul(|d|, a1)), gain) (signed), vel = ApproachValue(vel, desired, a0*dt), cur += vel*dt, clamp at target.
  3. Target y correction: +0x130 += (+0x128>>16) * [10] (pull north by (z+zoff)·cos p).
  4. Position: dx=(tx-sx)>>16, dy=(ty-sy)>>16, d=ISqrt(dx²+dy²); per axis want = FixMul((dx<<16)/d, gain), capped to ±FixSqrt(|dx|*a1) (cap uses accel a1, else a0); vel = ApproachValue(vel, want, accel*dt), pos += vel*dt, no overshoot.
  5. Height: StepAxis(+0x114 → +0x134), [8] = +0x114.
  6. [6] = +0x10c - +0x1c4, [7] = +0x110 - +0x1c8; recompute matrix/top/left if pitch or H changed. Steady state (used by view.py): cam = (tx, ty - (tz+zoff)·cos p - 12).

3. Floor walk (RenderWorldView 0x419dd0) ​

X0 = (camx>>16) + left;  Y0 = (camy>>16) - 32 + top
col = X0>>5;  xrel = left - (X0&31)          // tile-aligned left edge, px rel. camera
row = Y0>>5;  yrel = top - (Y0&31) - 32      // one tile margin above the screen
i  = [10]*yrel + H                           // = H - Y*cos p
xs = FixMul(xrel<<16, T[-i>>16])             // screen x of the row edge (rel. centre)
ys = FixDiv(300*[11], F + i) * yrel          // screen y (rel. centre)
tw = FixDiv(F, F + i) << 5                   // tile width on screen
on = 0
loop per row (far → near):
  ytop = ys
  if ys >= cy: if !on: break  else on = 0    // one extra row below the screen, no floor
  if on: while xs+tw < -0x20 - cx: col++, xrel += 32, xs += tw   // skip columns left of screen
  queue(cell[row][col-1])                    // one margin column (models only)
  yrel += 32; recompute xs', ys', tw' for the row's bottom edge
  xt = xs + cx; xb = cx + xs'
  while xt < w<<16:                          // cells left→right
     queue(cell)
     xt += tw_top;  if on: emit trapezoid cel
        c0=(prev xt & ~0x7fff, ytop+cy) c1=(xt & ~0x7fff, ytop+cy)
        c2=(xb+tw' & ~0x7fff, ys'+cy)   c3=(prev xb & ~0x7fff, ys'+cy)
  queue(one more cell on the right)
  row++;  if ys' < cy: on = 1               // the first row (above the screen) is models-only
after the loop: objects on the off-map pseudo cell (0x452d40) inside the visited box, then
View_DrawQueuedModels, flush.

Floor cel = copy of ART.CAR CCB cell&0x7f (off-map cells use CCB 2 = deep water), flags &~LAST | 0x1000 (explicit corners), mode 0xc (Cel_DrawTrapezoid), or 0x13 (Cel_DrawQuad) when terrain ∈ {1,2,4..0x33} (water/shore). x corners are rounded down to 0.5 px (only visible in hi-res, where corners are >>15); y corners are exact. Screen pixel = corner>>16 + clipX/Y.

Cel_DrawTrapezoid 0x40fe4f: rows c0.y .. c3.y inclusive (H=c3.y-c0.y); left edge c0→c3, right c1→c2, dL=((c3x-c0x)<<16)/H, dR=((c2x-c1x)<<16)/H, dv=((h<<16)-1)/H; per row n=(R>>16)-(L>>16) pixels (right end exclusive), du=(w<<16)/n, texel src[(v>>16)*w + (u>>16)], colour 0 skipped unless BGND. Adjacent tiles share the bottom/top row and the right/left x, so there are no gaps.

Cel_DrawQuad 0x410b9c (models, mode 0x13): scanline edge list. Edges (Cel_ScanEdge 0x410120): c0→c1 (u 0→(w-1)), c1→c2 (v 0→(h-1)), c3→c2, c0→c3; per-edge dx = Δx<<16/Δy, du=(Δu+1)/Δy, sorted by (top y, x, dx); horizontal edges kept with dx=0x7fffffff. The first two edges are left/right; when a row reaches the next edge's top y it replaces the edge whose bottom equals it (left) or else the right one. Spans are inclusive [L>>16, R>>16], du=(uR-uL)/(n), affine (no perspective correction).

Cel_DrawShadowSpans 0x411ba5 (mode 0xd): same trapezoid walk (H = c3.y-c0.y+1), each source row holds u8 x0,x1 pairs in 1/256 of the row width: x = ((b*((R-L)>>4))>>4 + L)>>16, dst = darken[4][dst].

4. Model queue and depth sort (View_QueueModel 0x4085a0) ​

View_QueueCellContents 0x419cf0: for a cell with BNO t≠0, queue BNO[t].model at the cell centre ((x+16)<<16, (y+16)<<16, 0); then every live object in the cell chain with obj+0x3c at obj+0x40. Each part of the chain (+4) gets its own item (pool 0x483f80, 0x34 bytes, max 512):

pos = copy; if def+0x28: def+0x28(def,&pos)            // Model_PosJitter: pos.xy += jit[(x>>21&15)+(y>>21&15)*16].dx,dy
item.def = def
item.x = def.dx - camx + pos.x ; item.y = pos.y - camy + def.dy
if def+0x24 == 0:
   static cell:  zb = H - def.zbias ; item.team = (cell>>14)&3 ; yaw = pitch = 0
   object:       zb = H - obj+0x50 - def.zbias ; yaw = obj+0x4c ; team = obj+0x10
else zb = H - hook(item, cell, obj)                    // hook may set yaw/pitch/team/cell and DAT_0045f398
item.z = (def.flags & 0x10) ? 0 : pos.z + def.dz
(x',y',z') = item.xyz * C
key = (y'>>8) - z' + zb + (|x'|>>9)
item.z = z' - H
DAT_0045f398: 0 → sorted insert; >0 draw immediately; <0 drop.

Sorted insert: walk from the tail backwards while new.key > node.key; insert after the stopping node. The list is in descending key order and View_DrawQueuedModels 0x408e50 draws from the head, so larger key = farther = drawn first; ties keep queue order (row far→near, left→right, parts in chain order). All floor cels are drawn before any model. Float: key ≈ -depth + y'/256 + |x'|/512 - zbias.

5. Model draw functions ​

Common tail Model_EmitFaces(tv, proj, item, def, view) 0x4088a0: ProjectVertices(proj, tv, &item.xyz, view, nverts) 0x401290:

iz = (v.z + item.z) >> 16 ; if iz > 0x1ff: reject the whole part
s  = T[iz]
X  = ((((v.x + item.x) >> 14) * s) & 0xfffe0003) >> 2  + cx     // int32 product, 0.5 px rounding
Y  = ((((v.y + item.y) >> 14) * s) & 0xfffe0003) >> 2  + cy

Faces (0x20 bytes): {sprite, flags, a, b, c0, c1, c2, c3}. nfaces>0: draw face if (!(flags&1) || X[a] <= X[b]) && (!(flags&2) || Y[a] <= Y[b]) (back-face test on a vertex pair). nfaces≤0: draw faces listed in def+0x3c[(yaw & 0xfff9ffff)>>19] (8 yaw octants; byte list ends <0), no test. CCB = copy of ART.CAR sprite + ((flags&8) ? item.team : 0) (team colour variants), explicit corners c0..c3 = projected verts (Cel_SetQuadCorners 0x426c10), draw mode = the sprite's PRE0 (0 normal, 1/2 shadow darken[4]/[2], 3/4 blend, 5 brighten, 0xd shadow spans, 0x10 key-blend for CCB 452-456/524-578, 0x11 PLUT remap). Face flag bits 8-9 = debris class used by SpawnDebrisBurst when the building is destroyed; 0x10/0x80 not read by the renderer.

fnmatrix applied to the vertices
Model_DrawStatic 0x408840C (cached in def+0x40 while def+0x3c == pitch)
Model_DrawYaw 0x408a20Y[yaw>>16]*C if yaw&0xffff0000 else C
Model_DrawYawPitch 0x408aa0pitch=
Model_DrawTurret 0x408b80Rx(item+0x20·4) · Rx'(item+0x30·4) [· Y[(item+0x24)>>16]] · Y[yaw] · C (CosFixed built on the fly)
Model_DrawTilted 0x408d40(P[pitch]*Y[yaw] or Y[yaw]) * C * P[DAT_00440c4c]

Wrappers (patch the shared def, then call one of the above):

  • Model_DrawBuilding 0x41f550 (BLDG/FACT): r = jit(cell).b3 (CellJitterEntry 0x417820 = entry (idx&15) + (idx&0x1e0)/2); table T = g_BldgWallSprites 0x4478d8 [909,931,915,927,919,923] or g_FactWallSprites 0x4478f0 [938,960,944,956,948,952] (chosen by def+8 == 0x447890); door side d=(def.flags>>16)&0xff (0 N,1 W,2 E,3 S); roof face4 = T[0]+s8[0x4478d0+(r&7)] (0,0,2,2,2,4,4,4); wall(face k, neighbour n) = T[1] if n is BNO 51 (rubble) else (d==k ? ((r&0x18)==0x18 ? T[3]:T[2]) : (((r&0x60)>>5)==k ? T[5] : T[4])); face1 (W) uses +2; faces 1/2/3 use neighbours W/E/S.
  • Model_TowerHook 0x4174e0 (TOWER/LARGE_TOWER turret part): no object → hidden if cell HP bits (0xe000000) are 0; if the viewing player's vehicle is an enemy Vehicle, ActivateTurret 0x423c10 may replace it by a live Turret object (then hidden); else team=cell side, yaw=(r&0x3f)<<16, pitch=(jit.b0 as u8)<<14 (random rest pose), returns zbias.
  • Model_CellHook 0x41f380: team from cell, remembers the cell (for the building wrapper).
  • Gates (Model_DrawGateH_W/E, GateV_N/S): leaf width = 1.0 - (gate obj+0x64 hi16), sprite 857/858 → static map = closed (no Gate object until opened; SpawnGate then clears the BNO).
  • Combat objects (render/view.c): projectiles hook 0x41f800 (yaw, pitch obj+0x60, frame obj+0x71 as team), grenade hook 0x41f830 (team = tumble frame + 12 for team 1); explosions: graphic 0x44aa20 has no model — hook 0x420680 + draw 0x4206b0 draw the descriptor's own quads (RenderExpl, see docs/game.md §6), the parts 0x452dc8/0x452e0c (ground flash, DrawStatic) continue with it; debris 0x433a60 (RenderDebris, one cel on 4 corners, fade by FUN_00407df0 = hidden below 1/16 else drawn twice); Stay 0x435e40/0x435e10 draws only the first part of the wrapped graphic with pitch obj+0x5c. The draw-time removals (idle debris / expired Stay) are reported through RenderObj.draw_flags and applied by effects_reap_drawn.
  • Dest wrappers (DestBush/Tree/WTower/Fuel) pick a rubble face/variant from the jitter byte (not needed for fresh maps; view.py draws them with their stored faces).

6. Map / per-cell data used by the renderer ​

Cells built as LoadLevelMap 0x4322f0 (+SetCellStaticObject 0x417850): terrain from tile table 0x452858, BNO terrain override, HP = clamp(strength,1,15) in bits 25-27 (3 bits!), side bits 14-15. Jitter table g_CellJitter256 0x472290 (InitCellJitterTable 0x41f190): srand(sum of raw tile bytes), 256 × {RandRange(25)-12, RandRange(25)-12, RandRange(11), RandRange(256)} with MSVC rand (s=s*214013+2531011; (s>>16)&0x7fff), RandRange(n) = ((rand()&0x7fff)*2*n)>>16; the previous seed is restored afterwards.

7. Model format (models.json) ​

188 model parts in .data (187 with geometry, 1 special: BNO 76 0x449fd0, hook 0x41f950 destroyed bridge), 598 faces, 1782 vertices. Part struct: see tools/models.py docstring. Vertex = 3×s32 16.16 (x east, y south, z up) relative to the part origin (+ offset +0x14..+0x1c). JSON per part: addr, draw_fn, next, flags, offset, zbias, depth_hook, pos_hook, verts, faces[{sprite, flags, test[a,b], corners[4], vis, team_offset, debris_class, sprite_mode, sprite_size}], orders_by_yaw8, used_by. Unreferenced (dead or reached via pointers not found): 0x446570 0x446738 0x449ae8 0x44a610 0x44c6b0 0x44ce90 0x44d7d0 0x44e240 0x44e3c0 0x452e0c.

BNO → model parts ​

BNOnameparts (addr: draw fn, faces, z-bias, hook)
0BNO_NOTHINGnone (not placeable)
1BNO_BUSH_1S0x4440a8 DrawStatic 3f zb1.5 +jitter
2BNO_BUSH_2S0x4440f0 DrawStatic 3f zb1.5 +jitter
3BNO_PALM_1S0x444490 DrawStatic 2f zb21 +jitter; 0x4443c8 DrawStatic 2f zb9 +jitter
4BNO_PALM_2S0x4448e8 DrawStatic 2f zb18 +jitter; 0x444780 DrawStatic 2f zb7.5 +jitter
5BNO_TREE_S0x444b38 DrawStatic 2f zb18; 0x4449d0 DrawStatic 2f zb7.5
6BNO_CACTUS0x444d18 DrawStatic 3f zb15 +jitter; 0x444cd0 DrawStatic 1f zb7.5 +jitter
7BNO_ROCK_1S0x444ed0 DrawStatic 1f zb-10 +jitter
8BNO_ROCK_2S0x444f18 DrawStatic 1f zb-10 +jitter; 0x444ed0 DrawStatic 1f zb-10 +jitter
9BNO_ROCK_1T0x444f60 DrawStatic 1f zb-10 +jitter
10BNO_ROCK_2T0x444fa8 DrawStatic 1f zb-10 +jitter; 0x444f60 DrawStatic 1f zb-10 +jitter
11BNO_PLANTER_BUSH0x4470d0 DrawStatic 1f zb-40
12BNO_PLANTER_BERRY0x447118 DrawStatic 1f zb-40
13BNO_PLANTER_STONE0x447160 DrawStatic 1f zb-40
14BNO_AMMO0x44a360 DrawAmmo 5f zb12; 0x44a2d8 DrawStatic 1f zb5
15BNO_PRISON_S0x446258 DrawStatic 4f zb12; 0x4461d0 DrawStatic 1f zb5; 0x446070 DrawStatic 5f zb12
16BNO_PRISON_E0x446258 DrawStatic 4f zb12; 0x4461d0 DrawStatic 1f zb5; 0x446070 DrawStatic 5f zb12
17BNO_PRISON_W0x446258 DrawStatic 4f zb12; 0x4461d0 DrawStatic 1f zb5; 0x446070 DrawStatic 5f zb12
18BNO_WATCHTOWER_NEnone (not placeable)
19BNO_WATCHTOWER_NWnone (not placeable)
20BNO_WATCHTOWER_SEnone (not placeable)
21BNO_WATCHTOWER_SWnone (not placeable)
22BNO_FLAG0x446998 DrawStatic 8f zb12; 0x4467a0 DrawStatic 1f zb5
23BNO_BLDG_N0x447908 DrawBuilding 5f zb12 CellHook; 0x447230 DrawStatic 1f zb5; 0x446e60 DrawStatic 1f zb-5
24BNO_BLDG_S0x44794c DrawBuilding 5f zb12 CellHook; 0x447230 DrawStatic 1f zb5; 0x446e60 DrawStatic 1f zb-5
25BNO_BLDG_E0x447990 DrawBuilding 5f zb12 CellHook; 0x447230 DrawStatic 1f zb5; 0x446e60 DrawStatic 1f zb-5
26BNO_BLDG_W0x4479d4 DrawBuilding 5f zb12 CellHook; 0x447230 DrawStatic 1f zb5; 0x446e60 DrawStatic 1f zb-5
27BNO_FACT_N0x447a18 DrawBuilding 5f zb12 CellHook; 0x447278 DrawStatic 1f zb5; 0x446ea8 DrawStatic 1f zb-5
28BNO_FACT_S0x447a5c DrawBuilding 5f zb12 CellHook; 0x447278 DrawStatic 1f zb5; 0x446ea8 DrawStatic 1f zb-5
29BNO_FACT_E0x447aa0 DrawBuilding 5f zb12 CellHook; 0x447278 DrawStatic 1f zb5; 0x446ea8 DrawStatic 1f zb-5
30BNO_FACT_W0x447ae4 DrawBuilding 5f zb12 CellHook; 0x447278 DrawStatic 1f zb5; 0x446ea8 DrawStatic 1f zb-5
31BNO_BNKR_N0x448268 DrawStatic 11f zb12; 0x447dd0 DrawStatic 1f zb5
32BNO_BNKR_S0x4482b0 DrawStatic 11f zb12; 0x447dd0 DrawStatic 1f zb5
33BNO_BNKR_E0x448708 DrawStatic 11f zb12; 0x447dd0 DrawStatic 1f zb5
34BNO_BNKR_W0x448750 DrawStatic 11f zb12; 0x447dd0 DrawStatic 1f zb5
35BNO_OUTPOST0x447d00 DrawStatic 5f zb12 CellHook; 0x447cb8 DrawStatic 1f zb5
36BNO_HOSP_S0x448df8 DrawStatic 5f zb12; 0x4488e0 DrawStatic 1f zb2
37BNO_HOSP_E0x448e40 DrawStatic 5f zb12; 0x448924 DrawStatic 1f zb2; 0x44881c DrawStatic 1f zb-50
38BNO_HOSP_W0x448e88 DrawStatic 5f zb12; 0x448968 DrawStatic 1f zb2; 0x4487d8 DrawStatic 1f zb-50
39BNO_FUEL_N0x445ddc DrawStatic 10f zb12; 0x445cc8 DrawStatic 1f zb-40; 0x445bfc DrawStatic 1f zb5
40BNO_FUEL_S0x445d98 DrawStatic 10f zb12; 0x445bb8 DrawStatic 1f zb5
41BNO_FUEL_E0x445e20 DrawStatic 10f zb12; 0x445d0c DrawStatic 1f zb-40; 0x445c40 DrawStatic 1f zb5
42BNO_FUEL_W0x445e64 DrawStatic 10f zb12; 0x445d50 DrawStatic 1f zb-40; 0x445c84 DrawStatic 1f zb5
43BNO_GATE_H0x4428a8 DrawGateH_W 7f zb14.1; 0x4428ec DrawGateH_E 7f zb14.1
44BNO_GATE_V0x442d70 DrawGateV_N 8f zb14.1; 0x442db4 DrawGateV_S 8f zb14.1
45BNO_WALL_V0x442278 DrawStatic 3f zb15
46BNO_WALL_H0x441d68 DrawStatic 3f zb15
47BNO_TENT_V0x4491c0 DrawStatic 4f zb10
48BNO_TENT_H0x449208 DrawStatic 4f zb10
49BNO_TOWER0x441840 DrawYawPitch 5f zb24.5 TowerHook; 0x4414f0 DrawStatic 5f zb14; 0x441338 DrawStatic 1f zb5
50BNO_LARGE_TOWER0x4418a0 DrawYawPitch 5f zb29.5 TowerHook; 0x441698 DrawStatic 5f zb14; 0x4415b0 DrawStatic 1f zb5
51BNO_STD_DEST0x449318 DrawStatic 1f zb-40 HookStdDest
52BNO_DEST_JAILnone (not placeable)
53BNO_DEST_HOSP0x448ef0 DrawStatic 1f zb-40
54BNO_LDMG_WALL_H0x441df8 DrawStatic 4f zb15
55BNO_RDMG_WALL_H0x441db0 DrawStatic 4f zb15
56BNO_BDMG_WALL_H0x441fa0 DrawStatic 5f zb15
57BNO_LDMG_WALL_V0x442308 DrawStatic 4f zb15
58BNO_RDMG_WALL_V0x4422c0 DrawStatic 4f zb15
59BNO_BDMG_WALL_V0x4424b0 DrawStatic 5f zb15
60BNO_DMG_TOWER0x441a78 DrawStatic 1f zb-40; 0x4419a0 DrawStatic 5f zb14
61BNO_DESTROYED_TOWER0x441ac0 DrawStatic 1f zb-40
62BNO_DMG_FLAG0x446b08 DrawStatic 4f zb12; 0x446a00 DrawStatic 1f zb5
63BNO_DEST_FLAG0x446d28 DrawStatic 4f zb12; 0x446c60 DrawStatic 1f zb5
64BNO_DEST_TENT0x449270 DrawStatic 1f zb-50 HookDestTent
65BNO_DEST_AMMO0x44a3e8 DrawStatic 2f zb-50
66BNO_DEST_GATE_H0x442e98 DrawStatic 1f zb-50
67BNO_DEST_GATE_V0x442e98 DrawStatic 1f zb-50
68BNO_READY_TOWER0x4414f0 DrawStatic 5f zb14; 0x441338 DrawStatic 1f zb5
69BNO_WTOWER_NE0x445450 DrawStatic 5f zb12; 0x4450f8 DrawStatic 1f zb5
70BNO_WTOWER_NW0x445494 DrawStatic 5f zb12; 0x44513c DrawStatic 1f zb5
71BNO_WTOWER_SE0x4454d8 DrawStatic 5f zb12; 0x445180 DrawStatic 1f zb5
72BNO_WTOWER_SW0x44551c DrawStatic 5f zb12; 0x4451c4 DrawStatic 1f zb5
73BNO_DEST_PRISON0x4462c0 DrawStatic 1f zb-40
74BNO_BRIGE_H0x449d90 DrawStatic 1f zb-40
75BNO_BRIGE_V0x449dd8 DrawStatic 1f zb-40
76BNO_BRIGE_V0x449fd0 special (hook Model_HookBridgeDest)
77BNO_DEST_FUEL0x445fa8 DrawDestFuel 2f zb-50 CellHook
78BNO_DEST_WTOWER_NE0x4455e0 DrawDestWTower 1f zb-50
79BNO_DEST_WTOWER_NW0x445624 DrawDestWTower 1f zb-50
80BNO_DEST_WTOWER_SE0x445668 DrawDestWTower 1f zb-50
81BNO_DEST_WTOWER_SW0x4456ac DrawDestWTower 1f zb-50
82BNO_DEST_OUTPOST0x447d68 DrawStatic 1f zb-50
83BNO_DEST_PLANTER0x4471a8 DrawStatic 1f zb-40 HookDestPlanter
84BNO_DEST_TREE_S0x444588 DrawDestTree 1f zb-40 CellHook +jitter
85BNO_DEST_TREE_D0x444698 DrawDestTree 1f zb-40 CellHook +jitter
86BNO_DEST_TREE0x4445d0 DrawDestTree 1f zb-40 CellHook
87BNO_DEST_BUSH_10x444178 DrawDestBush 1f zb-40 CellHook +jitter
88BNO_DEST_BUSH_20x444200 DrawDestBush 1f zb-40 CellHook +jitter
89BNO_STORAGE_LIGHT0x44b000 DrawStatic 1f zb-50
90BNO_ACTIVE_TOWER0x441538 DrawStatic 5f zb14 HookHide
usermodel
class MAN (0x43fb10+0x14)0x43f900 Model_DrawMan (8 verts, 2 faces)
code: MAN walk (ManUpdate)0x43f900 Model_DrawMan (8 verts, 2 faces)
code: MAN swim (ManUpdate)0x43f998 Model_DrawManSwim (4 verts, 1 faces)
code: MAN misc (0x4064d0/0x406550)0x43fa30 Model_DrawStatic (4 verts, 1 faces)
class SUB (0x43fc10+0x14)0x43fbb0 Model_DrawSub (4 verts, 1 faces)
class Turret Gun (0x44ad40+0x14)0x441840 Model_DrawYawPitch (12 verts, 5 faces)
class Turret Gun (large) (0x44ad90+0x14)0x4418a0 Model_DrawYawPitch (12 verts, 5 faces)
code: large turret (TurretInit)0x4418a0 Model_DrawYawPitch (12 verts, 5 faces)
code: gate H (SpawnGate)0x4428a8 Model_DrawGateH_W (15 verts, 7 faces)
code: gate V (SpawnGate)0x442d70 Model_DrawGateV_N (16 verts, 8 faces)
class Storage (rising) (0x443288+0x14)0x4431f8 Model_DrawStoragePad (12 verts, 5 faces)
class Storage (lowering) (0x4432d8+0x14)0x4431f8 Model_DrawStoragePad (12 verts, 5 faces)
class Mine (0x455008+0x14)0x4493b0 Model_DrawStatic (4 verts, 1 faces)
code: mine (MineUpdate)0x449478 Model_DrawStatic (4 verts, 1 faces)
class Missle (0x450950+0x14)0x4495c0 Model_DrawYawPitch (8 verts, 3 faces)
class Death Missle (0x4509f8+0x14)0x4495c0 Model_DrawYawPitch (8 verts, 3 faces)
class TRACER (0x4509a0+0x14)0x4495c0 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 1 model (+0x2c)0x4495c0 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 3 model (+0x2c)0x4495c0 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 4 model (+0x2c)0x4495c0 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 5 model (+0x2c)0x4495c0 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 6 model (+0x2c)0x4495c0 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 1 shadow (+0x30)0x449628 Model_DrawYaw (4 verts, 1 faces)
projectile type 3 shadow (+0x30)0x449628 Model_DrawYaw (4 verts, 1 faces)
projectile type 4 shadow (+0x30)0x449628 Model_DrawYaw (4 verts, 1 faces)
projectile type 5 shadow (+0x30)0x449628 Model_DrawYaw (4 verts, 1 faces)
projectile type 6 shadow (+0x30)0x449628 Model_DrawYaw (4 verts, 1 faces)
projectile type 2 model (+0x2c)0x449730 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 8 model (+0x2c)0x449730 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 9 model (+0x2c)0x449730 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 2 shadow (+0x30)0x449798 Model_DrawYaw (4 verts, 1 faces)
projectile type 8 shadow (+0x30)0x449798 Model_DrawYaw (4 verts, 1 faces)
projectile type 9 shadow (+0x30)0x449798 Model_DrawYaw (4 verts, 1 faces)
projectile type 10 model (+0x2c)0x4498a0 Model_DrawYawPitch (8 verts, 3 faces)
projectile type 10 shadow (+0x30)0x449908 Model_DrawYaw (4 verts, 1 faces)
projectile type 0 model (+0x2c)0x4499a0 Model_DrawTilted (4 verts, 1 faces)
projectile type 7 model (+0x2c)0x4499a0 Model_DrawTilted (4 verts, 1 faces)
projectile type 11 model (+0x2c)0x4499a0 Model_DrawTilted (4 verts, 1 faces)
projectile type 0 shadow (+0x30)0x449a08 Model_DrawYaw (4 verts, 1 faces)
projectile type 7 shadow (+0x30)0x449a08 Model_DrawYaw (4 verts, 1 faces)
projectile type 11 shadow (+0x30)0x449a08 Model_DrawYaw (4 verts, 1 faces)
class Grenade (0x450d48+0x14)0x449aa0 Model_DrawTilted (4 verts, 1 faces)
code: bridge debris (0x41f870)0x449e20 Model_DrawStatic (4 verts, 1 faces)
code: bridge debris (0x41f870)0x449e68 Model_DrawStatic (4 verts, 1 faces)
code: bridge debris (0x41f870)0x449eb0 Model_DrawStatic (4 verts, 1 faces)
code: bridge debris (0x41f870)0x449ef8 Model_DrawStatic (4 verts, 1 faces)
code: bridge debris (0x41f870)0x449f40 Model_DrawStatic (4 verts, 1 faces)
code: bridge debris (0x41f870)0x449f88 Model_DrawStatic (4 verts, 1 faces)
code: tank turret (Model_DrawTank)0x44ccb0 Model_DrawYaw (22 verts, 8 faces)
class Vehicle (0x44b128+0x14)0x44cd10 Model_DrawTank (24 verts, 6 faces)
class Destroyed Vehicle (0x44b0d8+0x14)0x44cd10 Model_DrawTank (24 verts, 6 faces)
vehicle Tank def[0x52] body0x44cd10 Model_DrawTank (24 verts, 6 faces)
vehicle Tank def[0x53] shadow0x44cdc0 Model_DrawYaw (4 verts, 1 faces)
vehicle Heli def[0x53] shadow0x44cdc0 Model_DrawYaw (4 verts, 1 faces)
vehicle Tank def[0x55] part 0x550x44ce48 Model_DrawYaw (8 verts, 2 faces)
vehicle Heli def[0x55] part 0x550x44ce48 Model_DrawYaw (8 verts, 2 faces)
vehicle Tank def[0x58] wreck A0x44cfe0 Model_DrawYaw (12 verts, 3 faces)
vehicle Tank def[0x59] wreck B0x44cfe0 Model_DrawYaw (12 verts, 3 faces)
vehicle MSV def[0x52] body0x44d650 Model_DrawMSV (52 verts, 14 faces)
vehicle MSV def[0x53] shadow0x44d700 Model_DrawYaw (4 verts, 1 faces)
vehicle MSV def[0x55] part 0x550x44d788 Model_DrawYaw (8 verts, 2 faces)
vehicle MSV def[0x58] wreck A0x44d920 Model_DrawYaw (12 verts, 3 faces)
vehicle MSV def[0x59] wreck B0x44d920 Model_DrawYaw (12 verts, 3 faces)
code: jeep (Model_DrawJeep)0x44dfb0 Model_DrawYaw (48 verts, 12 faces)
vehicle Jeep def[0x52] body0x44e070 Model_DrawJeep (44 verts, 11 faces)
code: jeep part (0x42f5c0)0x44e110 Model_DrawYaw (6 verts, 2 faces)
vehicle Jeep def[0x53] shadow0x44e1a8 Model_DrawJeepShadow (4 verts, 1 faces)
vehicle Jeep def[0x55] part 0x550x44e328 Model_DrawYaw (8 verts, 2 faces)
vehicle Jeep def[0x58] wreck A0x44e510 Model_DrawYaw (12 verts, 3 faces)
vehicle Jeep def[0x59] wreck B0x44e510 Model_DrawYaw (12 verts, 3 faces)
code: carried flag (FlagUpdate)0x44e610 Model_DrawFlagCarried (8 verts, 2 faces)
class Flag (0x44ae30+0x14)0x44e740 Model_DrawFlag (8 verts, 2 faces)
code: flag (FlagUpdate)0x44e740 Model_DrawFlag (8 verts, 2 faces)
code: heli shadow (0x417e40/Model_DrawHeliShadow)0x44e9b8 Model_DrawYaw (4 verts, 1 faces)
vehicle Heli def[0x52] body0x44ee68 Model_DrawHeli (44 verts, 12 faces)
code: heli (lift/dock)0x44ee68 Model_DrawHeli (44 verts, 12 faces)
vehicle Heli def[0x58] wreck A0x44eec8 Model_DrawYawPitch (44 verts, 12 faces)
code: rotor A (0x42fb10)0x44f058 Model_DrawYaw (4 verts, 1 faces)
code: rotor B (0x42fba0)0x44f188 Model_DrawYaw (4 verts, 1 faces)
code: rotor (lift, 0x42b5e0)0x44f1d0 Model_DrawRotorB (4 verts, 1 faces)
vehicle Heli def[0x59] wreck B0x44f298 Model_DrawYaw (4 verts, 1 faces)
code: explosion part (0x41fd90)0x452dc8 Model_DrawStatic (4 verts, 1 faces)
class Drone (0x455778+0x14)0x455680 Model_DrawDrone (50 verts, 15 faces)
code: drone shadow (SpawnDrone)0x455730 Model_DrawDroneShadow (4 verts, 1 faces)

8. Open questions / caveats ​

  • The driving camera (H=-170 → 2.3× at the centre, ~4 tiles visible) comes straight from g_VehCamHeight + ViewAddTracker; it has not been compared with a real screenshot.
  • FixDiv is x87 80-bit in the original; view.py uses doubles (possible 1-ulp differences).
  • Cel_DrawQuad port follows the edge-pairing logic but the exact rounding of Span_* for every mode (and the n==0 case using EDX garbage) is not proven pixel-exact; Cel_DrawTrapezoid and Cel_DrawShadowSpans are straight ports.
  • Vehicle wrappers are ported in src/render/view.c (draw_tank/msv/jeep/heli/rotor_a/b/storage*, depth hooks in obj_hook): tank turret 0x44ccb0 = hull yaw + state+0x58, barrel verts 15..21 = 0x44c938 rotated about x by state+0x50·4 + 0x44c704; MSV launcher verts 44..51 (0x44d028/0x44d094), face 13 sprite 0x146 − frame; jeep tyre sprites 0x1c9 + (x>>16 & 3), boat morph table 0x44de70 (verts 36..43) and 0x44dfb0 / wake 0x44e110 (scale cache DAT_0044dc80); heli: Model_DrawTurret with pitch = obj+0x70, roll = state+0x88, tail fold 0x120000·(1−spin-up) on verts 0x44ee40, rotor disc 0x44ea00 by speed (0x44e850/898/8e0, 4 = two resting blades 0x44e9b8), ground shadow = class-4 child (0x44f1d0 + rotor shadow 0x44f0a0) at (x + 85·(z>>8), y − z/2) (render_heli_shadow); lift: pad saves the last cel, platform draws obj+0x60's model at once, doors slide u68·0x4ccc, the saved cel is re-added. Hook 0x417d20 draws the lift immediately while the cell terrain has bit 1 clear (92). Test sheets: out/vehicles/*.png (render_test). Model_DrawTilted uses P[DAT_00440c4c = 0x38] after C.
  • Water/palette cycling (Pal_CycleWater) is not applied (static palette); HUD cels are not drawn, only the 1PBSCRL.RFA status-bar bitmap under the view.
  • Model_DrawTurret: item+0x20 pitch (Rx), +0x30 roll (Ry), +0x24 extra yaw (mode 1); only the heli uses it.
  • Face flag bits 0x10/0x80 and def+0x0c (size) meaning unconfirmed.

OpenRF is released under the GPL-3.0. Return Fire is © 1995–1996 Silent Software / Prolific. OpenRF contains no original code or assets. The cross-platform and browser builds run on gasm.