Skip to content

Configurable glow map intensity per lighting profile and per texture - #7865

Open
dustinspace217 wants to merge 5 commits into
scp-fs2open:masterfrom
dustinspace217:graphics/configurable-glow
Open

dustinspace217 wants to merge 5 commits into
scp-fs2open:masterfrom
dustinspace217:graphics/configurable-glow

Conversation

@dustinspace217

@dustinspace217 dustinspace217 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What?

Glow maps get two settings instead of the fixed values in the model shader (intensity 1.5, HDR multiplier 3.0):

  • A lighting profile can set $Glow Map Intensity: (default 1.5) and $Glow Map HDR Multiplier: (default twice the intensity, so 3.0).
  • A texture can override both in a plain-text file named after it and placed next to it, such as shockwave3d.mat beside MediaVPs' shockwave3d texture for the 3D shockwave.
  • Decals use their diffuse texture's .mat, or their glow texture's if they have no diffuse, and otherwise the profile's values.

With none of these set, rendering is unchanged.

Why?

Goober asked for this for the Scroll of Atankharzim. Some glow maps, like a shockwave's, need to be brighter or dimmer than everything else, and right now the only way to change glow strength is to edit the shaders.

How?

  • The two constants become per-draw shader values (glowIntensity and glowHdrMultiplier in the model block, and two fields that were padding in the decal block). static_asserts pin the C++ layouts.
  • A .mat is read once, when its texture loads, and kept with the bitmap. So it follows texture replacement, covers every frame of an EFF, ANI or APNG, and applies wherever the texture is drawn. Edits need a restart.
  • .mat files are UTF-8 (a byte order mark is fine); ; starts a comment. Unknown entries, bad numbers and files in other encodings get a warning and are ignored. A Latin-1 .mat is ignored rather than converted as Unicode-mode tables are. Values are capped at 100 so glow stays within the 16-bit emissive buffer, and lighting profiles get the same checks.
  • .mat joins the file types indexed in data/maps and data/effects, so older builds never see the files. A .mat follows the usual mod priority: a mod can add one for a MediaVPs texture, and a mod that replaces a texture but wants the default glow can ship an empty .mat.
  • A .mat belongs to the base texture: replacing only a glow map keeps the base texture's .mat.
  • For the HDR multiplier, the texture's own value wins. Next comes a value the profile sets explicitly. Otherwise it's twice the intensity in use, so under HDR the glow grows with the square of the intensity; setting the HDR multiplier keeps it linear.
  • The old constants stay in lighting.sdr for mods that ship their own copies of the model shaders. A mod that overrides main-v/g/f.sdr has to update all three, as with any change to that block.
  • In modular tables, set intensity and HDR multiplier together: once a table sets the multiplier, a later table can't send it back to "twice the intensity".

Testing?

  • Unit tests for the profile defaults, precedence and value checks, and for .mat files: both keys, comments, UTF-8 comments, tabs, CRLF, a UTF-8 BOM, case and extension in the name, data/maps and data/effects, missing files, other encodings, control bytes and truncated UTF-8, bad, negative, nan and too-large values, unknown entries, and that a texture, an EFF and an APNG all carry their .mat. All pass in Release and Debug, shuffled and repeated. I also broke each rule on purpose and checked that a test failed.
  • In game on nightly 26.1.0-20261013 with MediaVPs 5.0.2, OpenGL and Vulkan: with no .mat, screenshots match the nightly apart from 5 pixels on one hull that come from the build settings, not this change. A .mat with intensity 0 on the Hatshepsut's textures removes only that ship's glow, and the nightly ignores the files. A replacement texture uses its own .mat. The lab's hidden-diffuse view still applies the .mat, and one texture's .mat doesn't change the next texture drawn.
  • Decals on OpenGL: unchanged at defaults, and a .mat on a decal's texture brightens only that decal.

Anything Else?

Following the discussion on Discord, the per-texture values now live in a .mat file next to the texture instead of in the POF, so this no longer depends on #7864. The branch still carries #7863's commit until that merges; nothing here depends on it now.

While testing I found that decals don't draw at all under Vulkan, including on current master (#7866). The review also turned up two existing overflows in the engine's text readers, filed as #7869 and #7870. Both are separate from this PR.

I used Claude Code to write the change and tests and to run the checks; I reviewed the code and this text.

@The-E

The-E commented Oct 14, 2026

Copy link
Copy Markdown
Member

This is fundamentally wrong and mixes concerns in a bad manner.

Lighting profiles are supposed to hold global settings that apply to an entire mission. They should not hold data that, as per the intent expressed in this and the other PRs linked to this, is set on a per-model basis. The proper way to communicate this data is through the material system, not lighting profiles.

@BMagnu

BMagnu commented Oct 14, 2026

Copy link
Copy Markdown
Member

The ltp should hold a global default that is then overridden by the per-model/-texture set data

@dustinspace217

Copy link
Copy Markdown
Contributor Author

To clarify the flow: the per-texture values do go through the material system. model_render_buffers sets them on the model_material for each texture slot, and convert_model_material copies them into the uniforms. The lighting profile only holds the global default, calibrated next to the tonemapper and exposure, which applies when a texture doesn't override it. If the concern is that model_material reads the profile for that fallback, I can resolve the default before it reaches the material instead. (The diff also includes #7863 and #7864's commits until those merge, which makes it look bigger than it is.)

@The-E

The-E commented Oct 14, 2026

Copy link
Copy Markdown
Member

My apologies, I did not read the flow correctly. I retract my objection.

dustinspace217 and others added 2 commits October 14, 2026 18:13
The char* version only checked the stored length with an Assertion, so in
release builds a string longer than the buffer was read past its end, and a
negative length wrote before it. It now reads at most buffer_size - 1
characters, skips the rest so the next read stays aligned, always terminates
the buffer, and logs when characters are dropped.

The SCP_string version clamps a negative or past-the-end length to what the
file still holds, so a corrupt length can't cause a huge allocation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ture

The shader multiplied every glow map by two fixed constants: 1.5 always, and
3.0 more with post-processing on. They are now per-draw shader values with the
same defaults, so nothing changes unless a mod sets them.

- Lighting profiles take $Glow Map Intensity: (default 1.5) and
  $Glow Map HDR Multiplier:, which defaults to twice the intensity, so the old
  1.5 x 3.0 is unchanged and raising the intensity raises both.
- A texture can override them in a plain-text file named after it and placed
  next to it, for example shockwave3d.mat beside the shockwave3d texture:
      $Glow Map Intensity: 2.5
      $Glow Map HDR Multiplier: 6
  The .mat is read once when the texture loads and kept with the bitmap, so it
  follows texture replacement, covers every frame of an EFF, ANI or APNG, and
  applies wherever the texture is drawn. Decals use their diffuse texture's
  .mat, or their glow texture's when they have no diffuse.
- Files must be plain ASCII; ';' starts a comment. Unknown entries, bad
  numbers and non-text files are ignored with a warning. Values are capped at
  100 so glow stays within the 16-bit float emissive buffer; lighting profile
  values get the same checks.
- .mat is added to the file types indexed in data/maps and data/effects, so
  builds without this change never see the files. A .mat follows the usual mod
  priority, like any other file.

The old shader constants stay in lighting.sdr for mods that ship their own
shaders referencing them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dustinspace217
dustinspace217 force-pushed the graphics/configurable-glow branch from 94d01af to fd5b5e4 Compare October 15, 2026 01:13
@dustinspace217
dustinspace217 marked this pull request as ready for review October 15, 2026 01:14
@dustinspace217

Copy link
Copy Markdown
Contributor Author

The revision is done. Per-texture values now come from a .mat file next to the texture instead of the POF, following the Discord discussion, so this no longer depends on #7864. The description is updated, and the PR is ready for review.

dustinspace217 and others added 2 commits October 15, 2026 03:10
FSO treats text files as UTF-8 in Unicode mode, so a .mat file with a non-ASCII
comment was being ignored. Valid UTF-8 is now read; other encodings, and files
with control bytes such as UTF-16 without a byte order mark, still get a
warning and are ignored. Release builds now also check that each rejected file
returns no material.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Mac clang-tidy step reads the PR diff as UTF-8 and fails on their bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dustinspace217

Copy link
Copy Markdown
Contributor Author

Small follow-up: .mat files are now read as UTF-8 rather than plain ASCII. FSO expects UTF-8 text in Unicode mode, and a single non-ASCII character in a comment was making the whole file get ignored. Other encodings still get a warning and are ignored. The description is updated to match.

The second commit marks the two deliberately non-UTF-8 test files as binary in .gitattributes. The Mac clang-tidy step reads the PR diff as UTF-8 and was failing on their bytes.

mprintf is compiled out of release builds, so the block never ran.

Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants