Glass Pane Vertical Bar — Cause, Fix and Rationale - song682/CatFrame-Compat GitHub Wiki
Status: fixed on the template side, built into CatFrameCompat 0.0.4
Scope: minecraft:glass_pane rendered through the CatFrame VMM pipeline with RPMCP CTM active
1. Symptom
A full-height, roughly 2 px wide vertical strip of the glass texture appears on a run of glass
panes; it disappears when the pane is isolated and it is not the pane's top/bottom plate
(glass_pane_top, i.e. the #edge texture) — it is made of the edge pixels of the #pane
(texture/blocks/glass.png) artwork. The same scene rendered by a mod that uses the classic
pane renderer (GlassPaneRenderer / PaneRenderHelper) does not show it.
2. Current state (what the fix is)
CatFrameCompat now ships a resource pack containing two templates that shadow CatFrame core's templates of the same name inside the mod jar:
src/main/resources/assets/minecraft/models/block/template_glass_pane_side.json
{ "from": [ 7, 0, 0 ], "to": [ 9, 16, 7 ],
"faces": {
"down": { "uv": [ 7, 9, 9, 16 ], "texture": "#edge", "cullface": "down" },
"up": { "uv": [ 7, 0, 9, 7 ], "texture": "#edge", "cullface": "up" },
"north": { "uv": [ 7, 0, 9, 16 ], "texture": "#edge", "cullface": "north" },
"west": { "uv": [ 0, 0, 7, 16 ], "texture": "#pane" },
"east": { "uv": [ 9, 0, 16, 16 ], "texture": "#pane" } } }
src/main/resources/assets/minecraft/models/block/template_glass_pane_side_alt.json
{ "from": [ 7, 0, 9 ], "to": [ 9, 16, 16 ],
"faces": {
"down": { "uv": [ 7, 9, 9, 16 ], "texture": "#edge", "cullface": "down" },
"up": { "uv": [ 7, 0, 9, 7 ], "texture": "#edge", "cullface": "up" },
"south": { "uv": [ 7, 0, 9, 16 ], "texture": "#edge", "cullface": "south" },
"west": { "uv": [ 9, 0, 16, 16 ], "texture": "#pane" },
"east": { "uv": [ 0, 0, 7, 16 ], "texture": "#pane" } } }
Diff against CatFrame core (tmps/CatFrame-0.8.3-deobf-sources/assets/minecraft/models/block/):
| file | face | CatFrame core | CatFrameCompat |
|---|---|---|---|
..._side.json |
west | [16, 0, 9, 16] |
[0, 0, 7, 16] |
..._side.json |
down | [7, 0, 9, 7] |
[7, 9, 9, 16] |
..._side.json |
up / down | no cullface | cullface up / cullface down |
..._side_alt.json |
west | [7, 0, 0, 16] |
[9, 0, 16, 16] |
..._side_alt.json |
down | [7, 0, 9, 7] |
[7, 9, 9, 16] |
..._side_alt.json |
up / down | no cullface | cullface up / cullface down |
Provenance: the west-UV and down-UV values come from the community "glass pane fix" pack
(credit: Made By WantedRobot); the up/down cullface entries were added locally afterwards and
are cosmetic only (they are not part of the mechanism described below).
No Java code is involved in the fix.
3. Root cause
The pane's flat faces are sampled with the wrong orientation relative to the texture the CTM engine hands us. Two conventions exist and they are mirror images of each other.
3.1 What CatFrame's built-in templates do
The templates (ambientocclusion: false, cuboid [7,0,0]–[9,16,7]) are the modern-vanilla
originals, which use local / mirrored arm UVs. With CatFrame's baking convention
(JsonModelBake.assignUVForFace: for WEST, s = iz, hence u0 = uv[0] sits at the cuboid's
min-z corner and u1 = uv[2] at max-z) this means, on the west-side flat of a north–south run:
sidenorth arm, z ∈ [0, 7/16] →uruns 16 → 9 (i.e. decreases along +z)- centre 2 px (noside, rotated y270) →
uruns 9 → 7 side_altsouth arm, z ∈ [9/16, 1] →uruns 7 → 0
So the tile's right edge lands on the north junction and its left edge on the next junction: the artwork is laid down mirrored.
3.2 What the CTM icon expects: RPMCP PaneRenderHelper
tmps/Right-Proper-MCPatcher-master/.../internal/modules/ctm/PaneRenderHelper.java is the
renderer the CTM icon set was authored for. Its constants and emitters state the convention
(PaneRenderHelper.png is that class' own Javadoc figure):
PANE_BELOW_UV = 7.0,PANE_ABOVE_UV = 9.0(L48-49) — the post/cross boundaries- block edges map to
u = 0 / 16(minX/minZ/maxX/maxZ) renderXNeg(x, zMin, zMax, uMin, uMax, ...)(L437-442) putsuMinatzMinanduMaxatzMax, i.e.uincreases along the arm- the arm segments are emitted as
uMin→uBelow(block edge → post) anduAbove→uMax(post → far block edge) (L409-435) - sides facing the other way use a mirrored icon range via
SideIconData.update(icon, negU)(L833-841)
3.3 Point-by-point comparison
| sample span | PaneRenderHelper |
CatFrame core | CatFrameCompat |
|---|---|---|---|
| Z-run, north arm west face | u 0 → 7 (renderXNegMinBelow) |
[16,0,9,16] → 16 → 9 |
[0,0,7,16] → 0 → 7 |
| Z-run, south arm west face | u 9 → 16 (renderXNegAboveMax) |
[7,0,0,16] → 7 → 0 |
[9,0,16,16] → 9 → 16 |
X-run, east arm (side y90, world north face) |
u 7 → 0 (renderZNegAboveMax) |
9 → 16 | 7 → 0 |
X-run, west arm (side_alt y90, world north face) |
u 16 → 9 (renderZNegMinBelow) |
0 → 7 | 16 → 9 |
| centre 2 px (noside) | 7 → 9 (renderXNegBelowAbove) |
9 → 7 | still 9 → 7 (residual) |
| down face (v axis) | 7 / 9 (TopIconData.vBelow/vAbove) |
[7,0,9,7] |
[7,9,9,16] |
Every value the fix introduces equals the PaneRenderHelper requirement; every value CatFrame
core shipped is its mirror. That is the entire fix.
3.4 Why a mirrored sampling is visible at all
The icon written into RenderContext#iconOverride is not a free-standing, freely tileable
texture. It is one of 47 composed tiles produced by CompactCTMDecoder: 5 base images, each
split into 4 quadrants, give 20 quarter-tiles a…t (the CompactCTM.png figure — ab / cd,
ef / gh, ij / kl, mn / op, qr / st), and a 47-entry LUT assembles one 16×16 tile per
connection state.
Only 5 of the 47 LUT entries are single-source (abcd, efgh, ijkl, mnop, qrst); the other
42 mix quadrants from different base images — entry 1 is ancp, i.e. left half from image 0 and
right half from image 3. Therefore the u direction along the arm is semantic: sampling it
backwards draws the other half's content, including the border/edge column that belongs at the
arm's outer end, across a span whose neighbours are connected. That is the bar.
4. The multipart / rotation factor
The pane is a multipart block and its arms are rotated by the blockstate, which is why one template-side fix is enough to cover all four orientations:
blockstates/glass_pane.jsonapplies arms asnorth→side,south→side_alt,east→side y90,west→side_alt y90; the nosides asy0 / y90 / y270.JsonModelBake.applyYRotation(L345-377) documents and implements "keep UVs unchanged": it rotates vertex positions,faceNormalandcullface, then re-winds the quad, so UV rides along rigidly with the geometry.- Consequence, re-derived to confirm it: on an east–west run the east arm (
sidey90) covers x ∈ [9/16, 1] and the fixed UV yieldsu7 → 0, equal torenderZNegAboveMax; the west arm (side_alty90) yieldsu16 → 9, equal torenderZNegMinBelow. Both orientations match, so the fix does not trade one axis for the other. - Residual: the pieces that actually get rotated are the nosides (the centre 2 px). They still
carry CatFrame's mirrored orientation, so the panel is not 100 % aligned to
PaneRenderHelper— currently invisible because the composed icon's centre columns carry no artwork, but it is the first suspect if a seam ever appears exactly in the middle of a pane.
5. Why this approach was chosen
A simple, basic, containable change was preferred over an engine-side one:
- Java untouched — no engine-side re-normalisation of pane side-face UVs, therefore none of the
copy-on-write hazard that comes with mutating
BakedQuads sourced from a shared bake cache. - The existing
iconOverrideplumbing (RpmcpRenderExtension) already delivers the right icon per face (NORTH→ZNeg,SOUTH→ZPos,WEST→XNeg,EAST→XPos); only the template's sampling convention had to be brought in line with the icon's composer. - The earlier CTM research document had already reached the same conclusion for route 1
("usable immediately, no development needed") and parked route 2 (engine-side linear
re-normalisation of
q.up/q.vp) as P3. - Rejected alternative: patch
apply/CtmRenderExtensionto rescale pane side-face UVs at render time — more capable, more risk, and unnecessary while the built-in template covers it.
6. Verification
Controlled-variable experiments, single variable = the arm west UV.
| group | setup | result |
|---|---|---|
| A | west face reverted to CatFrame core values ([16,0,9,16] / [7,0,0,16]), cullface and down unchanged |
the bar comes back ⇒ the west UV is the cause |
| B | all external resource packs disabled, only the built-in CatFrameCompat 0.0.4 jar | correct ⇒ the mod jar's builtin_packs/glass_pane_fix/assets/minecraft/models/block does override CatFrame core's same-named templates |
Build: gradlew build succeeds and the 0.0.4 jars contain both templates.
7. A correction to earlier reasoning (do not repeat)
An earlier derivation concluded that the two UV variants were "mathematically equivalent — the tile's two edge columns just swap left/right at the junction" and therefore that the west UV could not be the culprit. That was wrong; experiment A refutes it. The error was modelling the CTM icon as a symmetric, freely tileable texture, which §3.4 shows it is not.
8. Open items
.gitignore: the bareminecraftpattern is gone (# minecraft, then.minecraft,eclipse,run), sosrc/main/resources/assets/minecraft/**is trackable withoutgit add -f. If a root-level run directory namedminecraft/ever reappears, prefer the rooted pattern/minecraft/rather than restoring the unrooted one.- Optional pixel-level dump: compose the LUT entry for the observed connection state out of the 5 base images and print its columns, to close out "which column lands where" exactly.
- Corner/edge variants (ANCP vs MNOP) remain an acknowledged limitation (README): CatFrame's
generic neighbour detection does not use
canPaneConnectToBlock, so some corner states pick a different composite. Related to, but distinct from, the direction issue fixed here.
9. Anchors
- CatFrame core templates:
tmps/CatFrame-0.8.3-deobf-sources/assets/minecraft/models/block/template_glass_pane_{post,side,side_alt,noside,noside_alt}.jsonand.../blockstates/glass_pane.json - Baking:
.../decok/dfcdvadstf/catframe/model/core/baking/JsonModelBake.java—assignUVForFace(WEST:s = iz),applyYRotation(L345-377),recalculateWinding(L516-552) - RPMCP:
.../modules/ctm/PaneRenderHelper.java(L45-49 constants, L92-106 mask, L138-166 icon query, L409-551 quad emitters, L824-862 icon data) and.../modules/ctm/CompactCTMDecoder.java(L56-104 LUT + composition) - CatFrameCompat:
src/main/java/decok/dfcdvadstf/catframe/compact/mcpatcher/RpmcpRenderExtension.java