244 lines
11 KiB
Java
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));
|
|
}
|
|
}
|
|
}
|
|
|