mirror of
https://github.com/FabricMC/fabric.git
synced 2025-03-23 21:40:02 -04:00
Indigo shade related fixes and other changes (#2898)
* Apply disabled shade from vanilla quads directly to material - Remove QuadViewImpl.shade * Fix enhanced AO calculation and respect non-terrain culling state - Fix AoCalculator using AO face data computed with a potentially different shade state - Move non-cached computation code to separate method in AoCalculator - Turn AoCalculator's brightnessFunc and aoFunc into abstract methods - Do not check null check world in non-terrain AO calculation since it cannot be null - Pass through lightFace and shade state as method arguments in AoCalculator methods to prevent additional lookups - Do not check for the axis aligned flag in AbstractQuadRenderer.shadeFlatQuad - Respect cull parameter passed to non-terrain rendering by merging TerrainBlockRenderInfo into BlockRenderInfo - Use reusable search pos when calling Block.shouldDrawSide to prevent additional BlockPos allocation - Change BlockRenderContext.render and TerrainRenderContext.tessellateBlock to return void since return value is no longer used - Remove QuadViewImpl.vertexStart since it is unused * Add suggestions - Mark Direction parameter to BlockRenderInfo.shouldDrawFace as Nullable - Reuse MaterialFinder in FrameBakedModel (cherry picked from commit3a95925af4
) (cherry picked from commit1adbf277ee
)
This commit is contained in:
parent
6bd39c990e
commit
bf4864981b
12 changed files with 275 additions and 317 deletions
fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client
fabric-renderer-indigo/src/main/java/net/fabricmc/fabric
|
@ -37,9 +37,9 @@ import net.minecraft.util.math.BlockPos;
|
|||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
|
@ -57,9 +57,10 @@ final class FrameBakedModel implements BakedModel, FabricBakedModel {
|
|||
this.frameMesh = frameMesh;
|
||||
this.frameSprite = frameSprite;
|
||||
|
||||
Renderer renderer = RendererAccess.INSTANCE.getRenderer();
|
||||
this.translucentMaterial = renderer.materialFinder().blendMode(0, BlendMode.TRANSLUCENT).find();
|
||||
this.translucentEmissiveMaterial = renderer.materialFinder().blendMode(0, BlendMode.TRANSLUCENT).emissive(0, true).find();
|
||||
MaterialFinder finder = RendererAccess.INSTANCE.getRenderer().materialFinder();
|
||||
this.translucentMaterial = finder.blendMode(0, BlendMode.TRANSLUCENT).find();
|
||||
finder.clear();
|
||||
this.translucentEmissiveMaterial = finder.blendMode(0, BlendMode.TRANSLUCENT).emissive(0, true).find();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -52,6 +52,14 @@ public abstract class RenderMaterialImpl {
|
|||
return VALUES[index];
|
||||
}
|
||||
|
||||
public static Value setDisableDiffuse(Value material, int textureIndex, boolean disable) {
|
||||
if (material.disableDiffuse(textureIndex) != disable) {
|
||||
return byIndex(disable ? (material.bits | DIFFUSE_FLAG) : (material.bits & ~DIFFUSE_FLAG));
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
protected int bits;
|
||||
|
||||
public BlendMode blendMode(int textureIndex) {
|
||||
|
|
|
@ -39,8 +39,6 @@ import net.minecraft.util.math.MathHelper;
|
|||
import net.minecraft.util.math.Vec3f;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.impl.client.indigo.Indigo;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessAmbientOcclusionCalculator;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoFace.WeightFunction;
|
||||
|
@ -52,19 +50,7 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
|
|||
/**
|
||||
* Adaptation of inner, non-static class in BlockModelRenderer that serves same purpose.
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class AoCalculator {
|
||||
@FunctionalInterface
|
||||
public interface BrightnessFunc {
|
||||
int apply(BlockPos pos, BlockState state);
|
||||
}
|
||||
|
||||
/** Used to receive a method reference in constructor for ao value lookup. */
|
||||
@FunctionalInterface
|
||||
public interface AoFunc {
|
||||
float apply(BlockPos pos, BlockState state);
|
||||
}
|
||||
|
||||
public abstract class AoCalculator {
|
||||
/**
|
||||
* Vanilla models with cubic quads have vertices in a certain order, which allows
|
||||
* us to map them using a lookup. Adapted from enum in vanilla AoCalculator.
|
||||
|
@ -84,12 +70,14 @@ public class AoCalculator {
|
|||
private final AccessAmbientOcclusionCalculator vanillaCalc;
|
||||
private final BlockPos.Mutable lightPos = new BlockPos.Mutable();
|
||||
private final BlockPos.Mutable searchPos = new BlockPos.Mutable();
|
||||
private final BlockRenderInfo blockInfo;
|
||||
private final BrightnessFunc brightnessFunc;
|
||||
private final AoFunc aoFunc;
|
||||
protected final BlockRenderInfo blockInfo;
|
||||
|
||||
public abstract int light(BlockPos pos, BlockState state);
|
||||
|
||||
public abstract float ao(BlockPos pos, BlockState state);
|
||||
|
||||
/** caches results of {@link #computeFace(Direction, boolean, boolean)} for the current block. */
|
||||
private final AoFaceData[] faceData = new AoFaceData[12];
|
||||
private final AoFaceData[] faceData = new AoFaceData[24];
|
||||
|
||||
/** indicates which elements of {@link #faceData} have been computed for the current block. */
|
||||
private int completionFlags = 0;
|
||||
|
@ -101,13 +89,11 @@ public class AoCalculator {
|
|||
public final float[] ao = new float[4];
|
||||
public final int[] light = new int[4];
|
||||
|
||||
public AoCalculator(BlockRenderInfo blockInfo, BrightnessFunc brightnessFunc, AoFunc aoFunc) {
|
||||
public AoCalculator(BlockRenderInfo blockInfo) {
|
||||
this.blockInfo = blockInfo;
|
||||
this.brightnessFunc = brightnessFunc;
|
||||
this.aoFunc = aoFunc;
|
||||
this.vanillaCalc = VanillaAoHelper.get();
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
for (int i = 0; i < 24; i++) {
|
||||
faceData[i] = new AoFaceData();
|
||||
}
|
||||
}
|
||||
|
@ -123,12 +109,7 @@ public class AoCalculator {
|
|||
|
||||
switch (config) {
|
||||
case VANILLA:
|
||||
// prevent NPE in error case of failed reflection for vanilla calculator access
|
||||
if (vanillaCalc == null) {
|
||||
calcFastVanilla(quad);
|
||||
} else {
|
||||
calcVanilla(quad);
|
||||
}
|
||||
calcVanilla(quad);
|
||||
|
||||
// no point in comparing vanilla with itself
|
||||
shouldCompare = false;
|
||||
|
@ -156,7 +137,7 @@ public class AoCalculator {
|
|||
calcEnhanced(quad);
|
||||
}
|
||||
|
||||
if (shouldCompare && vanillaCalc != null) {
|
||||
if (shouldCompare) {
|
||||
float[] vanillaAo = new float[4];
|
||||
int[] vanillaLight = new int[4];
|
||||
calcVanilla(quad, vanillaAo, vanillaLight);
|
||||
|
@ -188,11 +169,11 @@ public class AoCalculator {
|
|||
|
||||
private void calcVanilla(MutableQuadViewImpl quad, float[] aoDest, int[] lightDest) {
|
||||
vanillaAoControlBits.clear();
|
||||
final Direction face = quad.lightFace();
|
||||
final Direction lightFace = quad.lightFace();
|
||||
quad.toVanilla(0, vertexData, 0, false);
|
||||
|
||||
VanillaAoHelper.updateShape(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, vertexData, face, vanillaAoData, vanillaAoControlBits);
|
||||
vanillaCalc.fabric_apply(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, quad.lightFace(), vanillaAoData, vanillaAoControlBits, quad.hasShade());
|
||||
VanillaAoHelper.updateShape(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, vertexData, lightFace, vanillaAoData, vanillaAoControlBits);
|
||||
vanillaCalc.fabric_apply(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, lightFace, vanillaAoData, vanillaAoControlBits, quad.hasShade());
|
||||
|
||||
System.arraycopy(vanillaCalc.fabric_colorMultiplier(), 0, aoDest, 0, 4);
|
||||
System.arraycopy(vanillaCalc.fabric_brightness(), 0, lightDest, 0, 4);
|
||||
|
@ -207,9 +188,9 @@ public class AoCalculator {
|
|||
}
|
||||
|
||||
if ((flags & CUBIC_FLAG) == 0) {
|
||||
vanillaPartialFace(quad, (flags & LIGHT_FACE_FLAG) != 0);
|
||||
vanillaPartialFace(quad, quad.lightFace(), (flags & LIGHT_FACE_FLAG) != 0, quad.hasShade());
|
||||
} else {
|
||||
vanillaFullFace(quad, (flags & LIGHT_FACE_FLAG) != 0);
|
||||
vanillaFullFace(quad, quad.lightFace(), (flags & LIGHT_FACE_FLAG) != 0, quad.hasShade());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,28 +198,26 @@ public class AoCalculator {
|
|||
switch (quad.geometryFlags()) {
|
||||
case AXIS_ALIGNED_FLAG | CUBIC_FLAG | LIGHT_FACE_FLAG:
|
||||
case AXIS_ALIGNED_FLAG | LIGHT_FACE_FLAG:
|
||||
vanillaPartialFace(quad, true);
|
||||
vanillaPartialFace(quad, quad.lightFace(), true, quad.hasShade());
|
||||
break;
|
||||
|
||||
case AXIS_ALIGNED_FLAG | CUBIC_FLAG:
|
||||
case AXIS_ALIGNED_FLAG:
|
||||
blendedPartialFace(quad);
|
||||
blendedPartialFace(quad, quad.lightFace(), quad.hasShade());
|
||||
break;
|
||||
|
||||
default:
|
||||
irregularFace(quad);
|
||||
irregularFace(quad, quad.hasShade());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void vanillaFullFace(QuadViewImpl quad, boolean isOnLightFace) {
|
||||
final Direction lightFace = quad.lightFace();
|
||||
computeFace(lightFace, isOnLightFace, quad.hasShade()).toArray(ao, light, VERTEX_MAP[lightFace.getId()]);
|
||||
private void vanillaFullFace(QuadViewImpl quad, Direction lightFace, boolean isOnLightFace, boolean shade) {
|
||||
computeFace(lightFace, isOnLightFace, shade).toArray(ao, light, VERTEX_MAP[lightFace.getId()]);
|
||||
}
|
||||
|
||||
private void vanillaPartialFace(QuadViewImpl quad, boolean isOnLightFace) {
|
||||
final Direction lightFace = quad.lightFace();
|
||||
AoFaceData faceData = computeFace(lightFace, isOnLightFace, quad.hasShade());
|
||||
private void vanillaPartialFace(QuadViewImpl quad, Direction lightFace, boolean isOnLightFace, boolean shade) {
|
||||
AoFaceData faceData = computeFace(lightFace, isOnLightFace, shade);
|
||||
final WeightFunction wFunc = AoFace.get(lightFace).weightFunc;
|
||||
final float[] w = this.w;
|
||||
|
||||
|
@ -249,36 +228,35 @@ public class AoCalculator {
|
|||
}
|
||||
}
|
||||
|
||||
/** used in {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace)} as return variable to avoid new allocation. */
|
||||
/** used in {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace, boolean shade)} 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. */
|
||||
private AoFaceData blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace) {
|
||||
private AoFaceData blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace, boolean shade) {
|
||||
final float w1 = AoFace.get(lightFace).depthFunc.apply(quad, vertexIndex);
|
||||
final float w0 = 1 - w1;
|
||||
return AoFaceData.weightedMean(computeFace(lightFace, true, quad.hasShade()), w0, computeFace(lightFace, false, quad.hasShade()), w1, tmpFace);
|
||||
return AoFaceData.weightedMean(computeFace(lightFace, true, shade), w0, computeFace(lightFace, false, shade), w1, tmpFace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace)} but optimizes if depth is 0 or 1.
|
||||
* Like {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace, boolean shade)} 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) {
|
||||
private AoFaceData gatherInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace, boolean shade) {
|
||||
final float w1 = AoFace.get(lightFace).depthFunc.apply(quad, vertexIndex);
|
||||
|
||||
if (MathHelper.approximatelyEquals(w1, 0)) {
|
||||
return computeFace(lightFace, true, quad.hasShade());
|
||||
return computeFace(lightFace, true, shade);
|
||||
} else if (MathHelper.approximatelyEquals(w1, 1)) {
|
||||
return computeFace(lightFace, false, quad.hasShade());
|
||||
return computeFace(lightFace, false, shade);
|
||||
} else {
|
||||
final float w0 = 1 - w1;
|
||||
return AoFaceData.weightedMean(computeFace(lightFace, true, quad.hasShade()), w0, computeFace(lightFace, false, quad.hasShade()), w1, tmpFace);
|
||||
return AoFaceData.weightedMean(computeFace(lightFace, true, shade), w0, computeFace(lightFace, false, shade), w1, tmpFace);
|
||||
}
|
||||
}
|
||||
|
||||
private void blendedPartialFace(QuadViewImpl quad) {
|
||||
final Direction lightFace = quad.lightFace();
|
||||
AoFaceData faceData = blendedInsetFace(quad, 0, lightFace);
|
||||
private void blendedPartialFace(QuadViewImpl quad, Direction lightFace, boolean shade) {
|
||||
AoFaceData faceData = blendedInsetFace(quad, 0, lightFace, shade);
|
||||
final WeightFunction wFunc = AoFace.get(lightFace).weightFunc;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
@ -291,7 +269,7 @@ public class AoCalculator {
|
|||
/** used exclusively in irregular face to avoid new heap allocations each call. */
|
||||
private final Vec3f vertexNormal = new Vec3f();
|
||||
|
||||
private void irregularFace(MutableQuadViewImpl quad) {
|
||||
private void irregularFace(MutableQuadViewImpl quad, boolean shade) {
|
||||
final Vec3f faceNorm = quad.faceNormal();
|
||||
Vec3f normal;
|
||||
final float[] w = this.w;
|
||||
|
@ -307,7 +285,7 @@ public class AoCalculator {
|
|||
|
||||
if (!MathHelper.approximatelyEquals(0f, x)) {
|
||||
final Direction face = x > 0 ? Direction.EAST : Direction.WEST;
|
||||
final AoFaceData fd = gatherInsetFace(quad, i, face);
|
||||
final AoFaceData fd = gatherInsetFace(quad, i, face, shade);
|
||||
AoFace.get(face).weightFunc.apply(quad, i, w);
|
||||
final float n = x * x;
|
||||
final float a = fd.weigtedAo(w);
|
||||
|
@ -325,7 +303,7 @@ public class AoCalculator {
|
|||
|
||||
if (!MathHelper.approximatelyEquals(0f, y)) {
|
||||
final Direction face = y > 0 ? Direction.UP : Direction.DOWN;
|
||||
final AoFaceData fd = gatherInsetFace(quad, i, face);
|
||||
final AoFaceData fd = gatherInsetFace(quad, i, face, shade);
|
||||
AoFace.get(face).weightFunc.apply(quad, i, w);
|
||||
final float n = y * y;
|
||||
final float a = fd.weigtedAo(w);
|
||||
|
@ -343,7 +321,7 @@ public class AoCalculator {
|
|||
|
||||
if (!MathHelper.approximatelyEquals(0f, z)) {
|
||||
final Direction face = z > 0 ? Direction.SOUTH : Direction.NORTH;
|
||||
final AoFaceData fd = gatherInsetFace(quad, i, face);
|
||||
final AoFaceData fd = gatherInsetFace(quad, i, face, shade);
|
||||
AoFace.get(face).weightFunc.apply(quad, i, w);
|
||||
final float n = z * z;
|
||||
final float a = fd.weigtedAo(w);
|
||||
|
@ -362,6 +340,19 @@ public class AoCalculator {
|
|||
}
|
||||
}
|
||||
|
||||
private AoFaceData computeFace(Direction lightFace, boolean isOnBlockFace, boolean shade) {
|
||||
final int faceDataIndex = shade ? (isOnBlockFace ? lightFace.getId() : lightFace.getId() + 6) : (isOnBlockFace ? lightFace.getId() + 12 : lightFace.getId() + 18);
|
||||
final int mask = 1 << faceDataIndex;
|
||||
final AoFaceData result = faceData[faceDataIndex];
|
||||
|
||||
if ((completionFlags & mask) == 0) {
|
||||
completionFlags |= mask;
|
||||
computeFace(result, lightFace, isOnBlockFace, shade);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes smoothed brightness and Ao shading for four corners of a block face.
|
||||
* Outer block face is what you normally see and what you get when the second
|
||||
|
@ -370,155 +361,145 @@ public class AoCalculator {
|
|||
* in vanilla logic for some blocks that aren't full opaque cubes.
|
||||
* Except for parameterization, the logic itself is practically identical to vanilla.
|
||||
*/
|
||||
private AoFaceData computeFace(Direction lightFace, boolean isOnBlockFace, boolean shade) {
|
||||
final int faceDataIndex = isOnBlockFace ? lightFace.getId() : lightFace.getId() + 6;
|
||||
final int mask = 1 << faceDataIndex;
|
||||
final AoFaceData result = faceData[faceDataIndex];
|
||||
private void computeFace(AoFaceData result, Direction lightFace, boolean isOnBlockFace, boolean shade) {
|
||||
final BlockRenderView world = blockInfo.blockView;
|
||||
final BlockPos pos = blockInfo.blockPos;
|
||||
final BlockState blockState = blockInfo.blockState;
|
||||
final BlockPos.Mutable lightPos = this.lightPos;
|
||||
final BlockPos.Mutable searchPos = this.searchPos;
|
||||
BlockState searchState;
|
||||
|
||||
if ((completionFlags & mask) == 0) {
|
||||
completionFlags |= mask;
|
||||
|
||||
final BlockRenderView world = blockInfo.blockView;
|
||||
final BlockPos pos = blockInfo.blockPos;
|
||||
final BlockState blockState = blockInfo.blockState;
|
||||
final BlockPos.Mutable lightPos = this.lightPos;
|
||||
final BlockPos.Mutable searchPos = this.searchPos;
|
||||
BlockState searchState;
|
||||
|
||||
if (isOnBlockFace) {
|
||||
lightPos.set(pos, lightFace);
|
||||
} else {
|
||||
lightPos.set(pos);
|
||||
}
|
||||
|
||||
AoFace aoFace = AoFace.get(lightFace);
|
||||
|
||||
// Vanilla was further offsetting the positions for opaque block checks in the
|
||||
// direction of the light face, but it was actually mis-sampling and causing
|
||||
// visible artifacts in certain situations
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[0]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light0 = brightnessFunc.apply(searchPos, searchState);
|
||||
final float ao0 = aoFunc.apply(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear0 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[1]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light1 = brightnessFunc.apply(searchPos, searchState);
|
||||
final float ao1 = aoFunc.apply(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear1 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[2]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light2 = brightnessFunc.apply(searchPos, searchState);
|
||||
final float ao2 = aoFunc.apply(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear2 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[3]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light3 = brightnessFunc.apply(searchPos, searchState);
|
||||
final float ao3 = aoFunc.apply(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear3 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
// c = corner - values at corners of face
|
||||
int cLight0, cLight1, cLight2, cLight3;
|
||||
float cAo0, cAo1, cAo2, cAo3;
|
||||
|
||||
// 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) {
|
||||
cAo0 = ao0;
|
||||
cLight0 = light0;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[2]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo0 = aoFunc.apply(searchPos, searchState);
|
||||
cLight0 = brightnessFunc.apply(searchPos, searchState);
|
||||
}
|
||||
|
||||
if (!isClear3 && !isClear0) {
|
||||
cAo1 = ao0;
|
||||
cLight1 = light0;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[3]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo1 = aoFunc.apply(searchPos, searchState);
|
||||
cLight1 = brightnessFunc.apply(searchPos, searchState);
|
||||
}
|
||||
|
||||
if (!isClear2 && !isClear1) {
|
||||
cAo2 = ao1;
|
||||
cLight2 = light1;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[2]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo2 = aoFunc.apply(searchPos, searchState);
|
||||
cLight2 = brightnessFunc.apply(searchPos, searchState);
|
||||
}
|
||||
|
||||
if (!isClear3 && !isClear1) {
|
||||
cAo3 = ao1;
|
||||
cLight3 = light1;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[3]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo3 = aoFunc.apply(searchPos, searchState);
|
||||
cLight3 = brightnessFunc.apply(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;
|
||||
searchPos.set(pos, lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
|
||||
if (isOnBlockFace || !searchState.isOpaqueFullCube(world, searchPos)) {
|
||||
lightCenter = brightnessFunc.apply(searchPos, searchState);
|
||||
} else {
|
||||
lightCenter = brightnessFunc.apply(pos, blockState);
|
||||
}
|
||||
|
||||
float aoCenter = aoFunc.apply(lightPos, world.getBlockState(lightPos));
|
||||
float worldBrightness = world.getBrightness(lightFace, shade);
|
||||
|
||||
result.a0 = ((ao3 + ao0 + cAo1 + aoCenter) * 0.25F) * worldBrightness;
|
||||
result.a1 = ((ao2 + ao0 + cAo0 + aoCenter) * 0.25F) * worldBrightness;
|
||||
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));
|
||||
if (isOnBlockFace) {
|
||||
lightPos.set(pos, lightFace);
|
||||
} else {
|
||||
lightPos.set(pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
AoFace aoFace = AoFace.get(lightFace);
|
||||
|
||||
// Vanilla was further offsetting the positions for opaque block checks in the
|
||||
// direction of the light face, but it was actually mis-sampling and causing
|
||||
// visible artifacts in certain situations
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[0]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light0 = light(searchPos, searchState);
|
||||
final float ao0 = ao(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear0 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[1]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light1 = light(searchPos, searchState);
|
||||
final float ao1 = ao(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear1 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[2]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light2 = light(searchPos, searchState);
|
||||
final float ao2 = ao(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear2 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[3]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light3 = light(searchPos, searchState);
|
||||
final float ao3 = ao(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear3 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
// c = corner - values at corners of face
|
||||
int cLight0, cLight1, cLight2, cLight3;
|
||||
float cAo0, cAo1, cAo2, cAo3;
|
||||
|
||||
// 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) {
|
||||
cAo0 = ao0;
|
||||
cLight0 = light0;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[2]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo0 = ao(searchPos, searchState);
|
||||
cLight0 = light(searchPos, searchState);
|
||||
}
|
||||
|
||||
if (!isClear3 && !isClear0) {
|
||||
cAo1 = ao0;
|
||||
cLight1 = light0;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[3]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo1 = ao(searchPos, searchState);
|
||||
cLight1 = light(searchPos, searchState);
|
||||
}
|
||||
|
||||
if (!isClear2 && !isClear1) {
|
||||
cAo2 = ao1;
|
||||
cLight2 = light1;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[2]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo2 = ao(searchPos, searchState);
|
||||
cLight2 = light(searchPos, searchState);
|
||||
}
|
||||
|
||||
if (!isClear3 && !isClear1) {
|
||||
cAo3 = ao1;
|
||||
cLight3 = light1;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[3]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo3 = ao(searchPos, searchState);
|
||||
cLight3 = light(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;
|
||||
searchPos.set(pos, lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
|
||||
if (isOnBlockFace || !searchState.isOpaqueFullCube(world, searchPos)) {
|
||||
lightCenter = light(searchPos, searchState);
|
||||
} else {
|
||||
lightCenter = light(pos, blockState);
|
||||
}
|
||||
|
||||
float aoCenter = ao(lightPos, world.getBlockState(lightPos));
|
||||
float worldBrightness = world.getBrightness(lightFace, shade);
|
||||
|
||||
result.a0 = ((ao3 + ao0 + cAo1 + aoCenter) * 0.25F) * worldBrightness;
|
||||
result.a1 = ((ao2 + ao0 + cAo0 + aoCenter) * 0.25F) * worldBrightness;
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,6 +38,7 @@ import net.minecraft.util.math.Direction;
|
|||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl.Value;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.NormalHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.TextureHelper;
|
||||
|
@ -116,9 +117,13 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
|
|||
data[baseIndex + HEADER_BITS] = EncodingFormat.cullFace(0, cullFace);
|
||||
nominalFace(quad.getFace());
|
||||
colorIndex(quad.getColorIndex());
|
||||
|
||||
if (!quad.hasShade()) {
|
||||
material = RenderMaterialImpl.setDisableDiffuse((Value) material, 0, true);
|
||||
}
|
||||
|
||||
material(material);
|
||||
tag(0);
|
||||
shade(quad.hasShade());
|
||||
isGeometryInvalid = true;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.mesh;
|
|||
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_BITS;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_COLOR_INDEX;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_STRIDE;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_TAG;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.QUAD_STRIDE;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.VERTEX_COLOR;
|
||||
|
@ -52,7 +51,6 @@ public class QuadViewImpl implements QuadView {
|
|||
/** True when geometry flags or light face may not match geometry. */
|
||||
protected boolean isGeometryInvalid = true;
|
||||
protected final Vec3f faceNormal = new Vec3f();
|
||||
private boolean shade = true;
|
||||
|
||||
/** Size and where it comes from will vary in subtypes. But in all cases quad is fully encoded to array. */
|
||||
protected int[] data;
|
||||
|
@ -268,15 +266,7 @@ public class QuadViewImpl implements QuadView {
|
|||
return Float.intBitsToFloat(data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_V]);
|
||||
}
|
||||
|
||||
public int vertexStart() {
|
||||
return baseIndex + HEADER_STRIDE;
|
||||
}
|
||||
|
||||
public boolean hasShade() {
|
||||
return shade && !material().disableDiffuse(0);
|
||||
}
|
||||
|
||||
public void shade(boolean shade) {
|
||||
this.shade = shade;
|
||||
return !material().disableDiffuse(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform;
|
|||
import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||
|
||||
/**
|
||||
|
@ -234,9 +233,9 @@ public abstract class AbstractQuadRenderer {
|
|||
* even for un-shaded quads. These are also applied with AO shading but that is done in AO calculator.
|
||||
*/
|
||||
private void shadeFlatQuad(MutableQuadViewImpl quad) {
|
||||
if ((quad.geometryFlags() & GeometryHelper.AXIS_ALIGNED_FLAG) == 0 || quad.hasVertexNormals()) {
|
||||
// Quads that aren't direction-aligned or that have vertex normals need to be shaded
|
||||
// using interpolation - vanilla can't handle them. Generally only applies to modded models.
|
||||
if (quad.hasVertexNormals()) {
|
||||
// Quads that have vertex normals need to be shaded using interpolation - vanilla can't
|
||||
// handle them. Generally only applies to modded models.
|
||||
final float faceShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade());
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.util.function.Consumer;
|
|||
import java.util.function.Supplier;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
|
@ -43,10 +42,20 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoLuminanceFix;
|
|||
*/
|
||||
public class BlockRenderContext extends AbstractRenderContext {
|
||||
private final BlockRenderInfo blockInfo = new BlockRenderInfo();
|
||||
private final AoCalculator aoCalc = new AoCalculator(blockInfo, this::brightness, this::aoLevel);
|
||||
|
||||
private final AoCalculator aoCalc = new AoCalculator(blockInfo) {
|
||||
@Override
|
||||
public int light(BlockPos pos, BlockState state) {
|
||||
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, state, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float ao(BlockPos pos, BlockState state) {
|
||||
return AoLuminanceFix.INSTANCE.apply(blockInfo.blockView, pos, state);
|
||||
}
|
||||
};
|
||||
|
||||
private VertexConsumer bufferBuilder;
|
||||
private boolean didOutput = false;
|
||||
// These are kept as fields to avoid the heap allocation for a supplier.
|
||||
// BlockModelRenderer allows the caller to supply both the random object and seed.
|
||||
private Random random;
|
||||
|
@ -94,25 +103,11 @@ public class BlockRenderContext extends AbstractRenderContext {
|
|||
}
|
||||
};
|
||||
|
||||
private int brightness(BlockPos pos, BlockState state) {
|
||||
if (blockInfo.blockView == null) {
|
||||
return LightmapTextureManager.MAX_LIGHT_COORDINATE;
|
||||
}
|
||||
|
||||
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, state, pos);
|
||||
}
|
||||
|
||||
private float aoLevel(BlockPos pos, BlockState state) {
|
||||
final BlockRenderView blockView = blockInfo.blockView;
|
||||
return blockView == null ? 1f : AoLuminanceFix.INSTANCE.apply(blockView, pos, state);
|
||||
}
|
||||
|
||||
private VertexConsumer outputBuffer(RenderLayer renderLayer) {
|
||||
didOutput = true;
|
||||
return bufferBuilder;
|
||||
}
|
||||
|
||||
public boolean render(BlockRenderView blockView, BakedModel model, BlockState state, BlockPos pos, MatrixStack matrixStack, VertexConsumer buffer, Random random, long seed, int overlay) {
|
||||
public void render(BlockRenderView blockView, BakedModel model, BlockState state, BlockPos pos, MatrixStack matrixStack, VertexConsumer buffer, boolean cull, Random random, long seed, int overlay) {
|
||||
this.bufferBuilder = buffer;
|
||||
this.matrix = matrixStack.peek().getPositionMatrix();
|
||||
this.normalMatrix = matrixStack.peek().getNormalMatrix();
|
||||
|
@ -120,9 +115,8 @@ public class BlockRenderContext extends AbstractRenderContext {
|
|||
this.seed = seed;
|
||||
|
||||
this.overlay = overlay;
|
||||
this.didOutput = false;
|
||||
aoCalc.clear();
|
||||
blockInfo.setBlockView(blockView);
|
||||
blockInfo.prepareForWorld(blockView, cull);
|
||||
blockInfo.prepareForBlock(state, pos, model.useAmbientOcclusion());
|
||||
|
||||
((FabricBakedModel) model).emitBlockQuads(blockView, state, pos, randomSupplier, this);
|
||||
|
@ -131,8 +125,6 @@ public class BlockRenderContext extends AbstractRenderContext {
|
|||
this.bufferBuilder = null;
|
||||
this.random = null;
|
||||
this.seed = seed;
|
||||
|
||||
return didOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,9 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.render;
|
|||
import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
|
@ -39,7 +42,8 @@ import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
|||
*/
|
||||
public class BlockRenderInfo {
|
||||
private final BlockColors blockColorMap = MinecraftClient.getInstance().getBlockColors();
|
||||
public final Random random = new Random();
|
||||
private final BlockPos.Mutable searchPos = new BlockPos.Mutable();
|
||||
private final Random random = new Random();
|
||||
public BlockRenderView blockView;
|
||||
public BlockPos blockPos;
|
||||
public BlockState blockState;
|
||||
|
@ -47,6 +51,10 @@ public class BlockRenderInfo {
|
|||
boolean defaultAo;
|
||||
RenderLayer defaultLayer;
|
||||
|
||||
private boolean enableCulling;
|
||||
private int cullCompletionFlags;
|
||||
private int cullResultFlags;
|
||||
|
||||
public final Supplier<Random> randomSupplier = () -> {
|
||||
final Random result = random;
|
||||
long seed = this.seed;
|
||||
|
@ -60,18 +68,22 @@ public class BlockRenderInfo {
|
|||
return result;
|
||||
};
|
||||
|
||||
public void setBlockView(BlockRenderView blockView) {
|
||||
public void prepareForWorld(BlockRenderView blockView, boolean enableCulling) {
|
||||
this.blockView = blockView;
|
||||
this.enableCulling = enableCulling;
|
||||
}
|
||||
|
||||
public void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAO) {
|
||||
this.blockPos = blockPos;
|
||||
this.blockState = blockState;
|
||||
// in the unlikely case seed actually matches this, we'll simply retrieve it more than one
|
||||
// in the unlikely case seed actually matches this, we'll simply retrieve it more than once
|
||||
seed = -1L;
|
||||
defaultAo = modelAO && MinecraftClient.isAmbientOcclusionEnabled() && blockState.getLuminance() == 0;
|
||||
|
||||
defaultLayer = RenderLayers.getBlockLayer(blockState);
|
||||
|
||||
cullCompletionFlags = 0;
|
||||
cullResultFlags = 0;
|
||||
}
|
||||
|
||||
public void release() {
|
||||
|
@ -83,8 +95,25 @@ public class BlockRenderInfo {
|
|||
return 0xFF000000 | blockColorMap.getColor(blockState, blockView, blockPos, colorIndex);
|
||||
}
|
||||
|
||||
boolean shouldDrawFace(Direction face) {
|
||||
return true;
|
||||
boolean shouldDrawFace(@Nullable Direction face) {
|
||||
if (face == null || !enableCulling) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final int mask = 1 << face.getId();
|
||||
|
||||
if ((cullCompletionFlags & mask) == 0) {
|
||||
cullCompletionFlags |= mask;
|
||||
|
||||
if (Block.shouldDrawSide(blockState, blockView, blockPos, face, searchPos.set(blockPos, face))) {
|
||||
cullResultFlags |= mask;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return (cullResultFlags & mask) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
RenderLayer effectiveRenderLayer(BlendMode blendMode) {
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.client.indigo.renderer.render;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
public class TerrainBlockRenderInfo extends BlockRenderInfo {
|
||||
private int cullCompletionFlags;
|
||||
private int cullResultFlags;
|
||||
|
||||
@Override
|
||||
public void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAO) {
|
||||
super.prepareForBlock(blockState, blockPos, modelAO);
|
||||
cullCompletionFlags = 0;
|
||||
cullResultFlags = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean shouldDrawFace(Direction face) {
|
||||
if (face == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final int mask = 1 << face.getId();
|
||||
|
||||
if ((cullCompletionFlags & mask) == 0) {
|
||||
cullCompletionFlags |= mask;
|
||||
|
||||
if (Block.shouldDrawSide(blockState, blockView, blockPos, face, blockPos.offset(face))) {
|
||||
cullResultFlags |= mask;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return (cullResultFlags & mask) != 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.render;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ import java.util.function.Consumer;
|
|||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
|
||||
import net.minecraft.client.render.chunk.ChunkBuilder;
|
||||
import net.minecraft.client.render.chunk.ChunkBuilder.BuiltChunk;
|
||||
import net.minecraft.client.render.chunk.ChunkBuilder.ChunkData;
|
||||
import net.minecraft.client.render.chunk.ChunkRendererRegion;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
|
@ -46,9 +46,19 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
|
|||
public class TerrainRenderContext extends AbstractRenderContext {
|
||||
public static final ThreadLocal<TerrainRenderContext> POOL = ThreadLocal.withInitial(TerrainRenderContext::new);
|
||||
|
||||
private final TerrainBlockRenderInfo blockInfo = new TerrainBlockRenderInfo();
|
||||
private final BlockRenderInfo blockInfo = new BlockRenderInfo();
|
||||
private final ChunkRenderInfo chunkInfo = new ChunkRenderInfo();
|
||||
private final AoCalculator aoCalc = new AoCalculator(blockInfo, chunkInfo::cachedBrightness, chunkInfo::cachedAoLevel);
|
||||
private final AoCalculator aoCalc = new AoCalculator(blockInfo) {
|
||||
@Override
|
||||
public int light(BlockPos pos, BlockState state) {
|
||||
return chunkInfo.cachedBrightness(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float ao(BlockPos pos, BlockState state) {
|
||||
return chunkInfo.cachedAoLevel(pos, state);
|
||||
}
|
||||
};
|
||||
|
||||
private final AbstractMeshConsumer meshConsumer = new AbstractMeshConsumer(blockInfo, chunkInfo::getInitializedBuffer, aoCalc, this::transform) {
|
||||
@Override
|
||||
|
@ -84,8 +94,8 @@ public class TerrainRenderContext extends AbstractRenderContext {
|
|||
}
|
||||
};
|
||||
|
||||
public void prepare(ChunkRendererRegion blockView, BuiltChunk chunkRenderer, ChunkData chunkData, BlockBufferBuilderStorage builders) {
|
||||
blockInfo.setBlockView(blockView);
|
||||
public void prepare(ChunkRendererRegion blockView, BuiltChunk chunkRenderer, ChunkBuilder.ChunkData chunkData, BlockBufferBuilderStorage builders) {
|
||||
blockInfo.prepareForWorld(blockView, true);
|
||||
chunkInfo.prepare(blockView, chunkRenderer, chunkData, builders);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,11 +50,11 @@ public abstract class MixinBlockModelRenderer implements AccessBlockModelRendere
|
|||
protected abstract void getQuadDimensions(BlockRenderView blockView, BlockState blockState, BlockPos blockPos, int[] vertexData, Direction face, float[] aoData, BitSet controlBits);
|
||||
|
||||
@Inject(at = @At("HEAD"), method = "render(Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/client/render/model/BakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;ZLjava/util/Random;JI)Z", cancellable = true)
|
||||
private void hookRender(BlockRenderView blockView, BakedModel model, BlockState state, BlockPos pos, MatrixStack matrix, VertexConsumer buffer, boolean checkSides, Random rand, long seed, int overlay, CallbackInfoReturnable<Boolean> ci) {
|
||||
private void hookRender(BlockRenderView blockView, BakedModel model, BlockState state, BlockPos pos, MatrixStack matrix, VertexConsumer buffer, boolean cull, Random rand, long seed, int overlay, CallbackInfoReturnable<Boolean> ci) {
|
||||
if (!((FabricBakedModel) model).isVanillaAdapter()) {
|
||||
BlockRenderContext context = fabric_contexts.get();
|
||||
// Note that we do not support face-culling here (so checkSides is ignored)
|
||||
ci.setReturnValue(context.render(blockView, model, state, pos, matrix, buffer, rand, seed, overlay));
|
||||
context.render(blockView, model, state, pos, matrix, buffer, cull, rand, seed, overlay);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue