Various Indigo Improvements (#295)

- Fix #199 Vanilla Bug: Glowstone creates AO shade
- Fix #289 Rendering on the Cutout and Transparent Layers together interferes with water rendering
- Fix #290 Smooth lighting breaks on continuous angled surfaces
- Fix #291 QuadEmitter not cleared on MeshBuilder.build()
- Fix #292 Render context QuadEmitter crash with flat lighting
- Fix #293 Relax vertex order requirements for modded quads
- Fix swapped color components for grass and other blocks
- Add option to partially support non-standard vertex formats
This commit is contained in:
grondag 2019-07-07 23:14:07 +02:00 committed by asie
parent 6519cfb41e
commit 099c1e8b8c
19 changed files with 251 additions and 286 deletions

View file

@ -1,5 +1,5 @@
archivesBaseName = "fabric-renderer-api-v1"
version = getSubprojectVersion(project, "0.1.0")
version = getSubprojectVersion(project, "0.1.1")
dependencies {
compile project(path: ':fabric-api-base', configuration: 'dev')

View file

@ -1,5 +1,5 @@
archivesBaseName = "fabric-renderer-indigo"
version = getSubprojectVersion(project, "0.1.7")
version = getSubprojectVersion(project, "0.1.8")
dependencies {
compile project(path: ':fabric-api-base', configuration: 'dev')

View file

@ -34,13 +34,15 @@ import java.util.Properties;
public class Indigo implements ClientModInitializer {
public static final boolean ALWAYS_TESSELATE_INDIGO;
public static final boolean ENSURE_VERTEX_FORMAT_COMPATIBILITY;
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;
public static final boolean FIX_EXTERIOR_VERTEX_LIGHTING;
public static final boolean FIX_LUMINOUS_AO_SHADE;
private static final Logger LOGGER = LogManager.getLogger();
public static final Logger LOGGER = LogManager.getLogger();
private static boolean asBoolean(String property, boolean defValue) {
switch (asTriState(property)) {
@ -53,6 +55,7 @@ public class Indigo implements ClientModInitializer {
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static <T extends Enum> T asEnum(String property, T defValue) {
if (property == null || property.isEmpty()) {
return defValue;
@ -102,11 +105,15 @@ public class Indigo implements ClientModInitializer {
}
}
ALWAYS_TESSELATE_INDIGO = asBoolean((String) properties.computeIfAbsent("always-tesselate-blocks", (a) -> "auto"), true);
final boolean forceCompatibility = IndigoMixinConfigPlugin.shouldForceCompatibility();
ENSURE_VERTEX_FORMAT_COMPATIBILITY = forceCompatibility;
// necessary because OF alters the BakedModel vertex format and will confuse the fallback model consumer
ALWAYS_TESSELATE_INDIGO = !forceCompatibility && asBoolean((String) properties.computeIfAbsent("always-tesselate-blocks", (a) -> "auto"), true);
AMBIENT_OCCLUSION_MODE = asEnum((String) properties.computeIfAbsent("ambient-occlusion-mode", (a) -> "hybrid"), AoConfig.HYBRID);
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);
FIX_EXTERIOR_VERTEX_LIGHTING = asBoolean((String) properties.computeIfAbsent("fix-exterior-vertex-lighting", (a) -> "auto"), true);
FIX_LUMINOUS_AO_SHADE = asBoolean((String) properties.computeIfAbsent("fix-luminous-block-ambient-occlusion", (a) -> "auto"), false);
try (FileOutputStream stream = new FileOutputStream(configFile)) {
properties.store(stream, "Indigo properties file");
@ -119,6 +126,9 @@ public class Indigo implements ClientModInitializer {
public void onInitializeClient() {
if (IndigoMixinConfigPlugin.shouldApplyIndigo()) {
LOGGER.info("[Indigo] Registering Indigo renderer!");
if(IndigoMixinConfigPlugin.shouldForceCompatibility()) {
LOGGER.info("[Indigo] Compatibility mode enabled.");
}
RendererAccess.INSTANCE.registerRenderer(IndigoRenderer.INSTANCE);
} else {
LOGGER.info("[Indigo] Different rendering plugin detected; not applying Indigo.");

View file

@ -18,6 +18,8 @@ package net.fabricmc.indigo;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.ModMetadata;
import org.spongepowered.asm.lib.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
@ -26,21 +28,37 @@ import java.util.List;
import java.util.Set;
public class IndigoMixinConfigPlugin implements IMixinConfigPlugin {
private static final String JSON_ELEMENT = "fabric-renderer-api-v1:contains_renderer";
private static Boolean indigoApplicable;
/** Set by other renderers to disable loading of Indigo */
private static final String JSON_KEY_DISABLE_INDIGO = "fabric-renderer-api-v1:contains_renderer";
/** Disables vanilla block tesselation and ensures vertex format compatibility */
private static final String JSON_KEY_FORCE_COMPATIBILITY = "fabric-renderer-indigo:force_compatibility";
static boolean shouldApplyIndigo() {
if (indigoApplicable != null) return indigoApplicable;
private static boolean needsLoad = true;
private static boolean indigoApplicable = true;
private static boolean forceCompatibility = false;
private static void loadIfNeeded() {
if(needsLoad) {
for (ModContainer container : FabricLoader.getInstance().getAllMods()) {
if (container.getMetadata().containsCustomElement(JSON_ELEMENT)) {
final ModMetadata meta = container.getMetadata();
if (meta.containsCustomElement(JSON_KEY_DISABLE_INDIGO)) {
indigoApplicable = false;
return false;
} else if (meta.containsCustomElement(JSON_KEY_FORCE_COMPATIBILITY)) {
forceCompatibility = true;
}
}
needsLoad = false;
}
}
static boolean shouldApplyIndigo() {
loadIfNeeded();
return indigoApplicable;
}
indigoApplicable = true;
return true;
static boolean shouldForceCompatibility() {
loadIfNeeded();
return forceCompatibility;
}
@Override

View file

@ -16,6 +16,8 @@
package net.fabricmc.indigo.renderer.accessor;
import net.fabricmc.indigo.renderer.mesh.QuadViewImpl;
public interface AccessBufferBuilder {
void fabric_putVanillaData(int[] data, int start, boolean isItemFormat);
void fabric_putQuad(QuadViewImpl quad);
}

View file

@ -112,31 +112,37 @@ public class AoCalculator {
public void compute(MutableQuadViewImpl quad, boolean isVanilla) {
final AoConfig config = Indigo.AMBIENT_OCCLUSION_MODE;
boolean shouldMatch = false;
final boolean shouldCompare;
switch(config) {
case VANILLA:
calcVanilla(quad);
// no point in comparing vanilla with itself
shouldCompare = false;
break;
case EMULATE:
calcFastVanilla(quad);
shouldMatch = Indigo.DEBUG_COMPARE_LIGHTING && isVanilla;
shouldCompare = Indigo.DEBUG_COMPARE_LIGHTING && isVanilla;
break;
case HYBRID:
if(isVanilla) {
calcFastVanilla(quad);
break;
}
// else fall through to enhanced
default:
case HYBRID:
if(isVanilla) {
shouldCompare = Indigo.DEBUG_COMPARE_LIGHTING;
calcFastVanilla(quad);
} else {
shouldCompare = false;
calcEnhanced(quad);
}
break;
case ENHANCED:
shouldMatch = calcEnhanced(quad);
shouldCompare = false;
calcEnhanced(quad);
}
if (shouldMatch) {
if (shouldCompare) {
float[] vanillaAo = new float[4];
int[] vanillaLight = new int[4];
@ -178,28 +184,21 @@ public class AoCalculator {
}
}
/** returns true if should match vanilla results */
private boolean calcEnhanced(MutableQuadViewImpl quad) {
private void calcEnhanced(MutableQuadViewImpl quad) {
switch(quad.geometryFlags()) {
case AXIS_ALIGNED_FLAG | CUBIC_FLAG | LIGHT_FACE_FLAG:
vanillaFullFace(quad, true);
return Indigo.DEBUG_COMPARE_LIGHTING;
case AXIS_ALIGNED_FLAG | LIGHT_FACE_FLAG:
vanillaPartialFace(quad, true);
return Indigo.DEBUG_COMPARE_LIGHTING;
break;
case AXIS_ALIGNED_FLAG | CUBIC_FLAG:
blendedFullFace(quad);
return false;
case AXIS_ALIGNED_FLAG:
blendedPartialFace(quad);
return false;
break;
default:
irregularFace(quad);
return false;
break;
}
}
@ -246,11 +245,6 @@ public class AoCalculator {
}
}
private void blendedFullFace(QuadViewImpl quad) {
final Direction lightFace = quad.lightFace();
blendedInsetFace(quad, 0, lightFace).toArray(ao, light, VERTEX_MAP[lightFace.getId()]);
}
private void blendedPartialFace(QuadViewImpl quad) {
final Direction lightFace = quad.lightFace();
AoFaceData faceData = blendedInsetFace(quad, 0, lightFace);
@ -283,12 +277,15 @@ public class AoCalculator {
final AoFaceData fd = gatherInsetFace(quad, i, face);
AoFace.get(face).weightFunc.apply(quad, i, w);
final float n = x * x;
ao += n * fd.weigtedAo(w);
sky += n * fd.weigtedSkyLight(w);
block += n * fd.weigtedBlockLight(w);
maxAo = fd.maxAo(maxAo);
maxSky = fd.maxSkyLight(maxSky);
maxBlock = fd.maxBlockLight(maxBlock);
final float a = fd.weigtedAo(w);
final int s = fd.weigtedSkyLight(w);
final int b = fd.weigtedBlockLight(w);
ao += n * a;
sky += n * s;
block += n * b;
maxAo = a;
maxSky = s;
maxBlock = b;
}
final float y = normal.y();
@ -297,12 +294,15 @@ public class AoCalculator {
final AoFaceData fd = gatherInsetFace(quad, i, face);
AoFace.get(face).weightFunc.apply(quad, i, w);
final float n = y * y;
ao += n * fd.weigtedAo(w);
sky += n * fd.weigtedSkyLight(w);
block += n * fd.weigtedBlockLight(w);
maxAo = fd.maxAo(maxAo);
maxSky = fd.maxSkyLight(maxSky);
maxBlock = fd.maxBlockLight(maxBlock);
final float a = fd.weigtedAo(w);
final int s = fd.weigtedSkyLight(w);
final int b = fd.weigtedBlockLight(w);
ao += n * a;
sky += n * s;
block += n * b;
maxAo = Math.max(maxAo, a);
maxSky = Math.max(maxSky, s);
maxBlock = Math.max(maxBlock, b);
}
final float z = normal.z();
@ -311,12 +311,15 @@ public class AoCalculator {
final AoFaceData fd = gatherInsetFace(quad, i, face);
AoFace.get(face).weightFunc.apply(quad, i, w);
final float n = z * z;
ao += n * fd.weigtedAo(w);
sky += n * fd.weigtedSkyLight(w);
block += n * fd.weigtedBlockLight(w);
maxAo = fd.maxAo(maxAo);
maxSky = fd.maxSkyLight(maxSky);
maxBlock = fd.maxBlockLight(maxBlock);
final float a = fd.weigtedAo(w);
final int s = fd.weigtedSkyLight(w);
final int b = fd.weigtedBlockLight(w);
ao += n * a;
sky += n * s;
block += n * b;
maxAo = Math.max(maxAo, a);
maxSky = Math.max(maxSky, s);
maxBlock = Math.max(maxBlock, b);
}
aoResult[i] = (ao + maxAo) * 0.5f;

View file

@ -36,8 +36,7 @@ public enum AoConfig {
* aligned quads not on the block face will have interpolated brightness based
* on depth instead of the all-or-nothing brightness of vanilla.<p>
*
* Unit (full face) quads must still have the vanilla fixed winding order but smaller
* quads can have vertices in any (counter-clockwise) order.<p>
* Non-vanilla quads can have vertices in any (counter-clockwise) order.<p>
*/
ENHANCED,

View file

@ -58,22 +58,10 @@ class AoFaceData {
return (int) (b0 * w[0] + b1 * w[1] + b2 * w[2] + b3 * w[3]) & 0xFF;
}
int maxBlockLight(int oldMax) {
final int i = b0 > b1 ? b0 : b1;
final int j = b2 > b3 ? b2 : b3;
return Math.max(oldMax, i > j ? i : j);
}
int weigtedSkyLight(float[] w) {
return (int) (s0 * w[0] + s1 * w[1] + s2 * w[2] + s3 * w[3]) & 0xFF;
}
int maxSkyLight(int oldMax) {
final int i = s0 > s1 ? s0 : s1;
final int j = s2 > s3 ? s2 : s3;
return Math.max(oldMax, i > j ? i : j);
}
int weightedCombinedLight(float[] w) {
return weigtedSkyLight(w) << 16 | weigtedBlockLight(w);
}
@ -82,13 +70,6 @@ class AoFaceData {
return a0 * w[0] + a1 * w[1] + a2 * w[2] + a3 * w[3];
}
float maxAo(float oldMax) {
final float x = a0 > a1 ? a0 : a1;
final float y = a2 > a3 ? a2 : a3;
final float z = x > y ? x : y;
return oldMax > z ? oldMax : z;
}
void toArray(float[] aOut, int[] bOut, int[] vertexMap) {
aOut[vertexMap[0]] = a0;
aOut[vertexMap[1]] = a1;

View file

@ -0,0 +1,42 @@
/*
* 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.indigo.renderer.aocalc;
import net.fabricmc.indigo.Indigo;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockView;
/**
* Implements a fix to prevent luminous blocks from casting AO shade.
* Will give normal result if fix is disabled.
*/
@FunctionalInterface
public interface AoLuminanceFix {
float apply(BlockView view, BlockPos pos);
AoLuminanceFix INSTANCE = Indigo.FIX_LUMINOUS_AO_SHADE ? AoLuminanceFix::fixed : AoLuminanceFix::vanilla;
static float vanilla(BlockView view, BlockPos pos) {
return view.getBlockState(pos).getAmbientOcclusionLightLevel(view, pos);
}
static float fixed(BlockView view, BlockPos pos) {
final BlockState state = view.getBlockState(pos);
return state.getLuminance() == 0 ? state.getAmbientOcclusionLightLevel(view, pos) : 1f;
}
}

View file

@ -1,120 +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.indigo.renderer.helper;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormatElement;
import net.minecraft.client.render.VertexFormats;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class BufferBuilderTransformHelper {
/**
* Fast copying mode; used only if the vanilla format is an exact match.
*/
public static final int MODE_COPY_FAST = 0;
/**
* Padded copying mode; used when the vanilla format is an exact match,
* but includes additional data at the end. Will emit a warning.
*/
public static final int MODE_COPY_PADDED = 1;
/**
* ShadersMod compatibility mode; as MODE_COPY_PADDED, but populates in
* the correct normal values as provided by the mod.
*
* Assumes a format of [vertex, color, texture, lmap, normal], all in
* their respective vanilla formats, plus any amount of additional data
* afterwards.
*/
public static final int MODE_COPY_PADDED_SHADERSMOD = 2;
/**
* Unsupported mode; an error will be emitted and no quads will be
* pushed to the buffer builder.
*/
public static final int MODE_UNSUPPORTED = 3;
private static final Map<VertexFormat, Integer> vertexFormatCache = new ConcurrentHashMap<>();
private static final Set<VertexFormat> errorEmittedFormats = Sets.newConcurrentHashSet();
private static final Logger logger = LogManager.getLogger();
public static void emitUnsupportedError(VertexFormat format) {
// This can be slow, as it's only called on unsupported formats - which is already an error condition.
if (errorEmittedFormats.add(format)) {
logger.error("[Indigo] Unsupported vertex format! " + format);
}
}
private static int computeProcessingMode(VertexFormat f) {
if (
f.getElementCount() >= 4 && f.getVertexSizeInteger() >= 7
&& f.getElement(0).equals(VertexFormats.POSITION_ELEMENT)
&& f.getElement(1).equals(VertexFormats.COLOR_ELEMENT)
&& f.getElement(2).equals(VertexFormats.UV_ELEMENT)
) {
if (
f.getElement(3).equals(VertexFormats.LMAP_ELEMENT)
|| f.getElement(3).equals(VertexFormats.NORMAL_ELEMENT)
) {
if (
f.getElementCount() >= 5
&& f.getElement(3).equals(VertexFormats.LMAP_ELEMENT)
&& f.getElement(4).equals(VertexFormats.NORMAL_ELEMENT)
) {
logger.debug("[Indigo] Classified format as ShadersMod-compatible: " + f);
return MODE_COPY_PADDED_SHADERSMOD;
} else if (f.getElementCount() == 4) {
logger.debug("[Indigo] Classified format as vanilla-like: " + f);
return MODE_COPY_FAST;
} else {
logger.debug("[Indigo] Unsupported but likely vanilla-compliant vertex format. " + f);
return MODE_COPY_PADDED;
}
}
}
return MODE_UNSUPPORTED;
}
public static int getProcessingMode(VertexFormat format) {
// Fast passthrough for the most common vanilla block/item formats.
if (format == VertexFormats.POSITION_COLOR_UV_LMAP || format == VertexFormats.POSITION_COLOR_UV_NORMAL) {
return MODE_COPY_FAST;
} else {
Integer cached = vertexFormatCache.get(format);
if (cached == null) {
// VertexFormats are mutable, so we need to make an immutable copy.
format = new VertexFormat(format);
cached = computeProcessingMode(format);
vertexFormatCache.put(format, cached);
}
return cached;
}
}
}

View file

@ -50,6 +50,7 @@ public class MeshBuilderImpl implements MeshBuilder {
int[] packed = new int[index];
System.arraycopy(data, 0, packed, 0, index);
index = 0;
maker.begin(data, index);
return new MeshImpl(packed);
}

View file

@ -18,18 +18,15 @@ package net.fabricmc.indigo.renderer.mixin;
import java.nio.IntBuffer;
import net.fabricmc.indigo.Indigo;
import net.fabricmc.indigo.renderer.helper.BufferBuilderTransformHelper;
import net.fabricmc.indigo.renderer.mesh.EncodingFormat;
import net.minecraft.client.render.VertexFormat;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import net.fabricmc.indigo.Indigo;
import net.fabricmc.indigo.renderer.accessor.AccessBufferBuilder;
import net.fabricmc.indigo.renderer.mesh.QuadViewImpl;
import net.minecraft.client.render.BufferBuilder;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormatElement;
@Mixin(BufferBuilder.class)
public abstract class MixinBufferBuilder implements AccessBufferBuilder {
@ -40,73 +37,71 @@ public abstract class MixinBufferBuilder implements AccessBufferBuilder {
@Shadow public abstract VertexFormat getVertexFormat();
private static final int VERTEX_STRIDE_INTS = 7;
private static final int VERTEX_STRIDE_BYTES = VERTEX_STRIDE_INTS * 4;
private static final int QUAD_STRIDE_INTS = VERTEX_STRIDE_INTS * 4;
private static final int QUAD_STRIDE_BYTES = QUAD_STRIDE_INTS * 4;
private int fabric_processingMode;
@Override
public void fabric_putQuad(QuadViewImpl quad) {
if(Indigo.ENSURE_VERTEX_FORMAT_COMPATIBILITY) {
bufferCompatibly(quad);
} else {
bufferFast(quad);
}
}
@Inject(at = @At("RETURN"), method = "begin")
private void afterBegin(int mode, VertexFormat passedFormat, CallbackInfo info) {
fabric_processingMode = BufferBuilderTransformHelper.getProcessingMode(getVertexFormat());
private void bufferFast(QuadViewImpl quad) {
grow(QUAD_STRIDE_BYTES);
bufInt.position(getCurrentSize());
bufInt.put(quad.data(), quad.vertexStart(), QUAD_STRIDE_INTS);
vertexCount += 4;
}
/**
* Similar to {@link BufferBuilder#putVertexData(int[])} but
* accepts an array index so that arrays containing more than one
* quad don't have to be copied to a transfer array before the call.
* Uses buffer vertex format to drive buffer population.
* Relies on logic elsewhere to ensure coordinates don't include chunk offset
* (because buffer builder will handle that.)<p>
*
* It also always assumes the vanilla data format and is capable of
* transforming data from it to a different, non-vanilla data format.
* Calling putVertexData() would likely be a little faster but this approach
* gives us a chance to pass vertex normals to shaders, which isn't possible
* with the standard block format. It also doesn't require us to encode a specific
* custom format directly, which would be prone to breakage outside our control.
*/
@Override
public void fabric_putVanillaData(int[] data, int start, boolean isItemFormat) {
switch (fabric_processingMode) {
case BufferBuilderTransformHelper.MODE_COPY_FAST: {
this.grow(QUAD_STRIDE_BYTES);
this.bufInt.position(this.getCurrentSize());
this.bufInt.put(data, start, QUAD_STRIDE_INTS);
} break;
case BufferBuilderTransformHelper.MODE_COPY_PADDED: {
int currSize = this.getCurrentSize();
int formatSizeBytes = getVertexFormat().getVertexSize();
int formatSizeInts = formatSizeBytes / 4;
this.grow(formatSizeBytes * 4);
this.bufInt.position(currSize);
this.bufInt.put(data, start, VERTEX_STRIDE_INTS);
this.bufInt.position(currSize + formatSizeInts);
this.bufInt.put(data, start + 7, VERTEX_STRIDE_INTS);
this.bufInt.position(currSize + formatSizeInts * 2);
this.bufInt.put(data, start + 14, VERTEX_STRIDE_INTS);
this.bufInt.position(currSize + formatSizeInts * 3);
this.bufInt.put(data, start + 21, VERTEX_STRIDE_INTS);
} break;
case BufferBuilderTransformHelper.MODE_COPY_PADDED_SHADERSMOD: {
int currSize = this.getCurrentSize();
int formatSizeBytes = getVertexFormat().getVertexSize();
int formatSizeInts = formatSizeBytes / 4;
this.grow(formatSizeBytes * 4);
this.bufInt.position(currSize);
this.bufInt.put(data, start, VERTEX_STRIDE_INTS);
this.bufInt.put(data[start + EncodingFormat.NORMALS_OFFSET_VANILLA]);
this.bufInt.position(currSize + formatSizeInts);
this.bufInt.put(data, start + 7, VERTEX_STRIDE_INTS);
this.bufInt.put(data[start + EncodingFormat.NORMALS_OFFSET_VANILLA + 1]);
this.bufInt.position(currSize + formatSizeInts * 2);
this.bufInt.put(data, start + 14, VERTEX_STRIDE_INTS);
this.bufInt.put(data[start + EncodingFormat.NORMALS_OFFSET_VANILLA + 2]);
this.bufInt.position(currSize + formatSizeInts * 3);
this.bufInt.put(data, start + 21, VERTEX_STRIDE_INTS);
this.bufInt.put(data[start + EncodingFormat.NORMALS_OFFSET_VANILLA + 3]);
} break;
case BufferBuilderTransformHelper.MODE_UNSUPPORTED:
// Don't emit any quads.
BufferBuilderTransformHelper.emitUnsupportedError(getVertexFormat());
return;
private void bufferCompatibly(QuadViewImpl quad) {
final VertexFormat format = getVertexFormat();;
final int elementCount = format.getElementCount();
for(int i = 0; i < 4; i++) {
for(int j = 0; j < elementCount; j++) {
VertexFormatElement e = format.getElement(j);
switch(e.getType()) {
case COLOR:
final int c = quad.spriteColor(i, 0);
((BufferBuilder)(Object)this).color(c & 0xFF, (c >>> 8) & 0xFF, (c >>> 16) & 0xFF, (c >>> 24) & 0xFF);
break;
case NORMAL:
((BufferBuilder)(Object)this).normal(quad.normalX(i), quad.normalY(i), quad.normalZ(i));
break;
case POSITION:
((BufferBuilder)(Object)this).vertex(quad.x(i), quad.y(i), quad.z(i));
break;
case UV:
if(e.getIndex() == 0) {
((BufferBuilder)(Object)this).texture(quad.spriteU(i, 0), quad.spriteV(i, 0));
} else {
final int b = quad.lightmap(i);
((BufferBuilder)(Object)this).texture((b >> 16) & 0xFFFF, b & 0xFFFF);
}
break;
// these types should never occur and/or require no action
case MATRIX:
case BLEND_WEIGHT:
case PADDING:
default:
break;
this.vertexCount += 4;
}
}
((BufferBuilder)(Object)this).next();
}
}
}

View file

@ -28,6 +28,8 @@ import net.fabricmc.indigo.renderer.RenderMaterialImpl.Value;
import net.fabricmc.indigo.renderer.IndigoRenderer;
import net.fabricmc.indigo.renderer.accessor.AccessBufferBuilder;
import net.fabricmc.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.indigo.renderer.helper.ColorHelper;
import net.fabricmc.indigo.renderer.helper.GeometryHelper;
import net.fabricmc.indigo.renderer.mesh.EncodingFormat;
import net.fabricmc.indigo.renderer.mesh.MeshImpl;
import net.fabricmc.indigo.renderer.mesh.MutableQuadViewImpl;
@ -57,6 +59,8 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
// only used via RenderContext.getEmitter()
@Override
public Maker emit() {
lightFace = GeometryHelper.lightFace(this);
ColorHelper.applyDiffuseShading(this, false);
renderQuad(this);
clear();
return this;

View file

@ -69,7 +69,7 @@ public abstract class AbstractQuadRenderer {
/** final output step, common to all renders */
private void bufferQuad(MutableQuadViewImpl quad, int renderLayer) {
bufferFunc.get(renderLayer).fabric_putVanillaData(quad.data(), quad.vertexStart(), false);
bufferFunc.get(renderLayer).fabric_putQuad(quad);
}
// routines below have a bit of copy-paste code reuse to avoid conditional execution inside a hot loop

View file

@ -27,6 +27,7 @@ import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.indigo.renderer.accessor.AccessBufferBuilder;
import net.fabricmc.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.indigo.renderer.aocalc.AoLuminanceFix;
import net.fabricmc.indigo.renderer.mesh.MutableQuadViewImpl;
import net.fabricmc.indigo.renderer.mixin.BufferBuilderOffsetAccessor;
import net.minecraft.block.BlockState;
@ -67,10 +68,7 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC
private float aoLevel(BlockPos pos) {
final ExtendedBlockView blockView = blockInfo.blockView;
if(blockView == null) {
return 1f;
}
return blockView.getBlockState(pos).getAmbientOcclusionLightLevel(blockView, pos);
return blockView == null ? 1f : AoLuminanceFix.INSTANCE.apply(blockView, pos);
}
private AccessBufferBuilder outputBuffer(int renderLayer) {

View file

@ -18,8 +18,10 @@ package net.fabricmc.indigo.renderer.render;
import it.unimi.dsi.fastutil.longs.Long2FloatOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import net.fabricmc.indigo.Indigo;
import net.fabricmc.indigo.renderer.accessor.AccessBufferBuilder;
import net.fabricmc.indigo.renderer.accessor.AccessChunkRenderer;
import net.fabricmc.indigo.renderer.aocalc.AoLuminanceFix;
import net.fabricmc.indigo.renderer.mesh.MutableQuadViewImpl;
import net.minecraft.block.Block.OffsetType;
import net.minecraft.block.BlockRenderLayer;
@ -67,6 +69,7 @@ public class ChunkRenderInfo {
private final Long2FloatOpenHashMap aoLevelCache;
private final BlockRenderInfo blockInfo;
private final BlockPos.Mutable chunkOrigin = new BlockPos.Mutable();
ChunkRenderTask chunkTask;
ChunkRenderData chunkData;
ChunkRenderer chunkRenderer;
@ -102,6 +105,7 @@ public class ChunkRenderInfo {
}
void prepare(ChunkRenderer chunkRenderer, BlockPos.Mutable chunkOrigin, boolean [] resultFlags) {
this.chunkOrigin.set(chunkOrigin);
this.chunkData = chunkTask.getRenderData();
this.chunkRenderer = chunkRenderer;
this.resultFlags = resultFlags;
@ -129,9 +133,18 @@ public class ChunkRenderInfo {
void beginBlock() {
final BlockState blockState = blockInfo.blockState;
final BlockPos blockPos = blockInfo.blockPos;
// When we are using the BufferBuilder input methods, the builder will
// add the chunk offset for us, so we should only apply the block offset.
if(Indigo.ENSURE_VERTEX_FORMAT_COMPATIBILITY) {
offsetX = (float) (blockPos.getX());
offsetY = (float) (blockPos.getY());
offsetZ = (float) (blockPos.getZ());
} else {
offsetX = (float) (chunkOffsetX + blockPos.getX());
offsetY = (float) (chunkOffsetY + blockPos.getY());
offsetZ = (float) (chunkOffsetZ + blockPos.getZ());
}
if(blockState.getBlock().getOffsetType() != OffsetType.NONE) {
Vec3d offset = blockState.getOffsetPos(blockInfo.blockView, blockPos);
@ -153,7 +166,7 @@ public class ChunkRenderInfo {
BlockRenderLayer layer = LAYERS[layerIndex];
if (!chunkData.isBufferInitialized(layer)) {
chunkData.markBufferInitialized(layer); // start buffer
((AccessChunkRenderer) chunkRenderer).fabric_beginBufferBuilding(builder, blockInfo.blockPos);
((AccessChunkRenderer) chunkRenderer).fabric_beginBufferBuilding(builder, chunkOrigin);
}
result = (AccessBufferBuilder) builder;
}
@ -188,7 +201,7 @@ public class ChunkRenderInfo {
long key = pos.asLong();
float result = aoLevelCache.get(key);
if (result == Float.MAX_VALUE) {
result = blockView.getBlockState(pos).getAmbientOcclusionLightLevel(blockView, pos);
result = AoLuminanceFix.INSTANCE.apply(blockView, pos);
aoLevelCache.put(key, result);
}
return result;

View file

@ -33,6 +33,7 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.indigo.renderer.RenderMaterialImpl;
import net.fabricmc.indigo.renderer.accessor.AccessBufferBuilder;
import net.fabricmc.indigo.renderer.helper.ColorHelper;
import net.fabricmc.indigo.renderer.helper.GeometryHelper;
import net.fabricmc.indigo.renderer.mesh.EncodingFormat;
import net.fabricmc.indigo.renderer.mesh.MeshImpl;
import net.fabricmc.indigo.renderer.mesh.MutableQuadViewImpl;
@ -127,6 +128,8 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo
@Override
public Maker emit() {
lightFace = GeometryHelper.lightFace(this);
ColorHelper.applyDiffuseShading(this, false);
renderQuad();
clear();
return this;
@ -182,7 +185,7 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo
c = ColorHelper.multiplyColor(quadColor, c);
q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(c));
}
fabricBuffer.fabric_putVanillaData(quadData, EncodingFormat.VERTEX_START_OFFSET, true);
fabricBuffer.fabric_putQuad(q);
}
private void renderQuad() {

View file

@ -25,6 +25,7 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform;
import net.fabricmc.indigo.renderer.RenderMaterialImpl.Value;
import net.fabricmc.indigo.Indigo;
import net.fabricmc.indigo.renderer.IndigoRenderer;
import net.fabricmc.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.indigo.renderer.helper.GeometryHelper;
@ -58,6 +59,20 @@ public class TerrainFallbackConsumer extends AbstractQuadRenderer implements Con
private static Value MATERIAL_FLAT = (Value) IndigoRenderer.INSTANCE.materialFinder().disableAo(0, true).find();
private static Value MATERIAL_SHADED = (Value) IndigoRenderer.INSTANCE.materialFinder().find();
/**
* Controls 1x warning for vanilla quad vertex format when running in compatibility mode.
*/
private static boolean logCompatibilityWarning = true;
private static boolean isCompatible(int[] vertexData) {
final boolean result = vertexData.length == 28;
if(!result && logCompatibilityWarning) {
logCompatibilityWarning = false;
Indigo.LOGGER.warn("[Indigo] Encountered baked quad with non-standard vertex format. Some blocks will not be rendered");
}
return result;
}
private final int[] editorBuffer = new int[28];
private final ChunkRenderInfo chunkInfo;
@ -109,8 +124,13 @@ public class TerrainFallbackConsumer extends AbstractQuadRenderer implements Con
}
private void renderQuad(BakedQuad quad, Direction cullFace, Value defaultMaterial) {
final int[] vertexData = quad.getVertexData();
if(Indigo.ENSURE_VERTEX_FORMAT_COMPATIBILITY && !isCompatible(vertexData)) {
return;
}
final MutableQuadViewImpl editorQuad = this.editorQuad;
System.arraycopy(quad.getVertexData(), 0, editorBuffer, 0, 28);
System.arraycopy(vertexData, 0, editorBuffer, 0, 28);
editorQuad.cullFace(cullFace);
final Direction lightFace = quad.getFace();
editorQuad.lightFace(lightFace);

View file

@ -24,8 +24,6 @@ import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.indigo.renderer.aocalc.AoCalculator;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.block.BlockRenderManager;
import net.minecraft.client.render.chunk.ChunkRenderTask;
import net.minecraft.client.render.chunk.ChunkRenderer;
import net.minecraft.client.render.chunk.ChunkRendererRegion;
@ -34,7 +32,6 @@ import net.minecraft.util.crash.CrashException;
import net.minecraft.util.crash.CrashReport;
import net.minecraft.util.crash.CrashReportSection;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ExtendedBlockView;
/**
* Implementation of {@link RenderContext} used during terrain rendering.
@ -48,7 +45,6 @@ public class TerrainRenderContext extends AbstractRenderContext implements Rende
private final AoCalculator aoCalc = new AoCalculator(blockInfo, chunkInfo::cachedBrightness, chunkInfo::cachedAoLevel);
private final TerrainMeshConsumer meshConsumer = new TerrainMeshConsumer(blockInfo, chunkInfo, aoCalc, this::transform);
private final TerrainFallbackConsumer fallbackConsumer = new TerrainFallbackConsumer(blockInfo, chunkInfo, aoCalc, this::transform);
private final BlockRenderManager blockRenderManager = MinecraftClient.getInstance().getBlockRenderManager();
public void setBlockView(ChunkRendererRegion blockView) {
blockInfo.setBlockView(blockView);