Repository navigation
Table-driven FS1 background nebula (FS1 mesh and palettes, generated patterns) - #7853
dustinspace217 wants to merge 14 commits into
Conversation
Restores the FS1 background nebula that the engine has parsed and loaded since retail but has not drawn since the palette manager was removed in 2017. The mesh is drawn on the far sphere before the stars, after the skybox, and only when a mission has no skybox model and is not a full nebula mission. The colour ramp is the first 20 entries of the FS1 game palette for the mission's nebula and colour (gamepalette<N>-<CC>.pcx), uploaded as a one-row texture so the GPU interpolates between ramp entries. Missing palette or mesh files log a line and draw nothing. The mesh loader now rejects triangle indices outside the point list and closes the file on every failure path, since the renderer consumes the indices. qtFRED's background editor and FRED's nebula colour control reload the nebula when the fields change, so the editors preview it.
|
Screenshots, updated for the current branch (510d47c), headless, post-processing on at the default lighting profile. Training Mission 1 with Nebula01 Grey blue: FS1 itself on the left (Direct3D under Wine, 16-bit), FSPort's copy of the mission on this branch on the right (OpenGL, with FS1's palette file): SM1-01 with Nebula01 Red, OpenGL: The same on Vulkan: |
|
This is certainly a fine way to do it. I was working on this a few months ago and my version restored the .neb and pallette files as tabled values in a table so that users can create whole new patterns and colors just with a bit of text editing, removing the need for .neb files entirely. I also exposed various generation values to those tables (samples and other things) so that users could use the system to recreate FS1's style or make something entirely new with the same code. Worth considering. |
|
I like that idea. I'll investigate. Does your solution still exist? |
https://github.com/MjnMixael/fs2open.github.com/tree/ofp/merged/restore-fs1-nebula It is feature complete and usable in my own fork. It's not something I'll be PRing to SCP, so feel free to do it however you prefer. Just thought I'd share what I came up with for the same code. |
|
Thanks, that's generous. I reviewed the branch. I'd most like to keep the ability to edit the patterns and colors that the table provides, and the ability to make new ones without touching code. What I'd like to keep from mine is the easy ability to use the actual FS1 data, so FSPort and other mods can get the exact original look and feel, if desired. I think they fit together. Both end as a brightness field on a sphere colored by a ramp, so a pattern entry could take an optional Two things I noticed while reading, in case they're useful in your fork: Are you fine with me porting the table code into this work with you credited on the commits? I'd probably do it as a follow-up PR on top of this one rather than grow this one, but I'm happy either way. |
|
Adding a Mesh line is smart. I dig it. My solution was to include the original data as built-in tables so they are always available. But adding the mesh line is a nice add on as well. Bonus is that it supports the off chance someone made a custom .neb file we don't know about. Yeah you can grab the table code and QtFRED UI for this feature if you want. If they are close enough and SCP merges yours I'll move mine on my fork in your direction the next time I sync SCP commits. |
Ported from MjnMixael's ofp/merged/restore-fs1-nebula (bb43618..47d8a1c, engine and FRED parts, squashed; the qtFRED part is redone against the SCP dialog model in a later commit). Patterns and colors come from generated_nebula.tbl and *-gneb.tbm; the mission's +Nebula: and +Color: names resolve through those registries, and unknown names round-trip.
A pattern entry can name an FS1 .neb mesh (+Mesh:) and, with a color's +Palette Suffix:, the FS1 palette file; when those files exist the mesh is drawn through the palette's 20-entry ramp, exactly as FS1 did, and otherwise the entry's generated clouds and RGB fade are used. The built-in Nebula01-03 and the nine colors carry both forms. One draw path, additive, so stars and a skybox show through where the nebula is dark. Table system and generated renderer by MjnMixael (ofp/merged/restore-fs1-nebula).
…: line The mod-level $FS1 nebula backgrounds: switch decides by default, so missions that merely carry stale +Nebula: lines stay unchanged and FSPort can turn the feature on in one line. A mission can override either way with $FS1 Nebula: YES or NO at the end of its background section, where builds that do not know the line skip it with the rest of the section. The engine and the Lab parse it; the saver writes it after $Environment Map: only when set. The draw decision moves from nebula_init to nebula_render, so an editor toggle takes effect without reloading the mesh.
The background editor's pattern and color combos list the table's entries (plus a name the table does not know, kept as read), and a Default/On/Off combo sets the mission's $FS1 Nebula: line. qtFRED keeps the SCP dialog model with the nebula reloaded on each change; FRED gets the same combo.
- Saver: drop a stale versioned $FS1 Nebula: line when the switch is Default, and rewrite the line in place only while the raw text still holds it. A compatibility save after Default prefixed the next section header; switching back on in the same session then truncated the file. - Mesh loader: check every read and reject points off the map, so a damaged .neb falls back to the generated pattern instead of drawing zeroed geometry. - Key the baked texture on the mesh or generated path too, and close the nebula when the registries are refilled. - Honor the Planets/Backgrounds detail option and the nebula detail flag again, as the mesh-only renderer did. - Lab: reset the nebula and the mission switch when a background loads. - qtFRED: keep an unknown pattern or color name when it is picked again after a known one; drop the 99-color assert FRED already dropped. - FRED: give the mission switch its own row inside the group, apply it on selection, and keep the controls enabled while an unknown pattern name is held. Table system and generated renderer by MjnMixael (ofp/merged/restore-fs1-nebula).
FS1's Direct3D and Glide renderers give each mesh corner the palette ramp entry its brightness rounds down to, (b * 19) / 255, and blend those colors across the triangle through a plain white texture. The mesh path here sampled the 20-entry ramp per pixel instead, which kept the fraction (up to one ramp step brighter) and walked through the in-between entries inside each triangle. The mesh path now sets the vertex colors from the ramp and draws untextured; the generated path is unchanged. FS1 blended in screen space; this blends with perspective. A screen-space variant drew hard edges where the large sky triangles cross behind the camera, which FS1 avoided by clipping in software first, so it is left out.
With post-processing on, the scene is linearized, scaled by the lighting profile's exposure, tonemapped and returned to sRGB, which made the FS1 mesh about 20% brighter than its palette colors (measured against FS1 itself on a reference mission). The mesh path now draws colors that post-processing turns back into the palette's: the default Uncharted curve inverts as a quadratic and Linear is only the exposure. Bloom, light shafts and post effects still apply on top. Other tonemappers, and a zero or invalid exposure, keep the palette colors as they are, logged once per curve. The shader linearizes after interpolating, so exact corners alone are not enough: on a bright palette that left up to 15 steps inside a triangle. Each mesh triangle is drawn as 8x8 flat pieces whose corners carry the blend of the triangle's corner colors at that point, so the picture without post-processing is unchanged and every piece's corners are compensated. The colors come from a 256-entry table and are redone only when post-processing, the curve or the exposure changes. A color the curve cannot reach is clipped rather than pushed above 1, since the bloom pass picks up anything above 1 and no other background goes there. At the default exposure (Uncharted, 4) the ceiling is 218 of 255 and the brightest ramp entry of the 27 FS1 nebula palettes is 183, so nothing clips; at exposure 1 the brightest parts of Blue clip. Measured per pixel against the same frame without post-processing, on Nebula01 Blue and Grey blue: worst 2 steps at the default exposure (OpenGL and Vulkan) and with Linear; it was 16 before the pieces. The FS1 meshes have 113 to 123 triangles, so this draws about 24,000 vertices; on the software renderer the tests use, the median frame on the retail mission went from 16.1 to 16.7 ms. A GPU was not measured.
The per-corner mesh build named its corner pointers "color", which shadows the global type of that name; Debug builds compile with -Werror=shadow and failed on it.
A generated pattern that names a palette prefix, with a color that names a suffix, now maps its baked brightness onto that FS1 palette's ramp, the same 20 entries the mesh path uses, instead of tinting by the RGB. This covers custom patterns written against the FS1 palettes and the built-in Nebula01 to 03 when their mesh file is missing or damaged. The bake blends between ramp entries rather than rounding down as the mesh path does, since rounding a smooth field to 20 levels would band. A missing palette file still falls back to the RGB tint with the same debug-log line, now read through one shared function. Through a palette, brightness above 1 (+Intensity over 1) stops at the last entry, as on the mesh path; the table documents this. A brightness that is not a number (an "nan" or overflowing table value) maps to the first entry.
The baked texture and its bitmap were only released by the next mission load; stars_level_close now releases them with the environment map. The unused Neb2_filenames table and NUM_NEBULAS are gone, and the noise comment names the ANL noise the engine already has and why this bake does not use it.



What?
Brings back the FS1-style background nebula as a table-driven feature. Each
generated_nebula.tblentry (or*-gneb.tbm) either names the FS1 data (+Mesh:,+Palette Prefix:,+Palette Suffix:) or describes a generated nebula with clouds and noise. The built-in table ships Nebula01 to 03 and the nine FS1 colors both ways. Of FS1's nebula palette files, retail FS2 ships only Red's and FSPort none, so the other colors fade to their RGB unless a mod ships them; that fade stays within 4 of 255 steps of FS1's ramps.It draws only when a mod sets
$FS1 nebula backgrounds: YESin game_settings.tbl or a mission ends its background section with$FS1 Nebula: YES; a mission'sNOoverrides the mod. qtFRED and FRED get combos from the table plus Default / On / Off for the mission line.The table system and generated renderer are MjnMixael's, ported from his branch with his consent and authorship. The mesh path, switches and editor work are mine.
Why?
Nothing has drawn
+Nebula:and+Color:since the palette manager went away in 2017, so FSPort's SM1-01 shows plain stars where FS1 had a red nebula. His version makes new nebulae a text edit; mine restores the original look. This keeps both.How?
A mesh is drawn the way FS1's Direct3D and Glide renderers did: each corner takes the palette entry its brightness rounds down to, and colors blend across the triangle. With post-processing on, the tonemapper would brighten them by about a fifth, so the mesh gets colors the tonemapper turns back into the palette's, with each triangle split into 8x8 pieces so this holds inside it. Colors are capped below the bloom threshold, like the rest of the background. A generated entry bakes his cloud field into a texture colored through the palette when one is named, else by the RGB. Missing files fall back with a debug-log line.
Screenshots?
In a comment below.
Testing?
Linux, headless harness, OpenGL and Vulkan. Against FS1 under Wine, the brightest nebula color on screen equals the palette's top entry and the shapes match; what remains is FS1's 16-bit output, framing, and its screen-space blending. Switches, fallbacks, a custom pattern, FSPort over its skybox,
mission-set-nebulaandset-post-effectwork, and an older build reads the new line safely. qtFRED saves it in both formats. FRED's dialog was not built here; the Lab and environment maps are untested.Anything Else?
This supersedes #7855. I used Claude Code for the port, review, FS1 comparison and test runs; I reviewed the code and this text.