Improve the sprite countertransformer a lot

This commit is contained in:
quat1024 2023-09-03 19:33:23 -04:00
parent 1e4027ef23
commit b95e006770

View File

@ -78,6 +78,17 @@ public class TemplateAppearanceManager {
return m.get(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state)));
}
private static final int[] MAGIC_BAKEFLAGS_SBOX = new int[16];
static {
//left to right -> u0 v0 u1 v1
//the bit is set if the coordinate is "high"
MAGIC_BAKEFLAGS_SBOX[0b1110] = 0;
MAGIC_BAKEFLAGS_SBOX[0b1000] = MutableQuadView.BAKE_ROTATE_90;
MAGIC_BAKEFLAGS_SBOX[0b0001] = MutableQuadView.BAKE_ROTATE_180;
MAGIC_BAKEFLAGS_SBOX[0b0111] = MutableQuadView.BAKE_ROTATE_270;
//TODO: handle more cases. Also, it might be possible to drop v1 and still have the table be unambiguous
}
//I'm pretty sure ConcurrentHashMap semantics allow for this function to be called multiple times on the same key, on different threads.
//The computeIfAbsent map update will work without corrupting the map, but there will be some "wasted effort" computing the value twice.
//The results are going to be the same, apart from their serialNumbers differing (= their equals & hashCode differing).
@ -90,7 +101,7 @@ public class TemplateAppearanceManager {
//Only for parsing vanilla quads:
Renderer r = TemplatesClientApi.getInstance().getFabricRenderer();
QuadEmitter emitter = r.meshBuilder().getEmitter();
QuadEmitter emitterAsParser = r.meshBuilder().getEmitter();
RenderMaterial defaultMat = r.materialFinder().clear().find();
Sprite[] sprites = new Sprite[6];
@ -111,40 +122,28 @@ public class TemplateAppearanceManager {
if(sprite == null) continue;
sprites[dir.ordinal()] = sprite;
//GOAL: Reconstruct the `"rotation": 90` stuff that a json model might provide, so we can bake our texture on the same way
emitter.fromVanilla(arbitraryQuad, defaultMat, dir);
int lowHighSignature = 0;
for(int i = 0; i < 4; i++) {
//For some reason the uvs stored on the Sprite have less ?precision? than the ones retrieved from the QuadEmitter.
// [STDOUT]: emitter u 0.14065552, sprite min u 0.140625
//?precision? in question marks cause it could be float noise. It's way higher than the epsilon in MathHelper.approximatelyEquals.
//So im gonna guesstimate using "is it closer to the sprite's min or max u", rather than doing an approximately-equals check
float diffMinU = Math.abs(emitter.u(i) - sprite.getMinU());
float diffMaxU = Math.abs(emitter.u(i) - sprite.getMaxU());
boolean minU = diffMinU < diffMaxU;
float diffMinV = Math.abs(emitter.v(i) - sprite.getMinV());
float diffMaxV = Math.abs(emitter.v(i) - sprite.getMaxV());
boolean minV = diffMinV < diffMaxV;
lowHighSignature <<= 2;
lowHighSignature |= (minU ? 2 : 0) | (minV ? 1 : 0);
}
//Problem: Some models (eg. pistons, stone, glazed terracotta) have their UV coordinates permuted in
//non-standard ways. The actual png image appears sideways, but the original model's UVs rotate it right way up again.
//If I simply display the texture on my model, it will appear sideways, like it does in the png.
//If I can discover the pattern of rotations and flips on the original model, I can "un-rotate" the texture back
//into a standard orientation.
//
//Solution: Look at the first and second vertices of the orignial quad, and decide whether their UV coordinates
//are "low" or "high" compared to the middle of the sprite. The first two vertices uniquely determine the pattern
//of the other two (since UV coordinates are unique and shouldn't cross). There are 16 possibilities so this information
//is easily summarized in a bitfield, and the correct fabric rendering API "bake flags" to un-transform the sprite
//are looked up with a simple table.
emitterAsParser.fromVanilla(arbitraryQuad, defaultMat, dir);
if(lowHighSignature == 0b11100001) {
bakeFlags[dir.ordinal()] = 0;
} else if(lowHighSignature == 0b10000111) {
bakeFlags[dir.ordinal()] = MutableQuadView.BAKE_ROTATE_90;
} else if(lowHighSignature == 0b00011110) {
bakeFlags[dir.ordinal()] = MutableQuadView.BAKE_ROTATE_180;
} else if(lowHighSignature == 0b01111000) {
bakeFlags[dir.ordinal()] = MutableQuadView.BAKE_ROTATE_270;
} else {
//TODO handle more cases.
//Its not critical error or anything, the texture will show rotated or flipped
//System.out.println("unknown sig " + Integer.toString(lowHighSignature, 2) + ", state: " + state + ", sprite: " + sprite.getContents().getId() + ", side: " + dir);
}
float spriteUAvg = (sprite.getMinU() + sprite.getMaxU()) / 2;
float spriteVAvg = (sprite.getMinV() + sprite.getMaxV()) / 2;
bakeFlags[dir.ordinal()] = MAGIC_BAKEFLAGS_SBOX[
(emitterAsParser.u(0) < spriteUAvg ? 8 : 0) |
(emitterAsParser.v(0) < spriteVAvg ? 4 : 0) |
(emitterAsParser.u(1) < spriteUAvg ? 2 : 0) |
(emitterAsParser.v(1) < spriteVAvg ? 1 : 0)
];
}
//Fill out any missing values in the sprites array, since failure to pick textures shouldn't lead to NPEs later on