Skip to content

Read per-texture properties from a new TXPR chunk in POF files - #7864

Closed
dustinspace217 wants to merge 2 commits into
scp-fs2open:masterfrom
dustinspace217:model/per-texture-properties
Closed

dustinspace217 wants to merge 2 commits into
scp-fs2open:masterfrom
dustinspace217:model/per-texture-properties

Conversation

@dustinspace217

Copy link
Copy Markdown
Contributor

What?

POF models can now carry a properties string for each texture, read from a new TXPR chunk. Nothing uses the strings yet; they're the groundwork for per-texture settings like glow brightness.

Depends on #7863 (its commit is the first one here), so this stays a draft until that merges.

Why?

Goober5000 asked for settings that belong to a texture wherever its model is used: ships, weapons, skyboxes, shockwaves. The first use is glow brightness for a 3D shockwave in The Scroll of Atankharzim. Submodel $props don't fit, since they belong to one submodel and stop at 256 characters, and later settings like curves will need far more.

How?

  • TXPR holds a count, then one length-prefixed string per TXTR entry, in the same order; an empty string means none. Older builds skip it like any unknown chunk, so there's no POF version bump.
  • The strings go on polymodel::texture_properties, one per texture. They're read with the SCP_string cfread_string_len from Make cfread_string_len read no more than fits in the buffer #7863, so there's no 256-character limit, and reading stops at the end of the chunk.
  • A string that runs past the end of the chunk is dropped with a warning, and so is a count that doesn't match the texture count.
  • Virtual POFs that add a submodel bring along the properties of the textures they add.
  • POF-Tools writes and edits the chunk: Add per-texture properties (TXPR chunk) Baezon/pof-tools#192. Older POF-Tools versions and PCS2 drop it when they re-save a model.

Testing?

test/src/model/test_texture_properties.cpp loads small POFs made with the POF-Tools branch. It checks:

  • three textures, including an empty entry and a 6,000-character string;
  • no chunk at all;
  • a count too low, a count too high, and a string running past the chunk (byte-patched copies, Release only, because in Debug a warning thrown mid-load leaves parsing paused for later tests);
  • a virtual POF that grafts a submodel.

I also broke each new check on purpose to confirm a test fails. Release and Debug unit tests pass with no warnings, and clang-tidy (CI's check) is clean.

In game, a Scroll model re-saved with properties loads with the same geometry as the original, on this branch and on master.

Anything Else?

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

dustinspace217 and others added 2 commits October 14, 2026 01:08
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>
Models can now carry a properties string for each texture, for settings that
belong to a texture wherever the model is used (glow brightness is the first
planned use). The TXPR chunk holds a count followed by one length-prefixed
string per TXTR entry, in the same order; an empty string means no properties.
The strings are stored on polymodel::texture_properties, one per texture, and
nothing reads them yet.

The chunk is new, so older builds skip it like any unknown chunk and no POF
version bump is needed. The strings are read with the SCP_string
cfread_string_len, so there is no 256-character limit as there is for submodel
properties. Reading stops at the end of the chunk, a string that runs past it
is dropped with a warning, and a count that doesn't match the texture count
logs a warning. Virtual POFs that add a submodel carry its textures'
properties along with the textures.

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

The-E commented Oct 14, 2026

Copy link
Copy Markdown
Member

Why is this not a tbl setting?

What I am seeing here is a feature that relies on a change in the model file and a change in FSO, all stringly typed and with no actual feature tied to it - the FSO PR reads this new chunk, I see the test looking for some "$curve" property, but there's nothing done with it.
IMHO, the proper place for settings like this is the relevant table, not the model: TBL files have the immense advantage of not requiring specialized tools to edit, and are thus easier to access, understand and ultimately use than model file properties.

@BMagnu

BMagnu commented Oct 14, 2026

Copy link
Copy Markdown
Member

The point is that this is meant as the place to store the data for per-texture data (the glow map boost level), intrinsic to the model.
There is no good table for that data.
It shouldn't go into the ships table or something, because it needs to work for all uses of POFs. Ships, weapons, skyboxes, cockpit models, etc. And it should work the same for everything, without a modder needing to keep all these tables constant.
The only other sensible table would be a new, per-texture sidecar table, which is IMO strictly inferior to adding more POF properties, keeping the data sensibly in the mode.

As for nothing being used it for: This is a very different place of the engine, and keeping the POF change isolated is a lot cleaner.

@dustinspace217

Copy link
Copy Markdown
Contributor Author

Closing this: following the Discord discussion, per-texture properties will live in a .mat text file next to the texture instead of in the POF. #7865 now reads them from there.

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