mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-15 00:14:28 -04:00
Fix Indigo handling of sculk sensor AO (#3200)
* Fix Indigo handling of sculk sensor AO. Fixes #3153 * Split offset and mean formula lighting config options
This commit is contained in:
parent
e91849a835
commit
9172968c53
4 changed files with 86 additions and 17 deletions
fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue