Repository navigation
Outdoor Maps Changes
Two mission worlds promoted from 34-line placeholders to playable maps:
| World | Mission | Objective | Next |
|---|---|---|---|
GameData/Worlds/Sektor_01_SilentPines |
Sektor_01_SilentPines (Sefirah 1, Terran Prime) |
Objective_ClearArea — kill 5 |
Sektor_02_MegacityGrid |
GameData/Worlds/KryoArchipel_Severny |
KryoArchipel_Severny (Sefirah 2, Hydrosis Prime) |
Objective_CollectRelics — collect 3 |
Refinery_Complex_02B |
Direction was Unreal-'98 outdoor set pieces (NyLeve's Falls / a dusk cascade). Both are
all-brush maps: 150 and 136 primitives, 0 errors and 0 headroom traps on worldcheck.
Collision is AABB brushes only. A heightmap supplies a terrain surface (per-cell
SampleHeightmapY ground clamp) but no lateral collision, so a heightmap cliff is
walk-through. Dust_Ravine is the only shipped heightmap and it is nearly flat (10 units of
relief over 180), which is exactly the safe role: gentle ground relief, with all real
elevation from brushes. These maps skip the heightmap and build the gorge/terraces from boxes
and cylinders.
Every walkable rise is 0.9, not 1.0: kStepHeight is exactly 1.0f, so a 1.0 step sits
on a float comparison boundary.
# Cascade sheet (0.2 thick), 0.1 clear of the solid face it hangs on
add box <x> <y> <zmid> 0.2 <h> <d> 0 \
texPath=GameData/Tex/Global/EFX/waterfall_streaks.png texScaleU=1.5 texScaleV=1 \
flags=268962944 surfAlpha=0.9 panV=-0.35
268962944 = SURF_UNLIT 2048 | SURF_TRANSLUCENT 128 | SURF_TWO_SIDED 1024 |
SURF_PAN_V 524288 | SURF_NO_BSP_CUTS 268435456.
-
UNLITmatters: without it the sheet takes scene lighting and a dusk waterfall renders as a grey slab. -
NO_BSP_CUTSkeeps the sheet cosmetic so it is not an invisible wall. - Negative
panVscrolls the texture downward (uv.y += uPan.y * uTime). - Pools/rivers use
flags=264320(UNLIT | PAN_U | TRANSLUCENT) withsurfAlpha=0.8.
SilentPines' falls also needed a rock face behind the notch (x 86.1..87.5): with the notch
open to the skybox the water read as a blue panel rather than a fall.
The repo shipped one water texture, liquid_water.png: a high-contrast blue tile pattern. On a
large pool it reads as bathroom tiling, and on a cascade sheet as a flat blue panel. Two
textures were generated instead (deterministic — fixed RNG seeds, so re-running rewrites
identical bytes):
| Texture | Size | Purpose |
|---|---|---|
water_pool.png |
64x64 RGB | Dark, low-contrast ripple field (mean RGB ≈ 18/40/58). Pool slabs are `UNLIT |
waterfall_streaks.png |
64x128 RGBA | Vertical streaks with punched transparent gaps (12.3% of pixels alpha 0, mean alpha 156). |
The alpha is the whole point: Surface.fs multiplies the sampled texture's alpha by uAlpha,
so transparent holes are what turn a sheet into falling water. The first version of the
generator drew 150 anti-aliased lines over a 64px width and measured 84% of pixels inked —
a near-solid pale panel. The script now draws 90 thin streaks and then erases 34 small
rectangles with CompositingMode.SourceCopy, which is what guarantees real holes rather than
low-alpha haze.
OzoneLoader::BuildBox multiplies generated UVs by 16 when h >= 1.0 and 8 when thin,
and the authored texScaleU/V is applied on top of that. Values in the shipped-demo range
(8–14) therefore compound to hundreds of repeats on a large brush and render as moire noise.
Author texScale near 1 for big surfaces. This was the single largest visual defect found
by rendering the maps, and it is invisible to worldcheck.
worldcheck --headroom-all both worlds: 0 errors, headroom traps: 0
worldcheck --asset-refs both: no new dangling asset references, 0 case mismatches
client --shot all captures render; every texPath loads and decodes
in-game log sky zone defs bind and their on_enter actions fire
One benign warning per world: multiple playerstarts; only #1 is ever used — the maps author a
second playerstart for parity with the other worlds; PawnSystem::GetFirstPlayerStart only
reads #1.
Two independent defects, both measured rather than guessed:
-
Cap band. The skybox draws top/bottom faces with a different texture (
useCap) on a horizontal quad, so every wall top met an unrelated image. The band was a 50-level (day) / 24-level (dusk) tonal step. - Corner panels. The six quads are separate planes and the four wall quads do not agree on their V mapping. Measured at a cube corner: adjacent walls differed by 33-40 colour levels at the same screen row, and the outer panels ramped horizontally within a row while the wall facing the camera was flat. That is a per-face V offset, so no texture can fix it — a vertically symmetric texture only cancels a flip (it took the step from 41 to 33).
The fix, in three parts:
-
tools/gen_sky_textures.ps1generates horizontally periodic, vertically symmetric gradients (sky_grad_day.png,sky_grad_dusk.png): every horizontal variation is a sum ofsin(2*pi*k*u)with integer k and the last column is overwritten with column 0, so corners match by construction; andcolour(y) == colour(h-1-y), so a V flip is invisible. -
Sky.fssamples V from WORLD ELEVATION, not from the quad's texcoord:elevV = clamp(dirW.y * 0.5 + 0.5, 0, 1). Elevation is a property of the view ray, so every quad — walls and cap alike — agrees by construction. This is what actually removed the corners. - Both worlds pass the same gradient as cap and sides (
levelinfo <grad> <grad>); with a separate cap image the elevation V made the cap's flat zenith meet the walls' 91%-to-zenith row, leaving a faint trapezoid.
Measured on the same cameras (largest adjacent-pixel step in the sky):
| before | after | |
|---|---|---|
| SilentPines, seam column x=215 at rows 20 / 60 / 120 | 33 / 28 / 20 | 1 / 1 / 1 |
| KryoArchipel, seam column x=215 | 40 / 36 / 25 | 5 / 4 / 3 |
| SilentPines, cap band | 50 levels | 1 |
| KryoArchipel, cap band | 24 levels | 1 |
| EngineTest (legacy panorama), hard straight band | 16.06 lum | 2.13 lum |
Residuals: a ≤5-level step at the U face boundary (each face still supplies its own fragTexCoord.x), and a faint trapezoid when looking steeply up at the zenith. Both are shading-level, not the hard panels of before.
Behaviour change to know about: because V now tracks elevation, a wall samples only V ≥ 0.5. Legacy panorama skies therefore show only the upper half of their image on the walls — sky_noon.png / sky_day.png now display their top half, which also moved that art's own baked 26-31 level wrap seam onto a constant elevation contour. Legacy worlds measured better (EngineTest's band fell 16.06 → 2.13), but the change is visible and is not confined to the two outdoor maps.
-
Sky box cube seams - fixed (see above). The hard panels, the cap band and the corner
tonals are gone. What remains is a <=5-level step at each U face boundary (each face still
supplies its own
fragTexCoord.x) and a faint trapezoid when looking steeply up at the zenith. -
Water is still a scrolling texture, not a simulation. No foam sheet, refraction or depth
term exists;
waterfall_streaks.png,water_pool.pngandfoam.pngare paint, and the motion comes from the pan. Foam now reads at the cascade impacts (an impact band, a 3.4x faster churn wall, and shore strips), but it is 1998, not 2004. - Outdoor scale reads arena-like. With no terrain and AABB-only collision, a wide valley is a floor plate between tall boxes. Dusk Falls was tightened for this reason (depth 168 -> 102, pools 27x24/44x24/38x28 -> 20x18/26x18/26x22, aqueduct narrowed, seven ruin fragments added); the terraces now have landmarks and a horizon instead of reading as empty plains.
-
No waterfall audio loop. Script
play_soundis one-shot; the keyed loop API is stat-driven only. The falls are silent.
- Boundary treatment decides whether an outdoor map reads as outdoors: a 44-unit painted ring at the map bounds turns every sightline into a courtyard wall. A ~12-unit rock ridge leaves the sky visible and still stops the player walking off the world.
- Place the spawn at the edge of a height, not in the middle of a plateau, or the first frame is a floor.
- Box arg order is
x y z w h d rot: the 5th number is vertical size, the 6th is Y depth.
Angels95 - AngelTech Engine | repository | 8ea8239
Built on raylib 5.5.