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

302 lines
10 KiB
Java

package net.minecraft.client.renderer.block.model;
import com.mojang.math.Transformation;
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.BlockMath;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import net.minecraft.util.Mth;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
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;
public static BakedQuad bakeQuad(
Vector3f posFrom,
Vector3f posTo,
BlockElementFace face,
TextureAtlasSprite sprite,
Direction facing,
ModelState transform,
@Nullable BlockElementRotation rotation,
boolean shade,
int lightEmission
) {
BlockFaceUV blockFaceUV = face.uv();
if (transform.isUvLocked()) {
blockFaceUV = recomputeUVs(face.uv(), facing, transform.getRotation());
}
float[] fs = new float[blockFaceUV.uvs.length];
System.arraycopy(blockFaceUV.uvs, 0, fs, 0, fs.length);
float f = sprite.uvShrinkRatio();
float g = (blockFaceUV.uvs[0] + blockFaceUV.uvs[0] + blockFaceUV.uvs[2] + blockFaceUV.uvs[2]) / 4.0F;
float h = (blockFaceUV.uvs[1] + blockFaceUV.uvs[1] + blockFaceUV.uvs[3] + blockFaceUV.uvs[3]) / 4.0F;
blockFaceUV.uvs[0] = Mth.lerp(f, blockFaceUV.uvs[0], g);
blockFaceUV.uvs[2] = Mth.lerp(f, blockFaceUV.uvs[2], g);
blockFaceUV.uvs[1] = Mth.lerp(f, blockFaceUV.uvs[1], h);
blockFaceUV.uvs[3] = Mth.lerp(f, blockFaceUV.uvs[3], h);
int[] is = makeVertices(blockFaceUV, sprite, facing, setupShape(posFrom, posTo), transform.getRotation(), rotation);
Direction direction = calculateFacing(is);
System.arraycopy(fs, 0, blockFaceUV.uvs, 0, fs.length);
if (rotation == null) {
recalculateWinding(is, direction);
}
return new BakedQuad(is, face.tintIndex(), direction, sprite, shade, lightEmission);
}
public static BlockFaceUV recomputeUVs(BlockFaceUV uv, Direction facing, Transformation rotation) {
Matrix4f matrix4f = BlockMath.getUVLockTransform(rotation, facing).getMatrix();
float f = uv.getU(uv.getReverseIndex(0));
float g = uv.getV(uv.getReverseIndex(0));
Vector4f vector4f = matrix4f.transform(new Vector4f(f / 16.0F, g / 16.0F, 0.0F, 1.0F));
float h = 16.0F * vector4f.x();
float i = 16.0F * vector4f.y();
float j = uv.getU(uv.getReverseIndex(2));
float k = uv.getV(uv.getReverseIndex(2));
Vector4f vector4f2 = matrix4f.transform(new Vector4f(j / 16.0F, k / 16.0F, 0.0F, 1.0F));
float l = 16.0F * vector4f2.x();
float m = 16.0F * vector4f2.y();
float n;
float o;
if (Math.signum(j - f) == Math.signum(l - h)) {
n = h;
o = l;
} else {
n = l;
o = h;
}
float p;
float q;
if (Math.signum(k - g) == Math.signum(m - i)) {
p = i;
q = m;
} else {
p = m;
q = i;
}
float r = (float)Math.toRadians(uv.rotation);
Matrix3f matrix3f = new Matrix3f(matrix4f);
Vector3f vector3f = matrix3f.transform(new Vector3f(Mth.cos(r), Mth.sin(r), 0.0F));
int s = Math.floorMod(-((int)Math.round(Math.toDegrees(Math.atan2(vector3f.y(), vector3f.x())) / 90.0)) * 90, 360);
return new BlockFaceUV(new float[]{n, p, o, q}, s);
}
private static int[] makeVertices(
BlockFaceUV uvs,
TextureAtlasSprite sprite,
Direction orientation,
float[] posDiv16,
Transformation transformation,
@Nullable BlockElementRotation partRotation
) {
int[] is = new int[32];
for (int i = 0; i < 4; i++) {
bakeVertex(is, i, orientation, uvs, posDiv16, sprite, transformation, partRotation);
}
return is;
}
private static float[] setupShape(Vector3f min, Vector3f max) {
float[] fs = new float[Direction.values().length];
fs[Constants.MIN_X] = min.x() / 16.0F;
fs[Constants.MIN_Y] = min.y() / 16.0F;
fs[Constants.MIN_Z] = min.z() / 16.0F;
fs[Constants.MAX_X] = max.x() / 16.0F;
fs[Constants.MAX_Y] = max.y() / 16.0F;
fs[Constants.MAX_Z] = max.z() / 16.0F;
return fs;
}
private static void bakeVertex(
int[] vertexData,
int vertexIndex,
Direction facing,
BlockFaceUV blockFaceUV,
float[] posDiv16,
TextureAtlasSprite sprite,
Transformation transformation,
@Nullable BlockElementRotation partRotation
) {
VertexInfo vertexInfo = FaceInfo.fromFacing(facing).getVertexInfo(vertexIndex);
Vector3f vector3f = new Vector3f(posDiv16[vertexInfo.xFace], posDiv16[vertexInfo.yFace], posDiv16[vertexInfo.zFace]);
applyElementRotation(vector3f, partRotation);
applyModelRotation(vector3f, transformation);
fillVertex(vertexData, vertexIndex, vector3f, sprite, blockFaceUV);
}
private static void fillVertex(int[] vertexData, int vertexIndex, Vector3f vector, TextureAtlasSprite sprite, BlockFaceUV blockFaceUV) {
int i = vertexIndex * 8;
vertexData[i] = Float.floatToRawIntBits(vector.x());
vertexData[i + 1] = Float.floatToRawIntBits(vector.y());
vertexData[i + 2] = Float.floatToRawIntBits(vector.z());
vertexData[i + 3] = -1;
vertexData[i + 4] = Float.floatToRawIntBits(sprite.getU(blockFaceUV.getU(vertexIndex) / 16.0F));
vertexData[i + 4 + 1] = Float.floatToRawIntBits(sprite.getV(blockFaceUV.getV(vertexIndex) / 16.0F));
}
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, Vector3f origin, Matrix4f transform, Vector3f 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 = new Vector3f(Float.intBitsToFloat(faceData[0]), Float.intBitsToFloat(faceData[1]), Float.intBitsToFloat(faceData[2]));
Vector3f vector3f2 = new Vector3f(Float.intBitsToFloat(faceData[8]), Float.intBitsToFloat(faceData[9]), Float.intBitsToFloat(faceData[10]));
Vector3f vector3f3 = new Vector3f(Float.intBitsToFloat(faceData[16]), Float.intBitsToFloat(faceData[17]), Float.intBitsToFloat(faceData[18]));
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()) {
Vec3i vec3i = direction2.getUnitVec3i();
Vector3f vector3f7 = new Vector3f(vec3i.getX(), vec3i.getY(), vec3i.getZ());
float g = vector3f6.dot(vector3f7);
if (g >= 0.0F && g > f) {
f = g;
direction = direction2;
}
}
return direction == null ? Direction.UP : direction;
}
}
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 = Float.intBitsToFloat(is[j]);
float g = Float.intBitsToFloat(is[j + 1]);
float h = Float.intBitsToFloat(is[j + 2]);
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 = Float.intBitsToFloat(is[o]);
float q = Float.intBitsToFloat(is[o + 1]);
float r = Float.intBitsToFloat(is[o + 2]);
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];
}
}
}
}
}