Fix Indigo handling of sculk sensor AO ()

* Fix Indigo handling of sculk sensor AO. Fixes 

* Split offset and mean formula lighting config options
This commit is contained in:
Technici4n 2023-07-18 13:54:39 +02:00 committed by GitHub
parent e91849a835
commit 9172968c53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 17 deletions
fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo

View file

@ -40,6 +40,12 @@ public class Indigo implements ClientModInitializer {
/** Set true in dev env to confirm results match vanilla when they should. */
public static final boolean DEBUG_COMPARE_LIGHTING;
public static final boolean FIX_SMOOTH_LIGHTING_OFFSET;
public static final boolean FIX_MEAN_LIGHT_CALCULATION;
/**
* Same value as {@link #FIX_MEAN_LIGHT_CALCULATION} because it is only required when the mean formula is changed,
* but different field to clearly separate code paths where we change emissive handling.
*/
public static final boolean FIX_EMISSIVE_LIGHTING;
public static final boolean FIX_EXTERIOR_VERTEX_LIGHTING;
public static final boolean FIX_LUMINOUS_AO_SHADE;
@ -115,6 +121,8 @@ public class Indigo implements ClientModInitializer {
AMBIENT_OCCLUSION_MODE = asEnum((String) properties.computeIfAbsent("ambient-occlusion-mode", (a) -> "hybrid"), AoConfig.HYBRID);
DEBUG_COMPARE_LIGHTING = asBoolean((String) properties.computeIfAbsent("debug-compare-lighting", (a) -> "auto"), false);
FIX_SMOOTH_LIGHTING_OFFSET = asBoolean((String) properties.computeIfAbsent("fix-smooth-lighting-offset", (a) -> "auto"), true);
FIX_MEAN_LIGHT_CALCULATION = asBoolean((String) properties.computeIfAbsent("fix-mean-light-calculation", (a) -> "auto"), true);
FIX_EMISSIVE_LIGHTING = FIX_MEAN_LIGHT_CALCULATION;
FIX_EXTERIOR_VERTEX_LIGHTING = asBoolean((String) properties.computeIfAbsent("fix-exterior-vertex-lighting", (a) -> "auto"), true);
FIX_LUMINOUS_AO_SHADE = asBoolean((String) properties.computeIfAbsent("fix-luminous-block-ambient-occlusion", (a) -> "auto"), false);

View file

@ -16,7 +16,6 @@
package net.fabricmc.fabric.impl.client.indigo.renderer.aocalc;
import static java.lang.Math.max;
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.AXIS_ALIGNED_FLAG;
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.CUBIC_FLAG;
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.LIGHT_FACE_FLAG;
@ -34,11 +33,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.render.block.BlockModelRenderer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.LightType;
import net.fabricmc.fabric.impl.client.indigo.Indigo;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoFace.WeightFunction;
@ -385,6 +387,7 @@ public abstract class AoCalculator {
searchState = world.getBlockState(searchPos);
final int light0 = light(searchPos, searchState);
final float ao0 = ao(searchPos, searchState);
final boolean em0 = hasEmissiveLighting(world, searchPos, searchState);
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
searchPos.move(lightFace);
@ -397,6 +400,7 @@ public abstract class AoCalculator {
searchState = world.getBlockState(searchPos);
final int light1 = light(searchPos, searchState);
final float ao1 = ao(searchPos, searchState);
final boolean em1 = hasEmissiveLighting(world, searchPos, searchState);
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
searchPos.move(lightFace);
@ -409,6 +413,7 @@ public abstract class AoCalculator {
searchState = world.getBlockState(searchPos);
final int light2 = light(searchPos, searchState);
final float ao2 = ao(searchPos, searchState);
final boolean em2 = hasEmissiveLighting(world, searchPos, searchState);
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
searchPos.move(lightFace);
@ -421,6 +426,7 @@ public abstract class AoCalculator {
searchState = world.getBlockState(searchPos);
final int light3 = light(searchPos, searchState);
final float ao3 = ao(searchPos, searchState);
final boolean em3 = hasEmissiveLighting(world, searchPos, searchState);
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
searchPos.move(lightFace);
@ -432,6 +438,7 @@ public abstract class AoCalculator {
// c = corner - values at corners of face
int cLight0, cLight1, cLight2, cLight3;
float cAo0, cAo1, cAo2, cAo3;
boolean cEm0, cEm1, cEm2, cEm3;
// If neighbors on both sides of the corner are opaque, then apparently we use the light/shade
// from one of the sides adjacent to the corner. If either neighbor is clear (no light subtraction)
@ -439,53 +446,64 @@ public abstract class AoCalculator {
if (!isClear2 && !isClear0) {
cAo0 = ao0;
cLight0 = light0;
cEm0 = em0;
} else {
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[2]);
searchState = world.getBlockState(searchPos);
cAo0 = ao(searchPos, searchState);
cLight0 = light(searchPos, searchState);
cEm0 = hasEmissiveLighting(world, searchPos, searchState);
}
if (!isClear3 && !isClear0) {
cAo1 = ao0;
cLight1 = light0;
cEm1 = em0;
} else {
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[3]);
searchState = world.getBlockState(searchPos);
cAo1 = ao(searchPos, searchState);
cLight1 = light(searchPos, searchState);
cEm1 = hasEmissiveLighting(world, searchPos, searchState);
}
if (!isClear2 && !isClear1) {
cAo2 = ao1;
cLight2 = light1;
cEm2 = em1;
} else {
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[2]);
searchState = world.getBlockState(searchPos);
cAo2 = ao(searchPos, searchState);
cLight2 = light(searchPos, searchState);
cEm2 = hasEmissiveLighting(world, searchPos, searchState);
}
if (!isClear3 && !isClear1) {
cAo3 = ao1;
cLight3 = light1;
cEm3 = em1;
} else {
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[3]);
searchState = world.getBlockState(searchPos);
cAo3 = ao(searchPos, searchState);
cLight3 = light(searchPos, searchState);
cEm3 = hasEmissiveLighting(world, searchPos, searchState);
}
// If on block face or neighbor isn't occluding, "center" will be neighbor brightness
// Doesn't use light pos because logic not based solely on this block's geometry
int lightCenter;
boolean emCenter;
searchPos.set(pos, lightFace);
searchState = world.getBlockState(searchPos);
if (isOnBlockFace || !searchState.isOpaqueFullCube(world, searchPos)) {
lightCenter = light(searchPos, searchState);
emCenter = hasEmissiveLighting(world, searchPos, searchState);
} else {
lightCenter = light(pos, blockState);
emCenter = hasEmissiveLighting(world, pos, blockState);
}
float aoCenter = ao(lightPos, world.getBlockState(lightPos));
@ -496,10 +514,39 @@ public abstract class AoCalculator {
result.a2 = ((ao2 + ao1 + cAo2 + aoCenter) * 0.25F) * worldBrightness;
result.a3 = ((ao3 + ao1 + cAo3 + aoCenter) * 0.25F) * worldBrightness;
result.l0(meanBrightness(light3, light0, cLight1, lightCenter));
result.l1(meanBrightness(light2, light0, cLight0, lightCenter));
result.l2(meanBrightness(light2, light1, cLight2, lightCenter));
result.l3(meanBrightness(light3, light1, cLight3, lightCenter));
result.l0(meanBrightness(light3, light0, cLight1, lightCenter, em3, em0, cEm1, emCenter));
result.l1(meanBrightness(light2, light0, cLight0, lightCenter, em2, em0, cEm0, emCenter));
result.l2(meanBrightness(light2, light1, cLight2, lightCenter, em2, em1, cEm2, emCenter));
result.l3(meanBrightness(light3, light1, cLight3, lightCenter, em3, em1, cEm3, emCenter));
}
public static int getLightmapCoordinates(BlockRenderView world, BlockState state, BlockPos pos) {
if (Indigo.FIX_EMISSIVE_LIGHTING) {
// Same as WorldRenderer.getLightmapCoordinates but without the hasEmissiveLighting check.
// We don't want emissive lighting to influence the minimum lightmap in a quad,
// so when the fix is enabled we apply emissive lighting after the quad minimum is computed.
// See AoCalculator#meanBrightness.
int i = world.getLightLevel(LightType.SKY, pos);
int j = world.getLightLevel(LightType.BLOCK, pos);
int k = state.getLuminance();
if (j < k) {
j = k;
}
return i << 20 | j << 4;
} else {
return WorldRenderer.getLightmapCoordinates(world, state, pos);
}
}
private boolean hasEmissiveLighting(BlockRenderView world, BlockPos pos, BlockState state) {
if (Indigo.FIX_EMISSIVE_LIGHTING) {
return state.hasEmissiveLighting(world, pos);
} else {
// When the fix is disabled, emissive lighting was already applied and does not need to be accounted for.
return false;
}
}
/**
@ -507,11 +554,30 @@ public abstract class AoCalculator {
* Still need to substitute or edges are too dark but consistently use the min
* value from all four samples.
*/
private static int meanBrightness(int a, int b, int c, int d) {
if (Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
return a == 0 || b == 0 || c == 0 || d == 0 ? meanEdgeBrightness(a, b, c, d) : meanInnerBrightness(a, b, c, d);
private static int meanBrightness(int lightA, int lightB, int lightC, int lightD, boolean emA, boolean emB, boolean emC, boolean emD) {
if (Indigo.FIX_MEAN_LIGHT_CALCULATION) {
if (lightA == 0 || lightB == 0 || lightC == 0 || lightD == 0) {
// Normalize values to non-zero minimum
final int min = nonZeroMin(nonZeroMin(lightA, lightB), nonZeroMin(lightC, lightD));
lightA = Math.max(lightA, min);
lightB = Math.max(lightB, min);
lightC = Math.max(lightC, min);
lightD = Math.max(lightD, min);
}
if (Indigo.FIX_EMISSIVE_LIGHTING) {
// Apply the fullbright lightmap from emissive blocks at the very end so it cannot influence
// the minimum lightmap and produce incorrect results (for example, sculk sensors in a dark room)
if (emA) lightA = LightmapTextureManager.MAX_LIGHT_COORDINATE;
if (emB) lightB = LightmapTextureManager.MAX_LIGHT_COORDINATE;
if (emC) lightC = LightmapTextureManager.MAX_LIGHT_COORDINATE;
if (emD) lightD = LightmapTextureManager.MAX_LIGHT_COORDINATE;
}
return meanInnerBrightness(lightA, lightB, lightC, lightD);
} else {
return vanillaMeanBrightness(a, b, c, d);
return vanillaMeanBrightness(lightA, lightB, lightC, lightD);
}
}
@ -534,9 +600,4 @@ public abstract class AoCalculator {
if (b == 0) return a;
return Math.min(a, b);
}
private static int meanEdgeBrightness(int a, int b, int c, int d) {
final int min = nonZeroMin(nonZeroMin(a, b), nonZeroMin(c, d));
return meanInnerBrightness(max(a, min), max(b, min), max(c, min), max(d, min));
}
}

View file

@ -19,7 +19,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.render;
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.client.render.model.BakedModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos;
@ -40,7 +39,7 @@ public class BlockRenderContext extends AbstractBlockRenderContext {
return new AoCalculator(blockInfo) {
@Override
public int light(BlockPos pos, BlockState state) {
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, state, pos);
return AoCalculator.getLightmapCoordinates(blockInfo.blockView, state, pos);
}
@Override

View file

@ -32,6 +32,7 @@ import net.minecraft.client.render.chunk.ChunkRendererRegion;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoLuminanceFix;
/**
@ -127,7 +128,7 @@ public class ChunkRenderInfo {
int result = brightnessCache.get(key);
if (result == Integer.MAX_VALUE) {
result = WorldRenderer.getLightmapCoordinates(blockView, state, pos);
result = AoCalculator.getLightmapCoordinates(blockView, state, pos);
brightnessCache.put(key, result);
}