diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/Indigo.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/Indigo.java index 225911cc7..fab4f76dc 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/Indigo.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/Indigo.java @@ -35,6 +35,10 @@ import java.util.Properties; public class Indigo implements ClientModInitializer { public static final boolean ALWAYS_TESSELATE_INDIGO; public static final AoConfig AMBIENT_OCCLUSION_MODE; + /** 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; + private static final Logger LOGGER = LogManager.getLogger(); private static boolean asBoolean(String property, boolean defValue) { @@ -99,6 +103,8 @@ public class Indigo implements ClientModInitializer { ALWAYS_TESSELATE_INDIGO = asBoolean((String) properties.computeIfAbsent("always-tesselate-blocks", (a) -> "auto"), true); AMBIENT_OCCLUSION_MODE = asEnum((String) properties.computeIfAbsent("ambient-occlusion-mode", (a) -> "enhanced"), AoConfig.ENHANCED); + 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); try (FileOutputStream stream = new FileOutputStream(configFile)) { properties.store(stream, "Indigo properties file"); diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/aocalc/AoCalculator.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/aocalc/AoCalculator.java index 22ee0db2d..cd5dba3d9 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/aocalc/AoCalculator.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/aocalc/AoCalculator.java @@ -74,8 +74,6 @@ public class AoCalculator { } private static final Logger LOGGER = LogManager.getLogger(); - // TODO: make this actually configurable? - private static final boolean fixSmoothLighting = true; private final VanillaAoCalc vanillaCalc; private final BlockPos.Mutable lightPos = new BlockPos.Mutable(); @@ -112,12 +110,8 @@ public class AoCalculator { completionFlags = 0; } - /** Set true in dev env to confirm results match vanilla when they should */ - private static final boolean DEBUG = Boolean.valueOf(System.getProperty("fabric.debugAoLighting", "false")); - public void compute(MutableQuadViewImpl quad, boolean isVanilla) { final AoConfig config = Indigo.AMBIENT_OCCLUSION_MODE; - boolean shouldMatch = false; switch(config) { @@ -127,7 +121,7 @@ public class AoCalculator { case EMULATE: calcFastVanilla(quad); - shouldMatch = DEBUG && isVanilla; + shouldMatch = Indigo.DEBUG_COMPARE_LIGHTING && isVanilla; break; case HYBRID: @@ -164,7 +158,7 @@ public class AoCalculator { } } } - + private void calcVanilla(MutableQuadViewImpl quad) { vanillaCalc.compute(blockInfo, quad, ao, light); } @@ -173,28 +167,15 @@ public class AoCalculator { int flags = quad.geometryFlags(); // force to block face if shape is full cube - matches vanilla logic - if(((flags & LIGHT_FACE_FLAG) == 0) && Block.isShapeFullCube(blockInfo.blockState.getCollisionShape(blockInfo.blockView, blockInfo.blockPos))) { + if((flags & LIGHT_FACE_FLAG) == 0 && (flags & AXIS_ALIGNED_FLAG) == AXIS_ALIGNED_FLAG && Block.isShapeFullCube(blockInfo.blockState.getCollisionShape(blockInfo.blockView, blockInfo.blockPos))) { flags |= LIGHT_FACE_FLAG; } - - switch(flags) { - case AXIS_ALIGNED_FLAG | CUBIC_FLAG | LIGHT_FACE_FLAG: - vanillaFullFace(quad, true); - break; - - case AXIS_ALIGNED_FLAG | LIGHT_FACE_FLAG: - vanillaPartialFace(quad, true); - break; - - case AXIS_ALIGNED_FLAG | CUBIC_FLAG: - vanillaFullFace(quad, false); - break; - - default: - case AXIS_ALIGNED_FLAG: - vanillaPartialFace(quad, false); - break; - } + + if((flags & CUBIC_FLAG) == 0) { + vanillaPartialFace(quad, (flags & LIGHT_FACE_FLAG) != 0); + } else { + vanillaFullFace(quad, (flags & LIGHT_FACE_FLAG) != 0); + } } /** returns true if should match vanilla results */ @@ -202,11 +183,11 @@ public class AoCalculator { switch(quad.geometryFlags()) { case AXIS_ALIGNED_FLAG | CUBIC_FLAG | LIGHT_FACE_FLAG: vanillaFullFace(quad, true); - return DEBUG; + return Indigo.DEBUG_COMPARE_LIGHTING; case AXIS_ALIGNED_FLAG | LIGHT_FACE_FLAG: vanillaPartialFace(quad, true); - return DEBUG; + return Indigo.DEBUG_COMPARE_LIGHTING; case AXIS_ALIGNED_FLAG | CUBIC_FLAG: blendedFullFace(quad); @@ -222,12 +203,12 @@ public class AoCalculator { } } - private void vanillaFullFace(MutableQuadViewImpl quad, boolean isOnLightFace) { + private void vanillaFullFace(QuadViewImpl quad, boolean isOnLightFace) { final Direction lightFace = quad.lightFace(); computeFace(lightFace, isOnLightFace).toArray(ao, light, VERTEX_MAP[lightFace.getId()]); } - private void vanillaPartialFace(MutableQuadViewImpl quad, boolean isOnLightFace) { + private void vanillaPartialFace(QuadViewImpl quad, boolean isOnLightFace) { final Direction lightFace = quad.lightFace(); AoFaceData faceData = computeFace(lightFace, isOnLightFace); final WeightFunction wFunc = AoFace.get(lightFace).weightFunc; @@ -239,7 +220,7 @@ public class AoCalculator { } } - /** used in {@link #blendedInsetFace(VertexEditorImpl, Direction)} as return variable to avoid new allocation */ + /** used in {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace)} as return variable to avoid new allocation */ AoFaceData tmpFace = new AoFaceData(); /** Returns linearly interpolated blend of outer and inner face based on depth of vertex in face */ @@ -250,7 +231,7 @@ public class AoCalculator { } /** - * Like {@link #blendedInsetFace(VertexEditorImpl, Direction)} but optimizes if depth is 0 or 1. + * Like {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace)} but optimizes if depth is 0 or 1. * Used for irregular faces when depth varies by vertex to avoid unneeded interpolation. */ private AoFaceData gatherInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace) { @@ -265,12 +246,12 @@ public class AoCalculator { } } - private void blendedFullFace(MutableQuadViewImpl quad) { + private void blendedFullFace(QuadViewImpl quad) { final Direction lightFace = quad.lightFace(); blendedInsetFace(quad, 0, lightFace).toArray(ao, light, VERTEX_MAP[lightFace.getId()]); } - private void blendedPartialFace(MutableQuadViewImpl quad) { + private void blendedPartialFace(QuadViewImpl quad) { final Direction lightFace = quad.lightFace(); AoFaceData faceData = blendedInsetFace(quad, 0, lightFace); final WeightFunction wFunc = AoFace.get(lightFace).weightFunc; @@ -383,16 +364,16 @@ public class AoCalculator { // vanilla was further offsetting these in the direction of the light face // but it was actually mis-sampling and causing visible artifacts in certain situation searchPos.set(lightPos).setOffset(aoFace.neighbors[0]);//.setOffset(lightFace); - if(!fixSmoothLighting) searchPos.setOffset(lightFace); + if(!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.setOffset(lightFace); final boolean isClear0 = world.getBlockState(searchPos).getLightSubtracted(world, searchPos) == 0; searchPos.set(lightPos).setOffset(aoFace.neighbors[1]);//.setOffset(lightFace); - if(!fixSmoothLighting) searchPos.setOffset(lightFace); + if(!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.setOffset(lightFace); final boolean isClear1 = world.getBlockState(searchPos).getLightSubtracted(world, searchPos) == 0; searchPos.set(lightPos).setOffset(aoFace.neighbors[2]);//.setOffset(lightFace); - if(!fixSmoothLighting) searchPos.setOffset(lightFace); + if(!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.setOffset(lightFace); final boolean isClear2 = world.getBlockState(searchPos).getLightSubtracted(world, searchPos) == 0; searchPos.set(lightPos).setOffset(aoFace.neighbors[3]);//.setOffset(lightFace); - if(!fixSmoothLighting) searchPos.setOffset(lightFace); + if(!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.setOffset(lightFace); final boolean isClear3 = world.getBlockState(searchPos).getLightSubtracted(world, searchPos) == 0; // c = corner - values at corners of face @@ -469,7 +450,7 @@ public class AoCalculator { * value from all four samples. */ private static int meanBrightness(int a, int b, int c, int d) { - if(fixSmoothLighting) { + 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); } else { return vanillaMeanBrightness(a, b, c, d); diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/helper/GeometryHelper.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/helper/GeometryHelper.java index 527d60db6..b10536d0e 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/helper/GeometryHelper.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/helper/GeometryHelper.java @@ -39,7 +39,10 @@ public abstract class GeometryHelper { /** set when a quad is coplanar with its light face. Implies {@link #AXIS_ALIGNED_FLAG} */ public static final int LIGHT_FACE_FLAG = AXIS_ALIGNED_FLAG << 1; - + + private static final float EPS_MIN = 0.0001f; + private static final float EPS_MAX = 1.0f - EPS_MIN; + private GeometryHelper() {} /** @@ -56,10 +59,10 @@ public abstract class GeometryHelper { if(isParallelQuadOnFace(lightFace, quad)) { bits |= LIGHT_FACE_FLAG; } - if(isQuadCubic(lightFace, quad)) { - bits |= CUBIC_FLAG; - } } + if(isQuadCubic(lightFace, quad)) { + bits |= CUBIC_FLAG; + } return bits; } @@ -80,17 +83,17 @@ public abstract class GeometryHelper { } /** - * True if quad - already known to be parallel to a face - is actually coplanar with it.
+ * True if quad - already known to be parallel to a face - is actually coplanar with it. + * For compatibility with vanilla resource packs, also true if quad is outside the face.
* - * Test will be unreliable if not already parallel, use {@link #isQuadParallel(Direction, QuadView)} + * Test will be unreliable if not already parallel, use {@link #isQuadParallelToFace(Direction, QuadView)} * for that purpose. Expects convex quads with all points co-planar.
*/ public static boolean isParallelQuadOnFace(Direction lightFace, QuadView quad) { if(lightFace == null) return false; - final int coordinateIndex = lightFace.getAxis().ordinal(); - final float expectedValue = lightFace.getDirection() == AxisDirection.POSITIVE ? 1 : 0; - return equalsApproximate(quad.posByIndex(0, coordinateIndex), expectedValue); + final float x = quad.posByIndex(0, lightFace.getAxis().ordinal()); + return lightFace.getDirection() == AxisDirection.POSITIVE ? x >= EPS_MAX : x <= EPS_MIN; } /** @@ -101,7 +104,7 @@ public abstract class GeometryHelper { * quad vertices are coplanar with each other.
* * Expects convex quads with all points co-planar.
- * + * * @param lightFace MUST be non-null. */ public static boolean isQuadCubic(Direction lightFace, QuadView quad) { @@ -134,10 +137,13 @@ public abstract class GeometryHelper { return confirmSquareCorners(a, b, quad); } - + /** - * Used by {@link #isQuadCubic(Direction, int[], int, QuadSerializer)}. - * True if quad touches all four corners of unit square. + * Used by {@link #isQuadCubic(Direction, QuadView)}. + * True if quad touches all four corners of unit square.
+ * + * For compatibility with resource packs that contain models with quads exceeding + * block boundaries, considers corners outside the block to be at the corners. */ private static boolean confirmSquareCorners(int aCoordinate, int bCoordinate, QuadView quad) { int flags = 0; @@ -146,18 +152,18 @@ public abstract class GeometryHelper { final float a = quad.posByIndex(i, aCoordinate); final float b = quad.posByIndex(i, bCoordinate); - if(equalsApproximate(a, 0)) { - if(equalsApproximate(b, 0)) { + if(a <= EPS_MIN) { + if(b <= EPS_MIN) { flags |= 1; - } else if(equalsApproximate(b, 1)) { + } else if(b >= EPS_MAX) { flags |= 2; } else { return false; } - } else if(equalsApproximate(a, 1)) { - if(equalsApproximate(b, 0)) { + } else if(a >= EPS_MAX) { + if(b <= EPS_MIN) { flags |= 4; - } else if(equalsApproximate(b, 1)) { + } else if(b >= EPS_MAX) { flags |= 8; } else { return false;