minecraft-src/net/minecraft/client/renderer/block/model/FaceBakery.java
2025-07-04 03:45:38 +03:00

327 lines
11 KiB
Java

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.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.renderer.FaceInfo;
import net.minecraft.client.renderer.FaceInfo.Constants;
import net.minecraft.client.renderer.FaceInfo.VertexInfo;
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.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.joml.Vector3fc;
import org.joml.Vector4f;
@Environment(EnvType.CLIENT)
public class FaceBakery {
public static final int VERTEX_INT_SIZE = 8;
private static final float RESCALE_22_5 = 1.0F / (float)Math.cos((float) (Math.PI / 8)) - 1.0F;
private static final float RESCALE_45 = 1.0F / (float)Math.cos((float) (Math.PI / 4)) - 1.0F;
public static final int VERTEX_COUNT = 4;
private static final int COLOR_INDEX = 3;
public static final int UV_INDEX = 4;
@VisibleForTesting
static BlockElementFace.UVs defaultFaceUV(Vector3fc posFrom, Vector3fc posTo, Direction facing) {
return switch (facing) {
case DOWN -> new BlockElementFace.UVs(posFrom.x(), 16.0F - posTo.z(), posTo.x(), 16.0F - posFrom.z());
case UP -> new BlockElementFace.UVs(posFrom.x(), posFrom.z(), posTo.x(), posTo.z());
case NORTH -> new BlockElementFace.UVs(16.0F - posTo.x(), 16.0F - posTo.y(), 16.0F - posFrom.x(), 16.0F - posFrom.y());
case SOUTH -> new BlockElementFace.UVs(posFrom.x(), 16.0F - posTo.y(), posTo.x(), 16.0F - posFrom.y());
case WEST -> new BlockElementFace.UVs(posFrom.z(), 16.0F - posTo.y(), posTo.z(), 16.0F - posFrom.y());
case EAST -> new BlockElementFace.UVs(16.0F - posTo.z(), 16.0F - posTo.y(), 16.0F - posFrom.z(), 16.0F - posFrom.y());
};
}
public static BakedQuad bakeQuad(
Vector3fc posFrom,
Vector3fc posTo,
BlockElementFace face,
TextureAtlasSprite sprite,
Direction facing,
ModelState modelState,
@Nullable BlockElementRotation rotation,
boolean shade,
int lightEmission
) {
BlockElementFace.UVs uVs = face.uvs();
if (uVs == null) {
uVs = defaultFaceUV(posFrom, posTo, facing);
}
uVs = shrinkUVs(sprite, uVs);
Matrix4fc matrix4fc = modelState.inverseFaceTransformation(facing);
int[] is = makeVertices(uVs, face.rotation(), matrix4fc, sprite, facing, setupShape(posFrom, posTo), modelState.transformation(), rotation);
Direction direction = calculateFacing(is);
if (rotation == null) {
recalculateWinding(is, direction);
}
return new BakedQuad(is, face.tintIndex(), direction, sprite, shade, lightEmission);
}
private static BlockElementFace.UVs shrinkUVs(TextureAtlasSprite sprite, BlockElementFace.UVs uvs) {
float f = uvs.minU();
float g = uvs.minV();
float h = uvs.maxU();
float i = uvs.maxV();
float j = sprite.uvShrinkRatio();
float k = (f + f + h + h) / 4.0F;
float l = (g + g + i + i) / 4.0F;
return new BlockElementFace.UVs(Mth.lerp(j, f, k), Mth.lerp(j, g, l), Mth.lerp(j, h, k), Mth.lerp(j, i, l));
}
private static int[] makeVertices(
BlockElementFace.UVs uvs,
Quadrant rotation,
Matrix4fc inverseFaceTransform,
TextureAtlasSprite sprite,
Direction facing,
float[] shape,
Transformation transformation,
@Nullable BlockElementRotation partRotation
) {
FaceInfo faceInfo = FaceInfo.fromFacing(facing);
int[] is = new int[32];
for (int i = 0; i < 4; i++) {
bakeVertex(is, i, faceInfo, uvs, rotation, inverseFaceTransform, shape, sprite, transformation, partRotation);
}
return is;
}
private static float[] setupShape(Vector3fc posFrom, Vector3fc posTo) {
float[] fs = new float[Direction.values().length];
fs[Constants.MIN_X] = posFrom.x() / 16.0F;
fs[Constants.MIN_Y] = posFrom.y() / 16.0F;
fs[Constants.MIN_Z] = posFrom.z() / 16.0F;
fs[Constants.MAX_X] = posTo.x() / 16.0F;
fs[Constants.MAX_Y] = posTo.y() / 16.0F;
fs[Constants.MAX_Z] = posTo.z() / 16.0F;
return fs;
}
private static void bakeVertex(
int[] vertexData,
int vertexIndex,
FaceInfo faceInfo,
BlockElementFace.UVs uvs,
Quadrant rotation,
Matrix4fc inverseFaceTransform,
float[] shape,
TextureAtlasSprite sprite,
Transformation transformation,
@Nullable BlockElementRotation partRotation
) {
VertexInfo vertexInfo = faceInfo.getVertexInfo(vertexIndex);
Vector3f vector3f = new Vector3f(shape[vertexInfo.xFace], shape[vertexInfo.yFace], shape[vertexInfo.zFace]);
applyElementRotation(vector3f, partRotation);
applyModelRotation(vector3f, transformation);
float f = BlockElementFace.getU(uvs, rotation, vertexIndex);
float g = BlockElementFace.getV(uvs, rotation, vertexIndex);
float i;
float h;
if (MatrixUtil.isIdentity(inverseFaceTransform)) {
h = f;
i = g;
} else {
Vector3f vector3f2 = inverseFaceTransform.transformPosition(new Vector3f(cornerToCenter(f), cornerToCenter(g), 0.0F));
h = centerToCorner(vector3f2.x);
i = centerToCorner(vector3f2.y);
}
fillVertex(vertexData, vertexIndex, vector3f, sprite, h, i);
}
private static float cornerToCenter(float coord) {
return coord - 0.5F;
}
private static float centerToCorner(float coord) {
return coord + 0.5F;
}
private static void fillVertex(int[] vertexData, int vertexIndex, Vector3f pos, TextureAtlasSprite sprite, float u, float v) {
int i = vertexIndex * 8;
vertexData[i] = Float.floatToRawIntBits(pos.x());
vertexData[i + 1] = Float.floatToRawIntBits(pos.y());
vertexData[i + 2] = Float.floatToRawIntBits(pos.z());
vertexData[i + 3] = -1;
vertexData[i + 4] = Float.floatToRawIntBits(sprite.getU(u));
vertexData[i + 4 + 1] = Float.floatToRawIntBits(sprite.getV(v));
}
private static void applyElementRotation(Vector3f vec, @Nullable BlockElementRotation partRotation) {
if (partRotation != null) {
Vector3f vector3f;
Vector3f vector3f2;
switch (partRotation.axis()) {
case X:
vector3f = new Vector3f(1.0F, 0.0F, 0.0F);
vector3f2 = new Vector3f(0.0F, 1.0F, 1.0F);
break;
case Y:
vector3f = new Vector3f(0.0F, 1.0F, 0.0F);
vector3f2 = new Vector3f(1.0F, 0.0F, 1.0F);
break;
case Z:
vector3f = new Vector3f(0.0F, 0.0F, 1.0F);
vector3f2 = new Vector3f(1.0F, 1.0F, 0.0F);
break;
default:
throw new IllegalArgumentException("There are only 3 axes");
}
Quaternionf quaternionf = new Quaternionf().rotationAxis(partRotation.angle() * (float) (Math.PI / 180.0), vector3f);
if (partRotation.rescale()) {
if (Math.abs(partRotation.angle()) == 22.5F) {
vector3f2.mul(RESCALE_22_5);
} else {
vector3f2.mul(RESCALE_45);
}
vector3f2.add(1.0F, 1.0F, 1.0F);
} else {
vector3f2.set(1.0F, 1.0F, 1.0F);
}
rotateVertexBy(vec, new Vector3f(partRotation.origin()), new Matrix4f().rotation(quaternionf), vector3f2);
}
}
private static void applyModelRotation(Vector3f pos, Transformation transform) {
if (transform != Transformation.identity()) {
rotateVertexBy(pos, new Vector3f(0.5F, 0.5F, 0.5F), transform.getMatrix(), new Vector3f(1.0F, 1.0F, 1.0F));
}
}
private static void rotateVertexBy(Vector3f pos, Vector3fc origin, Matrix4fc transform, Vector3fc scale) {
Vector4f vector4f = transform.transform(new Vector4f(pos.x() - origin.x(), pos.y() - origin.y(), pos.z() - origin.z(), 1.0F));
vector4f.mul(new Vector4f(scale, 1.0F));
pos.set(vector4f.x() + origin.x(), vector4f.y() + origin.y(), vector4f.z() + origin.z());
}
private static Direction calculateFacing(int[] faceData) {
Vector3f vector3f = vectorFromData(faceData, 0);
Vector3f vector3f2 = vectorFromData(faceData, 8);
Vector3f vector3f3 = vectorFromData(faceData, 16);
Vector3f vector3f4 = new Vector3f(vector3f).sub(vector3f2);
Vector3f vector3f5 = new Vector3f(vector3f3).sub(vector3f2);
Vector3f vector3f6 = new Vector3f(vector3f5).cross(vector3f4).normalize();
if (!vector3f6.isFinite()) {
return Direction.UP;
} else {
Direction direction = null;
float f = 0.0F;
for (Direction direction2 : Direction.values()) {
float g = vector3f6.dot(direction2.getUnitVec3f());
if (g >= 0.0F && g > f) {
f = g;
direction = direction2;
}
}
return direction == null ? Direction.UP : direction;
}
}
private static float xFromData(int[] faceData, int index) {
return Float.intBitsToFloat(faceData[index]);
}
private static float yFromData(int[] faceData, int index) {
return Float.intBitsToFloat(faceData[index + 1]);
}
private static float zFromData(int[] faceData, int index) {
return Float.intBitsToFloat(faceData[index + 2]);
}
private static Vector3f vectorFromData(int[] faceData, int index) {
return new Vector3f(xFromData(faceData, index), yFromData(faceData, index), zFromData(faceData, index));
}
private static void recalculateWinding(int[] vertices, Direction direction) {
int[] is = new int[vertices.length];
System.arraycopy(vertices, 0, is, 0, vertices.length);
float[] fs = new float[Direction.values().length];
fs[Constants.MIN_X] = 999.0F;
fs[Constants.MIN_Y] = 999.0F;
fs[Constants.MIN_Z] = 999.0F;
fs[Constants.MAX_X] = -999.0F;
fs[Constants.MAX_Y] = -999.0F;
fs[Constants.MAX_Z] = -999.0F;
for (int i = 0; i < 4; i++) {
int j = 8 * i;
float f = xFromData(is, j);
float g = yFromData(is, j);
float h = zFromData(is, j);
if (f < fs[Constants.MIN_X]) {
fs[Constants.MIN_X] = f;
}
if (g < fs[Constants.MIN_Y]) {
fs[Constants.MIN_Y] = g;
}
if (h < fs[Constants.MIN_Z]) {
fs[Constants.MIN_Z] = h;
}
if (f > fs[Constants.MAX_X]) {
fs[Constants.MAX_X] = f;
}
if (g > fs[Constants.MAX_Y]) {
fs[Constants.MAX_Y] = g;
}
if (h > fs[Constants.MAX_Z]) {
fs[Constants.MAX_Z] = h;
}
}
FaceInfo faceInfo = FaceInfo.fromFacing(direction);
for (int jx = 0; jx < 4; jx++) {
int k = 8 * jx;
VertexInfo vertexInfo = faceInfo.getVertexInfo(jx);
float hx = fs[vertexInfo.xFace];
float l = fs[vertexInfo.yFace];
float m = fs[vertexInfo.zFace];
vertices[k] = Float.floatToRawIntBits(hx);
vertices[k + 1] = Float.floatToRawIntBits(l);
vertices[k + 2] = Float.floatToRawIntBits(m);
for (int n = 0; n < 4; n++) {
int o = 8 * n;
float p = xFromData(is, o);
float q = yFromData(is, o);
float r = zFromData(is, o);
if (Mth.equal(hx, p) && Mth.equal(l, q) && Mth.equal(m, r)) {
vertices[k + 4] = is[o + 4];
vertices[k + 4 + 1] = is[o + 4 + 1];
}
}
}
}
public static void extractPositions(int[] faceData, Consumer<Vector3f> output) {
for (int i = 0; i < 4; i++) {
output.accept(vectorFromData(faceData, 8 * i));
}
}
}