310 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
	
		
			10 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.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.Vector3f;
 | |
| import org.joml.Vector3fc;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| 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 NO_RESCALE = new Vector3f(1.0F, 1.0F, 1.0F);
 | |
| 	private static final Vector3fc BLOCK_MIDDLE = new Vector3f(0.5F, 0.5F, 0.5F);
 | |
| 
 | |
| 	@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[FaceInfo.Constants.MIN_X] = posFrom.x() / 16.0F;
 | |
| 		fs[FaceInfo.Constants.MIN_Y] = posFrom.y() / 16.0F;
 | |
| 		fs[FaceInfo.Constants.MIN_Z] = posFrom.z() / 16.0F;
 | |
| 		fs[FaceInfo.Constants.MAX_X] = posTo.x() / 16.0F;
 | |
| 		fs[FaceInfo.Constants.MAX_Y] = posTo.y() / 16.0F;
 | |
| 		fs[FaceInfo.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
 | |
| 	) {
 | |
| 		FaceInfo.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) {
 | |
| 			Vector3fc vector3fc = partRotation.axis().getPositive().getUnitVec3f();
 | |
| 			Matrix4fc matrix4fc = new Matrix4f().rotation(partRotation.angle() * (float) (Math.PI / 180.0), vector3fc);
 | |
| 			Vector3fc vector3fc2 = partRotation.rescale() ? computeRescale(partRotation) : NO_RESCALE;
 | |
| 			rotateVertexBy(vec, partRotation.origin(), matrix4fc, vector3fc2);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static Vector3fc computeRescale(BlockElementRotation partRotation) {
 | |
| 		if (partRotation.angle() == 0.0F) {
 | |
| 			return NO_RESCALE;
 | |
| 		} else {
 | |
| 			float f = Math.abs(partRotation.angle());
 | |
| 			float g = 1.0F / Mth.cos(f * (float) (Math.PI / 180.0));
 | |
| 
 | |
| 			return switch (partRotation.axis()) {
 | |
| 				case X -> new Vector3f(1.0F, g, g);
 | |
| 				case Y -> new Vector3f(g, 1.0F, g);
 | |
| 				case Z -> new Vector3f(g, g, 1.0F);
 | |
| 			};
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static void applyModelRotation(Vector3f pos, Transformation transform) {
 | |
| 		if (transform != Transformation.identity()) {
 | |
| 			rotateVertexBy(pos, BLOCK_MIDDLE, transform.getMatrix(), NO_RESCALE);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static void rotateVertexBy(Vector3f pos, Vector3fc origin, Matrix4fc transform, Vector3fc scale) {
 | |
| 		pos.sub(origin);
 | |
| 		transform.transformPosition(pos);
 | |
| 		pos.mul(scale);
 | |
| 		pos.add(origin);
 | |
| 	}
 | |
| 
 | |
| 	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[FaceInfo.Constants.MIN_X] = 999.0F;
 | |
| 		fs[FaceInfo.Constants.MIN_Y] = 999.0F;
 | |
| 		fs[FaceInfo.Constants.MIN_Z] = 999.0F;
 | |
| 		fs[FaceInfo.Constants.MAX_X] = -999.0F;
 | |
| 		fs[FaceInfo.Constants.MAX_Y] = -999.0F;
 | |
| 		fs[FaceInfo.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[FaceInfo.Constants.MIN_X]) {
 | |
| 				fs[FaceInfo.Constants.MIN_X] = f;
 | |
| 			}
 | |
| 
 | |
| 			if (g < fs[FaceInfo.Constants.MIN_Y]) {
 | |
| 				fs[FaceInfo.Constants.MIN_Y] = g;
 | |
| 			}
 | |
| 
 | |
| 			if (h < fs[FaceInfo.Constants.MIN_Z]) {
 | |
| 				fs[FaceInfo.Constants.MIN_Z] = h;
 | |
| 			}
 | |
| 
 | |
| 			if (f > fs[FaceInfo.Constants.MAX_X]) {
 | |
| 				fs[FaceInfo.Constants.MAX_X] = f;
 | |
| 			}
 | |
| 
 | |
| 			if (g > fs[FaceInfo.Constants.MAX_Y]) {
 | |
| 				fs[FaceInfo.Constants.MAX_Y] = g;
 | |
| 			}
 | |
| 
 | |
| 			if (h > fs[FaceInfo.Constants.MAX_Z]) {
 | |
| 				fs[FaceInfo.Constants.MAX_Z] = h;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		FaceInfo faceInfo = FaceInfo.fromFacing(direction);
 | |
| 
 | |
| 		for (int jx = 0; jx < 4; jx++) {
 | |
| 			int k = 8 * jx;
 | |
| 			FaceInfo.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));
 | |
| 		}
 | |
| 	}
 | |
| }
 |