# Sky rendering

The January 1998 software engine draws the horizon before flushing its distance-sorted terrain and object queue. Sun and moon discs and individual stars are painted into that background. Clouds are submitted to the scene queue and can obscure objects behind them.

## Stars, sun and moon

`STARMAP.BIN` contains 488 four-byte records in each warzone. The four files are identical. A record stores a colour offset followed by X, Y and Z indices into the rotated 65-entry medium grid. The coordinates are directions, independent of the camera position. The palette starts at index 168 and has 24 entries. The visibility threshold table at `004f0944` has 24 entries: `0, 0, 0x20000000, 0x40000000, 0x60000000, 0, 0x20000000, 0x40000000`, followed by zeros. Authored offsets include 10, 14 and 15 as well as 0 through 4.

`draw_star_map_` (`00498d88`) switches from the sun to stars and moon at night level 644,245,095. Cloud cover hides them below and within the cloud layer. The sky caller also suppresses them below the clouds in non-clear weather.

`draw_sun_` (`00498db8`) and `draw_moon_` (`00499148`) use the same circular shape and palette colour 191. Both require the light-source pitch to be at or below approximately -10 degrees. Their centre is the projected light-source vector shifted down by eight bits. A second projection, with 500,000 added to eye-space X, determines the radius. These projections allow centres outside the viewport, then clip the circle. The preliminary screen rejection uses fixed bounds of -100 to 740 horizontally and -100 to 580 vertically, even in 320 by 240 modes.

Sun glare begins when the forward eye-space component of the light unit vector exceeds `0x78000000`. Its strength is that component multiplied by 16, minus `0x80000000`, with the original 32-bit arithmetic. Palette fading uses the resulting value later in the frame.

The circle scanner (`004b6310`) uses integer midpoint stepping, writes symmetric horizontal spans, then paints each span once. The same scanner supplies solid, fine mesh, coarse mesh and translucent circles.

## C++ comparisons

`tools/check_cpp_circles.py` compares 8,808 original circle images and 408,858,624 pixels. `tools/check_cpp_celestial.py` compares 1,324 frames and 171,545,600 pixels, with 618 visible star/disc frames and 51 nonzero glare frames. Every compared pixel and glare value matches. Reports are in `work/checks/cpp/circles/` and `work/checks/cpp/celestial/`.

## Cloud mesh

`get_cloud_coords_` (`004ab760`) constructs a separate grid above the terrain. It reuses the terrain grid's light byte at each horizontal location, but replaces the height. The usual cloud height is 26,000 terrain units, or 6,656,000 world units. Overcast weather chooses 14,000 plus a small random offset. Visibility changes at the configured bottom and top of the cloud layer.

Below the layer, the cloud surface slopes toward the horizon. Beyond eight grid steps from the camera sector origin, height falls by 288 terrain units per approximate range step, or 155 in overcast weather. Above the clouds this drop is disabled. Vertex conversion uses the same huge, large and medium fixed-point tables as terrain, including their rounding and boundary progression.

`get_culled_cloud_sector_scan_` (`0049614c`) selects a heading-dependent wedge below the clouds, extending up to 14 sectors. Above them it selects a square up to 18 sectors away. Looking up more than 70 degrees reduces the scan to a square of three sectors. The scan keeps a narrow strip around the camera and clips to the map's usable cloud grid.

The 36 by 36 lookup at `004ef440` selects coarse four-step patches outside a central region and two-step patches inside it. Special three-triangle patches join those resolutions without cracks. The executable also has drawing cases for one-step patches, although this lookup never selects them. The 1,296 entries are reproduced by the C++ selection function, without embedding a copied table.

The outer patches use approximate range for depth sorting. The central patches use the greatest eye depth of their four corners. Their shared vertex brightness is adjusted near the horizon, then the queue is flushed with the terrain and other objects. Each patch becomes two or three Gouraud-shaded triangles using sky indices 166 and 167; the intensity table supplies the intermediate shades. Triangle order reverses above the cloud layer.

The original allocates an object-buffer slot before rejecting an outer cloud patch for distance. The reconstruction preserves that capacity effect. Its exhausted/rejected patch path uses a bounded horizon intensity instead of reading an uninitialized temporary.

`tools/check_cpp_cloud_geometry.py` matches 1,241,584 vertices across 168 viewpoints. `tools/check_cpp_cloud_frames.py` matches all 193,639 queued objects and 22,732,800 pixels in 296 cloud images. `tools/check_cpp_atmosphere.py` then matches 192 complete terrain, horizon, celestial and cloud frames across all warzones and weather states. These checks compare indexed drawing. The complete RGB palette comparisons are described below.


## Scene colours

Palette processing has an explicit order. Weather blending runs first, followed by sun glare, night darkening, cockpit-glass darkening and the full-screen infrared conversion. Each fader has a palette range and priority; equal priorities keep creation order. The colour interpolation operates on 16-bit fractional RGB channels using a rounded Q31 blend, then discards the fraction. Monochrome conversion uses the original integer approximations to 0.30 red, 0.59 green and 0.11 blue.

`CLEAR.RGB`, `OVERCAST.RGB` and `ABOVE.RGB` each supply 13 colours, starting at index 155. Above the clouds, the choice depends on both weather and the cloud-enable flag. The weather fader changes over the last 131,072 world units below or above the cloud boundary. Inside the cloud layer it becomes opaque grey, with a white transition near the top. Weather states 2 and 3 add persistent haze below the layer.

Night darkening is capped at Q31 value 1,964,947,537, approximately 91.5 percent. Stars and illuminated effects occupy palette entries outside that fade's range. Sun glare blends toward RGB `(255, 255, 215)`. Infrared mode 1 cancels night darkening and converts the affected scene entries to luminance in the green and blue channels. Mode 2 uses a separate indexed thermal remap, disables textures and Gouraud shading, hides glass and heat haze, and omits smoke/explosion billboards.

Fort Hood and Korea replace terrain entries 83 through 114 with a green ramp embedded in `APACHE95.EXE`. The C++ loader reads those 96 bytes from the selected original executable rather than redistributing them. Its PE reader checks the supported executable and bounds every section read. Three additional infrared/HUD remaps are calculated from the initial palette, before the sky and fog replacements.

`tools/check_cpp_faders.py` matches 2,400 palette compositions containing 9,591 faders. `tools/check_cpp_scene_palette.py` matches all 5,898,240 bytes in 2,304 combinations of weather, altitude, night, infrared and glare. Whole-scene tests match 84 RGB frames, covering 6,451,200 pixels; 12 further scenes reach the previously documented original model-normal errors. Reports: [faders](../verification/cpp/faders/report.json), [scene palettes](../verification/cpp/scene-palette/report.json), [RGB scenes](../verification/cpp/scene-rgb/report.json).
