Classify quads outside block bounds as full-bounds

Also improves configurability
This commit is contained in:
grondag 2019-05-30 06:00:16 -07:00
parent 792c1c35b3
commit 702c210a7a
3 changed files with 53 additions and 60 deletions

View file

@ -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");

View file

@ -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);

View file

@ -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.<p>
* 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.<p>
*
* 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.<p>
*/
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. <p>
*
* Expects convex quads with all points co-planar.<p>
*
*
* @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.<p>
*
* 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;