2025-11-24 22:52:51 +03:00

244 lines
11 KiB
Java

/*
* 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<Vector3f> output) {
for (int i = 0; i < 4; ++i) {
output.accept(FaceBakery.vectorFromData(data, 8 * i));
}
}
}