
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