All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.parzivail.p3d.P3dModel Maven / Gradle / Ivy

package com.parzivail.p3d;

import ;
import B;
import I;
import Z;
import com.google.common.io.LittleEndianDataInputStream;
import com.parzivail.util.client.model.AbstractModel;
import com.parzivail.util.client.model.ModelUtil;
import com.parzivail.util.data.DataReader;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.class_1058;
import net.minecraft.class_128;
import net.minecraft.class_148;
import net.minecraft.class_238;
import net.minecraft.class_2960;
import net.minecraft.class_4587;
import net.minecraft.class_4587.class_4665;
import net.minecraft.class_4588;
import net.minecraft.class_4597;
import net.minecraft.class_4608;
import net.minecraft.class_5819;
import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Vector3f;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public record P3dModel(int version, HashMap transformables, P3dObject[] rootObjects, class_238 bounds)
{
	@FunctionalInterface
	public interface PartTransformer
	{
		Matrix4f transform(T target, String objectName, float tickDelta);
	}

	@FunctionalInterface
	public interface VertexConsumerSupplier
	{
		class_4588 provideLayer(class_4597 vertexConsumerProvider, T target, String objectName);
	}

	@FunctionalInterface
	public interface SpriteSupplier
	{
		class_1058 provideLayer(T target, String objectName);
	}

	public static final int MAT_ID_DIFFUSE_OPAQUE = 0;
	public static final int MAT_ID_DIFFUSE_CUTOUT = 1;
	public static final int MAT_ID_DIFFUSE_TRANSLUCENT = 2;
	public static final int MAT_ID_EMISSIVE = 3;

	private static final String MODEL_MAGIC = "P3D";
	private static final String RIG_MAGIC = "P3DR";
	private static final int[] ACCEPTED_VERSIONS = { 0x02 };

	public static  Matrix4f identityTransformer(T instance, String socket, float tickDelta)
	{
		return new Matrix4f();
	}

	public void renderBlock(class_4587 matrix, QuadEmitter quadEmitter, P3dBlockRenderTarget target, PartTransformer transformer, SpriteSupplier spriteSupplier, Supplier randomSupplier, RenderContext context)
	{
		for (var mesh : rootObjects)
			renderMesh(matrix, quadEmitter, mesh, target, transformer, spriteSupplier, randomSupplier, context);
	}

	public  void render(class_4587 matrix, class_4597 vertexConsumerProvider, T target, PartTransformer transformer, VertexConsumerSupplier vertexConsumerSupplier, int light, float tickDelta, int r, int g, int b, int a)
	{
		for (var mesh : rootObjects)
			renderMesh(matrix, target, light, vertexConsumerProvider, mesh, tickDelta, transformer, vertexConsumerSupplier, r, g, b, a);
	}

	private  void renderMesh(class_4587 matrix, T target, int light, class_4597 vertexConsumerProvider, P3dObject o, float tickDelta, PartTransformer transformer, VertexConsumerSupplier vertexConsumerSupplier, int r, int g, int b, int a)
	{
		matrix.method_22903();

		var entry = transform(matrix, target, o, tickDelta, transformer);
		if (entry == null)
		{
			matrix.method_22909();
			return;
		}

		var vertexConsumer = vertexConsumerSupplier.provideLayer(vertexConsumerProvider, target, o.name);

		emitFaces(light, o, entry, vertexConsumer, r, g, b, a);

		for (var mesh : o.children)
			renderMesh(matrix, target, light, vertexConsumerProvider, mesh, tickDelta, transformer, vertexConsumerSupplier, r, g, b, a);

		matrix.method_22909();
	}

	private void renderMesh(class_4587 matrix, QuadEmitter quadEmitter, P3dObject o, P3dBlockRenderTarget target, PartTransformer transformer, SpriteSupplier spriteSupplier, Supplier randomSupplier, RenderContext context)
	{
		matrix.method_22903();

		var entry = transform(matrix, target, o, 0, transformer);
		if (entry == null)
		{
			matrix.method_22909();
			return;
		}

		var sprite = spriteSupplier.provideLayer(target, o.name);
		emitFaces(o, entry, quadEmitter, sprite);

		for (var mesh : o.children)
			renderMesh(matrix, quadEmitter, mesh, target, transformer, spriteSupplier, randomSupplier, context);

		matrix.method_22909();
	}

	public  void render(class_4587 matrix, class_4588 vertexConsumer, T target, PartTransformer transformer, int light, float tickDelta, int r, int g, int b, int a)
	{
		for (var mesh : rootObjects)
			renderMesh(matrix, target, light, vertexConsumer, mesh, tickDelta, transformer, r, g, b, a);
	}

	private  void renderMesh(class_4587 matrix, T target, int light, class_4588 vertexConsumer, P3dObject o, float tickDelta, PartTransformer transformer, int r, int g, int b, int a)
	{
		matrix.method_22903();

		var entry = transform(matrix, target, o, tickDelta, transformer);
		if (entry == null)
		{
			matrix.method_22909();
			return;
		}

		emitFaces(light, o, entry, vertexConsumer, r, g, b, a);

		for (var mesh : o.children)
			renderMesh(matrix, target, light, vertexConsumer, mesh, tickDelta, transformer, r, g, b, a);

		matrix.method_22909();
	}

	public  void transformToSocket(Matrix4f mat, String socketName, T target, float tickDelta, PartTransformer transformer)
	{
		var socket = transformables().get(socketName);
		for (var part : socket.ancestry)
		{
			mat.mul(part.transform);
			mat.mul(transformer.transform(target, part.name, tickDelta));
		}
		mat.mul(socket.transform);
	}

	@Nullable
	private  class_4587.class_4665 transform(class_4587 matrix, T target, P3dObject o, float tickDelta, PartTransformer transformer)
	{
		var entry = matrix.method_23760();
		entry.method_23761().mul(o.transform);
		entry.method_23762().mul(new Matrix3f(o.transform));

		if (transformer != null)
		{
			var transform = transformer.transform(target, o.name, tickDelta);

			if (transform == null)
			{
				return null;
			}

			entry.method_23761().mul(transform);
			entry.method_23762().mul(new Matrix3f(transform));
		}
		return entry;
	}

	private void emitFaces(int light, P3dObject o, class_4587.class_4665 entry, class_4588 vertexConsumer, int r, int g, int b, int a)
	{
		var modelMat = entry.method_23761();
		var normalMat = entry.method_23762();

		for (var face : o.faces)
		{
			emitVertex(light, vertexConsumer, modelMat, normalMat, face.positions[0], face.normal, face.texture[0], r, g, b, a);
			emitVertex(light, vertexConsumer, modelMat, normalMat, face.positions[1], face.normal, face.texture[1], r, g, b, a);
			emitVertex(light, vertexConsumer, modelMat, normalMat, face.positions[2], face.normal, face.texture[2], r, g, b, a);
			emitVertex(light, vertexConsumer, modelMat, normalMat, face.positions[3], face.normal, face.texture[3], r, g, b, a);
		}
	}

	private void emitFaces(P3dObject o, class_4587.class_4665 entry, QuadEmitter quadEmitter, class_1058 sprite)
	{
		var modelMat = entry.method_23761();
		var normalMat = entry.method_23762();

		for (var face : o.faces)
		{
			quadEmitter.colorIndex(1).spriteColor(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF).material(getBlockMaterial(o.material));

			var vA = modelMat.transformPosition(face.positions[0], new Vector3f());
			var vB = modelMat.transformPosition(face.positions[1], new Vector3f());
			var vC = modelMat.transformPosition(face.positions[2], new Vector3f());
			var vD = modelMat.transformPosition(face.positions[3], new Vector3f());

			var n = new Vector3f(face.normal.x, face.normal.y, face.normal.z);
			n.mul(normalMat);
			n.normalize();

			quadEmitter.pos(0, vA).normal(0, n).sprite(0, 0, face.texture[0].x, 1 - face.texture[0].y);
			quadEmitter.pos(1, vB).normal(1, n).sprite(1, 0, face.texture[1].x, 1 - face.texture[1].y);
			quadEmitter.pos(2, vC).normal(2, n).sprite(2, 0, face.texture[2].x, 1 - face.texture[2].y);
			quadEmitter.pos(3, vD).normal(3, n).sprite(3, 0, face.texture[3].x, 1 - face.texture[3].y);

			quadEmitter.spriteBake(0, sprite, MutableQuadView.BAKE_NORMALIZED);

			quadEmitter.emit();
		}
	}

	private RenderMaterial getBlockMaterial(byte material)
	{
		switch (material)
		{
			case MAT_ID_DIFFUSE_OPAQUE:
				return AbstractModel.MAT_DIFFUSE_OPAQUE;
			case MAT_ID_DIFFUSE_CUTOUT:
				return AbstractModel.MAT_DIFFUSE_CUTOUT;
			case MAT_ID_DIFFUSE_TRANSLUCENT:
				return AbstractModel.MAT_DIFFUSE_TRANSLUCENT;
			case MAT_ID_EMISSIVE:
				return AbstractModel.MAT_EMISSIVE;
			default:
			{
				var crashReport = class_128.method_560(new IllegalStateException("Unknown material ID"), String.format("Unknown material ID: %s", material));
				throw new class_148(crashReport);
			}
		}
	}

	private void emitVertex(int light, class_4588 vertexConsumer, Matrix4f modelMatrix, Matrix3f normalMatrix, Vector3f vertex, Vector3f normal, Vector3f texCoord, int r, int g, int b, int a)
	{
		vertexConsumer
				.method_22918(modelMatrix, vertex.x, vertex.y, vertex.z)
				.method_1336(r, g, b, a)
				.method_22913(texCoord.x, 1 - texCoord.y)
				.method_22922(class_4608.field_21444)
				.method_22916(light)
				.method_23763(normalMatrix, normal.x, normal.y, normal.z)
				.method_1344();
	}

	public static InputStream getStream(String domain, class_2960 resourceLocation)
	{
		return P3dModel.class.getClassLoader().getResourceAsStream(domain + "/" + resourceLocation.method_12836() + "/" + resourceLocation.method_12832());
	}

	public static P3dModel tryLoad(class_2960 modelFile, boolean hasVertexData)
	{
		try
		{
			var reader = getStream("data", modelFile);
			return read(reader, hasVertexData);
		}
		catch (IOException ex)
		{
			ex.printStackTrace();
			var crashReport = class_128.method_560(ex, String.format("Loading PR3R file: %s", modelFile));
			throw new class_148(crashReport);
		}
	}

	public static P3dModel read(InputStream stream, boolean hasVertexData) throws IOException
	{
		var objStream = new LittleEndianDataInputStream(stream);

		var magic = hasVertexData ? MODEL_MAGIC : RIG_MAGIC;

		// read header
		var identBytes = new byte[magic.length()];
		var read = objStream.read(identBytes);
		var ident = new String(identBytes);
		if (!ident.equals(magic) || read != identBytes.length)
			throw new IOException(String.format("Input file not %s model", magic));

		var version = objStream.readInt();

		if (!ArrayUtils.contains(ACCEPTED_VERSIONS, version))
			throw new IOException(String.format("Input file version is 0x%s, expected one of: %s", Integer.toHexString(version), getAcceptedVersionString()));

		// read sockets
		var numSockets = objStream.readInt();
		var transformables = new HashMap();

		for (var socketIdx = 0; socketIdx < numSockets; socketIdx++)
		{
			var name = DataReader.readNullTerminatedString(objStream);

			var hasParent = objStream.readBoolean();

			String parent = null;
			if (hasParent)
				parent = DataReader.readNullTerminatedString(objStream);

			var transform = DataReader.readMatrix4f(objStream);

			transformables.put(name, new P3dSocket(name, parent, transform));
		}

		// read objects
		var numObjects = objStream.readInt();
		var rootObjects = new P3dObject[numObjects];

		var rawVertices = new ArrayList();

		for (var objectIdx = 0; objectIdx < numObjects; objectIdx++)
		{
			var object = readObject(transformables, null, objStream, hasVertexData, rawVertices);
			transformables.put(object.name, object);

			rootObjects[objectIdx] = object;
		}

		// build ancestry trees for sockets and parts which enables directly calculating transformation
		buildAncestry(transformables);

		var bounds = ModelUtil.getBounds(rawVertices);

		return new P3dModel(version, transformables, rootObjects, bounds);
	}

	private static void buildAncestry(HashMap parts)
	{
		for (var part : parts.values())
		{
			var parentName = part.parent;
			while (parentName != null)
			{
				var parent = parts.get(parentName);
				part.ancestry.add(0, parent);
				parentName = parent.parent;
			}
		}
	}

	@NotNull
	private static P3dObject readObject(HashMap objects, String parent, LittleEndianDataInputStream objStream, boolean hasVertexData, ArrayList rawVertices) throws IOException
	{
		var name = DataReader.readNullTerminatedString(objStream);

		var transform = DataReader.readMatrix4f(objStream);

		var material = hasVertexData ? objStream.readByte() : (byte)0;

		var numFaces = hasVertexData ? objStream.readInt() : 0;
		var faces = new P3dFace[numFaces];

		for (var faceIdx = 0; faceIdx < numFaces; faceIdx++)
		{
			var positions = new Vector3f[4];
			var texture = new Vector3f[4];

			var normal = new Vector3f(objStream.readFloat(), objStream.readFloat(), objStream.readFloat());

			for (var vertIdx = 0; vertIdx < 4; vertIdx++)
			{
				positions[vertIdx] = new Vector3f(objStream.readFloat(), objStream.readFloat(), objStream.readFloat());
				texture[vertIdx] = new Vector3f(objStream.readFloat(), objStream.readFloat(), 0);

				rawVertices.add(positions[vertIdx]);
			}

			faces[faceIdx] = new P3dFace(positions, normal, texture);
		}

		var numChildren = objStream.readInt();
		var children = new P3dObject[numChildren];

		for (var childIdx = 0; childIdx < numChildren; childIdx++)
		{
			var m = readObject(objects, name, objStream, hasVertexData, rawVertices);
			objects.put(m.name, m);
			children[childIdx] = m;
		}

		return new P3dObject(name, parent, transform, material, faces, children);
	}

	private static String getAcceptedVersionString()
	{
		return Arrays.stream(ACCEPTED_VERSIONS).mapToObj(i -> "0x" + Integer.toHexString(i)).collect(Collectors.joining(", "));
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy