Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and Improve 0.5 Port #221

Merged
merged 3 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 71 additions & 36 deletions src/main/java/link/infra/indium/renderer/aocalc/AoCalculator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@

package link.infra.indium.renderer.aocalc;

import static java.lang.Math.max;
import static link.infra.indium.renderer.helper.GeometryHelper.AXIS_ALIGNED_FLAG;
import static link.infra.indium.renderer.helper.GeometryHelper.CUBIC_FLAG;
import static link.infra.indium.renderer.helper.GeometryHelper.LIGHT_FACE_FLAG;
import static me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess.getLightmap;
import static me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess.unpackAO;
import static me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess.unpackEM;
import static me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess.unpackFO;
import static me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess.getLightmap;
import static me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess.unpackOP;
import static net.minecraft.util.math.Direction.DOWN;
import static net.minecraft.util.math.Direction.EAST;
Expand All @@ -44,6 +44,7 @@
import link.infra.indium.renderer.mesh.QuadViewImpl;
import link.infra.indium.renderer.render.BlockRenderInfo;
import me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
Expand Down Expand Up @@ -369,76 +370,93 @@ private void computeFace(AoFaceData result, Direction lightFace, boolean isOnBlo
final int word0 = cache.get(lightPosX, lightPosY, lightPosZ, neighbors[0]);
final int light0 = getLightmap(word0);
final float ao0 = unpackAO(word0);
final boolean isClear0 = unpackOP(word0);
final boolean opaque0 = unpackOP(word0);
final boolean em0 = unpackEM(word0);

final int word1 = cache.get(lightPosX, lightPosY, lightPosZ, neighbors[1]);
final int light1 = getLightmap(word1);
final float ao1 = unpackAO(word1);
final boolean isClear1 = unpackOP(word1);
final boolean opaque1 = unpackOP(word1);
final boolean em1 = unpackEM(word1);

final int word2 = cache.get(lightPosX, lightPosY, lightPosZ, neighbors[2]);
final int light2 = getLightmap(word2);
final float ao2 = unpackAO(word2);
final boolean isClear2 = unpackOP(word2);
final boolean opaque2 = unpackOP(word2);
final boolean em2 = unpackEM(word2);

final int word3 = cache.get(lightPosX, lightPosY, lightPosZ, neighbors[3]);
final int light3 = getLightmap(word3);
final float ao3 = unpackAO(word3);
final boolean isClear3 = unpackOP(word3);
final boolean opaque3 = unpackOP(word3);
final boolean em3 = unpackEM(word3);

// 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)
// then we use values from the outwardly diagonal corner. (outwardly = position is one more away from light face)
if (!isClear2 && !isClear0) {
if (opaque2 && opaque0) {
cAo0 = ao0;
cLight0 = light0;
cEm0 = em0;
} else {
final int word02 = cache.get(lightPosX, lightPosY, lightPosZ, neighbors[0], neighbors[2]);
cAo0 = unpackAO(word02);
cLight0 = getLightmap(word02);
cEm0 = unpackEM(word02);
}

if (!isClear3 && !isClear0) {
if (opaque3 && opaque0) {
cAo1 = ao0;
cLight1 = light0;
cEm1 = em0;
} else {
final int word03 = cache.get(lightPosX, lightPosY, lightPosZ, neighbors[0], neighbors[3]);
cAo1 = unpackAO(word03);
cLight1 = getLightmap(word03);
cEm1 = unpackEM(word03);
}

if (!isClear2 && !isClear1) {
if (opaque2 && opaque1) {
cAo2 = ao1;
cLight2 = light1;
cEm2 = em1;
} else {
final int word12 = cache.get(lightPosX, lightPosY, lightPosZ, neighbors[1], neighbors[2]);
cAo2 = unpackAO(word12);
cLight2 = getLightmap(word12);
cEm2 = unpackEM(word12);
}

if (!isClear3 && !isClear1) {
if (opaque3 && opaque1) {
cAo3 = ao1;
cLight3 = light1;
cEm3 = em1;
} else {
final int word13 = cache.get(lightPosX, lightPosY, lightPosZ, neighbors[1], neighbors[3]);
cAo3 = unpackAO(word13);
cLight3 = getLightmap(word13);
cEm3 = unpackEM(word13);
}

int centerWord = cache.get(lightPosX, lightPosY, lightPosZ);

// 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;

if (isOnBlockFace || !unpackFO(centerWord)) {
lightCenter = getLightmap(centerWord);
if (isOnBlockFace && unpackFO(centerWord)) {
final int originWord = cache.get(x, y, z);
lightCenter = getLightmap(originWord);
emCenter = unpackEM(originWord);
} else {
lightCenter = getLightmap(cache.get(x, y, z));
lightCenter = getLightmap(centerWord);
emCenter = unpackEM(centerWord);
}

float aoCenter = unpackAO(centerWord);
Expand All @@ -449,34 +467,51 @@ private void computeFace(AoFaceData result, Direction lightFace, boolean isOnBlo
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(calculateCornerBrightness(light3, light0, cLight1, lightCenter, em3, em0, cEm1, emCenter));
result.l1(calculateCornerBrightness(light2, light0, cLight0, lightCenter, em2, em0, cEm0, emCenter));
result.l2(calculateCornerBrightness(light2, light1, cLight2, lightCenter, em2, em1, cEm2, emCenter));
result.l3(calculateCornerBrightness(light3, light1, cLight3, lightCenter, em3, em1, cEm3, emCenter));
}

/**
* Vanilla code excluded missing light values from mean but was not isotropic.
* 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) {
return a == 0 || b == 0 || c == 0 || d == 0 ? meanEdgeBrightness(a, b, c, d) : meanInnerBrightness(a, b, c, d);
}
private static int calculateCornerBrightness(int a, int b, int c, int d, boolean aem, boolean bem, boolean cem, boolean dem) {
// FIX: Normalize corner vectors correctly to the minimum non-zero value between each one to prevent
// strange issues
if ((a == 0) || (b == 0) || (c == 0) || (d == 0)) {
// Find the minimum value between all corners
final int min = minNonZero(minNonZero(a, b), minNonZero(c, d));

// Normalize the corner values
a = Math.max(a, min);
b = Math.max(b, min);
c = Math.max(c, min);
d = Math.max(d, min);
}

private static int meanInnerBrightness(int a, int b, int c, int d) {
// bitwise divide by 4, clamp to expected (positive) range
return a + b + c + d >> 2 & 0xFF00FF;
}
// FIX: 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 (aem) {
a = LightmapTextureManager.MAX_LIGHT_COORDINATE;
}
if (bem) {
b = LightmapTextureManager.MAX_LIGHT_COORDINATE;
}
if (cem) {
c = LightmapTextureManager.MAX_LIGHT_COORDINATE;
}
if (dem) {
d = LightmapTextureManager.MAX_LIGHT_COORDINATE;
}

private static int nonZeroMin(int a, int b) {
if (a == 0) return b;
if (b == 0) return a;
return Math.min(a, b);
return ((a + b + c + d) >> 2) & 0xFF00FF;
}

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));
private static int minNonZero(int a, int b) {
if (a == 0) {
return b;
} else if (b == 0) {
return a;
}

return Math.min(a, b);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@

import org.joml.Vector3f;

import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFacing;
import me.jellysquid.mods.sodium.client.util.DirectionUtil;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Direction.Axis;
import net.minecraft.util.math.Direction.AxisDirection;

import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
import net.minecraft.util.math.MathHelper;

/**
* Static routines of general utility for renderer implementations.
Expand Down Expand Up @@ -197,6 +199,33 @@ public static Direction lightFace(QuadView quad) {
}
}

// Copied from ModelQuadUtil.findNormalFace to prevent unnecessary allocation
public static ModelQuadFacing normalFace(QuadView quad) {
final Vector3f normal = quad.faceNormal();

if (!normal.isFinite()) {
return ModelQuadFacing.UNASSIGNED;
}

float maxDot = 0;
Direction closestFace = null;

for (Direction face : DirectionUtil.ALL_DIRECTIONS) {
float dot = normal.dot(face.getUnitVector());

if (dot > maxDot) {
maxDot = dot;
closestFace = face;
}
}

if (closestFace != null && MathHelper.approximatelyEquals(maxDot, 1.0f)) {
return ModelQuadFacing.fromDirection(closestFace);
}

return ModelQuadFacing.UNASSIGNED;
}

/**
* Simple 4-way compare, doesn't handle NaN values.
*/
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/link/infra/indium/renderer/mesh/EncodingFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import link.infra.indium.renderer.helper.GeometryHelper;
import link.infra.indium.renderer.material.RenderMaterialImpl;
import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFacing;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.minecraft.client.render.VertexFormat;
Expand Down Expand Up @@ -77,13 +78,17 @@ private EncodingFormat() { }
/** used for quick clearing of quad buffers. */
static final int[] EMPTY = new int[TOTAL_STRIDE];

private static final int DIRECTION_MASK = MathHelper.smallestEncompassingPowerOfTwo(ModelHelper.NULL_FACE_ID) - 1;
private static final int DIRECTION_MASK = MathHelper.smallestEncompassingPowerOfTwo(ModelHelper.NULL_FACE_ID + 1) - 1;
private static final int DIRECTION_BIT_COUNT = Integer.bitCount(DIRECTION_MASK);
private static final int FACING_MASK = MathHelper.smallestEncompassingPowerOfTwo(ModelQuadFacing.COUNT) - 1;
private static final int FACING_BIT_COUNT = Integer.bitCount(FACING_MASK);
private static final int CULL_SHIFT = 0;
private static final int CULL_INVERSE_MASK = ~(DIRECTION_MASK << CULL_SHIFT);
private static final int LIGHT_SHIFT = CULL_SHIFT + DIRECTION_BIT_COUNT;
private static final int LIGHT_INVERSE_MASK = ~(DIRECTION_MASK << LIGHT_SHIFT);
private static final int NORMALS_SHIFT = LIGHT_SHIFT + DIRECTION_BIT_COUNT;
private static final int NORMAL_FACE_SHIFT = LIGHT_SHIFT + DIRECTION_BIT_COUNT;
private static final int NORMAL_FACE_INVERSE_MASK = ~(FACING_MASK << NORMAL_FACE_SHIFT);
private static final int NORMALS_SHIFT = NORMAL_FACE_SHIFT + FACING_BIT_COUNT;
private static final int NORMALS_COUNT = 4;
private static final int NORMALS_MASK = (1 << NORMALS_COUNT) - 1;
private static final int NORMALS_INVERSE_MASK = ~(NORMALS_MASK << NORMALS_SHIFT);
Expand Down Expand Up @@ -115,6 +120,14 @@ static int lightFace(int bits, Direction face) {
return (bits & LIGHT_INVERSE_MASK) | (ModelHelper.toFaceIndex(face) << LIGHT_SHIFT);
}

static ModelQuadFacing normalFace(int bits) {
return ModelQuadFacing.VALUES[(bits >>> NORMAL_FACE_SHIFT) & FACING_MASK];
}

static int normalFace(int bits, ModelQuadFacing face) {
return (bits & NORMAL_FACE_INVERSE_MASK) | (face.ordinal() << NORMAL_FACE_SHIFT);
}

/** indicate if vertex normal has been set - bits correspond to vertex ordinals. */
static int normalFlags(int bits) {
return (bits >>> NORMALS_SHIFT) & NORMALS_MASK;
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/link/infra/indium/renderer/mesh/QuadViewImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import link.infra.indium.renderer.helper.GeometryHelper;
import link.infra.indium.renderer.helper.NormalHelper;
import link.infra.indium.renderer.material.RenderMaterialImpl;
import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFacing;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
import net.minecraft.util.math.Direction;

Expand All @@ -51,7 +52,7 @@
public class QuadViewImpl implements QuadView {
@Nullable
protected Direction nominalFace;
/** True when face normal, light face, or geometry flags may not match geometry. */
/** True when face normal, light face, normal face, or geometry flags may not match geometry. */
protected boolean isGeometryInvalid = true;
protected final Vector3f faceNormal = new Vector3f();

Expand Down Expand Up @@ -81,6 +82,9 @@ protected void computeGeometry() {
// depends on face normal
data[baseIndex + HEADER_BITS] = EncodingFormat.lightFace(data[baseIndex + HEADER_BITS], GeometryHelper.lightFace(this));

// depends on face normal
data[baseIndex + HEADER_BITS] = EncodingFormat.normalFace(data[baseIndex + HEADER_BITS], GeometryHelper.normalFace(this));

// depends on light face
data[baseIndex + HEADER_BITS] = EncodingFormat.geometryFlags(data[baseIndex + HEADER_BITS], GeometryHelper.computeShapeFlags(this));
}
Expand Down Expand Up @@ -172,6 +176,11 @@ public boolean hasVertexNormals() {
return normalFlags() != 0;
}

/** True if all vertex normals have been set. */
public boolean hasAllVertexNormals() {
return (normalFlags() & 0b1111) == 0b1111;
}

protected final int normalIndex(int vertexIndex) {
return baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_NORMAL;
}
Expand Down Expand Up @@ -220,6 +229,11 @@ public final Direction lightFace() {
return EncodingFormat.lightFace(data[baseIndex + HEADER_BITS]);
}

public final ModelQuadFacing normalFace() {
computeGeometry();
return EncodingFormat.normalFace(data[baseIndex + HEADER_BITS]);
}

@Override
@Nullable
public final Direction nominalFace() {
Expand Down
Loading
Loading