/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.annotations.VisibleForTesting * org.joml.Matrix4fc * org.joml.Vector3f * org.joml.Vector3fc * org.jspecify.annotations.Nullable */ package net.minecraft.client.renderer.block.model; import com.google.common.annotations.VisibleForTesting; import com.mojang.math.MatrixUtil; import com.mojang.math.Quadrant; import com.mojang.math.Transformation; import java.util.function.Consumer; import net.minecraft.client.renderer.FaceInfo; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BlockElementFace; import net.minecraft.client.renderer.block.model.BlockElementRotation; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.ModelState; import net.minecraft.core.Direction; import net.minecraft.util.Mth; import org.joml.Matrix4fc; import org.joml.Vector3f; import org.joml.Vector3fc; import org.jspecify.annotations.Nullable; public class FaceBakery { public static final int VERTEX_INT_SIZE = 8; public static final int VERTEX_COUNT = 4; private static final int COLOR_INDEX = 3; public static final int UV_INDEX = 4; private static final Vector3fc BLOCK_MIDDLE = new Vector3f(0.5f, 0.5f, 0.5f); @VisibleForTesting static BlockElementFace.UVs defaultFaceUV(Vector3fc from, Vector3fc to, Direction facing) { return switch (facing) { default -> throw new MatchException(null, null); case Direction.DOWN -> new BlockElementFace.UVs(from.x(), 16.0f - to.z(), to.x(), 16.0f - from.z()); case Direction.UP -> new BlockElementFace.UVs(from.x(), from.z(), to.x(), to.z()); case Direction.NORTH -> new BlockElementFace.UVs(16.0f - to.x(), 16.0f - to.y(), 16.0f - from.x(), 16.0f - from.y()); case Direction.SOUTH -> new BlockElementFace.UVs(from.x(), 16.0f - to.y(), to.x(), 16.0f - from.y()); case Direction.WEST -> new BlockElementFace.UVs(from.z(), 16.0f - to.y(), to.z(), 16.0f - from.y()); case Direction.EAST -> new BlockElementFace.UVs(16.0f - to.z(), 16.0f - to.y(), 16.0f - from.z(), 16.0f - from.y()); }; } public static BakedQuad bakeQuad(Vector3fc from, Vector3fc to, BlockElementFace face, TextureAtlasSprite icon, Direction facing, ModelState modelState, @Nullable BlockElementRotation elementRotation, boolean shade, int lightEmission) { BlockElementFace.UVs uvs = face.uvs(); if (uvs == null) { uvs = FaceBakery.defaultFaceUV(from, to, facing); } uvs = FaceBakery.shrinkUVs(icon, uvs); Matrix4fc uvTransform = modelState.inverseFaceTransformation(facing); int[] vertices = FaceBakery.makeVertices(uvs, face.rotation(), uvTransform, icon, facing, FaceBakery.setupShape(from, to), modelState.transformation(), elementRotation); Direction finalDirection = FaceBakery.calculateFacing(vertices); if (elementRotation == null) { FaceBakery.recalculateWinding(vertices, finalDirection); } return new BakedQuad(vertices, face.tintIndex(), finalDirection, icon, shade, lightEmission); } private static BlockElementFace.UVs shrinkUVs(TextureAtlasSprite icon, BlockElementFace.UVs current) { float minU = current.minU(); float minV = current.minV(); float maxU = current.maxU(); float maxV = current.maxV(); return new BlockElementFace.UVs(minU, minV, maxU, maxV); } private static int[] makeVertices(BlockElementFace.UVs uvs, Quadrant uvRotation, Matrix4fc uvTransform, TextureAtlasSprite icon, Direction facing, float[] shape, Transformation rotation, @Nullable BlockElementRotation elementRotation) { FaceInfo faceInfo = FaceInfo.fromFacing(facing); int[] vertexData = new int[32]; for (int i = 0; i < 4; ++i) { FaceBakery.bakeVertex(vertexData, i, faceInfo, uvs, uvRotation, uvTransform, shape, icon, rotation, elementRotation); } return vertexData; } private static float[] setupShape(Vector3fc from, Vector3fc to) { float[] shape = new float[Direction.values().length]; shape[FaceInfo.Constants.MIN_X] = from.x() / 16.0f; shape[FaceInfo.Constants.MIN_Y] = from.y() / 16.0f; shape[FaceInfo.Constants.MIN_Z] = from.z() / 16.0f; shape[FaceInfo.Constants.MAX_X] = to.x() / 16.0f; shape[FaceInfo.Constants.MAX_Y] = to.y() / 16.0f; shape[FaceInfo.Constants.MAX_Z] = to.z() / 16.0f; return shape; } private static void bakeVertex(int[] vertices, int index, FaceInfo faceInfo, BlockElementFace.UVs uvs, Quadrant uvRotation, Matrix4fc uvTransform, float[] shape, TextureAtlasSprite icon, Transformation rotation, @Nullable BlockElementRotation elementRotation) { float transformedV; float transformedU; FaceInfo.VertexInfo vertexInfo = faceInfo.getVertexInfo(index); Vector3f vertex = new Vector3f(shape[vertexInfo.xFace], shape[vertexInfo.yFace], shape[vertexInfo.zFace]); if (elementRotation != null) { FaceBakery.rotateVertexBy(vertex, elementRotation.origin(), elementRotation.transform()); } if (rotation != Transformation.identity()) { FaceBakery.rotateVertexBy(vertex, BLOCK_MIDDLE, rotation.getMatrix()); } float rawU = BlockElementFace.getU(uvs, uvRotation, index); float rawV = BlockElementFace.getV(uvs, uvRotation, index); if (MatrixUtil.isIdentity(uvTransform)) { transformedU = rawU; transformedV = rawV; } else { Vector3f transformedUV = uvTransform.transformPosition(new Vector3f(FaceBakery.cornerToCenter(rawU), FaceBakery.cornerToCenter(rawV), 0.0f)); transformedU = FaceBakery.centerToCorner(transformedUV.x); transformedV = FaceBakery.centerToCorner(transformedUV.y); } FaceBakery.fillVertex(vertices, index, vertex, icon, transformedU, transformedV); } private static float cornerToCenter(float value) { return value - 0.5f; } private static float centerToCorner(float value) { return value + 0.5f; } private static void fillVertex(int[] vertices, int index, Vector3f vertex, TextureAtlasSprite icon, float u, float v) { int startIndex = index * 8; vertices[startIndex] = Float.floatToRawIntBits(vertex.x()); vertices[startIndex + 1] = Float.floatToRawIntBits(vertex.y()); vertices[startIndex + 2] = Float.floatToRawIntBits(vertex.z()); vertices[startIndex + 3] = -1; vertices[startIndex + 4] = Float.floatToRawIntBits(icon.getU(u)); vertices[startIndex + 4 + 1] = Float.floatToRawIntBits(icon.getV(v)); } private static void rotateVertexBy(Vector3f vertex, Vector3fc origin, Matrix4fc transformation) { vertex.sub(origin); transformation.transformPosition(vertex); vertex.add(origin); } private static Direction calculateFacing(int[] vertices) { Vector3f a = FaceBakery.vectorFromData(vertices, 0); Vector3f b = FaceBakery.vectorFromData(vertices, 8); Vector3f c = FaceBakery.vectorFromData(vertices, 16); Vector3f ba = new Vector3f((Vector3fc)a).sub((Vector3fc)b); Vector3f bc = new Vector3f((Vector3fc)c).sub((Vector3fc)b); Vector3f normal = new Vector3f((Vector3fc)bc).cross((Vector3fc)ba).normalize(); if (!normal.isFinite()) { return Direction.UP; } Direction best = null; float closestProduct = 0.0f; for (Direction candidate : Direction.values()) { float product = normal.dot(candidate.getUnitVec3f()); if (!(product >= 0.0f) || !(product > closestProduct)) continue; closestProduct = product; best = candidate; } if (best == null) { return Direction.UP; } return best; } private static float xFromData(int[] vertices, int offset) { return Float.intBitsToFloat(vertices[offset]); } private static float yFromData(int[] vertices, int offset) { return Float.intBitsToFloat(vertices[offset + 1]); } private static float zFromData(int[] vertices, int offset) { return Float.intBitsToFloat(vertices[offset + 2]); } private static Vector3f vectorFromData(int[] vertices, int offset) { return new Vector3f(FaceBakery.xFromData(vertices, offset), FaceBakery.yFromData(vertices, offset), FaceBakery.zFromData(vertices, offset)); } private static void recalculateWinding(int[] vertices, Direction direction) { int[] oldVertices = new int[vertices.length]; System.arraycopy(vertices, 0, oldVertices, 0, vertices.length); float[] shape = new float[Direction.values().length]; shape[FaceInfo.Constants.MIN_X] = 999.0f; shape[FaceInfo.Constants.MIN_Y] = 999.0f; shape[FaceInfo.Constants.MIN_Z] = 999.0f; shape[FaceInfo.Constants.MAX_X] = -999.0f; shape[FaceInfo.Constants.MAX_Y] = -999.0f; shape[FaceInfo.Constants.MAX_Z] = -999.0f; for (int i = 0; i < 4; ++i) { int vertIndex = 8 * i; float x = FaceBakery.xFromData(oldVertices, vertIndex); float y = FaceBakery.yFromData(oldVertices, vertIndex); float z = FaceBakery.zFromData(oldVertices, vertIndex); if (x < shape[FaceInfo.Constants.MIN_X]) { shape[FaceInfo.Constants.MIN_X] = x; } if (y < shape[FaceInfo.Constants.MIN_Y]) { shape[FaceInfo.Constants.MIN_Y] = y; } if (z < shape[FaceInfo.Constants.MIN_Z]) { shape[FaceInfo.Constants.MIN_Z] = z; } if (x > shape[FaceInfo.Constants.MAX_X]) { shape[FaceInfo.Constants.MAX_X] = x; } if (y > shape[FaceInfo.Constants.MAX_Y]) { shape[FaceInfo.Constants.MAX_Y] = y; } if (!(z > shape[FaceInfo.Constants.MAX_Z])) continue; shape[FaceInfo.Constants.MAX_Z] = z; } FaceInfo info = FaceInfo.fromFacing(direction); for (int i = 0; i < 4; ++i) { int vertIndex = 8 * i; FaceInfo.VertexInfo vertInfo = info.getVertexInfo(i); float newX = shape[vertInfo.xFace]; float newY = shape[vertInfo.yFace]; float newZ = shape[vertInfo.zFace]; vertices[vertIndex] = Float.floatToRawIntBits(newX); vertices[vertIndex + 1] = Float.floatToRawIntBits(newY); vertices[vertIndex + 2] = Float.floatToRawIntBits(newZ); for (int oldIndex = 0; oldIndex < 4; ++oldIndex) { int oldVertIndex = 8 * oldIndex; float oldX = FaceBakery.xFromData(oldVertices, oldVertIndex); float oldY = FaceBakery.yFromData(oldVertices, oldVertIndex); float oldZ = FaceBakery.zFromData(oldVertices, oldVertIndex); if (!Mth.equal(newX, oldX) || !Mth.equal(newY, oldY) || !Mth.equal(newZ, oldZ)) continue; vertices[vertIndex + 4] = oldVertices[oldVertIndex + 4]; vertices[vertIndex + 4 + 1] = oldVertices[oldVertIndex + 4 + 1]; } } } public static void extractPositions(int[] data, Consumer output) { for (int i = 0; i < 4; ++i) { output.accept(FaceBakery.vectorFromData(data, 8 * i)); } } }