diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/ColorHelper.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/ColorHelper.java index fa995db10..8ad80e2ef 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/ColorHelper.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/ColorHelper.java @@ -20,12 +20,6 @@ import java.nio.ByteOrder; import it.unimi.dsi.fastutil.ints.Int2IntFunction; -import net.minecraft.client.render.model.BakedQuadFactory; -import net.minecraft.client.util.math.Vector3f; -import net.minecraft.util.math.Direction; - -import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; - /** * Static routines of general utility for renderer implementations. * Renderers are not required to use these helpers, but they were @@ -34,19 +28,6 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; public abstract class ColorHelper { private ColorHelper() { } - /** - * Implement on quads to use methods that require it. - * Allows for much cleaner method signatures. - */ - public interface ShadeableQuad extends MutableQuadView { - boolean isFaceAligned(); - - boolean needsDiffuseShading(int textureIndex); - } - - /** Same as vanilla values. */ - private static final float[] FACE_SHADE_FACTORS = { 0.5F, 1.0F, 0.8F, 0.8F, 0.6F, 0.6F }; - private static final Int2IntFunction colorSwapper = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? color -> ((color & 0xFF00FF00) | ((color & 0x00FF0000) >> 16) | ((color & 0xFF) << 16)) : color -> color; /** @@ -64,109 +45,24 @@ public abstract class ColorHelper { return color1; } - int alpha = ((color1 >> 24) & 0xFF) * ((color2 >> 24) & 0xFF) / 0xFF; - int red = ((color1 >> 16) & 0xFF) * ((color2 >> 16) & 0xFF) / 0xFF; - int green = ((color1 >> 8) & 0xFF) * ((color2 >> 8) & 0xFF) / 0xFF; - int blue = (color1 & 0xFF) * (color2 & 0xFF) / 0xFF; + final int alpha = ((color1 >> 24) & 0xFF) * ((color2 >> 24) & 0xFF) / 0xFF; + final int red = ((color1 >> 16) & 0xFF) * ((color2 >> 16) & 0xFF) / 0xFF; + final int green = ((color1 >> 8) & 0xFF) * ((color2 >> 8) & 0xFF) / 0xFF; + final int blue = (color1 & 0xFF) * (color2 & 0xFF) / 0xFF; return (alpha << 24) | (red << 16) | (green << 8) | blue; } /** Multiplies three lowest components by shade. High byte (usually alpha) unchanged. */ public static int multiplyRGB(int color, float shade) { - int alpha = ((color >> 24) & 0xFF); - int red = (int) (((color >> 16) & 0xFF) * shade); - int green = (int) (((color >> 8) & 0xFF) * shade); - int blue = (int) ((color & 0xFF) * shade); + final int alpha = ((color >> 24) & 0xFF); + final int red = (int) (((color >> 16) & 0xFF) * shade); + final int green = (int) (((color >> 8) & 0xFF) * shade); + final int blue = (int) ((color & 0xFF) * shade); return (alpha << 24) | (red << 16) | (green << 8) | blue; } - /** - * Same results as {@link BakedQuadFactory#method_3456(Direction)}. - */ - public static float diffuseShade(Direction direction) { - return FACE_SHADE_FACTORS[direction.getId()]; - } - - /** - * Formula mimics vanilla lighting for plane-aligned quads and - * is vaguely consistent with Phong lighting ambient + diffuse for others. - */ - public static float normalShade(float normalX, float normalY, float normalZ) { - return Math.min(0.5f + Math.abs(normalX) * 0.1f + (normalY > 0 ? 0.5f * normalY : 0) + Math.abs(normalZ) * 0.3f, 1f); - } - - public static float normalShade(Vector3f normal) { - return normalShade(normal.getX(), normal.getY(), normal.getZ()); - } - - /** - * @see #diffuseShade - */ - public static float vertexShade(ShadeableQuad q, int vertexIndex, float faceShade) { - return q.hasNormal(vertexIndex) ? normalShade(q.normalX(vertexIndex), q.normalY(vertexIndex), q.normalZ(vertexIndex)) : faceShade; - } - - /** - * Returns {@link #diffuseShade(Direction)} if quad is aligned to light face, - * otherwise uses face normal and {@link #normalShade}. - */ - public static float faceShade(ShadeableQuad quad) { - return quad.isFaceAligned() ? diffuseShade(quad.lightFace()) : normalShade(quad.faceNormal()); - } - - @FunctionalInterface - private interface VertexLighter { - void shade(ShadeableQuad quad, int vertexIndex, float shade); - } - - private static VertexLighter[] VERTEX_LIGHTERS = new VertexLighter[8]; - - static { - VERTEX_LIGHTERS[0b000] = (q, i, s) -> { }; - VERTEX_LIGHTERS[0b001] = (q, i, s) -> q.spriteColor(i, 0, multiplyRGB(q.spriteColor(i, 0), s)); - VERTEX_LIGHTERS[0b010] = (q, i, s) -> q.spriteColor(i, 1, multiplyRGB(q.spriteColor(i, 1), s)); - VERTEX_LIGHTERS[0b011] = (q, i, s) -> q.spriteColor(i, 0, multiplyRGB(q.spriteColor(i, 0), s)).spriteColor(i, 1, multiplyRGB(q.spriteColor(i, 1), s)); - VERTEX_LIGHTERS[0b100] = (q, i, s) -> q.spriteColor(i, 2, multiplyRGB(q.spriteColor(i, 2), s)); - VERTEX_LIGHTERS[0b101] = (q, i, s) -> q.spriteColor(i, 0, multiplyRGB(q.spriteColor(i, 0), s)).spriteColor(i, 2, multiplyRGB(q.spriteColor(i, 2), s)); - VERTEX_LIGHTERS[0b110] = (q, i, s) -> q.spriteColor(i, 1, multiplyRGB(q.spriteColor(i, 1), s)).spriteColor(i, 2, multiplyRGB(q.spriteColor(i, 2), s)); - VERTEX_LIGHTERS[0b111] = (q, i, s) -> q.spriteColor(i, 0, multiplyRGB(q.spriteColor(i, 0), s)).spriteColor(i, 1, multiplyRGB(q.spriteColor(i, 1), s)).spriteColor(i, 2, multiplyRGB(q.spriteColor(i, 2), s)); - } - - /** - * Honors vertex normals and uses non-cubic face normals for non-cubic quads. - * - * @param quad Quad to be shaded/unshaded. - * - * @param undo If true, will reverse prior application. Does not check that - * prior application actually happened. Use to "unbake" a quad. - * Some drift of colors may occur due to floating-point precision error. - */ - public static void applyDiffuseShading(ShadeableQuad quad, boolean undo) { - final float faceShade = faceShade(quad); - int i = quad.needsDiffuseShading(0) ? 1 : 0; - - if (quad.needsDiffuseShading(1)) { - i |= 2; - } - - if (quad.needsDiffuseShading(2)) { - i |= 4; - } - - if (i == 0) { - return; - } - - final VertexLighter shader = VERTEX_LIGHTERS[i]; - - for (int j = 0; j < 4; j++) { - final float vertexShade = vertexShade(quad, j, faceShade); - shader.shade(quad, j, undo ? 1f / vertexShade : vertexShade); - } - } - /** * Component-wise max. */ diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MeshBuilderImpl.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MeshBuilderImpl.java index 6ac1a70c2..92db2fea5 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MeshBuilderImpl.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MeshBuilderImpl.java @@ -19,7 +19,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; -import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper; import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper; /** @@ -38,7 +37,7 @@ public class MeshBuilderImpl implements MeshBuilder { protected void ensureCapacity(int stride) { if (stride > limit - index) { limit *= 2; - int[] bigger = new int[limit]; + final int[] bigger = new int[limit]; System.arraycopy(data, 0, bigger, 0, index); data = bigger; maker.data = bigger; @@ -47,7 +46,7 @@ public class MeshBuilderImpl implements MeshBuilder { @Override public Mesh build() { - int[] packed = new int[index]; + final int[] packed = new int[index]; System.arraycopy(data, 0, packed, 0, index); index = 0; maker.begin(data, index); @@ -76,7 +75,6 @@ public class MeshBuilderImpl implements MeshBuilder { geometryFlags(GeometryHelper.computeShapeFlags(this)); } - ColorHelper.applyDiffuseShading(this, false); index += EncodingFormat.TOTAL_STRIDE; ensureCapacity(EncodingFormat.TOTAL_STRIDE); baseIndex = index; diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MutableQuadViewImpl.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MutableQuadViewImpl.java index 71b1a15f3..2d5dd6db9 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MutableQuadViewImpl.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MutableQuadViewImpl.java @@ -38,16 +38,14 @@ import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer; import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl.Value; -import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper; import net.fabricmc.fabric.impl.client.indigo.renderer.helper.NormalHelper; import net.fabricmc.fabric.impl.client.indigo.renderer.helper.TextureHelper; -import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper.ShadeableQuad; /** * Almost-concrete implementation of a mutable quad. The only missing part is {@link #emit()}, * because that depends on where/how it is used. (Mesh encoding vs. render-time transformation). */ -public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEmitter, ShadeableQuad { +public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEmitter { public final void begin(int[] data, int baseIndex) { this.data = data; this.baseIndex = baseIndex; @@ -115,16 +113,6 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm return this; } - @Override - public boolean isFaceAligned() { - return (geometryFlags() & GeometryHelper.AXIS_ALIGNED_FLAG) != 0; - } - - @Override - public boolean needsDiffuseShading(int textureIndex) { - return textureIndex == 0 && !material().disableDiffuse(textureIndex); - } - @Override public MutableQuadViewImpl pos(int vertexIndex, float x, float y, float z) { final int index = baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_X; diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/QuadViewImpl.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/QuadViewImpl.java index 644fd4259..d964b0d22 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/QuadViewImpl.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/QuadViewImpl.java @@ -170,7 +170,7 @@ public class QuadViewImpl implements QuadView { @Override public void copyTo(MutableQuadView target) { - MutableQuadViewImpl quad = (MutableQuadViewImpl) target; + final MutableQuadViewImpl quad = (MutableQuadViewImpl) target; // copy everything except the header/material System.arraycopy(data, baseIndex + 1, quad.data, quad.baseIndex + 1, EncodingFormat.TOTAL_STRIDE - 1); quad.isFaceNormalInvalid = this.isFaceNormalInvalid; @@ -288,7 +288,7 @@ public class QuadViewImpl implements QuadView { } public boolean hasShade() { - return shade; + return shade && !material().disableDiffuse(0); } public void shade(boolean shade) { diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractMeshConsumer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractMeshConsumer.java index b2362d320..ba94aae49 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractMeshConsumer.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractMeshConsumer.java @@ -29,7 +29,6 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform; import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer; import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl; import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator; -import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper; import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper; import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat; import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshImpl; @@ -58,7 +57,6 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen @Override public Maker emit() { lightFace(GeometryHelper.lightFace(this)); - ColorHelper.applyDiffuseShading(this, false); renderQuad(this); clear(); return this; @@ -69,7 +67,7 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen @Override public void accept(Mesh mesh) { - MeshImpl m = (MeshImpl) mesh; + final MeshImpl m = (MeshImpl) mesh; final int[] data = m.data(); final int limit = data.length; int index = 0; diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractQuadRenderer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractQuadRenderer.java index 69b43af8e..4eed0b559 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractQuadRenderer.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractQuadRenderer.java @@ -25,14 +25,16 @@ import net.minecraft.block.BlockState; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.WorldRenderer; -import net.minecraft.util.math.Matrix4f; import net.minecraft.client.util.math.Vector3f; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import net.minecraft.util.math.Matrix3f; +import net.minecraft.util.math.Matrix4f; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform; import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator; import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper; +import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper; import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl; /** @@ -139,7 +141,9 @@ public abstract class AbstractQuadRenderer { /** for non-emissive mesh quads and all fallback quads with flat lighting. */ protected void tesselateFlat(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) { colorizeQuad(quad, blockColorIndex); - final int brightness = ColorHelper.multiplyRGB(flatBrightness(quad, blockInfo.blockState, blockInfo.blockPos), blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade())); + shadeFlatQuad(quad); + + final int brightness = flatBrightness(quad, blockInfo.blockState, blockInfo.blockPos); for (int i = 0; i < 4; i++) { quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), brightness)); @@ -151,6 +155,7 @@ public abstract class AbstractQuadRenderer { /** for emissive mesh quads with flat lighting. */ protected void tesselateFlatEmissive(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) { colorizeQuad(quad, blockColorIndex); + shadeFlatQuad(quad); for (int i = 0; i < 4; i++) { quad.lightmap(i, FULL_BRIGHTNESS); @@ -175,4 +180,68 @@ public abstract class AbstractQuadRenderer { // Unfortunately cannot use brightness cache here unless we implement one specifically for flat lighting. See #329 return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockState, mpos); } + + /** + * Starting in 1.16 flat shading uses dimension-specific diffuse factors that can be < 1.0 + * even for un-shaded quads. These are also applied with AO shading but that is done in AO calculator. + */ + private void shadeFlatQuad(MutableQuadViewImpl quad) { + if ((quad.geometryFlags() & GeometryHelper.AXIS_ALIGNED_FLAG) == 0 || quad.hasVertexNormals()) { + // Quads that aren't direction-aligned or that have vertex normals need to be shaded + // using interpolation - vanilla can't handle them. Generally only applies to modded models. + final float faceShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade()); + + for (int i = 0; i < 4; i++) { + quad.spriteColor(i, 0, ColorHelper.multiplyRGB(quad.spriteColor(i, 0), vertexShade(quad, i, faceShade))); + } + } else { + final float diffuseShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade()); + + if (diffuseShade != 1.0f) { + for (int i = 0; i < 4; i++) { + quad.spriteColor(i, 0, ColorHelper.multiplyRGB(quad.spriteColor(i, 0), diffuseShade)); + } + } + } + } + + private float vertexShade(MutableQuadViewImpl quad, int vertexIndex, float faceShade) { + return quad.hasNormal(vertexIndex) ? normalShade(quad.normalX(vertexIndex), quad.normalY(vertexIndex), quad.normalZ(vertexIndex), quad.hasShade()) : faceShade; + } + + /** + * Finds mean of per-face shading factors weighted by normal components. + * Not how light actually works but the vanilla diffuse shading model is a hack to start with + * and this gives reasonable results for non-cubic surfaces in a vanilla-style renderer. + */ + private float normalShade(float normalX, float normalY, float normalZ, boolean hasShade) { + float sum = 0; + float div = 0; + + if (normalX > 0) { + sum += normalX * blockInfo.blockView.getBrightness(Direction.EAST, hasShade); + div += normalX; + } else if (normalX < 0) { + sum += -normalX * blockInfo.blockView.getBrightness(Direction.WEST, hasShade); + div -= normalX; + } + + if (normalY > 0) { + sum += normalY * blockInfo.blockView.getBrightness(Direction.UP, hasShade); + div += normalY; + } else if (normalY < 0) { + sum += -normalY * blockInfo.blockView.getBrightness(Direction.DOWN, hasShade); + div -= normalY; + } + + if (normalZ > 0) { + sum += normalZ * blockInfo.blockView.getBrightness(Direction.SOUTH, hasShade); + div += normalZ; + } else if (normalZ < 0) { + sum += -normalZ * blockInfo.blockView.getBrightness(Direction.NORTH, hasShade); + div -= normalZ; + } + + return sum / div; + } } diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java index 77771d2fa..e8d0ed177 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java @@ -33,11 +33,11 @@ import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.render.model.json.ModelTransformation; import net.minecraft.client.render.model.json.ModelTransformation.Mode; -import net.minecraft.util.math.Matrix4f; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.Vector3f; import net.minecraft.item.ItemStack; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Matrix4f; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; @@ -144,7 +144,6 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo @Override public Maker emit() { lightFace(GeometryHelper.lightFace(this)); - ColorHelper.applyDiffuseShading(this, false); renderQuad(); clear(); return this;