Skip to content

fix(bake): a point-UV material packs as the one texel it samples — the Romanic's deck reads tan - #65

Merged
sswelm merged 2 commits into
masterfrom
fix/point-uv-swatch
Sep 19, 2026
Merged

sswelm merged 2 commits into
masterfrom
fix/point-uv-swatch

Conversation

@sswelm

@sswelm sswelm commented Sep 19, 2026

Copy link
Copy Markdown
Owner

Summary

"Despite quite high brightness and saturation the deck looks dark, while in the web preview it should look like the tops of the lifeboats."

Cause. The Romanic's deck material is a 512×1024 plank texture whose every face carries the same single UV: the texture is used as a colour picker, one tan texel (0.77, 0.68, 0.52). The bake packs it as a texture, and the point folds onto the bottom-left edge of its atlas cell. There the bilinear tap blends the neighbouring cell in, and every coarser mip averages the whole plank image, so the deck read as the image's mean (0.61, 0.52, 0.36), a dark brown that no albedo dial could lift back. The web preview samples the wrapped texture at the point and shows the tan.

Fix. Both atlas paths (static and animated) now detect a material whose whole UV span fits inside one texel of its own texture (BakerRules.PointUv, measured over every vertex that uses it, before packing), replace its albedo with an 8 px swatch of that texel and mark it flat, so the remap pins its vertices to the cell centre exactly like a factor-only material. Tiled materials and real textures are untouched. On this model seven of the 47 materials are point-UV: the deck planking, five hull plate textures and the white cladding; the Generate log names each one:

[Factory] <name> point-UV material 'mat14_0133_Gray1': every UV at (0, 1) — one texel of a 512x1024 texture, packed as the flat colour (196, 172, 133)

Test plan

  • bash tools/check.sh passes (855 tests, eight new cases on PointUv: the deck, exactly one texel, two texels, a line of texels, the tiled plating, NaN, an unmeasured span, a texture with no pixels).
  • Bake the Romanic: the log lists the seven point-UV materials, the deck comes out tan like the lifeboat tops in the Lab preview and in game, and the hull plating (tiled, spans of 54 × 117 tiles) looks as before.
  • A model with no point-UV material (the Teutonic, the AH-1) bakes byte-identical: the rule only replaces an albedo when it fires.

🤖 Generated with Claude Code

sswelm and others added 2 commits September 19, 2026 09:57
…e Romanic's deck reads tan, not the plank image's mean

User: "despite quite high brightness and saturation the deck looks
dark, while in the web preview it should look like the tops of the
lifeboats." The deck's material is a 512x1024 plank texture whose every
face carries the same single UV (glTF (0, 0)): the texture used as a
colour picker, one tan texel (0.77, 0.68, 0.52). Packed as a texture,
that point folds onto the bottom-left edge of its atlas cell, where the
bilinear tap blends the neighbouring cell in and every coarser mip
averages the whole plank image — the deck read as the image's mean
(0.61, 0.52, 0.36), dark brown, and no albedo dial could bring it back.

Both atlas paths (static and animated) now detect a material whose whole
UV span fits inside one texel of its own texture (BakerRules.PointUv,
measured over every vertex that uses it), replace its albedo with an
8 px swatch of that texel (wrapped, as the source viewer sampled it) and
mark it flat, so the remap pins its vertices to the cell centre like any
factor-only material. Tiled materials and real textures are untouched.
Seven of the Romanic's 47 materials are point-UV (deck planking, five
hull plate textures, the white cladding); the Generate log names each.

Tests: eight cases on PointUv (the deck, exactly one texel, two texels,
a line of texels, the tiled plating, NaN, an unmeasured span, a texture
with no pixels). Changelog and the Capabilities doc describe the rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… — MissingReferenceException, bake failed

User's first bake on this branch: "MissingReferenceException: The
object of type 'Texture2D' has been destroyed but you are still trying
to access it" from PointSwatches; the bake failed and restored its
backup. The log line took the width and height from the source albedo
AFTER DestroyImmediate had freed it. Size and name are read into locals
before the destroy now, as the tiled-material pass next to it does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sswelm

sswelm commented Sep 19, 2026

Copy link
Copy Markdown
Owner Author

Follow-up commit 90e9eb2: the first bake on this branch threw MissingReferenceException from PointSwatches — the log line read the source albedo's width and height after DestroyImmediate had freed it (the bake restored its backup, nothing was lost). Size and name are read into locals before the destroy now, as the tiled pass next to it does. Gate passes.

🤖 Generated with Claude Code

@sswelm

sswelm commented Sep 19, 2026

Copy link
Copy Markdown
Owner Author

In-editor Bake Tests run by the author on this branch (90e9eb2): PASS — 61 passed, 0 failed, 2 skipped, 6.9 min — whole catalog (12 models, static and animated paths), synthetic option cubes (20), Blender + animation options (4), control rig, every converted rig (20), golden snapshot (4). The Romanic itself bakes with the deck tan in game.

Review notes (self): nearest-texel sampling verified against a wrapped bilinear tap, which would blend the plank image's four corners to (0.41, 0.36, 0.28) — darker than before; the web preview does not blend either. Known gaps left as they are: the single-material path (atlas = the texture itself) still mip-averages a point-UV texture, and a point-UV material whose parts sit in different UV tiles stays on the old fold. Neither occurs on any registry model.

🤖 Generated with Claude Code

@sswelm
sswelm merged commit 8c18ac3 into master Sep 19, 2026
2 of 3 checks passed
@sswelm
sswelm deleted the fix/point-uv-swatch branch September 19, 2026 09:53
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.

1 participant