mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-22 15:47:57 -05:00
Classify quads outside block bounds as full-bounds
Also improves configurability
This commit is contained in:
parent
792c1c35b3
commit
702c210a7a
3 changed files with 53 additions and 60 deletions
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue