ref:692aaa6c0b2195f8e40c1a67aeac431884f893fc

Fix multi-block UV mapping: correct column assignment per facing direction

Root cause: getSubRegion() assigned blockCol left-to-right in world coordinates, then the renderer flipped U per-block. This caused each block to independently mirror its sub-region, scrambling the order (showed columns as 2,3,1 instead of 1,2,3). Fix: getSubRegion() now assigns blockCol based on the VIEWER's perspective for each facing direction: - NORTH: highest X = viewer's left = blockCol 0 (u starts at 0) - SOUTH: lowest X = viewer's left = blockCol 0 - EAST: highest Z = viewer's left = blockCol 0 - WEST: lowest Z = viewer's left = blockCol 0 The renderer vertex mapping stays the same for both single and multi-block (x0 gets u1, x1 gets u0) because the directional logic is now entirely in getSubRegion. 46 GameTests + 52 Rust tests + visual test, all passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SHA: 692aaa6c0b2195f8e40c1a67aeac431884f893fc
Author: Cole Christensen <cole.christensen@macmillan.com>
Date: 2026-03-20 14:27
Parents: 2741a8f
2 files changed +29 -13
Type
common/src/main/java/io/fangorn/alacrittymc/block/ScreenGroup.java +25 −12
@@ -117,30 +117,43 @@
/**
* Get the sub-region UV coordinates for a given member block position.
* UVs are returned PRE-FLIPPED for the facing direction so the renderer
* can use them directly without any per-block horizontal flip.
*
* For NORTH/EAST facing: viewer's LEFT is +X/+Z, so highest X/Z gets u=0 (start of text).
* @return float[4]: {u0, v0, u1, v1} in range [0, 1]
* For SOUTH/WEST facing: viewer's LEFT is -X/-Z, so lowest X/Z gets u=0.
*
* @return float[4]: {u0, v0, u1, v1} in range [0, 1], ready for direct use
*/
public float[] getSubRegion(BlockPos memberPos) {
int blockCol, blockRow;
// Determine this block's position within the grid
int blockCol = 0, blockRow = 0;
Direction right = facing.getClockWise();
if (facing.getAxis() == Direction.Axis.Z) {
int minX = members.stream().mapToInt(BlockPos::getX).min().orElse(0);
int maxX = members.stream().mapToInt(BlockPos::getX).max().orElse(0);
int maxY = members.stream().mapToInt(BlockPos::getY).max().orElse(0);
blockCol = memberPos.getX() - minX;
blockRow = maxY - memberPos.getY();
if (facing == Direction.NORTH) {
// Viewer looks south: viewer's LEFT is +X
// Highest X → leftmost on screen → u starts at 0
blockCol = maxX - memberPos.getX();
} else {
// Flip for south facing
if (facing == Direction.SOUTH) {
// SOUTH: viewer looks north: viewer's LEFT is -X
// Lowest X → leftmost on screen → u starts at 0
blockCol = memberPos.getX() - minX;
blockCol = gridCols - 1 - blockCol;
}
} else {
int minZ = members.stream().mapToInt(BlockPos::getZ).min().orElse(0);
int maxZ = members.stream().mapToInt(BlockPos::getZ).max().orElse(0);
int maxY = members.stream().mapToInt(BlockPos::getY).max().orElse(0);
blockCol = memberPos.getZ() - minZ;
blockRow = maxY - memberPos.getY();
// Flip for west facing
if (facing == Direction.WEST) {
blockCol = gridCols - 1 - blockCol;
if (facing == Direction.EAST) {
// Viewer looks west: viewer's LEFT is +Z
blockCol = maxZ - memberPos.getZ();
} else {
// WEST: viewer looks east: viewer's LEFT is -Z
blockCol = memberPos.getZ() - minZ;
}
}