Skip to content

Commit f83a331

Browse files
committed
Improve reliability of texture orientation computation
- Remove assumption that vertex order is aligned to world in a certain way - Merge UV rotation computation and UV winding order computation as both now use the same value (texture matrix determinant)
1 parent d1608f9 commit f83a331

File tree

4 files changed

+110
-70
lines changed

4 files changed

+110
-70
lines changed

src/main/java/me/pepperbell/continuity/client/processor/CompactCtmQuadProcessor.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,21 @@ public class CompactCtmQuadProcessor extends AbstractQuadProcessor {
3535
static {
3636
int[][] map = QUADRANT_INDEX_MAPS;
3737

38-
map[0] = new int[] { 0, 1, 2, 3 }; // 0 - 0 1 2 3
39-
map[1] = map[0].clone(); // 1 - 3 0 1 2
38+
map[0] = new int[] { 0, 1, 2, 3 }; // 0 1 2 3
39+
map[1] = map[0].clone(); // 3 0 1 2
4040
ArrayUtils.shift(map[1], 1);
41-
map[2] = map[1].clone(); // 2 - 2 3 0 1
41+
map[2] = map[1].clone(); // 2 3 0 1
4242
ArrayUtils.shift(map[2], 1);
43-
map[3] = map[2].clone(); // 3 - 1 2 3 0
43+
map[3] = map[2].clone(); // 1 2 3 0
4444
ArrayUtils.shift(map[3], 1);
4545

46-
map[4] = map[3].clone(); // 4 - 0 3 2 1
46+
map[4] = map[0].clone(); // 3 2 1 0
4747
ArrayUtils.reverse(map[4]);
48-
map[5] = map[4].clone(); // 5 - 1 0 3 2
48+
map[5] = map[4].clone(); // 0 3 2 1
4949
ArrayUtils.shift(map[5], 1);
50-
map[6] = map[5].clone(); // 6 - 2 1 0 3
50+
map[6] = map[5].clone(); // 1 0 3 2
5151
ArrayUtils.shift(map[6], 1);
52-
map[7] = map[6].clone(); // 7 - 3 2 1 0
52+
map[7] = map[6].clone(); // 2 1 0 3
5353
ArrayUtils.shift(map[7], 1);
5454
}
5555

@@ -351,7 +351,7 @@ public ProcessingResult processQuadInner(MutableQuadView quad, Sprite sprite, Bl
351351
int spriteIndexB;
352352
if (uSplit) {
353353
firstSplit = uSplit01;
354-
swapAB = orientation == 2 || orientation == 3 || orientation == 4 || orientation == 7;
354+
swapAB = orientation == 2 || orientation == 3 || orientation == 4 || orientation == 5;
355355
if ((vSignum0 + vSignum1 + vSignum2 + vSignum3) <= 0) {
356356
spriteIndexA = getSpriteIndex(0, connections);
357357
spriteIndexB = getSpriteIndex(3, connections);
@@ -361,7 +361,7 @@ public ProcessingResult processQuadInner(MutableQuadView quad, Sprite sprite, Bl
361361
}
362362
} else {
363363
firstSplit = vSplit01;
364-
swapAB = orientation == 1 || orientation == 2 || orientation == 4 || orientation == 5;
364+
swapAB = orientation == 1 || orientation == 2 || orientation == 5 || orientation == 6;
365365
if ((uSignum0 + uSignum1 + uSignum2 + uSignum3) <= 0) {
366366
spriteIndexA = getSpriteIndex(1, connections);
367367
spriteIndexB = getSpriteIndex(0, connections);
@@ -462,7 +462,7 @@ protected int getSpriteIndex(int quadrantIndex, int connections) {
462462
int index2 = (quadrantIndex + 3) % 4;
463463
boolean connected1 = ((connections >>> index1 * 2) & 1) == 1;
464464
boolean connected2 = ((connections >>> index2 * 2) & 1) == 1;
465-
if (connected1 && connected2) {
465+
if (connected1 & connected2) {
466466
if (((connections >>> (index2 * 2 + 1)) & 1) == 1) {
467467
return 1;
468468
}

src/main/java/me/pepperbell/continuity/client/processor/DirectionMaps.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ public final class DirectionMaps {
3636
map[3] = map[2].clone(); // u l d r
3737
ArrayUtils.shift(map[3], -1);
3838

39-
map[4] = map[0].clone(); // u r d l ; v - 1 ; h - 3
40-
ArrayUtils.reverse(map[4]);
41-
map[5] = map[4].clone(); // l u r d ; v - 0 ; h - 2
42-
ArrayUtils.shift(map[5], 1);
43-
map[6] = map[5].clone(); // d l u r ; v - 3 ; h - 1
44-
ArrayUtils.shift(map[6], 1);
45-
map[7] = map[6].clone(); // r d l u ; v - 2 ; h - 0
46-
ArrayUtils.shift(map[7], 1);
39+
map[4] = map[0].clone(); // r d l u
40+
ArrayUtils.swap(map[4], 0, 2);
41+
map[5] = map[1].clone(); // u r d l
42+
ArrayUtils.swap(map[5], 0, 2);
43+
map[6] = map[2].clone(); // l u r d
44+
ArrayUtils.swap(map[6], 0, 2);
45+
map[7] = map[3].clone(); // d l u r
46+
ArrayUtils.swap(map[7], 0, 2);
4747
}
4848
}
4949

src/main/java/me/pepperbell/continuity/client/processor/simple/RepeatSpriteProvider.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,21 @@ public Sprite getSprite(QuadView quad, Sprite sprite, BlockRenderView blockView,
9393
spriteY = -temp - 1;
9494
}
9595
case 4 -> {
96+
spriteX = -spriteX - 1;
97+
}
98+
case 5 -> {
9699
int temp = spriteX;
97100
spriteX = spriteY;
98101
spriteY = temp;
99102
}
100-
case 5 -> {
103+
case 6 -> {
101104
spriteY = -spriteY - 1;
102105
}
103-
case 6 -> {
106+
case 7 -> {
104107
int temp = spriteX;
105108
spriteX = -spriteY - 1;
106109
spriteY = -temp - 1;
107110
}
108-
case 7 -> {
109-
spriteX = -spriteX - 1;
110-
}
111111
}
112112

113113
spriteX %= width;

src/main/java/me/pepperbell/continuity/client/util/QuadUtil.java

Lines changed: 86 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public static void interpolate(MutableQuadView quad, Sprite oldSprite, Sprite ne
2424
}
2525
}
2626

27-
public static void assignLerpedUVs(MutableQuadView quad, Sprite sprite) {
27+
public static void assignLerpedUvs(MutableQuadView quad, Sprite sprite) {
2828
float delta = sprite.getAnimationFrameDelta();
2929
float centerU = (sprite.getMinU() + sprite.getMaxU()) * 0.5f;
3030
float centerV = (sprite.getMinV() + sprite.getMaxV()) * 0.5f;
@@ -41,7 +41,7 @@ public static void assignLerpedUVs(MutableQuadView quad, Sprite sprite) {
4141
public static void emitOverlayQuad(QuadEmitter emitter, Direction face, Sprite sprite, int color, RenderMaterial material) {
4242
emitter.square(face, 0, 0, 1, 1, 0);
4343
emitter.color(color, color, color, color);
44-
assignLerpedUVs(emitter, sprite);
44+
assignLerpedUvs(emitter, sprite);
4545
emitter.material(material);
4646
emitter.emit();
4747
}
@@ -79,56 +79,96 @@ public static boolean isQuadUnitSquare(QuadView quad) {
7979
return true;
8080
}
8181

82+
/**
83+
* Returns an int in range [0, 7] representing the texture orientation of the given quad relative to the world.
84+
*
85+
* <ul>
86+
* <li>0 - 0 degree counterclockwise rotation, counterclockwise UV winding order</li>
87+
* <li>1 - 90 degree counterclockwise rotation, counterclockwise UV winding order</li>
88+
* <li>2 - 180 degree counterclockwise rotation, counterclockwise UV winding order</li>
89+
* <li>3 - 270 degree counterclockwise rotation, counterclockwise UV winding order</li>
90+
* <li>4 - 0 degree counterclockwise rotation, clockwise UV winding order</li>
91+
* <li>5 - 90 degree counterclockwise rotation, clockwise UV winding order</li>
92+
* <li>6 - 180 degree counterclockwise rotation, clockwise UV winding order</li>
93+
* <li>7 - 270 degree counterclockwise rotation, clockwise UV winding order</li>
94+
* </ul>
95+
*/
8296
public static int getTextureOrientation(QuadView quad) {
83-
int rotation = getUVRotation(quad);
84-
if (getUVWinding(quad) == Winding.CLOCKWISE) {
85-
return rotation + 4;
97+
// Texture matrix
98+
float tm00 = quad.u(3) - quad.u(1);
99+
float tm01 = quad.v(3) - quad.v(1);
100+
float tm10 = quad.u(2) - quad.u(0);
101+
float tm11 = quad.v(2) - quad.v(0);
102+
// Determinant of texture matrix; also cross product of its column vectors
103+
float determinant = tm00 * tm11 - tm10 * tm01;
104+
if (determinant == 0) {
105+
return 0;
86106
}
87-
return rotation;
88-
}
107+
float s = 1 / determinant;
108+
// Second column of inverse texture matrix
109+
float itm10 = -tm10 * s;
110+
float itm11 = tm00 * s;
89111

90-
public static int getUVRotation(QuadView quad) {
91-
int minVertex = 0;
92-
float minDistance = 3.0f;
93-
for (int vertexId = 0; vertexId < 4; vertexId++) {
94-
float u = quad.u(vertexId);
95-
float v = quad.v(vertexId);
96-
float distance = u * u + v * v;
97-
if (distance < minDistance) {
98-
minDistance = distance;
99-
minVertex = vertexId;
112+
int xAxis;
113+
int xAxisSign;
114+
int yAxis;
115+
int yAxisSign;
116+
switch (quad.lightFace()) {
117+
case DOWN -> {
118+
xAxis = 0; // +X
119+
xAxisSign = 1;
120+
yAxis = 2; // +Z
121+
yAxisSign = 1;
122+
}
123+
case UP -> {
124+
xAxis = 0; // +X
125+
xAxisSign = 1;
126+
yAxis = 2; // -Z
127+
yAxisSign = -1;
128+
}
129+
case NORTH -> {
130+
xAxis = 0; // -X
131+
xAxisSign = -1;
132+
yAxis = 1; // +Y
133+
yAxisSign = 1;
134+
}
135+
case SOUTH -> {
136+
xAxis = 0; // +X
137+
xAxisSign = 1;
138+
yAxis = 1; // +Y
139+
yAxisSign = 1;
140+
}
141+
case WEST -> {
142+
xAxis = 2; // +Z
143+
xAxisSign = 1;
144+
yAxis = 1; // +Y
145+
yAxisSign = 1;
146+
}
147+
case EAST -> {
148+
xAxis = 2; // -Z
149+
xAxisSign = -1;
150+
yAxis = 1; // +Y
151+
yAxisSign = 1;
152+
}
153+
default -> {
154+
return 0;
100155
}
101156
}
102-
return minVertex;
103-
}
104-
105-
public static Winding getUVWinding(QuadView quad) {
106-
float u3 = quad.u(3);
107-
float v3 = quad.v(3);
108-
float u0 = quad.u(0);
109-
float v0 = quad.v(0);
110-
float u1 = quad.u(1);
111-
float v1 = quad.v(1);
112-
113-
float value = (u3 - u0) * (v1 - v0) - (v3 - v0) * (u1 - u0);
114-
if (value > 0) {
115-
return Winding.COUNTERCLOCKWISE;
116-
} else if (value < 0) {
117-
return Winding.CLOCKWISE;
118-
}
119-
return Winding.UNDEFINED;
120-
}
157+
// Position matrix
158+
float pm00 = quad.posByIndex(3, xAxis) - quad.posByIndex(1, xAxis);
159+
float pm01 = quad.posByIndex(3, yAxis) - quad.posByIndex(1, yAxis);
160+
float pm10 = quad.posByIndex(2, xAxis) - quad.posByIndex(0, xAxis);
161+
float pm11 = quad.posByIndex(2, yAxis) - quad.posByIndex(0, yAxis);
121162

122-
public enum Winding {
123-
COUNTERCLOCKWISE,
124-
CLOCKWISE,
125-
UNDEFINED;
163+
// Texture up vector in projected world space
164+
// Computed as (position matrix * inverse texture matrix * [0; -1]); [0; -1] is the texture up vector in texture space
165+
// Axis signs should be multiplied into position matrix values, but multiplying here instead saves 2 multiplications
166+
float x = -(pm00 * itm10 + pm10 * itm11) * xAxisSign;
167+
float y = -(pm01 * itm10 + pm11 * itm11) * yAxisSign;
126168

127-
public Winding reverse() {
128-
if (this == UNDEFINED) {
129-
return this;
130-
}
131-
return this == CLOCKWISE ? COUNTERCLOCKWISE : CLOCKWISE;
132-
}
169+
// Clamp vector to nearest axis-aligned direction
170+
// up/+y -> 0, left/-x -> 1, down/-y -> 2, right/+x -> 3
171+
// Add 4 if the UV winding order is clockwise
172+
return (Math.abs(y) >= Math.abs(x) ? (y > 0 ? 0 : 2) : (x > 0 ? 3 : 1)) + (determinant < 0 ? 4 : 0);
133173
}
134174
}

0 commit comments

Comments
 (0)