I was able to create a Fabric mod that fixes this issue by changing Minecraft's code. Hopefully the way I resolved this issue and the following insights will be useful.
The issue was mostly fixed after the following change in com.mojang.blaze3d.vertex.PoseStack
.
public void scale(float f, float g, float h) {
PoseStack.Pose pose = (PoseStack.Pose)this.poseStack.getLast();
pose.pose.scale(f, g, h);
if (f == g && g == h) {
if (f > 0.0F) {
return;
}
pose.normal.scale(-1.0F);
}
float i = 1.0F / f;
float j = 1.0F / g;
float k = 1.0F / h;
// Commented vanilla code
// float l = Mth.fastInvCubeRoot(i * j * k);
// pose.normal.scale(l * i, l * j, l * k);
// New code
pose.normal.scale(i, j, k);
}
However, after this change, the top of nose got darker the more the nose was scaled up. This is because Minecraft does not normalize the normal vector after it is transformed by the normal matrix. When the fragment shader uses the vertex normal vector to calculate lighting, it is not unit length, which results in dark lighting. Normalizing the vector after it is transformed in net.minecraft.client.model.geom.ModelPart$Cube#compile
fixes this issue, and thus completely resolves the initial issue.
public void compile(PoseStack.Pose pose, VertexConsumer vertexConsumer, int i, int j, float f, float g, float h, float k) {
Matrix4f matrix4f = pose.pose();
Matrix3f matrix3f = pose.normal();
for (ModelPart.Polygon polygon : this.polygons) {
Vector3f vector3f = matrix3f.transform(new Vector3f((Vector3fc)polygon.normal));
// Added code: normalize the vector
vector3f.normalize();
float l = vector3f.x();
float m = vector3f.y();
float n = vector3f.z();
for (ModelPart.Vertex vertex : polygon.vertices) {
float o = vertex.pos.x() / 16.0F;
float p = vertex.pos.y() / 16.0F;
float q = vertex.pos.z() / 16.0F;
Vector4f vector4f = matrix4f.transform(new Vector4f(o, p, q, 1.0F));
vertexConsumer.vertex(vector4f.x(), vector4f.y(), vector4f.z(), f, g, h, k, vertex.u, vertex.v, j, i, l, m, n);
}
}
}
MC-176864 is caused by a series of model matrix transformations reversing the vertex order for all item model quads. Minecraft renders items (that are not translucent block items) with culling enabled, meaning that quads only render on the side from which the vertex order is counter-clockwise. Since the order is reversed in the opposite hand, all quads render from the wrong side.
From my testing, however, it does seem like the lighting bugs described in that issue are directly related to this issue. The lighting was fixed once my suggested changes were applied.
In vanilla, the conditions for invalid values are usually met only when the model view PoseStack is scaled, but this stack's normal matrix does not matter since it is not uploaded to the shader or used in any other way.
The only places where the invalid values are actually used are in SignEditScreen#render, LoomScreen#renderBg, LoomScreen#renderPattern, and GameRenderer#renderItemActivationAnimation. GameRenderer#renderItemActivationAnimation is the only one that produces a visual effect. Right after being revived by a totem of undying, the totem in the activation animation will have lighting that does not transition smoothly as it spins. Applying my suggested changes fixes this.
Quads from block and item models always go through
VertexConsumer#putBulkData
. However,light_emission
is only applied right after flat lighting and smooth lighting during block model buffering and ignored during item model buffering. Considering that this feature would have been easier to implement by applying the light value inside ofVertexConsumer#putBulkData
instead, the fact that items do not supportlight_emission
seems intentional to me. However, I agree that items should support it.