mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-22 15:47:57 -05:00
Various Indigo lighting fixes (#640)
* Various lighting fixes * 20w22a * Various lighting fixes Co-authored-by: modmuss50 <modmuss50@gmail.com>
This commit is contained in:
parent
1aa9d47288
commit
021baa0119
7 changed files with 86 additions and 138 deletions
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue