diff --git a/common/src/main/java/net/irisshaders/iris/compat/sodium/mixin/MixinBakedModel.java b/common/src/main/java/net/irisshaders/iris/compat/sodium/mixin/MixinBakedModel.java new file mode 100644 index 0000000000..10de2100e3 --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/compat/sodium/mixin/MixinBakedModel.java @@ -0,0 +1,85 @@ +package net.irisshaders.iris.compat.sodium.mixin; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.caffeinemc.mods.sodium.api.math.MatrixHelper; +import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; +import net.caffeinemc.mods.sodium.api.vertex.format.common.EntityVertex; +import net.caffeinemc.mods.sodium.client.model.quad.ModelQuadView; +import net.caffeinemc.mods.sodium.client.render.frapi.helper.ColorHelper; +import net.caffeinemc.mods.sodium.client.render.immediate.model.BakedModelEncoder; +import net.irisshaders.iris.Iris; +import net.irisshaders.iris.mixinterface.QuadPositionAccess; +import net.irisshaders.iris.pipeline.QuadPositions; +import net.irisshaders.iris.uniforms.CapturedRenderingState; +import net.irisshaders.iris.uniforms.SystemTimeUniforms; +import net.irisshaders.iris.vertices.sodium.IrisEntityVertex; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.lwjgl.system.MemoryStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(BakedModelEncoder.class) +public abstract class MixinBakedModel { + @Shadow + private static int mergeLighting(int stored, int calculated) { + return 0; + } + + @Inject(method = "writeQuadVertices(Lnet/caffeinemc/mods/sodium/api/vertex/buffer/VertexBufferWriter;Lcom/mojang/blaze3d/vertex/PoseStack$Pose;Lnet/caffeinemc/mods/sodium/client/model/quad/ModelQuadView;IIIZ)V", at = @At("HEAD"), cancellable = true) + private static void redirectToIris(VertexBufferWriter writer, PoseStack.Pose matrices, ModelQuadView quad, int color, int light, int overlay, boolean colorize, CallbackInfo ci) { + if (Iris.isPackInUseQuick()) { + ci.cancel(); + writeIris(writer, matrices, quad, color, light, overlay, colorize); + } + } + + private static void writeIris(VertexBufferWriter writer, PoseStack.Pose matrices, ModelQuadView quad, int color, int light, int overlay, boolean colorize) { + Matrix3f matNormal = matrices.normal(); + Matrix4f matPosition = matrices.pose(); + + QuadPositions quadPositions = ((QuadPositionAccess) quad).getQuadPosition(CapturedRenderingState.INSTANCE.getEntityRollingId()); + + try (MemoryStack stack = MemoryStack.stackPush()) { + long buffer = stack.nmalloc(4 * IrisEntityVertex.STRIDE); + long ptr = buffer; + + float midU = (quad.getTexU(0) + quad.getTexU(1) + quad.getTexU(2) + quad.getTexU(3)) * 0.25f; + float midV = (quad.getTexV(0) + quad.getTexV(1) + quad.getTexV(2) + quad.getTexV(3)) * 0.25f; + + for (int i = 0; i < 4; i++) { + // The position vector + float x = quad.getX(i); + float y = quad.getY(i); + float z = quad.getZ(i); + + int newLight = mergeLighting(quad.getLight(i), light); + + int newColor = color; + + if (colorize) { + newColor = ColorHelper.multiplyColor(newColor, quad.getColor(i)); + } + + // The packed transformed normal vector + int normal = MatrixHelper.transformNormal(matNormal, matrices.trustedNormals, quad.getAccurateNormal(i)); + + // The transformed position vector + float xt = MatrixHelper.transformPositionX(matPosition, x, y, z); + float yt = MatrixHelper.transformPositionY(matPosition, x, y, z); + float zt = MatrixHelper.transformPositionZ(matPosition, x, y, z); + + quadPositions.setAndUpdate(SystemTimeUniforms.COUNTER.getAsInt(), i, xt, yt, zt); + + // TODO TANGENT + IrisEntityVertex.write(ptr, xt, yt, zt, quadPositions.velocityX[i], quadPositions.velocityY[i], quadPositions.velocityZ[i], newColor, quad.getTexU(i), quad.getTexV(i), overlay, newLight, normal, 0, midU, midV); + ptr += IrisEntityVertex.STRIDE; + } + + writer.push(stack, buffer, 4, IrisEntityVertex.FORMAT); + } + } +} diff --git a/common/src/main/java/net/irisshaders/iris/compat/sodium/mixin/MixinEntityRenderer.java b/common/src/main/java/net/irisshaders/iris/compat/sodium/mixin/MixinEntityRenderer.java new file mode 100644 index 0000000000..2b2c52bee7 --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/compat/sodium/mixin/MixinEntityRenderer.java @@ -0,0 +1,204 @@ +package net.irisshaders.iris.compat.sodium.mixin; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.caffeinemc.mods.sodium.api.math.MatrixHelper; +import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; +import net.caffeinemc.mods.sodium.client.render.immediate.model.EntityRenderer; +import net.caffeinemc.mods.sodium.client.render.immediate.model.ModelCuboid; +import net.irisshaders.iris.Iris; +import net.irisshaders.iris.mixinterface.ModelPartAccess; +import net.irisshaders.iris.pipeline.CubePositions; +import net.irisshaders.iris.uniforms.CapturedRenderingState; +import net.irisshaders.iris.uniforms.SystemTimeUniforms; +import net.irisshaders.iris.vertices.NormI8; +import net.irisshaders.iris.vertices.NormalHelper; +import net.irisshaders.iris.vertices.sodium.IrisEntityVertex; +import net.minecraft.core.Direction; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector2f; +import org.joml.Vector3f; +import org.joml.Vector4f; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(EntityRenderer.class) +public abstract class MixinEntityRenderer { + @Shadow + public static void prepareNormalsIfChanged(PoseStack.Pose matrices) { + } + + @Shadow + protected static void prepareVertices(PoseStack.Pose matrices, ModelCuboid cuboid) { + } + + @Shadow + @Final + private static int NUM_CUBE_FACES; + + @Shadow + @Final + private static int NUM_CUBE_VERTICES; + + @Shadow + @Final + private static Vector3f[][] VERTEX_POSITIONS_MIRRORED; + @Shadow + @Final + private static Vector3f[][] VERTEX_POSITIONS; + @Shadow + @Final + private static Vector2f[][] VERTEX_TEXTURES_MIRRORED; + @Shadow + @Final + private static Vector2f[][] VERTEX_TEXTURES; + @Shadow + @Final + private static int[] CUBE_NORMALS; + @Shadow + @Final + private static int[] CUBE_NORMALS_MIRRORED; + + @Unique + private static int[] CUBE_TANGENTS = new int[6]; + + @Unique + private static int[] CUBE_TANGENTS_MIRRORED = new int[6]; + + @Unique + private static final Vector2f[] MID_TEX_VALUES = new Vector2f[6]; + + static { + for (int i = 0; i < 6; i++) { + MID_TEX_VALUES[i] = new Vector2f(); + } + } + + private static final int + FACE_NEG_Y = 0, // DOWN + FACE_POS_Y = 1, // UP + FACE_NEG_X = 2, // WEST + FACE_NEG_Z = 3, // NORTH + FACE_POS_X = 4, // EAST + FACE_POS_Z = 5; // SOUTH + + + @Shadow + @Final + private static int[][] CUBE_VERTICES; + @Unique + private static final long SCRATCH_BUFFER_IRIS = MemoryUtil.nmemAlignedAlloc(64, NUM_CUBE_FACES * NUM_CUBE_VERTICES * IrisEntityVertex.STRIDE); + + @Inject(method = "renderCuboid", at = @At("HEAD"), cancellable = true) + private static void redirectToIris(PoseStack.Pose matrices, VertexBufferWriter writer, ModelCuboid cuboid, int light, int overlay, int color, CallbackInfo ci) { + if (Iris.isPackInUseQuick()) { + ci.cancel(); + renderCuboidIris(matrices, writer, cuboid, light, overlay, color); + } + } + private static void renderCuboidIris(PoseStack.Pose matrices, VertexBufferWriter writer, ModelCuboid cuboid, int light, int overlay, int color) { + prepareNormalsIfChanged(matrices); + + prepareVertices(matrices, cuboid); + prepareMidCoords(cuboid); + prepareTangentsIfChanged(matrices); + + var vertexCount = emitQuadsIris(cuboid, color, overlay, light); + + try (MemoryStack stack = MemoryStack.stackPush()) { + writer.push(stack, SCRATCH_BUFFER_IRIS, vertexCount, IrisEntityVertex.FORMAT); + } + } + + private static void prepareMidCoords(ModelCuboid cuboid) { + buildVertexMidTexCoord(MID_TEX_VALUES[FACE_NEG_Y], cuboid.u1, cuboid.v0, cuboid.u2, cuboid.v1); + buildVertexMidTexCoord(MID_TEX_VALUES[FACE_POS_Y], cuboid.u2, cuboid.v1, cuboid.u3, cuboid.v0); + buildVertexMidTexCoord(MID_TEX_VALUES[FACE_NEG_Z], cuboid.u1, cuboid.v1, cuboid.u2, cuboid.v2); + buildVertexMidTexCoord(MID_TEX_VALUES[FACE_POS_Z], cuboid.u4, cuboid.v1, cuboid.u5, cuboid.v2); + buildVertexMidTexCoord(MID_TEX_VALUES[FACE_NEG_X], cuboid.u2, cuboid.v1, cuboid.u4, cuboid.v2); + buildVertexMidTexCoord(MID_TEX_VALUES[FACE_POS_X], cuboid.u0, cuboid.v1, cuboid.u1, cuboid.v2); + } + + private static void buildVertexMidTexCoord(Vector2f midTexValue, float u1, float v1, float u2, float v2) { + midTexValue.set((u1 + u2) * 0.5f, (v1 + v2) * 0.5f); + } + + private static Matrix3f lastMatrix2 = new Matrix3f(); + + private static final Vector4f TANGENT_STORAGE = new Vector4f(); + + private static void prepareTangentsIfChanged(PoseStack.Pose matrices) { + if (!matrices.normal().equals(lastMatrix2)) { + lastMatrix2.set(matrices.normal()); + + for (int i = 0; i < 6; i++) { + CUBE_TANGENTS[i] = NormalHelper.computeTangent(TANGENT_STORAGE, NormI8.unpackX(CUBE_NORMALS[i]), NormI8.unpackY(CUBE_NORMALS[i]), NormI8.unpackZ(CUBE_NORMALS[i]), + VERTEX_POSITIONS[i][0].x, VERTEX_POSITIONS[i][0].y, VERTEX_POSITIONS[i][0].z, VERTEX_TEXTURES[i][0].x, VERTEX_TEXTURES[i][0].y, + VERTEX_POSITIONS[i][1].x, VERTEX_POSITIONS[i][1].y, VERTEX_POSITIONS[i][1].z, VERTEX_TEXTURES[i][1].x, VERTEX_TEXTURES[i][1].y, + VERTEX_POSITIONS[i][2].x, VERTEX_POSITIONS[i][2].y, VERTEX_POSITIONS[i][2].z, VERTEX_TEXTURES[i][2].x, VERTEX_TEXTURES[i][2].y); + } + + // When mirroring is used, the normals for EAST and WEST are swapped. + CUBE_TANGENTS_MIRRORED[FACE_NEG_Y] = CUBE_TANGENTS[FACE_NEG_Y]; + CUBE_TANGENTS_MIRRORED[FACE_POS_Y] = CUBE_TANGENTS[FACE_POS_Y]; + CUBE_TANGENTS_MIRRORED[FACE_NEG_Z] = CUBE_TANGENTS[FACE_NEG_Z]; + CUBE_TANGENTS_MIRRORED[FACE_POS_Z] = CUBE_TANGENTS[FACE_POS_Z]; + CUBE_TANGENTS_MIRRORED[FACE_POS_X] = CUBE_TANGENTS[FACE_NEG_X]; // mirrored + CUBE_TANGENTS_MIRRORED[FACE_NEG_X] = CUBE_TANGENTS[FACE_POS_X]; // mirrored + } + } + + private static int emitQuadsIris(ModelCuboid cuboid, int color, int overlay, int light) { + final var positions = cuboid.mirror ? VERTEX_POSITIONS_MIRRORED : VERTEX_POSITIONS; + final var textures = cuboid.mirror ? VERTEX_TEXTURES_MIRRORED : VERTEX_TEXTURES; + final var normals = cuboid.mirror ? CUBE_NORMALS_MIRRORED : CUBE_NORMALS; + final var tangents = cuboid.mirror ? CUBE_TANGENTS_MIRRORED : CUBE_TANGENTS; + + CubePositions velocity = ((ModelPartAccess) cuboid).getCubePosition(CapturedRenderingState.INSTANCE.getEntityRollingId()); + + var vertexCount = 0; + + long ptr = SCRATCH_BUFFER_IRIS; + + for (int quadIndex = 0; quadIndex < NUM_CUBE_FACES; quadIndex++) { + if (!cuboid.shouldDrawFace(quadIndex)) { + continue; + } + + float midU = MID_TEX_VALUES[quadIndex].x; + float midV = MID_TEX_VALUES[quadIndex].y; + + velocity.setAndUpdate(SystemTimeUniforms.COUNTER.getAsInt(), CUBE_VERTICES[quadIndex][0], positions[quadIndex][0].x, positions[quadIndex][0].y, positions[quadIndex][0].z); + emitIris(ptr, positions[quadIndex][0], color, textures[quadIndex][0], overlay, light, normals[quadIndex], tangents[quadIndex], velocity.velocityX[CUBE_VERTICES[quadIndex][0]], velocity.velocityY[CUBE_VERTICES[quadIndex][0]], velocity.velocityZ[CUBE_VERTICES[quadIndex][0]], midU, midV); + ptr += IrisEntityVertex.STRIDE; + + velocity.setAndUpdate(SystemTimeUniforms.COUNTER.getAsInt(), CUBE_VERTICES[quadIndex][1], positions[quadIndex][1].x, positions[quadIndex][1].y, positions[quadIndex][1].z); + emitIris(ptr, positions[quadIndex][1], color, textures[quadIndex][1], overlay, light, normals[quadIndex], tangents[quadIndex], velocity.velocityX[CUBE_VERTICES[quadIndex][1]], velocity.velocityY[CUBE_VERTICES[quadIndex][1]], velocity.velocityZ[CUBE_VERTICES[quadIndex][1]], midU, midV); + ptr += IrisEntityVertex.STRIDE; + + velocity.setAndUpdate(SystemTimeUniforms.COUNTER.getAsInt(), CUBE_VERTICES[quadIndex][2], positions[quadIndex][2].x, positions[quadIndex][2].y, positions[quadIndex][2].z); + emitIris(ptr, positions[quadIndex][2], color, textures[quadIndex][2], overlay, light, normals[quadIndex], tangents[quadIndex], velocity.velocityX[CUBE_VERTICES[quadIndex][2]], velocity.velocityY[CUBE_VERTICES[quadIndex][2]], velocity.velocityZ[CUBE_VERTICES[quadIndex][2]], midU, midV); + ptr += IrisEntityVertex.STRIDE; + + velocity.setAndUpdate(SystemTimeUniforms.COUNTER.getAsInt(), CUBE_VERTICES[quadIndex][3], positions[quadIndex][3].x, positions[quadIndex][3].y, positions[quadIndex][3].z); + emitIris(ptr, positions[quadIndex][3], color, textures[quadIndex][3], overlay, light, normals[quadIndex], tangents[quadIndex], velocity.velocityX[CUBE_VERTICES[quadIndex][3]], velocity.velocityY[CUBE_VERTICES[quadIndex][3]], velocity.velocityZ[CUBE_VERTICES[quadIndex][3]], midU, midV); + ptr += IrisEntityVertex.STRIDE; + + vertexCount += 4; + } + + return vertexCount; + } + + private static void emitIris(long ptr, Vector3f pos, int color, Vector2f tex, int overlay, int light, int normal, int tangent, + float prevX, float prevY, float prevZ, float midU, float midV) { + IrisEntityVertex.write(ptr, pos.x, pos.y, pos.z, prevX, prevY, prevZ, color, tex.x, tex.y, overlay, light, normal, tangent, midU, midV); + } +} diff --git a/common/src/main/java/net/irisshaders/iris/compat/sodium/mixin/MixinSingleQuadParticle.java b/common/src/main/java/net/irisshaders/iris/compat/sodium/mixin/MixinSingleQuadParticle.java new file mode 100644 index 0000000000..3d96ac5d5f --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/compat/sodium/mixin/MixinSingleQuadParticle.java @@ -0,0 +1,126 @@ +package net.irisshaders.iris.compat.sodium.mixin; + +import net.caffeinemc.mods.sodium.api.vertex.format.common.ParticleVertex; +import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.caffeinemc.mods.sodium.api.util.ColorABGR; +import net.irisshaders.iris.Iris; +import net.irisshaders.iris.pipeline.QuadPositions; +import net.irisshaders.iris.uniforms.SystemTimeUniforms; +import net.irisshaders.iris.vertices.sodium.IrisParticleVertex; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.particle.SingleQuadParticle; +import org.joml.Quaternionf; +import org.joml.Vector3f; +import org.lwjgl.system.MemoryStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(SingleQuadParticle.class) +public abstract class MixinSingleQuadParticle extends Particle { + @Shadow + public abstract float getQuadSize(float tickDelta); + + @Shadow + protected abstract float getU0(); + + @Shadow + protected abstract float getU1(); + + @Shadow + protected abstract float getV0(); + + @Shadow + protected abstract float getV1(); + + @Unique + private Vector3f transferVector = new Vector3f(); + + protected MixinSingleQuadParticle(ClientLevel level, double x, double y, double z) { + super(level, x, y, z); + } + + /** + * @reason Optimize function + * @author JellySquid + */ + @Overwrite + public void renderRotatedQuad(VertexConsumer vertexConsumer, Quaternionf quaternionf, float x, float y, float z, float tickDelta) { + float size = this.getQuadSize(tickDelta); + float minU = this.getU0(); + float maxU = this.getU1(); + float minV = this.getV0(); + float maxV = this.getV1(); + int light = this.getLightColor(tickDelta); + + var writer = VertexBufferWriter.of(vertexConsumer); + + int color = ColorABGR.pack(this.rCol, this.gCol, this.bCol, this.alpha); + + try (MemoryStack stack = MemoryStack.stackPush()) { + if (Iris.isPackInUseQuick()) { + long buffer = stack.nmalloc(4 * IrisParticleVertex.STRIDE); + long ptr = buffer; + + this.writeVertexIris(ptr, 0, quaternionf, x, y, z, 1.0F, -1.0F, size, maxU, maxV, color, light); + ptr += IrisParticleVertex.STRIDE; + + this.writeVertexIris(ptr, 1, quaternionf, x, y, z, 1.0F, 1.0F, size, maxU, minV, color, light); + ptr += IrisParticleVertex.STRIDE; + + this.writeVertexIris(ptr, 2, quaternionf, x, y, z, -1.0F, 1.0F, size, minU, minV, color, light); + ptr += IrisParticleVertex.STRIDE; + + this.writeVertexIris(ptr, 3, quaternionf, x, y, z, -1.0F, -1.0F, size, minU, maxV, color, light); + ptr += IrisParticleVertex.STRIDE; + + writer.push(stack, buffer, 4, IrisParticleVertex.FORMAT); + } else { + long buffer = stack.nmalloc(4 * ParticleVertex.STRIDE); + long ptr = buffer; + + this.writeVertex(ptr, quaternionf, x, y, z, 1.0F, -1.0F, size, maxU, maxV, color, light); + ptr += ParticleVertex.STRIDE; + + this.writeVertex(ptr, quaternionf, x, y, z, 1.0F, 1.0F, size, maxU, minV, color, light); + ptr += ParticleVertex.STRIDE; + + this.writeVertex(ptr, quaternionf, x, y, z, -1.0F, 1.0F, size, minU, minV, color, light); + ptr += ParticleVertex.STRIDE; + + this.writeVertex(ptr, quaternionf, x, y, z, -1.0F, -1.0F, size, minU, maxV, color, light); + ptr += ParticleVertex.STRIDE; + + writer.push(stack, buffer, 4, ParticleVertex.FORMAT); + } + } + } + + @Unique + private QuadPositions quadPositions = new QuadPositions(); + + @Unique + private void writeVertex(long ptr, Quaternionf quaternionf, float originX, float originY, float originZ, float posX, float posY, float size, float u, float v, int color, int light) { + transferVector.set(posX, posY, 0.0f); + transferVector.rotate(quaternionf); + transferVector.mul(size); + transferVector.add(originX, originY, originZ); + + ParticleVertex.put(ptr, transferVector.x(), transferVector.y(), transferVector.z(), u, v, color, light); + } + + @Unique + private void writeVertexIris(long ptr, int index, Quaternionf quaternionf, float originX, float originY, float originZ, float posX, float posY, float size, float u, float v, int color, int light) { + transferVector.set(posX, posY, 0.0f); + transferVector.rotate(quaternionf); + transferVector.mul(size); + transferVector.add(originX, originY, originZ); + + quadPositions.setAndUpdate(SystemTimeUniforms.COUNTER.getAsInt(), index, transferVector.x, transferVector.y, transferVector.z); + + IrisParticleVertex.put(ptr, transferVector.x(), transferVector.y(), transferVector.z(), quadPositions.velocityX[index], quadPositions.velocityY[index], quadPositions.velocityZ[index], u, v, color, light); + } +} diff --git a/common/src/main/java/net/irisshaders/iris/gl/shader/ProgramCreator.java b/common/src/main/java/net/irisshaders/iris/gl/shader/ProgramCreator.java index d86989103c..d8f958cfd0 100644 --- a/common/src/main/java/net/irisshaders/iris/gl/shader/ProgramCreator.java +++ b/common/src/main/java/net/irisshaders/iris/gl/shader/ProgramCreator.java @@ -21,6 +21,7 @@ public static int create(String name, GlShader... shaders) { GlStateManager._glBindAttribLocation(program, 12, "mc_midTexCoord"); GlStateManager._glBindAttribLocation(program, 13, "at_tangent"); GlStateManager._glBindAttribLocation(program, 14, "at_midBlock"); + GlStateManager._glBindAttribLocation(program, 15, "at_velocity"); GlStateManager._glBindAttribLocation(program, 0, "Position"); GlStateManager._glBindAttribLocation(program, 1, "UV0"); diff --git a/common/src/main/java/net/irisshaders/iris/helpers/Float16.java b/common/src/main/java/net/irisshaders/iris/helpers/Float16.java new file mode 100644 index 0000000000..0471d6646d --- /dev/null +++ b/common/src/main/java/net/irisshaders/iris/helpers/Float16.java @@ -0,0 +1,256 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.irisshaders.iris.helpers; + +/** + * Lifted from Apache Parquet MR project: + * https://github.com/apache/parquet-mr/blob/e87b80308869b77f914fcfd04364686e11158950/parquet-column/src/main/java/org/apache/parquet/schema/Float16.java + * + *
The format is laid out as follows: + * + *
+ * 1 11111 1111111111 + * ^ --^-- -----^---- + * sign | |_______ significand + * | + * -- exponent + *+ * + * Half-precision floating points can be useful to save memory and/or bandwidth at the expense of + * range and precision when compared to single-precision floating points (float32). Ref: + * https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/libcore/util/FP16.java + */ +public class Float16 { + // Positive infinity of type half-precision float. + public static final short POSITIVE_INFINITY = (short) 0x7c00; + // A Not-a-Number representation of a half-precision float. + public static final short NaN = (short) 0x7e00; + // The bitmask to and a number with to obtain the sign bit. + private static final int SIGN_MASK = 0x8000; + // The offset to shift by to obtain the exponent bits. + private static final int EXPONENT_SHIFT = 10; + // The bitmask to and a number shifted by EXPONENT_SHIFT right, to obtain exponent bits. + private static final int SHIFTED_EXPONENT_MASK = 0x1f; + // The bitmask to and a number with to obtain significand bits. + private static final int SIGNIFICAND_MASK = 0x3ff; + // The offset of the exponent from the actual value. + private static final int EXPONENT_BIAS = 15; + // The offset to shift by to obtain the sign bit. + private static final int SIGN_SHIFT = 15; + // The bitmask to AND with to obtain exponent and significand bits. + private static final int EXPONENT_SIGNIFICAND_MASK = 0x7fff; + + private static final int FP32_SIGN_SHIFT = 31; + private static final int FP32_EXPONENT_SHIFT = 23; + private static final int FP32_SHIFTED_EXPONENT_MASK = 0xff; + private static final int FP32_SIGNIFICAND_MASK = 0x7fffff; + private static final int FP32_EXPONENT_BIAS = 127; + private static final int FP32_QNAN_MASK = 0x400000; + private static final int FP32_DENORMAL_MAGIC = 126 << 23; + private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC); + + /** + * Returns true if the specified half-precision float value represents a Not-a-Number, false + * otherwise. + * + * @param h A half-precision float value + * @return True if the value is a NaN, false otherwise + */ + public static boolean isNaN(short h) { + return (h & EXPONENT_SIGNIFICAND_MASK) > POSITIVE_INFINITY; + } + + /** + * Compares the two specified half-precision float values. The following conditions apply during + * the comparison: + * + *
If the input is NaN, the returned value is NaN. If the input is Float POSITIVE_INFINITY or
+ * Float NEGATIVE_INFINITY, the returned value is respectively POSITIVE_INFINITY or
+ * NEGATIVE_INFINITY. If the input is 0 (positive or negative), the returned value is
+ * POSITIVE_ZERO or NEGATIVE_ZERO. If the input is a less than MIN_VALUE, the returned value is
+ * flushed to POSITIVE_ZERO or NEGATIVE_ZERO. If the input is a less than MIN_NORMAL, the returned
+ * value is a denorm half-precision float. Otherwise, the returned value is rounded to the nearest
+ * representable half-precision float value.
+ *
+ * @param f The single-precision float value to convert to half-precision
+ * @return A half-precision float value
+ */
+ public static short toFloat16(float f) {
+ int bits = Float.floatToRawIntBits(f);
+ int s = (bits >>> FP32_SIGN_SHIFT);
+ int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_SHIFTED_EXPONENT_MASK;
+ int m = bits & FP32_SIGNIFICAND_MASK;
+ int outE = 0;
+ int outM = 0;
+ if (e == 0xff) { // Infinite or NaN
+ outE = 0x1f;
+ outM = m != 0 ? 0x200 : 0;
+ } else {
+ e = e - FP32_EXPONENT_BIAS + EXPONENT_BIAS;
+ if (e >= 0x1f) { // Overflow
+ outE = 0x1f;
+ } else if (e <= 0) { // Underflow
+ if (e < -10) {
+ // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
+ } else {
+ // The fp32 value is a normalized float less than MIN_NORMAL,
+ // we convert to a denorm fp16
+ m = m | 0x800000;
+ int shift = 14 - e;
+ outM = m >> shift;
+ int lowm = m & ((1 << shift) - 1);
+ int hway = 1 << (shift - 1);
+ // if above halfway or exactly halfway and outM is odd
+ if (lowm + (outM & 1) > hway) {
+ // Round to nearest even
+ // Can overflow into exponent bit, which surprisingly is OK.
+ // This increment relies on the +outM in the return statement below
+ outM++;
+ }
+ }
+ } else {
+ outE = e;
+ outM = m >> 13;
+ // if above halfway or exactly halfway and outM is odd
+ if ((m & 0x1fff) + (outM & 0x1) > 0x1000) {
+ // Round to nearest even
+ // Can overflow into exponent bit, which surprisingly is OK.
+ // This increment relies on the +outM in the return statement below
+ outM++;
+ }
+ }
+ }
+ // The outM is added here as the +1 increments for outM above can
+ // cause an overflow in the exponent bit which is OK.
+ return (short) ((s << SIGN_SHIFT) | (outE << EXPONENT_SHIFT) + outM);
+ }
+
+ /**
+ * Returns a string representation of the specified half-precision float value. Calling this
+ * method is equivalent to calling Float.toString(toFloat(h))
. See {@link
+ * Float#toString(float)} for more information on the format of the string representation.
+ *
+ * @param h A half-precision float value in binary little-endian format
+ * @return A string representation of the specified value
+ */
+ public static String toFloatString(short h) {
+ return Float.toString(Float16.toFloat(h));
+ }
+}
diff --git a/common/src/main/java/net/irisshaders/iris/mixin/MixinBakedQuad.java b/common/src/main/java/net/irisshaders/iris/mixin/MixinBakedQuad.java
new file mode 100644
index 0000000000..420578989b
--- /dev/null
+++ b/common/src/main/java/net/irisshaders/iris/mixin/MixinBakedQuad.java
@@ -0,0 +1,38 @@
+package net.irisshaders.iris.mixin;
+
+import net.irisshaders.iris.mixinterface.QuadPositionAccess;
+import net.irisshaders.iris.pathways.EntityIdStorage;
+import net.irisshaders.iris.pipeline.QuadPositions;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+
+@Mixin(BakedQuad.class)
+public class MixinBakedQuad implements QuadPositionAccess {
+ private static final QuadPositions INVALID_VALUE = new QuadPositions();
+ @Unique
+ private QuadPositions[] quadPositions = null;
+
+ @Override
+ public QuadPositions getQuadPosition(int entityId) {
+ if (entityId == -1) return INVALID_VALUE;
+
+ if (quadPositions == null) {
+ quadPositions = new QuadPositions[EntityIdStorage.ENTITY_ID_STORAGE.size()];
+ }
+
+ if (entityId >= quadPositions.length) {
+ QuadPositions[] newArray = new QuadPositions[EntityIdStorage.ENTITY_ID_STORAGE.size()];
+
+ System.arraycopy(quadPositions, 0, newArray, 0, quadPositions.length);
+
+ this.quadPositions = newArray;
+ }
+
+ if (quadPositions[entityId] == null) {
+ quadPositions[entityId] = new QuadPositions();
+ }
+
+ return quadPositions[entityId];
+ }
+}
diff --git a/common/src/main/java/net/irisshaders/iris/mixin/MixinEntity.java b/common/src/main/java/net/irisshaders/iris/mixin/MixinEntity.java
new file mode 100644
index 0000000000..b29cab05d6
--- /dev/null
+++ b/common/src/main/java/net/irisshaders/iris/mixin/MixinEntity.java
@@ -0,0 +1,41 @@
+package net.irisshaders.iris.mixin;
+
+import net.irisshaders.iris.mixinterface.EntityUniqueId;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+import java.util.BitSet;
+
+import static net.irisshaders.iris.pathways.EntityIdStorage.ENTITY_ID_STORAGE;
+
+@Mixin(Entity.class)
+public abstract class MixinEntity implements EntityUniqueId {
+ @Unique
+ private int rollingId = -1;
+
+ @Override
+ public int iris$getRollingId() {
+ if (rollingId == -1) {
+ int newId = ENTITY_ID_STORAGE.nextClearBit(0);
+ rollingId = newId;
+ ENTITY_ID_STORAGE.set(newId);
+ }
+
+ return rollingId;
+ }
+
+ @Inject(method = "setRemoved", at = @At("HEAD"))
+ private void iris$clearId(Entity.RemovalReason removalReason, CallbackInfo ci) {
+ if (rollingId == -1) return;
+
+ ENTITY_ID_STORAGE.clear(rollingId);
+ rollingId = -1;
+ }
+}
diff --git a/common/src/main/java/net/irisshaders/iris/mixin/MixinModelCuboid.java b/common/src/main/java/net/irisshaders/iris/mixin/MixinModelCuboid.java
new file mode 100644
index 0000000000..45063686e2
--- /dev/null
+++ b/common/src/main/java/net/irisshaders/iris/mixin/MixinModelCuboid.java
@@ -0,0 +1,34 @@
+package net.irisshaders.iris.mixin;
+
+import net.caffeinemc.mods.sodium.client.render.immediate.model.ModelCuboid;
+import net.irisshaders.iris.mixinterface.ModelPartAccess;
+import net.irisshaders.iris.pathways.EntityIdStorage;
+import net.irisshaders.iris.pipeline.CubePositions;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+
+@Mixin(ModelCuboid.class)
+public class MixinModelCuboid implements ModelPartAccess {
+ private static final CubePositions INVALID_VALUE = new CubePositions();
+ @Unique
+ private CubePositions[] cubePositions = new CubePositions[EntityIdStorage.ENTITY_ID_STORAGE.size()];
+
+ @Override
+ public CubePositions getCubePosition(int entityId) {
+ if (entityId == -1) return INVALID_VALUE;
+
+ if (entityId >= cubePositions.length) {
+ CubePositions[] newArray = new CubePositions[EntityIdStorage.ENTITY_ID_STORAGE.size()];
+
+ System.arraycopy(cubePositions, 0, newArray, 0, cubePositions.length);
+
+ this.cubePositions = newArray;
+ }
+
+ if (cubePositions[entityId] == null) {
+ cubePositions[entityId] = new CubePositions();
+ }
+
+ return cubePositions[entityId];
+ }
+}
diff --git a/common/src/main/java/net/irisshaders/iris/mixin/MixinVertexFormatType.java b/common/src/main/java/net/irisshaders/iris/mixin/MixinVertexFormatType.java
new file mode 100644
index 0000000000..33d9950c79
--- /dev/null
+++ b/common/src/main/java/net/irisshaders/iris/mixin/MixinVertexFormatType.java
@@ -0,0 +1,31 @@
+package net.irisshaders.iris.mixin;
+
+import com.mojang.blaze3d.shaders.Program;
+import com.mojang.blaze3d.vertex.VertexFormatElement;
+import net.irisshaders.iris.gl.program.IrisProgramTypes;
+import net.irisshaders.iris.vertices.IrisFormatTypes;
+import org.apache.commons.lang3.ArrayUtils;
+import org.lwjgl.opengl.GL32C;
+import org.lwjgl.opengl.GL42C;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.Shadow;
+
+@Mixin(VertexFormatElement.Type.class)
+public class MixinVertexFormatType {
+ @SuppressWarnings("target")
+ @Shadow
+ @Final
+ @Mutable
+ private static VertexFormatElement.Type[] $VALUES;
+
+ static {
+ int baseOrdinal = $VALUES.length;
+
+ IrisFormatTypes.HALF_FLOAT
+ = VertexTypeAccessor.createFormatType("HALF_FLOAT", baseOrdinal, 2, "Half Float", GL32C.GL_HALF_FLOAT);
+
+ $VALUES = ArrayUtils.addAll($VALUES, IrisFormatTypes.HALF_FLOAT);
+ }
+}
diff --git a/common/src/main/java/net/irisshaders/iris/mixin/VertexTypeAccessor.java b/common/src/main/java/net/irisshaders/iris/mixin/VertexTypeAccessor.java
new file mode 100644
index 0000000000..16c788e85c
--- /dev/null
+++ b/common/src/main/java/net/irisshaders/iris/mixin/VertexTypeAccessor.java
@@ -0,0 +1,14 @@
+package net.irisshaders.iris.mixin;
+
+import com.mojang.blaze3d.shaders.Program;
+import com.mojang.blaze3d.vertex.VertexFormatElement;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Invoker;
+
+@Mixin(VertexFormatElement.Type.class)
+public interface VertexTypeAccessor {
+ @Invoker(value = "