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